Handling numerous speedwalks - and using a database
Posted by Jeffrey F. Pia on Fri 26 Jul 2002 10:59 PM — 58 posts, 194,019 views.
What is the best way to handle speedwalks? I have about 175 SWs to different area... not too bad, right? I could prolly just make aliases for em all... here's the kicker: I am collecting SWs to the various stationary mobs in each area too. Say there's an avg of 10 per area... probably more, but just for an example. What's the best way to store these SWs? I would probably want to call the SW by typing SW <area nickname> to get to the begining of the area, or SW <area nickname> <mob nickname>. Seems like I'd have to write a script containing an array... would I have to make an array for each area as well? Or would some type of 3 dimensional array would work? Thanks for any new ideas!
One fairly simple approach would be to store each one as a variable, replacing the space between the area and mob name with an underscore, and prefixing it with something. eg.
area = darkhaven, mob = storekeeper
variable = sw_darkhaven_storekeeper
MUSHclient internally looks up variables by name quite efficiently, so it should be fast to look for a particular one.
Then make an alias "SW *" (or maybe two, "SW *" and "SW * *" to detect the second case).
The alias could concatenate the wildcards appropriately and look up the speedwalk variable, eg.
sw = world.getvariable ("sw_" & wildcards (1) & "_" & wildcards (2))
Then evaluate and send it, eg.
world.send world.evaluatespeedwalk (sw)
area = darkhaven, mob = storekeeper
variable = sw_darkhaven_storekeeper
MUSHclient internally looks up variables by name quite efficiently, so it should be fast to look for a particular one.
Then make an alias "SW *" (or maybe two, "SW *" and "SW * *" to detect the second case).
The alias could concatenate the wildcards appropriately and look up the speedwalk variable, eg.
sw = world.getvariable ("sw_" & wildcards (1) & "_" & wildcards (2))
Then evaluate and send it, eg.
world.send world.evaluatespeedwalk (sw)
I like the idea... it's simple and easy, but I can see it's application being rather rigid and non flexible. I can also see making a listing of available areas as a chaotic mess of text parsing, or a second list which would need updating. What I have now is a table in Excel listing the Area Name, Mob Name, Room Name, and SW. What I *can* do is save this list in delimited text format. What I would *like* to do is keep that list separate from my main code, and only load it when I use my alias for SWs. I like the convenience of updating the information on a spreadsheet, and saving the list as a text file is quick and painless. Is there a way to load the file, similar to loading a separate script file, and have it populate an array? I can then loop through the array to find a match. I am not sure how processor intensive working with arrays is however. I was also thinking about creating a separate file for each area, to aid in updating and avoiding duplicating entries (a small list is easier to double check than a large list). Each area can have between 25 and 400 room, and about the same number of mobs, all of which require their own SW. As it won't change very often, I was thinking about keeping just the main SW to each area in my script file for quick access, and every other (secondary if you will) SW in these separate files. I think I can handle all the coding if I can just get the info from a seperate file into an array. What do you think? Is this doable? Is ODBC a more viable alternative?
I am rapidly coming to the conclusion that ODBC is the 'only' way to do things. Even my simple Druid Potion plugin has caused me more headaches due to fiddling with arrays, strings, splitting strings into arrays, joining them back to strings, etc, etc... that I have ever had writting anything. The lack of true type casting is part of the problem, but having to shift everything around between mushclient variables and script arrays is a major pain. A set of files is not much better, since you end up lagging the client slightly each time it has to read in the file. Also.. I am not sure how or if you could store it in a file and make it work reasonable well. :p
The only reason I haven't change the potion plugin over is I also haven't sat down with an ODBC manual to try and figure out how to create the database from in the script. I don't really want to include it as another file with the plugin, since I don't want it to contain anything initially. Of course with ODBC you should also be able to access you existing Excel spreadsheet. I know it is supposed to be possible. Just don't ask me how. ;)
The only reason I haven't change the potion plugin over is I also haven't sat down with an ODBC manual to try and figure out how to create the database from in the script. I don't really want to include it as another file with the plugin, since I don't want it to contain anything initially. Of course with ODBC you should also be able to access you existing Excel spreadsheet. I know it is supposed to be possible. Just don't ask me how. ;)
Anyone have an example of what the VBscript code for an ODBC query looks like and/or a website that touches on the topic?
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndao/html/daotoadoupdate.asp
If it doesn't load, just search for 'migrating DAO to ADO'. ;)
This requires some hunting to find stuff and some guess work. For instance, from a previous post I can guess that creating a database works a bit like this:
dim cat
cat = CreateObject("ADOX.Catalog")
cat.Create "Provider=Microsoft.Jet.OLEDB.4.0;Datasource=.\MyNewDB.MDB;"
However without testing is this is a major guess. I am not even sure ADOX is correct, since the normal set of ADO commands are prefixed ADODB. :p However, it is likely right, since ADOX is apparently an extension of the commands you normally can use. But basically since this is neither true VB nor 'normal' VBscript, any place where you would normallly do:
dim Something as new COMThingy.Element
or the complicated CLSID=Blahblahblah stuff, you have to use:
dim Something
Something = CreateObject("COMThingy.Element")
instead. But as I said. This is a guess based on other observations and is made even more comfusing by the fact that Javascript actually supports the 'normal' method that you would use in pure VB. :p We really need an example tutorial on this stuff for the supported script types. lol
If it doesn't load, just search for 'migrating DAO to ADO'. ;)
This requires some hunting to find stuff and some guess work. For instance, from a previous post I can guess that creating a database works a bit like this:
dim cat
cat = CreateObject("ADOX.Catalog")
cat.Create "Provider=Microsoft.Jet.OLEDB.4.0;Datasource=.\MyNewDB.MDB;"
However without testing is this is a major guess. I am not even sure ADOX is correct, since the normal set of ADO commands are prefixed ADODB. :p However, it is likely right, since ADOX is apparently an extension of the commands you normally can use. But basically since this is neither true VB nor 'normal' VBscript, any place where you would normallly do:
dim Something as new COMThingy.Element
or the complicated CLSID=Blahblahblah stuff, you have to use:
dim Something
Something = CreateObject("COMThingy.Element")
instead. But as I said. This is a guess based on other observations and is made even more comfusing by the fact that Javascript actually supports the 'normal' method that you would use in pure VB. :p We really need an example tutorial on this stuff for the supported script types. lol
>dim cat
>cat = CreateObject("ADOX.Catalog")
>cat.Create "Provider=Microsoft.Jet.OLEDB.4.0;Datasource=.\MyNewDB.MDB;"
Hmm.. This is really odd..
Checking the typename I get 'Table', so I assume it is creating the object, but the ADOX object insists that is doesn't support the Create property or method. :p Either MS's page lies or I did something wrong, just wish I had a clue what... $#$%^#...
>cat = CreateObject("ADOX.Catalog")
>cat.Create "Provider=Microsoft.Jet.OLEDB.4.0;Datasource=.\MyNewDB.MDB;"
Hmm.. This is really odd..
Checking the typename I get 'Table', so I assume it is creating the object, but the ADOX object insists that is doesn't support the Create property or method. :p Either MS's page lies or I did something wrong, just wish I had a clue what... $#$%^#...
Try this post: Can I access SQL database in MUSHclient with Jscript? . This is for Jscript but the technique is pretty identical in VB.
True. That is where you should likely start looking, though... There is another post that is more accurate. As I said, you have to remember to use CreateObject, where full VB and java both use New. That can be seriously confusing till you realize the difference.
------
This doesn't help me much though. ADOX is supposed to support 'creation' of a database. I would like to avoid A) including an existing database with a plugin or B) forcing someone to use the ODBC control panel to make one. :p
Mushclient can be a very strange animal with this sort of thing and you can't use the usual COM controls for databases to create one, but you also don't have access to DBEngine, which is the way you would normally create one in full VB. This is very frustrating and limits ones options.
This and the fact that I can find no simple way in VB to support the creation of non-predeclared Activex controls in VB is bugging the heck out of me. I know both should be possible, but there just isn't any decent documentation on either one, not even at the MS website. I hate the idea
of having to pre-make a database. I also, in the case of the ActiveX controls problem, don't really feel like fighting with C++ and MFC to get around VB's limits in this respect, but I may have to.
Apparently it never occured to MS that someone would ever want databases that are accessed by scripts, but not pre-existing and programs that impliment VBscript, but lack the ability to display ActiveX controls. lol It is making life quite interesting for me.
------
This doesn't help me much though. ADOX is supposed to support 'creation' of a database. I would like to avoid A) including an existing database with a plugin or B) forcing someone to use the ODBC control panel to make one. :p
Mushclient can be a very strange animal with this sort of thing and you can't use the usual COM controls for databases to create one, but you also don't have access to DBEngine, which is the way you would normally create one in full VB. This is very frustrating and limits ones options.
This and the fact that I can find no simple way in VB to support the creation of non-predeclared Activex controls in VB is bugging the heck out of me. I know both should be possible, but there just isn't any decent documentation on either one, not even at the MS website. I hate the idea
of having to pre-make a database. I also, in the case of the ActiveX controls problem, don't really feel like fighting with C++ and MFC to get around VB's limits in this respect, but I may have to.
Apparently it never occured to MS that someone would ever want databases that are accessed by scripts, but not pre-existing and programs that impliment VBscript, but lack the ability to display ActiveX controls. lol It is making life quite interesting for me.
Quote:
This doesn't help me much though. ADOX is supposed to support 'creation' of a database. I would like to avoid A) including an existing database with a plugin or B) forcing someone to use the ODBC control panel to make one.
This doesn't help me much though. ADOX is supposed to support 'creation' of a database. I would like to avoid A) including an existing database with a plugin or B) forcing someone to use the ODBC control panel to make one.
That should be theoretically possible. :)
The ODBC control panel doesn't strictly speaking make databases, it links to an existing one. If we could make one via 'create table' in the plugin (if necessary) then it should be possible to access it. Maybe.
Well... Actually the control panel 'can' create one. Also, create table doesn't generate a database itself, it mearly adds a new 'table' to an existing database source. It you have no Data source to connect to, you can't connect to it and create a table.
Basically it seems to work like this:
Both connect you 'through' the database driver, but ADODDB and ODBC where, I think, intentionally designed to limit the ability to actually create a data source file to system admins and programs which hard code file creation into themselves (like Excel). There are no ActiveX controls that provide this except ADOX and it keeps telling me that the method isn't supported. :p
Now... I could provide a single pre-created datasource, but then I would have to either copy and rename it some how for each world or create a seperate table for each world within it. The problem with the first option is I am not sure you can do so in scripting, while the second means if someone moves the data file, thinking it is only connected to one world, all the others stop working as well. :p It looks like it may turn out to be the only way to make it work though, since I can't find another solution.
Basically, with ADOX not working, the ODBC control panel being to confusing to use imho and no access to DBEngine (which I am fairly sure is not a COM object), there just isn't a way to create a new file from inside a script.
Basically it seems to work like this:
ADOX, DBEngine or ODBC Control Panel
|
\____Create
|
V
Driver _____ (Mydata.MDB is created here.)
_\|
Driver <--> Data Source (Mydata.MDB)
^
|
V
____Connect to Data Source
/
ADODB and ODBC
And database actually looks like:
Data source (Mydata.MDB)
|
|-Table (MyTable) - This is what ADODB creates and contains the 'database'.
| \
| Records
|
|-Table (FredsTable)
|
Etc.Both connect you 'through' the database driver, but ADODDB and ODBC where, I think, intentionally designed to limit the ability to actually create a data source file to system admins and programs which hard code file creation into themselves (like Excel). There are no ActiveX controls that provide this except ADOX and it keeps telling me that the method isn't supported. :p
Now... I could provide a single pre-created datasource, but then I would have to either copy and rename it some how for each world or create a seperate table for each world within it. The problem with the first option is I am not sure you can do so in scripting, while the second means if someone moves the data file, thinking it is only connected to one world, all the others stop working as well. :p It looks like it may turn out to be the only way to make it work though, since I can't find another solution.
Basically, with ADOX not working, the ODBC control panel being to confusing to use imho and no access to DBEngine (which I am fairly sure is not a COM object), there just isn't a way to create a new file from inside a script.
Quote:
create table doesn't generate a database itself, it mearly adds a new 'table' to an existing database source
create table doesn't generate a database itself, it mearly adds a new 'table' to an existing database source
True, you need 'create database' for that.
I'll have to experiment a bit. Your examples imply the use of Access which I wouldn't use these days, for one thing it doesn't really support - readily - access from multiple PCs. Secondly, not everyone has Office with Access (Access is an optional extra, and isn't cheap).
I would use mySQL, which is Open Source, and rather than create one database per world, create either a table per world, or probably better, a single table with a "world id" column in it. Otherwise you end up cluttering your disk with lots of database files.
As for ODBC, I'll take a look at adding support for mySQL natively into MUSHclient, which gets around the need for any ODBC interaction at all. However there may be a problem linking it unless mySQL is compiled into it, which may bloat it. I'll check it out, it might not be too bad.
That would be very helpful for people who want to keep lots of data, as it would be fast, and also save immediately, even if the world itself crashes for some reason. Then you could write stand-alone tools (eg. using Access) that access the data and play with it.
Actually.. A standard install of windows has the ODBC drivers for Foxpro, Excel, Access, and several others available through the Jet libraries. Unless you are running maybe a real old version of 95, those drivers should be there and accessable through ADO. The problem is just that there is not a way to create one, only access those that exist. You don't have to have Access installed to use them.
On the other hand MySQL has to be installed seperately and that goes right back to the original problem of needing some extra program or file that you shouldn't need to install to use the existing functions. :p I am not sure that is really a good solution to the problem, though if you ever ported mushclient to Linux, then maybe it would make sense. For now though...
On the other hand MySQL has to be installed seperately and that goes right back to the original problem of needing some extra program or file that you shouldn't need to install to use the existing functions. :p I am not sure that is really a good solution to the problem, though if you ever ported mushclient to Linux, then maybe it would make sense. For now though...
Quote:
>dim cat
>cat = CreateObject("ADOX.Catalog")
>cat.Create "Provider=Microsoft.Jet.OLEDB.4.0;Datasource=.\MyNewDB.MDB;"
Hmm.. This is really odd..
Checking the typename I get 'Table', so I assume it is creating the object, but the ADOX object insists that is doesn't support the Create property or method.
>dim cat
>cat = CreateObject("ADOX.Catalog")
>cat.Create "Provider=Microsoft.Jet.OLEDB.4.0;Datasource=.\MyNewDB.MDB;"
Hmm.. This is really odd..
Checking the typename I get 'Table', so I assume it is creating the object, but the ADOX object insists that is doesn't support the Create property or method.
I got that to work with minor variations. It is important to use the word "set" - I think that is where your example went wrong.
The code below gives two scripts, one creates a database (I presume you would have some sort of check to see if it already exists).
The second creates a table. I pulled out the constant definitions from the help file to make creating the table easier.
I haven't done the recordset selection etc., which is the trivial stuff once you have this going.
const adBigInt = 20
const adBinary = 128
const adBoolean = 11
const adBSTR = 8
const adChapter = 136
const adChar = 129
const adCurrency = 6
const adDate = 7
const adDBDate = 133
const adDBTime = 134
const adDBTimeStamp = 135
const adDecimal = 14
const adDouble = 5
const adEmpty = 0
const adError = 10
const adFileTime = 64
const adGUID = 72
const adIDispatch = 9
const adInteger = 3
const adIUnknown = 13
const adLongVarBinary = 205
const adLongVarChar = 201
const adLongVarWChar = 203
const adNumeric = 131
const adPropVariant = 138
const adSingle = 4
const adSmallInt = 2
const adTinyInt = 16
const adUnsignedBigInt = 21
const adUnsignedInt = 19
const adUnsignedSmallInt = 18
const adUnsignedTinyInt = 17
const adUserDefined = 132
const adVarBinary = 204
const adVarChar = 200
const adVariant = 12
const adVarNumeric = 139
const adVarWChar = 202
const adWChar = 130
sub CreateDatabase
Dim catDB
Set catDB = CreateObject ("ADOX.Catalog")
catDB.Create "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=c:\mushclient\mydb.mdb;" & _
"Jet OLEDB:Engine Type=5;"
Set catDB = Nothing
end sub
sub CreateTable
dim catDB
dim tblNew
Set catDB = CreateObject ("ADOX.Catalog")
' Open the catalog.
catDB.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=c:\mushclient\mydb.mdb"
Set tblNew = CreateObject ("ADOX.Table")
With tblNew
' Create a new Table object.
.Name = "Contacts"
' Create fields and append them to the
' Columns collection of the new Table object.
With .Columns
.Append "FirstName", adVarWChar
.Append "LastName", adVarWChar
.Append "Phone", adVarWChar
.Append "Notes", adLongVarWChar
End With
End With
' Add the new Table to the Tables collection of the database.
catDB.Tables.Append tblNew
Set tblNew = nothing
Set catDB = Nothing
end sub
Note how the example doesn't use the ODBC control panel at all, so you should be able to slot it into a plugin.
In the line:
Jet OLEDB:Engine Type=5
If you use "type=4" you get an Access 97 database, whilst Type=5 gives an Access 2000 database. You might want to use Type=4, however I found that on my PC to open it in Access I had to convert it (to 2000) anyway, so it was no big saving.
In the line:
Jet OLEDB:Engine Type=5
If you use "type=4" you get an Access 97 database, whilst Type=5 gives an Access 2000 database. You might want to use Type=4, however I found that on my PC to open it in Access I had to convert it (to 2000) anyway, so it was no big saving.
Also, see posts on previous page for how to set up the database from scratch, and create a table.
To demonstrate adding a record to the database ...
Note that these examples are rather simple, for instance I haven't defined a primary key. I am trying to show the basic syntax here, rather than provide a fabulous solution to data management problems.
The quotes might look strange, but you need to use two double-quotes inside a string to get a single double-quote in the finished result. An example of what we are trying to generate is:
You can try this from the command window like this:
To demonstrate adding a record to the database ...
sub AddRecordTable (first, last, phone, notes)
dim db
Set db = CreateObject ("ADODB.Connection")
' Open the connection
db.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=c:\mushclient\mydb.mdb"
db.Execute "INSERT INTO Contacts (FirstName, LastName," & _
"Phone, Notes) VALUES (" & _
"""" & first & """, " & _
"""" & last & """, " & _
"""" & phone & """, " & _
"""" & notes & """ );"
db.Close
Set db = Nothing
end sub
Note that these examples are rather simple, for instance I haven't defined a primary key. I am trying to show the basic syntax here, rather than provide a fabulous solution to data management problems.
The quotes might look strange, but you need to use two double-quotes inside a string to get a single double-quote in the finished result. An example of what we are trying to generate is:
INSERT INTO Contacts (FirstName, LastName, Phone, Notes)
VALUES ("John", "Smith", "7777 2222", "a person")
You can try this from the command window like this:
/AddRecordTable "here", "is", "a", "test"
Finally here is an example that reads data from the database and displays it ...
sub ShowRecords
dim db, rst
Set db = CreateObject ("ADODB.Connection")
Set rst = CreateObject ("ADODB.Recordset")
' Open the connection
db.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=c:\mushclient\mydb.mdb"
' Open the Recordset
rst.Open "SELECT * FROM contacts", db
' display each record
Do Until rst.EOF
' display each field
For Each fld In rst.Fields
world.tell fld.Value & ";"
Next
world.note "" ' newline
rst.MoveNext
Loop
db.Close
Set rst = Nothing
Set db = Nothing
end sub
If you prefer creating tables using SQL syntax (as I do) here is an example of creating the table in SQL, rather than using properties of the objects. The example uses one of my own tables (for recording events) that looks like this (in MySQL):
In MySQL the "identity" column is called "auto_increment" but apart from that they are much the same.
You can see that the 'create table' sets up a table with a primary key, auto increment, which is an integer, a date, a time, and a comment.
mysql> explain event;
+----------+--------------+------+-----+------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------+------+-----+------------+----------------+
| event_id | int(11) | | PRI | NULL | auto_increment |
| edate | date | | | 0000-00-00 | |
| etime | time | YES | | NULL | |
| comment | varchar(255) | | | | |
+----------+--------------+------+-----+------------+----------------+
In MySQL the "identity" column is called "auto_increment" but apart from that they are much the same.
You can see that the 'create table' sets up a table with a primary key, auto increment, which is an integer, a date, a time, and a comment.
sub CreateTable2
dim db
Set db = CreateObject ("ADODB.Connection")
' Open the connection
db.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=c:\mushclient\mydb.mdb"
db.Execute _
"CREATE TABLE event (" & _
" event_id int NOT NULL IDENTITY," & _
" edate date NOT NULL," & _
" etime time default NULL," & _
" comment varchar(255) NOT NULL default ''," & _
" PRIMARY KEY (event_id)" & _
")"
db.Close
Set db = Nothing
end sub
Thanks again Nick as I said in the email. Figure I should post to this anyway, so anyone else that didn't notice the update will. ;)
One comment though.. You suggest using a type 5, instead of type 4 for the Access database. The only problem here is that the point in most cases is to more easilly deal with complex data in mushclient, not to load it into access. There is a pretty good chance that most people will have a version of the access drivers that support the earlier version, but otherwise they would have to update the ODBC drivers on their computer to support the database. It is sort of the same situation as with MySQL. I see no real solution to it though, since the driver version could also be wrong. :p This is of course why most programs that support DBs include the updated driver set as part of their install. I am not entirely sure how the drivers deal with outdated requests...
Oh yes.. As to why no one posted.. It is Labor Day weekend in the US, thus most of the normal posters where probably too drunk to type. lol Either that or doing other national holiday sort of stuff. ;)
One comment though.. You suggest using a type 5, instead of type 4 for the Access database. The only problem here is that the point in most cases is to more easilly deal with complex data in mushclient, not to load it into access. There is a pretty good chance that most people will have a version of the access drivers that support the earlier version, but otherwise they would have to update the ODBC drivers on their computer to support the database. It is sort of the same situation as with MySQL. I see no real solution to it though, since the driver version could also be wrong. :p This is of course why most programs that support DBs include the updated driver set as part of their install. I am not entirely sure how the drivers deal with outdated requests...
Oh yes.. As to why no one posted.. It is Labor Day weekend in the US, thus most of the normal posters where probably too drunk to type. lol Either that or doing other national holiday sort of stuff. ;)
Quote:
You suggest using a type 5, instead of type 4 for the Access database. The only problem here is that the point in most cases is to more easilly deal with complex data in mushclient, not to load it into access.
You suggest using a type 5, instead of type 4 for the Access database. The only problem here is that the point in most cases is to more easilly deal with complex data in mushclient, not to load it into access.
I agree, if you are not planning to use Access, probably type 4 is better, and more likely to be installed.
However if you are going to use Access, then when you try to open the database it wants to convert it (creating a new file under a different name), which means you then have to close the original one, rename the new one as the original name, and then start using it again. This might not be totally intuitive.
The nice thing about Access, if you have it, is you can do reports, forms, queries etc. quite simply, so if your data lends itself to it, you can fiddle with it "offline".
db.Open = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=c:\mushclient\mydb.mdb"
If I wanted to access a DB stored in a text file or Excel spreadsheet, would I just change the Data Source, or would I have to change the Provider as well?
I believe you just change the data source.. Jet is the ODBC library and it supports all of the following (at least in the install I have, though a few things like Oracle and SQL have been added by other programs on my comp.):
Access - .mdb
Ironically this was also the extension used by Visual Basic for DOS's ISAM databases, but MS didn't supply a way to convert them... Which bugs me, since I wrote a program a while back that used VB DOS... :p
dBase, FoxPro - .dbf (?? How do you tell it which one to use or are they actually the same...?)
Excel - .xls
Paradox - .db
Text - .txt,.csv
I believe that simply changing the file extension probably handles the correct creation, but the docs I have looked at so far a a little sketchy about things like the 'Type=' Nick mentioned earlier. :p In fact the docs I have read don't even mention that option, but then they also mostly use the DBEngine or the like instead of ADOX.
Access - .mdb
Ironically this was also the extension used by Visual Basic for DOS's ISAM databases, but MS didn't supply a way to convert them... Which bugs me, since I wrote a program a while back that used VB DOS... :p
dBase, FoxPro - .dbf (?? How do you tell it which one to use or are they actually the same...?)
Excel - .xls
Paradox - .db
Text - .txt,.csv
I believe that simply changing the file extension probably handles the correct creation, but the docs I have looked at so far a a little sketchy about things like the 'Type=' Nick mentioned earlier. :p In fact the docs I have read don't even mention that option, but then they also mostly use the DBEngine or the like instead of ADOX.
why not just use a plugin an a couple of mushclient variables?
sw_a_x - name of the x area <space> path to the x area.
n_a - number of areas
sw_m_y - name of the y mob <space> name of mob area <space> path to mob from start of area.
n_m - number of mobs
with a few simple functions, a few IF and FOR you can get all the informations you want with that... and as the plugin stores the variables in a file you can handle a lot of variables with easy.
adding and deleting paths without repeat, its easy too... check how i do it in my WindScript Loan.
i will do a plugin like i said when i have a few time and maybe you can take a look and see if you like it.
sw_a_x - name of the x area <space> path to the x area.
n_a - number of areas
sw_m_y - name of the y mob <space> name of mob area <space> path to mob from start of area.
n_m - number of mobs
with a few simple functions, a few IF and FOR you can get all the informations you want with that... and as the plugin stores the variables in a file you can handle a lot of variables with easy.
adding and deleting paths without repeat, its easy too... check how i do it in my WindScript Loan.
i will do a plugin like i said when i have a few time and maybe you can take a look and see if you like it.
Because Avariel, as 'easy' as you imply it to be, it really isn't. I spent 3 days recoding a script I used to report potion types I could make to use mushclient variables to store some of the information and make it complient with my muds "don't just hand out information players should learn on their own" policies. I then recoded it again, then again, and again, etc. Till I am now at version 3.1 and I think it is finally stable. However, there are a few things that my hard coded custom version does that the plugin still can't and the reason is because the code needed to handle a mix of mushclient variables and complex interrelations is too much of a major in the backside to code.
In the case of the original poster to this thread, the problem was less complex, but required maintaining a list of multiple paths, not just one at a time. I dealt with the same problem by once again hard coding the paths into my script in a case statement, but doing so is inflexible and added a very large chunk up code that does nothing more than what a database would have done far more efficiently. Mushclient variable are very useful for simple things, but when you are trying to handle large numbers of individual paths or complex connection, the fact that you have to convert them every time into an array, then back, make sure each variable has the same number of records (or just the right number), etc., rapidly becomes confusing and overly complicated.
Your method works, but is far harder to maintain, get working right and most importantly modify if your decide to make heavy modifications to the it for some reason. My own plugin checks a variable that stores a version number for the current plugin when it installs and if it doesn't match the current plugin, deletes all of the variables and recreates a few of them and leaving the user data empty. It does this because the structure of the strings used to track the data has changed about 5 times since I started designing the plugin. This is not a reasonable way to do things, but transfering the old data into new variables would have required maybe fifty lines of code 'for each previous version'. By comparison, with a database you just move the stuff you want to keep to new tables, or if some information is redundant or unneeded, just treat those fields as empty.
Put simply for simple things mushclient variables are useful, but when you have to start designing a database in scripting using those variables, you may as well avoid the aggrivation and use a real one. ;) lol
In the case of the original poster to this thread, the problem was less complex, but required maintaining a list of multiple paths, not just one at a time. I dealt with the same problem by once again hard coding the paths into my script in a case statement, but doing so is inflexible and added a very large chunk up code that does nothing more than what a database would have done far more efficiently. Mushclient variable are very useful for simple things, but when you are trying to handle large numbers of individual paths or complex connection, the fact that you have to convert them every time into an array, then back, make sure each variable has the same number of records (or just the right number), etc., rapidly becomes confusing and overly complicated.
Your method works, but is far harder to maintain, get working right and most importantly modify if your decide to make heavy modifications to the it for some reason. My own plugin checks a variable that stores a version number for the current plugin when it installs and if it doesn't match the current plugin, deletes all of the variables and recreates a few of them and leaving the user data empty. It does this because the structure of the strings used to track the data has changed about 5 times since I started designing the plugin. This is not a reasonable way to do things, but transfering the old data into new variables would have required maybe fifty lines of code 'for each previous version'. By comparison, with a database you just move the stuff you want to keep to new tables, or if some information is redundant or unneeded, just treat those fields as empty.
Put simply for simple things mushclient variables are useful, but when you have to start designing a database in scripting using those variables, you may as well avoid the aggrivation and use a real one. ;) lol
nod i agree with you, Shadowfyr. using mushclient variables to build complex databases isnt the most eficient way of doing it, but i dont consider this sw database that much complex... at least not as your potion script.
the script i use now... i havent put it in a plugin look yet, can handle a few sw. i dont have much ( 58 areas with 5 mobs each average ).
i have a diferent structure as the one i post up.. i was trying to change it but i guess that way make it a lot more rigid.
i have a variable for number of areas ( n_area ) and other for number of mobs ( n_mob ) that are update each time i add or remove an area or a mob.
each area have 2 variables - name and dir to it ( area_name_1 area_dir_1 area_name_2 area_dir_2 etc.. )
each mob have 3 variables - name, area and dir ( mob_name_1 mob_area_1 mob_dir_1 etc...)
update for areas...
each time i try to add an area i do a loop from 1 to n_area checking the area_name_ variables to see if the area is already there... if exists display the existing area_dir_ ... if not create a new area_name_ and area_dir_ and increase n_area by 1.
each time i try to delete an area i do a loop from 1 to n_area checking the area_name_ variables to see if the area is there, and do a loop from 1 to n_mob checking the mob_area_ variables to see if its associated to any mob... if exists and no mob associated delete area_name_ and area_dir_ for that area and decrease n_area by 1... if not display the existing area dont exists or display that are mobs in the area.
to get a list of all areas i do a loop from 1 to n_area checking the area_name_ and send a note for each.
update for mobs...
each time i try to add a mob i do a loop from 1 to n_mob checking the mob_name_ variables to see if the mob is already there, and do a loop from 1 to n_area checking the area_name_ variables to see if the area is there... if the mob exists display the existing mob_dir_ and mob_area_ ... if the area dont exist display the area dont exist... if not create mob_name_ , mob_area_ , mob_dir_ and increase n_mob by 1.
each time i try to delete a mob i do a loop from 1 to n_mob checking the mob_name_ variables to see if its there... if exists delete mob_name_ , mob_area_ and mob_dir_ for that mob and decrease n_mob by 1... if not display the existing mob dont exist.
to get a list of all mob i do a loop from 1 to n_mob checking the mob_name_ and send a note for each.
going for areas...
each time i try to go to an area i do a loop from 1 to n_area checking the area_name_ variables to see if the area is already there... if exists evaluate area_dir_ ... if not display area dont exist.
going for mobs...
i have 2 choices... going from base and going from area entrance.
in both cases i do a loop from 1 to n_mob checking the mob_name_ variables to see if its there... if not display mob dont exist.
going from area entrance... evaluate mob_dir_
going from base... do a loop from 1 to n_area checking the area_name_ variables to get area number, then evaluate area_dir_ , then evaluate mob_dir_ .
i say again... i agree this isnt the best way of doing it but this isnt a prob that hard to code.
if you want more information stored you dont need a big change in variables... just add a variable to correspondent mob or area... you think you have useless information stored? delete all variables of that kind. maybe thats to much variables for a mob or an area but... changing version wont be a prob, you just need to know what area_name_ area_dir mob_name_ etc. means.
im not saying he should do like this, i was just showing another option.
the script i use now... i havent put it in a plugin look yet, can handle a few sw. i dont have much ( 58 areas with 5 mobs each average ).
i have a diferent structure as the one i post up.. i was trying to change it but i guess that way make it a lot more rigid.
i have a variable for number of areas ( n_area ) and other for number of mobs ( n_mob ) that are update each time i add or remove an area or a mob.
each area have 2 variables - name and dir to it ( area_name_1 area_dir_1 area_name_2 area_dir_2 etc.. )
each mob have 3 variables - name, area and dir ( mob_name_1 mob_area_1 mob_dir_1 etc...)
update for areas...
each time i try to add an area i do a loop from 1 to n_area checking the area_name_ variables to see if the area is already there... if exists display the existing area_dir_ ... if not create a new area_name_ and area_dir_ and increase n_area by 1.
each time i try to delete an area i do a loop from 1 to n_area checking the area_name_ variables to see if the area is there, and do a loop from 1 to n_mob checking the mob_area_ variables to see if its associated to any mob... if exists and no mob associated delete area_name_ and area_dir_ for that area and decrease n_area by 1... if not display the existing area dont exists or display that are mobs in the area.
to get a list of all areas i do a loop from 1 to n_area checking the area_name_ and send a note for each.
update for mobs...
each time i try to add a mob i do a loop from 1 to n_mob checking the mob_name_ variables to see if the mob is already there, and do a loop from 1 to n_area checking the area_name_ variables to see if the area is there... if the mob exists display the existing mob_dir_ and mob_area_ ... if the area dont exist display the area dont exist... if not create mob_name_ , mob_area_ , mob_dir_ and increase n_mob by 1.
each time i try to delete a mob i do a loop from 1 to n_mob checking the mob_name_ variables to see if its there... if exists delete mob_name_ , mob_area_ and mob_dir_ for that mob and decrease n_mob by 1... if not display the existing mob dont exist.
to get a list of all mob i do a loop from 1 to n_mob checking the mob_name_ and send a note for each.
going for areas...
each time i try to go to an area i do a loop from 1 to n_area checking the area_name_ variables to see if the area is already there... if exists evaluate area_dir_ ... if not display area dont exist.
going for mobs...
i have 2 choices... going from base and going from area entrance.
in both cases i do a loop from 1 to n_mob checking the mob_name_ variables to see if its there... if not display mob dont exist.
going from area entrance... evaluate mob_dir_
going from base... do a loop from 1 to n_area checking the area_name_ variables to get area number, then evaluate area_dir_ , then evaluate mob_dir_ .
i say again... i agree this isnt the best way of doing it but this isnt a prob that hard to code.
if you want more information stored you dont need a big change in variables... just add a variable to correspondent mob or area... you think you have useless information stored? delete all variables of that kind. maybe thats to much variables for a mob or an area but... changing version wont be a prob, you just need to know what area_name_ area_dir mob_name_ etc. means.
im not saying he should do like this, i was just showing another option.
Personally, I try and be as considerate as possible to overall "overhead" when I'm working on any particular project. I would presonally really try to avoid writing a plugin that required more than about 20-30 variables.
Some things to take into account:
For example, my spell queue, should it ever be converted to a plugin, needs to be as quick as possible, so I would probably elect not to use an external file of any kind.
A database of item descriptions is a pretty low priority where speed is concerned, and given that the database would be quite large, this would definately go in an external file.
Speedwalks, in my opinion, have a low priority where speed is concerned. If the client lags for a moment while it pulls data from an external file, it's not so bad, since you aren't likely to execute a speedwalk while in battle. Also, a speedwalk database could concievable grow quite large, especially on a mud where new areas are developed frequently.
Anyhow, just thought I would throw in my two cents. :)
Some things to take into account:
- How urgently are the values needed?
- Will the values be needed in combat (Lag)?
- Are there any other reasons lag should be of concern?
- How many values are expected?
For example, my spell queue, should it ever be converted to a plugin, needs to be as quick as possible, so I would probably elect not to use an external file of any kind.
A database of item descriptions is a pretty low priority where speed is concerned, and given that the database would be quite large, this would definately go in an external file.
Speedwalks, in my opinion, have a low priority where speed is concerned. If the client lags for a moment while it pulls data from an external file, it's not so bad, since you aren't likely to execute a speedwalk while in battle. Also, a speedwalk database could concievable grow quite large, especially on a mud where new areas are developed frequently.
Anyhow, just thought I would throw in my two cents. :)
Avariel, if you have 58 speedwalks.. have you considered a simple insertion sort for puting the stuff in to the list? Doing so requires some tricks like using 'redim preserve' to resize the array (something I had a problem getting to work right though). But even if you had to use two arrays, having the data sorted and using a binary search would speed things up of large amounts for data.
A binary search in case you don't know basically does this:
I tried using this once to also do the insertion, but gave up. Mostly because I forgot that last if/then test to keep in from infinite looping. lol For insertion the new record should always be at 'en'. ;)
In any case, this just about halves the time needed to find something in a large array.
A binary search in case you don't know basically does this:
function find (name)
dim names, walks 'Storing your location names and speed walks.
names = split(world.getvariable("???"),",") 'What ever ones you use.
walks = split(world.getvariable("???"),",")
w
fd = false
st = 0
en = ubound(record)
do until fd or st = en
cr = st + int((en-st)/2)
if names(cr) > name then
en = cr
else if names(cr) < name then
st = cr
else
fd = True
end if
if le = en and lst = st then 'Otherwise it goes forever if no match exists.
exit do
else
lst = st
le = en
end if
loop
if fd then
find = walks(cr)
else
find = "Unknown!" 'Check for this in the calling script (since sending it is pointless.) ;)
end if
end functionI tried using this once to also do the insertion, but gave up. Mostly because I forgot that last if/then test to keep in from infinite looping. lol For insertion the new record should always be at 'en'. ;)
In any case, this just about halves the time needed to find something in a large array.
Hi guys :) Thanks for all your tips. I am having tons of fun (only slight sarcasm) learning how to do this stuff. Oh, just fyi, the MUD I'm doing this for has over 200 areas, the largest having 400 or so rooms and at least as many mobs, the smallest having 25 rooms and about 50 mobs. I have a nice lofty spreadsheet with all the data I've collected, but I might break it out into several spreadsheets, as it's getting rather large and unwieldy. I am going all out and trying to make this as comprehensive as possible, including SWs from portals, clan exits, manor exits, passdoor on/off, and alternate entrances. I only have the main SWs done so far, so when I get everything else entered, it's pretty much going to triple or quadruple in size. Heh, variables are pretty much out of the picture at this point. I'm still having problems getting the code to recognize an Excel spreadsheet as a DB though... I tried the code Nick posted earlier, changing the .mdb to a .xls, but the client just lags for a minute and returns error -2147467259, Unrecognized database format. *sigh* I may have to turn to Access... yet another thing I know little about... heh, I think I'm going to put this on my resume when I get it done....
Yes, a spreadsheet is not exactly a database, although in some ways they are similar.
You can make an Access database and import a spreadsheet into it, so migrating the data shouldn't be too hard.
To do the job efficiently you really need good database design, which I think is called "third normal form" from memory.
What this means in practice, is that each table in the database should hold one "kind" of data, and there should not be repetition (ie. the name of a mob should be held once, and if you need to refer to it in two different places you move it into a separate table).
For instance, you might have a table of area names, table of room names, table of exits, table of speedwalks and so on.
You can make an Access database and import a spreadsheet into it, so migrating the data shouldn't be too hard.
To do the job efficiently you really need good database design, which I think is called "third normal form" from memory.
What this means in practice, is that each table in the database should hold one "kind" of data, and there should not be repetition (ie. the name of a mob should be held once, and if you need to refer to it in two different places you move it into a separate table).
For instance, you might have a table of area names, table of room names, table of exits, table of speedwalks and so on.
Yes, I totally agree... I had read somewhere that you could use ODBC with flat text files and Excel spreadsheets, but the more I think about it, the more I realize a full DB is the only real way to go. I need to do a bit of research on planning a relational DB, just so I make sure I get it right the first time. I played around with a small DB, and got it to work great! Thanks for all the help with the sample code you provided. A few more questions though:
Why would you create a table using SQL as opposed to within the DB itself? and what is the difference between using Type 4 and Type 5 as mentioned above?
Why would you create a table using SQL as opposed to within the DB itself? and what is the difference between using Type 4 and Type 5 as mentioned above?
Type 4 uses the Access 97 engine, type 5 uses the Access 2000 engine. Which is best really depends on which engine you have installed, however type 4 is more likely to be compatible with a greater number of users. I'm not sure what would happen if you left the type out, it might default to whatever is installed.
As for creating the table, at the end of the day the database engine gets the SQL command to create it, however using the VB COM objects abstracts that away from you a bit, the end result will be the same.
As I have a book on SQL I find it simpler to use the actual commands, rather than reading all the MS documentation to work out what to do with their COM objects to achieve the result I want anyway.
As for creating the table, at the end of the day the database engine gets the SQL command to create it, however using the VB COM objects abstracts that away from you a bit, the end result will be the same.
As I have a book on SQL I find it simpler to use the actual commands, rather than reading all the MS documentation to work out what to do with their COM objects to achieve the result I want anyway.
One thing I am not sure of... One older windows installs the Jet controls may not be v4.0 and newer updates of them could end up being v4.1 or even 5.0, etc. I haven't looked at the ADOX stuff and the like too closely yet (hard to find good docs on some of this stuff), but is it possible that for compatibility purposes our plugins should use the ADOX or ADODB catalog features to get the correct Jet version string to use in accessing it, before trying to use the other commands?
Seems to me this may be a good idea or some systems may reject them due to version conflicts. Just a thought. ;)
Seems to me this may be a good idea or some systems may reject them due to version conflicts. Just a thought. ;)
I've been trying to build a database using MS Access. No VBS programming or anything else (yet). This question doesn't really belong here since it isn't MUSHclient related, but I'll ask anyway. Once I get a database built and working smoothly in Access, I'll attempt to build a MUSHclient scripted interface to it.
I have one field called "Weapon Damage Types", and I've set it as a multi-select listbox (Extended). There are nine options in the listbox.
I've build a form for inputting data. The problem is the listbox. When I click any of the items in the listbox, they are highlighted, but if I move to a different record and then come back, the items in the listbox are no longer highlighted. I want them to be. Indeed, if I move away from that record again, I don't know if Access updates the record to have none selected.
I've tried using a "Requery" macro in several places, but I just can't seem to get it to work the way I want. Any suggestions?
I have one field called "Weapon Damage Types", and I've set it as a multi-select listbox (Extended). There are nine options in the listbox.
I've build a form for inputting data. The problem is the listbox. When I click any of the items in the listbox, they are highlighted, but if I move to a different record and then come back, the items in the listbox are no longer highlighted. I want them to be. Indeed, if I move away from that record again, I don't know if Access updates the record to have none selected.
I've tried using a "Requery" macro in several places, but I just can't seem to get it to work the way I want. Any suggestions?
Hmm - personally I wouldn't requery at all, as that would refresh the original data.
Hard to say without seeing the database. If it isn't too big zip it up and email it to me.
Hard to say without seeing the database. If it isn't too big zip it up and email it to me.
OK, I see what is happening, sort of. :)
You have a listbox where you can make multiple selections (eg. Acid and Magical) however you are storing the results into a single integer.
I can't see how multiple items can fit into one number. Either:
You have a listbox where you can make multiple selections (eg. Acid and Magical) however you are storing the results into a single integer.
I can't see how multiple items can fit into one number. Either:
- Make it a combo box (like weapon quality) if you only really want one; or
- Store it as a bitmap into the database. You would need to have an event hander (eg. onchange or something) that uses the selected items to construct a bitmap (eg. acid = 1, Asphyxiation = 2, Burning = 4, Crushing = 8 and so on), adding them together and then storing the result in the database.
I'm not sure if Access has some automatic way of doing that.
Well, more tham just being visually appealing, the database should actually sore the multiply selected values.
It is possible in the game for weapons to do multiple types of damage, which is why that particular field is configured as a listbox.
I noticed that the internal result was being stored as a byte. I can't figure out how to change that. Access picks the field type when you go through the automaked "lookup" process.
I've spent much time already, searching the internet for an answer... and haven't been able to find one.
I'm not sure how I'll work around this problem, but I don't like the bitmap idea. As I said earlier, once this works smoothly in Access itself, I plan to pull values with a MUSHclient script, and trying to process a bitmap would be near impossible.
I appreciate your help anyway, Nick. :)
It is possible in the game for weapons to do multiple types of damage, which is why that particular field is configured as a listbox.
I noticed that the internal result was being stored as a byte. I can't figure out how to change that. Access picks the field type when you go through the automaked "lookup" process.
I've spent much time already, searching the internet for an answer... and haven't been able to find one.
I'm not sure how I'll work around this problem, but I don't like the bitmap idea. As I said earlier, once this works smoothly in Access itself, I plan to pull values with a MUSHclient script, and trying to process a bitmap would be near impossible.
I appreciate your help anyway, Nick. :)
Heh, I'm a dolt sometimes. Thought you meant a graphic bitmap, till I reviewed my own posting, and saw my idiocy.
Yeah, your idea is good. I considered it myself too. First, I wanted to see if there was some straight forward way of handling this issue. Microsoft provides the option of using a listbox, I figure surely they have an automated way of storing the selected data!
At times, the "Requery" macro seemed to work, but problems would arise because of the timing of the refresh. I would often send Access into an infinite loop as it requeried repetitevly, or it would requery and update properly, but then immediately lose the displayed values.
Somewhere I came upon a knowledgebase article (regarding a previous version of Access), noting this problem, but there was no work-around provided. From what I gather, the multiply selected data IS stored, but the form presents the listbox as blank every time it is refreshed. Absolutely un-intuitive and stupid, if ya ask me.
I'll look into this more later, it's low priority for now. Any help is still appreciated. :)
Yeah, your idea is good. I considered it myself too. First, I wanted to see if there was some straight forward way of handling this issue. Microsoft provides the option of using a listbox, I figure surely they have an automated way of storing the selected data!
At times, the "Requery" macro seemed to work, but problems would arise because of the timing of the refresh. I would often send Access into an infinite loop as it requeried repetitevly, or it would requery and update properly, but then immediately lose the displayed values.
Somewhere I came upon a knowledgebase article (regarding a previous version of Access), noting this problem, but there was no work-around provided. From what I gather, the multiply selected data IS stored, but the form presents the listbox as blank every time it is refreshed. Absolutely un-intuitive and stupid, if ya ask me.
I'll look into this more later, it's low priority for now. Any help is still appreciated. :)
Now that I think about it, the problem is really one of database design. ;)
Even using a bitmap you are limiting yourself to (say) 32 damage types per item, which may be fine for you, but is still a limitation. Strictly speaking, in database design, you are not supposed to store "repeated values" in a row. In your case, multiple damage types are repeated values.
Thus, I would design it like this:
I would use an item "id" so that if you change the name of the item its primary key doesn't change (eg. if you misspell "An iron hand-axe"). The primary key is important as we will see in a moment.
So far, so good.
Now the important bit, you need to "link" the many-to-many relationship, of multiple items that can have multiple damage types using a third table ...
Thus, every row in this third table represents a damage type for a particular item.
Say you had an item "1-An iron hand-axe" that has damage types: 5-cutting and 9-piercing then you would have two records:
By making the primary key the combination you guarantee that each item in the third table is unique (they must be unique in the original tables, and the combination is therefore unique).
Now, finally, to present it visually. Make a third form for the third table (eg. use combo-boxes), and then make this third form a sub-form of the item form.
What this will do, with very little effort on your part, is to let you add any number of damage items (separate rows) for any item. Do a "new record" in the sub-form to add a damage item, or "delete record" to remove a damage item.
Even using a bitmap you are limiting yourself to (say) 32 damage types per item, which may be fine for you, but is still a limitation. Strictly speaking, in database design, you are not supposed to store "repeated values" in a row. In your case, multiple damage types are repeated values.
Thus, I would design it like this:
AOD Items table
ItemID - int, autoincrement, primary key
Item Name - name of item
Item Type Key - Location where this item is equipped.
(and so on)
I would use an item "id" so that if you change the name of the item its primary key doesn't change (eg. if you misspell "An iron hand-axe"). The primary key is important as we will see in a moment.
Weapon damage types table
Weapon Damage Index - int, autoincrement, primary key
Weapon Damage Label - text
So far, so good.
Now the important bit, you need to "link" the many-to-many relationship, of multiple items that can have multiple damage types using a third table ...
Item/damage type table
ItemID - int
Weapon Damage Index - int
Primary key: combination of Weapon Damage Index and ItemID
Thus, every row in this third table represents a damage type for a particular item.
Say you had an item "1-An iron hand-axe" that has damage types: 5-cutting and 9-piercing then you would have two records:
1, 5 (item 1 has damage type 5)
1, 9 (item 1 has damage type 9)
By making the primary key the combination you guarantee that each item in the third table is unique (they must be unique in the original tables, and the combination is therefore unique).
Now, finally, to present it visually. Make a third form for the third table (eg. use combo-boxes), and then make this third form a sub-form of the item form.
What this will do, with very little effort on your part, is to let you add any number of damage items (separate rows) for any item. Do a "new record" in the sub-form to add a damage item, or "delete record" to remove a damage item.
Way to complex there Nick. In general it is not an issue anyway. Muds need to have a set number of damage types so that mobs and weapons can be matched to determine who gets hurt. As such there is little change that a mob be added to the game which suddenly aquired 'plasma' damage or vulnerability to the same, for example, without needing to recode a large part of the muds core code. It seems unlikely to me that any mud would have more that 16-20 'true' damage types total, though they may use any number of names to describe them. This is basically the case here.
Also, while redundant info is technically bad, in some cases it is a null issue, since you can much more easilly search for a 'crushing' type than worry that you may have entered 'cruhing' someplace. The damage type table would be small, fixed in size and since weapons 'could' have multiple types, you end up storing a non-fixed number of indexes to the table. This is worse, since instead of worrying that you have 17 types, instead of the 16 you get with an unsigned integer, you have to worry about your table only allowing 4 types per weapon and some new weapon having 5 types. It is usually easier to allow an extra integer for spill over, in case of having more than you expect, than adding a new field.
Ironically this would be even easier is COBOL, where records are just raw data, and you create the actually field definitions in the program accessing the database. You can thus add padding bytes where you think the fields may outgrow the expected contents from the start, just in case you needed to change it later. I have done that with binary file access under QBasic, but most so called modern databases don't (as far as I know) allow you to re-cast the table contents using a different set of fields. A bit of a flaw imho, since your program has to regenerate the entire file to effect the proper changes.
Anyway, in this case a bit-flag system will work best.
Also, while redundant info is technically bad, in some cases it is a null issue, since you can much more easilly search for a 'crushing' type than worry that you may have entered 'cruhing' someplace. The damage type table would be small, fixed in size and since weapons 'could' have multiple types, you end up storing a non-fixed number of indexes to the table. This is worse, since instead of worrying that you have 17 types, instead of the 16 you get with an unsigned integer, you have to worry about your table only allowing 4 types per weapon and some new weapon having 5 types. It is usually easier to allow an extra integer for spill over, in case of having more than you expect, than adding a new field.
Ironically this would be even easier is COBOL, where records are just raw data, and you create the actually field definitions in the program accessing the database. You can thus add padding bytes where you think the fields may outgrow the expected contents from the start, just in case you needed to change it later. I have done that with binary file access under QBasic, but most so called modern databases don't (as far as I know) allow you to re-cast the table contents using a different set of fields. A bit of a flaw imho, since your program has to regenerate the entire file to effect the proper changes.
Anyway, in this case a bit-flag system will work best.
Quote:
The damage type table would be small, fixed in size and since weapons 'could' have multiple types, you end up storing a non-fixed number of indexes to the table. This is worse, since instead of worrying that you have 17 types, instead of the 16 you get with an unsigned integer, you have to worry about your table only allowing 4 types per weapon and some new weapon having 5 types.
The damage type table would be small, fixed in size and since weapons 'could' have multiple types, you end up storing a non-fixed number of indexes to the table. This is worse, since instead of worrying that you have 17 types, instead of the 16 you get with an unsigned integer, you have to worry about your table only allowing 4 types per weapon and some new weapon having 5 types.
I agree that maybe a "bitmap" would be best if you have a fairly low number of types, although it makes it more complicated in the form, however I don't understand the above point. I wasn't suggesting a "table of integers" inside the existing record, but a separate table, which being its own database table can have unlimited size, allowing for nil to any number of damage types per weapon.
eg., assuming each line is a database record:
1, Sword <-- weapon table
2, Machete <-- weapon table
1, Cutting <-- damage type table
2, Pierce <-- damage type table
3, Slice <-- damage type table
1, 2 <-- weapon/damage table - sword pierces
1, 3 <-- weapon/damage table - sword slices
2, 1 <-- weapon/damage table - machete cuts
Now if you want to find which weapon slices, you search for records in the weapon/damage where the damage type = 3, and using a (SQL) join find which weapon it is. ie., in the example above, a search for damage type 3 gives you the fact that it is weapon type 1, which you then lookup the weapon table to see it is a sword. This sounds complicated, but is a simple SQL statement. Off the top of my head it would be:
SELECT * from weapon, damage_type
WHERE damage_type.damage_id = 3
AND damage_type.damage_id = weapon.damage_id
AND damage_type.weapon_id = weapon.weapon_id
ORDER BY weapon.name
Yeah. I wasn't thinking of it that way. That would work, but is perhaps more appropriate for non-fixed information. I.e. If you expect to add new data, then it is useful, otherwise a database is a waste of resources and time, since it takes longer to look up a record through that method than match to a fixed list using a built in array. Using a complete database program you probably are not given a choice, since there is likely no way to store such a table in anything other than the database itself. However, when using your own custom script or program and a fixed table, there is no point in adding extra overhead by looking up just the name of a damage type in a third table. But otherwise you are correct, it is more flexible, despite being overkill on most muds.
I am not exactly sure why the problem Magnum is having happens though. If I was using a list box, I might consider using a list of possibles, then a second window I could 'add' the items to, then for plain lookup, just the list of what is actually in there. Not to mention my own code for displaying it. A good DB program should let you do so, but MS probably went the 'let the program do it all' route that they so enjoy screwing us up with. lol
I am not exactly sure why the problem Magnum is having happens though. If I was using a list box, I might consider using a list of possibles, then a second window I could 'add' the items to, then for plain lookup, just the list of what is actually in there. Not to mention my own code for displaying it. A good DB program should let you do so, but MS probably went the 'let the program do it all' route that they so enjoy screwing us up with. lol
Quote:
1, Sword <-- weapon table
2, Machete <-- weapon table
1, Cutting <-- damage type table
2, Pierce <-- damage type table
3, Slice <-- damage type table
1, 2 <-- weapon/damage table - sword pierces
1, 3 <-- weapon/damage table - sword slices
2, 1 <-- weapon/damage table - machete cuts
1, Sword <-- weapon table
2, Machete <-- weapon table
1, Cutting <-- damage type table
2, Pierce <-- damage type table
3, Slice <-- damage type table
1, 2 <-- weapon/damage table - sword pierces
1, 3 <-- weapon/damage table - sword slices
2, 1 <-- weapon/damage table - machete cuts
I'm no whiz at DB design (which is why all my SWs are still sitting in an Excel spreadsheet), but I would like to learn... why would the above 3 tables be more efficient than one table? For example, these records:
Wpn table
Sword, Pierce
Sword, Slice
Machete, Cutting
Could be accessed with this SQL code:
SELECT * from Wpn
WHERE Wpn.type = "Sword"
ORDER BY Wpn.type
I think the point I'm missing is in regards to redundant data. Is the association of text strings to integers as IDs just to save space?
I'm not worried about saving a few bytes here and there. Your design has a few problems ...
My solution may seem complex but it does address those issues. :)
Quote:
Wpn table
Sword, Pierce
Sword, Slice
Machete, Cutting
Wpn table
Sword, Pierce
Sword, Slice
Machete, Cutting
- Do you have one sword, that pierces and cuts, or two swords, one that pierces and one that cuts?
- If you misspell something (eg. "sword" as "sord") then you may have to correct it in multiple places.
- As you are entering damage as a string you may enter it differently in different places (eg. "pierces" and "pierce") which makes it hard to find all weapons that pierce.
- If you had two swords, both that pierce, how would you delete one of them? If you did this:
DELETE FROM from Wpn WHERE Wpn.type = "Sword" AND Wpn.damage = "Pierce"
you would delete both of them. That is what the unique IDs are for. - How do you quickly see a list of all possible damage types? By storing them in the main table as they are used you don't have a list anywhere of the unused ones. Thus in your short example, if there was a damage type of "magic" you have no way of presenting that to the user as the damage type "magic" is not used yet.
My solution may seem complex but it does address those issues. :)
Well we are looking at this situation (and most muds would use something similar):
You look up the weapon to get the number, then use the number to get the damage index and use it to look up the name in the array.
Three Tables - Pointless, unless you are actually using Access or the like and "can't" store the names in a faster array structure. It is also quite slow, since you have to ask the database for the weapon number, then ask it to look up each type assigned to the weapon and then finally ask it for each of the names for those retrieved types. This wastes a lot of time and is even worse when used from scripting, since ADODB access is twice as slow as the same look up performed by a compiled program through direct database API calls.
The first option is 'always' better, if you know that the thing you are going to look for won't change, even if you have to use a long integer to store all the possible combos. The second and third options are best left for if you don't know for certain how many types you have to worry about and even then, you are better off to use the third option to 'store' the names, but the second option to use them by loading the types into a global array (thus avoiding having to perform multiple ADODB searches each time for the names).
If I planned to do that I would use a OnPluginConnect sub to load all the types into a mushclient variable, padding them to equal lengths, then just multiply the type number by that length to get the name. Doing so would eliminate the time taken to look up each type when reading a record, while also avoid the hassle created by having to split the string into an array every time. If there where like 100+ types though, you wouldn't have much choice but the use the database. :p
Basically I agree that one table is always better, but it depends on how complex the situation is and your version assumes that a weapon only does one type of damage, which is not a good assumption to make. Even some types of normal swords can do slashing 'edge' attacks, blunt trauma and piercing damage. It just depends how you hit someone with it. ;)
Weapon Damage Type(s)
------------ ------------------
Zeldan's Flameblade Heat, Edge, Magic
The solutions to this is one of the following:
Bit-flag method - Stores type in an unsigned integer where the types are:
1 - Edged
2 - Blunt
4 - Pierce
8 - Heat
16 - Cold
32 - Poison
64 - Mind
128 - Magic
256 - Asphyxiation
512 - Acid
So The above weapon would be stored as "Zelden's Flameblade,137" and the actually types would be returned from an array in the script, where the element in the array is the 'bit position' used to store that type.
Two Tables - Also uses an array in the script, but would use the following:
Table 1: Weapons
Weapon Weapon Number
------------ ------------------
Zeldan's Flameblade 1
Table 2: Types
Index to the Weapon Damage Type
------------------- ------------------
1 3
1 0
1 7You look up the weapon to get the number, then use the number to get the damage index and use it to look up the name in the array.
Three Tables - Pointless, unless you are actually using Access or the like and "can't" store the names in a faster array structure. It is also quite slow, since you have to ask the database for the weapon number, then ask it to look up each type assigned to the weapon and then finally ask it for each of the names for those retrieved types. This wastes a lot of time and is even worse when used from scripting, since ADODB access is twice as slow as the same look up performed by a compiled program through direct database API calls.
The first option is 'always' better, if you know that the thing you are going to look for won't change, even if you have to use a long integer to store all the possible combos. The second and third options are best left for if you don't know for certain how many types you have to worry about and even then, you are better off to use the third option to 'store' the names, but the second option to use them by loading the types into a global array (thus avoiding having to perform multiple ADODB searches each time for the names).
If I planned to do that I would use a OnPluginConnect sub to load all the types into a mushclient variable, padding them to equal lengths, then just multiply the type number by that length to get the name. Doing so would eliminate the time taken to look up each type when reading a record, while also avoid the hassle created by having to split the string into an array every time. If there where like 100+ types though, you wouldn't have much choice but the use the database. :p
Basically I agree that one table is always better, but it depends on how complex the situation is and your version assumes that a weapon only does one type of damage, which is not a good assumption to make. Even some types of normal swords can do slashing 'edge' attacks, blunt trauma and piercing damage. It just depends how you hit someone with it. ;)
Quote:
It is also quite slow, since you have to ask the database for the weapon number, then ask it to look up each type assigned to the weapon and then finally ask it for each of the names for those retrieved types.
It is also quite slow, since you have to ask the database for the weapon number, then ask it to look up each type assigned to the weapon and then finally ask it for each of the names for those retrieved types.
It's not that bad, as you get all those pieces of information in a single query (joining the tables) and thus only do one ODBC call, not three. Also, the database server should be caching frequently-accessed and small pieces of data, so the time needed to do those joins could be quite small.
The reason I suggested the third table is that Magnum was having trouble making a form that used the two tables, but basically had a many-to-many relationship which isn't supported in third-normal form with two tables, without mucking around. The third table does things the "correct" way (in database design) and thus can be implemented simply in an Access form.
True Nick. I hadn't thought of using a single request, but then breaking it up like that would also tend to mean that you have to enter each record for the weapon's types seperately. Otherwise you are right back to 'will this form store all the values or only one?' problem he already has. Unless you can build a script in the form within Access, (paradox allows this, as does dBase, but where talking about an MS -we do it all for you, so it won't work right- product). lol Even if I had it installed I would probably not use it to design it as Magnum is doing, since I would have to rewrite most of the stuff the form does in vbscript anyway. And if that is the case, why do it the way Access would in the first place? ;)
Look, this is what I am talking about ...

Near the bottom of the form is the sub-form which shows all records (any number) from the weapon/damage table. As you can see it is nice and visual, you can click on any one and delete it, or click on the new record symbol ">*" to add a new one. You just select them from the combo box. Nice and simple.
This example does not use any scripting.
Ok. That is nice. So you can test it in there. However I still stand by the statement that you end up having to produce the same result in vbscript, once you do have it working. It does not therefore benefit you to even try to do it inside Access itself, unless you really think you want to look at it when not inside mushclient. If all computers had two monitors and dual-head graphics cards, maybe it would be, since you could then drop Access on the second display, but for the purposes of using the database from mushclient on most computers, it is imho not necessarilly that helpful.
But that is just my personal opinion. ;)
But that is just my personal opinion. ;)
Currently, all of my items are stored in an Excel spreadsheet. The one benefit to this, is that it visually looks very pretty.
I don't know how practical it is to write VBScript to both retrieve and create/modify data in a spreadsheet. I figured a database might be better. (MS Access, since I have it).
Naturally, the first step for me, is to get it all working right in Access. Later I will try and iteract with the database using MUSHclient scripts.
There are three main item types: Weapons, wearable/holdable items, and 'the rest'. I figured it would be sensible to put them all in ONE database, even though it would mean that some fields would be marked "Not applicable".
Eventually, it would be sweet to be able to use an alias to do an item search on pretty much any field.
A third option I considered, was just to provide a checkbox (Boolean) field for each damage type, and just check the ones that are appropriate. On the form, I might try and program it so that it would be impossible to have "Not applicable" checked while any damage type is checked. Shouldn't be too hard.
It's highly unlikely that any new damage types will ever be introduced. Even if they are, Access allows you to add a new field to an existing database, though it may take some hard drive thrashing time to get it done.
In respect to my original plan, I can see that I might have to abandon it, though I hate doing so without learning how to manage the problem. Part of the reason I took this project on was just to learn how to use Access. I still have a feeling there is a simple fix, but that no one seems to know what it is.
Anyway, with that proposition I just offered, I guess I now have three choices: Checkboxes, bit-map, or Nick's special lookup.
I'll mull it over a little while longer... see what you think of my idea, then maybe I'll take another stab at it soon.
I don't know how practical it is to write VBScript to both retrieve and create/modify data in a spreadsheet. I figured a database might be better. (MS Access, since I have it).
Naturally, the first step for me, is to get it all working right in Access. Later I will try and iteract with the database using MUSHclient scripts.
There are three main item types: Weapons, wearable/holdable items, and 'the rest'. I figured it would be sensible to put them all in ONE database, even though it would mean that some fields would be marked "Not applicable".
Eventually, it would be sweet to be able to use an alias to do an item search on pretty much any field.
A third option I considered, was just to provide a checkbox (Boolean) field for each damage type, and just check the ones that are appropriate. On the form, I might try and program it so that it would be impossible to have "Not applicable" checked while any damage type is checked. Shouldn't be too hard.
It's highly unlikely that any new damage types will ever be introduced. Even if they are, Access allows you to add a new field to an existing database, though it may take some hard drive thrashing time to get it done.
In respect to my original plan, I can see that I might have to abandon it, though I hate doing so without learning how to manage the problem. Part of the reason I took this project on was just to learn how to use Access. I still have a feeling there is a simple fix, but that no one seems to know what it is.
Anyway, with that proposition I just offered, I guess I now have three choices: Checkboxes, bit-map, or Nick's special lookup.
I'll mull it over a little while longer... see what you think of my idea, then maybe I'll take another stab at it soon.
Oops, I went back to look at page 3, and forgot there was a page 4.
That looks pretty good, Nick. I kind of wanted to display my own form window like you did, but only you can do that. :)
Are there any measures in place to keep me (the user) from selecting the same damage type twice within the sub-form? If it's not built in, I assume I can write VBS behind the scene's to error check. (Something you were wondering about Shadowfyr - Yes, it is possible). I have barely glanced at that though. This is my first time using Access. I haven't touched a database program since DBase IV, 15 years ago or so. (Ran under DOS).
Do you still feel this is a better method than the one I mentioned in my last post?
Thanks a ton for your assistance, Nick (and others). Much appreciated. :)
That looks pretty good, Nick. I kind of wanted to display my own form window like you did, but only you can do that. :)
Are there any measures in place to keep me (the user) from selecting the same damage type twice within the sub-form? If it's not built in, I assume I can write VBS behind the scene's to error check. (Something you were wondering about Shadowfyr - Yes, it is possible). I have barely glanced at that though. This is my first time using Access. I haven't touched a database program since DBase IV, 15 years ago or so. (Ran under DOS).
Do you still feel this is a better method than the one I mentioned in my last post?
Thanks a ton for your assistance, Nick (and others). Much appreciated. :)
Quote:
Are there any measures in place to keep me (the user) from selecting the same damage type twice within the sub-form?
Are there any measures in place to keep me (the user) from selecting the same damage type twice within the sub-form?
The combination of weapon/damage is the primary key, and you cannot have two primary keys the same, thus an attempt to add two of them will be rejected by Access.
You can have the modified database back if you want.
Yes, please mail it back to me. :)
OK, I emailed it to you. I did a query to show how you would get the list of weapon types back in a single query.
Gday People.
I was wondering if i could get a copy of some of the files you used to create this project- I'm having similar sorts of trouble in my speedwalking project (see browsers and mushclient post). When I manage to get my program done i'll let everyone know...
I was wondering if i could get a copy of some of the files you used to create this project- I'm having similar sorts of trouble in my speedwalking project (see browsers and mushclient post). When I manage to get my program done i'll let everyone know...
I haven't actually returned to this project since recieving the file from Nick. It's rather low priority for me so there is no hurry, although pretty educational as well, so I probably will get around to it sooner or later.
Your E-mail address is private in your bio, so I can't mail it to you. Mine is private too, so you can't mail me....
Ok, download it here: http://www.MagnumsWorld.com/ftp/AODitems.zip
I can't guarantee the file will stay there long term.
Your E-mail address is private in your bio, so I can't mail it to you. Mine is private too, so you can't mail me....
Ok, download it here: http://www.MagnumsWorld.com/ftp/AODitems.zip
I can't guarantee the file will stay there long term.
Thanks for those files, but now i have yet another question:
I've been attempting to make an item database.... It gets the item info from the mud and stores it in variables, passes this to the active x control, and then *should* pass it into a database. I figure its probably my coding, and i know the problem is in the active X, but not what it is :P. Here it is (i've added comments in !signs)
Public theworld As World
Sub GetStuff(theworld As World)
Dim object, itemtype, itemis, forgelvl, diceno, dicesize, !etc!
object = theworld.GetVariable("Object")
itemtype = theworld.GetVariable("ItemType")
!This is supposed to make one big field out of several variables!
itemis = theworld.GetVariable("ItemIs") + _
theworld.GetVariable("Savingpara") + _
theworld.GetVariable("Savingspell") + _
theworld.GetVariable("Poison") + _
theworld.GetVariable("spell") + _
theworld.GetVariable("abilities") + _
theworld.GetVariable("capacity")
forgelvl = theworld.GetVariable("ForgeLvl")
diceno = theworld.GetVariable("DiceNo")
dicesize = theworld.GetVariable("DiceSize")
!and etc!
Dim db
Set db = CreateObject("ADODB.Connection")
' Open the connection
db.Open = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=C:\Program Files\MUSHclient\ishnaf\DBproject\newdatabase.mdb"
db.Execute "INSERT INTO Items (Item, ItemType, ItemIs, ForgeLevel, DiceNo, DiceSize, Weight, Value, Rent, ACApply, Armor, CON, INT, WIS, DEX, CHA, STR, Hitroll, Damroll, maxhit, maxmana, Uselevel, Age) VALUES (" & _
"""" & Item & """, " & _
"""" & itemtype & """, " & _
"""" & itemis & """, " & _
"""" & ForgeLevel & """, " & _
"""" & diceno & """, " & _
!"""" & all the other bits& """, " & _!
"""" & dicesize & """ );"
db.Close
Set db = Nothing
End Sub
Hope you can understand that :)
I've been attempting to make an item database.... It gets the item info from the mud and stores it in variables, passes this to the active x control, and then *should* pass it into a database. I figure its probably my coding, and i know the problem is in the active X, but not what it is :P. Here it is (i've added comments in !signs)
Public theworld As World
Sub GetStuff(theworld As World)
Dim object, itemtype, itemis, forgelvl, diceno, dicesize, !etc!
object = theworld.GetVariable("Object")
itemtype = theworld.GetVariable("ItemType")
!This is supposed to make one big field out of several variables!
itemis = theworld.GetVariable("ItemIs") + _
theworld.GetVariable("Savingpara") + _
theworld.GetVariable("Savingspell") + _
theworld.GetVariable("Poison") + _
theworld.GetVariable("spell") + _
theworld.GetVariable("abilities") + _
theworld.GetVariable("capacity")
forgelvl = theworld.GetVariable("ForgeLvl")
diceno = theworld.GetVariable("DiceNo")
dicesize = theworld.GetVariable("DiceSize")
!and etc!
Dim db
Set db = CreateObject("ADODB.Connection")
' Open the connection
db.Open = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=C:\Program Files\MUSHclient\ishnaf\DBproject\newdatabase.mdb"
db.Execute "INSERT INTO Items (Item, ItemType, ItemIs, ForgeLevel, DiceNo, DiceSize, Weight, Value, Rent, ACApply, Armor, CON, INT, WIS, DEX, CHA, STR, Hitroll, Damroll, maxhit, maxmana, Uselevel, Age) VALUES (" & _
"""" & Item & """, " & _
"""" & itemtype & """, " & _
"""" & itemis & """, " & _
"""" & ForgeLevel & """, " & _
"""" & diceno & """, " & _
!"""" & all the other bits& """, " & _!
"""" & dicesize & """ );"
db.Close
Set db = Nothing
End Sub
Hope you can understand that :)
Try doing it into a variable and displaying that. eg.
x = "INSERT INTO Items (Item ... blah blah ...
world.note x
db.Execute x
That way you can see if the SQL looks OK.
x = "INSERT INTO Items (Item ... blah blah ...
world.note x
db.Execute x
That way you can see if the SQL looks OK.
I have now written a lengthy plugin to demonstrate creating a database, creating a table, adding data, getting it back, deleting it, and general actions on it.