Game Development Community

dev|Pro Game Development Curriculum

Torque DB - Journal System Example

by Matthew Langley · 04/14/2005 (11:29 pm) · 65 comments

Torque DB: Journal System Example

Here is the zip file

This is a completely TorqueScript Database, included is a bunch of pics and description of it, the best advice I could give is to go into the database scripts... I have included some good commenting and good descriptions before most functions :)

Note: this also includes a full Journal System example... for this example I modified guiMessageVectorCtrl.cc and guiMessageVectorCtrl.h

here I will detail my modifications and how to quickly add those to yours... (I didn't add much and I didn't take away anything)...

note: the book graphic included was done using an academic version of Photoshop, it is for non-commercial use only

Steps to implement this resource:

First - either back up your entire Torque folder or at least back up your guiMessageVectorCtrl.cc and guiMessageVectorCtrl.h (Torque\SDK\engine\gui by default)

1. Modify the source code

first go to "guiMessageVectorCtrl.h"
search for this line of code

//   void onMouseMove(const GuiEvent &event);

and uncomment it... like this

void onMouseMove(const GuiEvent &event);

thats all for the .h file ;) We're just adding that function back in that was commented out

second go to "guiMessageVectorCtrl.cc"


now scroll down all the way to the bottom of the file, you should see this set of code

mMouseDown      = false;
   mMouseSpecialLine = -1;
   mMouseSpecialRef  = -1;

add this before it

//---------------------------------------------
   //Matthew "King BoB" Langley - Resource: Journal System...
   Con::executef(this, 2, "clickLineCallback", Con::getIntArg( mMouseSpecialLine ));
   //---------------------------------------------

this is simply providing a callback that passes the line you click on

now add this function to the end of the file

//---------------------------------------------
//Matthew "King BoB" Langley - Resource: Journal System...


void GuiMessageVectorCtrl::onMouseMove(const GuiEvent& event)
{
	//Parent::onMouseMove(event);

	S32 currLine;
   	S32 currRef;
   	findSpecialFromCoord(globalToLocalCoord(event.mousePoint), &currLine, &currRef);

   	Con::executef(this, 2, "moveLineCallback", Con::getIntArg( currLine ));

}

//---------------------------------------------


and that (as you've probably guessed) is simply passing the line you move over... note: this is not optimized, this probably could be cut down a lot, I'm simply using an existing function, I could make my own function (based off of the "findSpecialFromCoord" that only returns the line; however, I presently don't have the time to do so =/ This seems to not affect things much though, have run into little FPS loss (if any)... I look forward to hearing results...


2. Script end of it

ok, well the script end of it is very easy, simply take the included zip file... unzip it and it should contain three folders a .gui file and a .png image, grab the two folders database and journal, drop these in Torque\SDK\example\starter.fps\client (or if you decide to put them elsewhere)... and be sure to include these exec statements (easy to put them in Torque\SDK\example\starter.fps\client\init.cs)

exec("./scripts/journal/journal.cs");
exec("./scripts/database/TorqueDB.cs");

now grab the .gui file and .png
journal.gui
journalbook.png

then put them in Torque\SDK\example\starter.fps\client\ui\

and be sure to add an exec for

exec("./ui/journal.gui");

best place is to put it in init.cs as well with the other .gui exec statements


make sure you change

$torqueDBPath = "starter.fps/data/journalData/";

if needed (its at the top of file.cs)

that will point to where you want to put your journalData folder... this holds the config and output text file... note I have two Dump commands in file.cs... one for plain text and the regular one will encrypt... later I will include some instructions on generating new encryptions... (its a very basic encryption)

ok, now if you recompile for the C++ changes, then simply fire up Torque... bring up the console and type
journalDebug();

you should see the book come up with your journal entries, try selecting and clicking... note the first menu takes you a level "above" "entry"... after that you cannot get back to entry (unless you add your own way to)... I'm hoping to add more functionality to an overal view, though right now wanted to at least release this.




now here is some more information about the rest of this, hopefully I can create some step by step instruction, but again just wanted to get this out there at least :)



here is the layout
www.razedskyz.com/games/torque/plan/journalDiagram1(small).JPGwww.razedskyz.com/games/torque/plan/journalDiagram2(small).JPG

here is the journal system in action
www.razedskyz.com/games/torque/plan/journalpic1.JPGwww.razedskyz.com/games/torque/plan/journalpic2.JPG
first here is the config file... a basic text file.

www.razedskyz.com/games/torque/plan/journalconfig.JPG
as you can see it starts off with

this begins what you can think of a new journal class or type... you don't even really have to think of it as journal specific... as you can see down a few lines I have ... which works perfectly well.

after the "<>" tag (this starts the type)...
you see text starting with a "-"... this denotes a property... so we now assign name, text, and item to the "entry" type.

and name, value, power to the "item" type.

the next file you can see how we use it.

www.razedskyz.com/games/torque/plan/journaltext.JPG
here we see an .. (note: for simplicity I have it started the same way with an html style <> tag)
then the sub property -name is being assigned "BoB"... then values are assigned to text and item... in this case you could think of it as if the player talked to "BoB" and was assigned the quest with the reward (which you could easily change item to reward in the config file) by accomplishing "Go here and do that."
Next entry I clip off some entries for testing... and at the end i have an Item... beginning with the same tag and ending with the tag...(this all keeps it extremeley simple to write and edit, though when I'm finished you should never have to manually edit journal.txt... even though as you can see its simple enough

ok, so I've shown you a couple of text file, how exciting, lol... ok let me show you some debug info output from loading these file through my parsers...

www.razedskyz.com/games/torque/plan/console.JPG
all of these variables are scriptobjects used as arrays, minus $journalMainTags... which was left as a standard single dimension array... I used scripts objects due to the need of having multi-dimension arrays (even got up to a 3 dimensional array =/)... plus as I saw in the resource I beleive these can then be passed (though still have to test)... which adds a large ammount of usability if one wanted to change them to local variables instead.

ok... first off
$journalMainTags...
this (as you can probably see) holds each different type loaded from the config file... right now simply two... entry and item...
more importantly these arrays run together, so you grab the array value of entry or item and then get full access to all the values under the main array.
(as you can see I like seperating things for organization and usability)...

$journalSubTags contains the properties of those main taqgs listed in $journalMainTags...

as you can see [0][0] is name
and [1][0] is name...
reinforcing my method of keeping them aligned with eachother.

$mainValueArray holds the instance (or what you could think of as an instance for you OO programmers out there) of each type loaded from journal.txt
[0] - entry
[0] - entry
[0] - entry
[1] - item
following the journal.txt format of three entries and an item... this is good for quick navigation and later when I implement searching abilities, optimizing the access of information without looping through the entire main array.

now we get to the big boy, the 3-dimensional array... $subValueArray... the first array value denotes the same as the other arrays... the type... 0 is for the first type loaded, entry... 1 is for the second type loaded, item. Again as you have seen this is all dynamically loaded through the config file. It then loads an instance of each of those... first an instance of the "entry"..
[0][0] denoting the first instance (the second 0)... of the first class/type (the first 0)... then it defines [0][0][0] = BoB
this takes the first intance of the first class and adds its first property (again using OO programming principals) and sets it to BoB... this would be the name...
note it loads these in order of the config file... not the journal file...
if you put
name
item
text

it will load
[0] - name
[1] - item
[2] - text

even if the journal is written
item
name
text

NOTE this was added , lol this is a bit dated writeup (at least part of this is a bit dated, though a good description) - ignore the "which I plan on adding"
... now with the right array writing functions (which I plan on adding, as well as an easy template to write your own for customization) the order should always be correct, but figured I'd specify that it loads the config file, then loads the journal and comparesthe first config entry until it gets the journal entry matching, it doesnt simply load it, hoping it all is in proper order. This added for some interesting loops and code, but was well worth it for the control it grants you.

after the first entry it then addes
[0][1][0] - BoB2

first [0] means first class/type (still entry) the [1] meaning the second 'instance' of this class/type... and [0] being the first property... which can be referenced by the class/type number
[0] and the property number [0]...
to
$journalSubTags.contents[0].contents[0] - name
to find the property name.

then it adds a third instance (as many instances as you want can be loaded)... then it goes to the new class/type... [1]... which can be referenced using the same number with
$journalMainTags[1] - item

so it loads item data in
[1][0][0] - rod of death
[1][0][1] - 500
[1][0][2] - death


according to the properties in
$journalSubTags.contents[1].contents[0] - name
$journalSubTags.contents[1].contents[1] - value
$journalSubTags.contents[1].contents[2] - power


[image][/image]



------------------- ARRAYS --------------------------
$journalMainTags
------------------------------------------------------------------
$journalMainTags[0] - entry
$journalMainTags[1] - item
------------------------------------------------------------------
$journalSubTags
------------------------------------------------------------------
$journalSubTags.contents[0].contents[0] - name
$journalSubTags.contents[0].contents[1] - text
$journalSubTags.contents[0].contents[2] - item
$journalSubTags.contents[0].contents[3] - image
$journalSubTags.contents[1].contents[0] - name
$journalSubTags.contents[1].contents[1] - value
$journalSubTags.contents[1].contents[2] - power
------------------------------------------------------------------
$mainValueArray
------------------------------------------------------------------
$mainValueArray.contents[0] - 3
$mainValueArray.contents[1] - 1
------------------------------------------------------------------
$subValueArray
------------------------------------------------------------------
$subValueArray.contents[0].contents[0].contents[0] - Quest given by: BoB
$subValueArray.contents[0].contents[0].contents[1] - Quest text: Go here and do that
$subValueArray.contents[0].contents[0].contents[2] - Quest item: sword of fire
$subValueArray.contents[0].contents[0].contents[3] - Dark.Game/client/ui/GarageGames.jpg
$subValueArray.contents[0].contents[1].contents[0] - BoB2
$subValueArray.contents[0].contents[1].contents[1] - slay the beast and return its head
$subValueArray.contents[0].contents[1].contents[2] - rod of death
$subValueArray.contents[0].contents[1].contents[3] - Dark.Game/client/ui/background.jpg
$subValueArray.contents[0].contents[2].contents[0] - BoB3
$subValueArray.contents[0].contents[2].contents[1] - Go here and do that3
$subValueArray.contents[0].contents[2].contents[2] - shield of valor
$subValueArray.contents[1].contents[0].contents[0] - rod of death
$subValueArray.contents[1].contents[0].contents[1] - 500
$subValueArray.contents[1].contents[0].contents[2] - death
------------------------------------------------------------------

---------------------- VARIABLES -----------------------
$journalArrayCount contents: 22
$mainTagCount contents: 2
$subTagCount[0] contents: 4
$subTagCount[1] contents: 3
$sub2TagCount contents: 0
"



Functions for the journal system

--------------------- File loading and unloading Functions---------------------------

.dumpFile()

This function takes all of the existing information in the arrays and dumps it to a config (setup file)

as well as a journal file that holds all of the entries, data, etc.


.loadFile()

This function is the heap of loops that parses the config file to gain access to the values it will parse

the journal file with. It loads all the information into the arrays as well as the counters that keep track of

everything.

--------------------- Data Manipulation Functions (what you will most likely use the most

of)---------------------------

------Delete functions

.deleteClass()

This function will delete a class if you pass it the class you want deleted... for example
say I wanted to delete the "entry" class...
TorqueDB.deleteProperty("entry");
This will delete that class, as well as all of the instances of that class, updating the arrays
and counters properly


.deleteProperty()

This function will delete a property if you pass it the class and the property you want deleted... for

example
say I wanted to delete the "name" property of the "entry" class...
TorqueDB.deleteProperty("entry", "name");
This will delete that property for the class, and in all of the instances of that class, updating the arrays
and counters properly


.deleteInstance()

This function will delete an instance if you pass it the class, identifying property and value(instance)

of that
property... for example... say I want to delete the "entry"(class) with the "name" (property) of "BoB2"

(instance)
TorqueDB.deleteInstance("entry", "name", "BoB2");
this successfully deleted that instance and correctly updates the arrays and counters

------Write functions

.writeClass()

This will add a new class to the data that will eventually be dumped to the config file
Important to remember you must write the class with this function then populate its
properties with the following functions... You must include one property and value
so it will add one instance on creation, further instances can be added via the
TorqueDB.addInstance function as used inside this function... for example
TorqueDB.writeClass("spell", "name", "ball of fire");
this will add the spell class, which in turn adds "" and "" to the config


.writeProperty()

This will add a new property to the specified class that will eventually be dumped to
the config file... for example
TorqueDB.writeProperty("spell", "damage");
this will add the spell class, which in turn adds "" and "" to the config

------Write functions - to the data (journal in my example)

.writePropertyValue()

This function will write a value to a property. For example... say I wanted to change the name of my

"rod of death"
under the "item" class. I would do this
TorqueDB.writePropertyvalue("item", "name", "rod of death", "name", "new rod of death");
As you can see I pass all the information needed to identify the rod of death and to change its property


.addInstance()

This function will add an instance... Key things to remember, the class must exist and the
property must exist already... using the above functions this is accomplished...
you must specify at least one starting value to reference the instance, can be any that you
created or that are already there
for example... say you wanted to add an instance of the "item" class, thats called
"Sword of Fire"
TorqueDB.addInstance("item", "name", "Sword of Fire");
now the instance of item with the name property of "Sword of Fire" has been added
from here you can use TorqueDB.writePropertyValue to add values to the existing properties
of the "item" class or the TorqueDB.writeProperty to add more properties to the "item"
class
NOTE: the following is a bit more advanced, knowledge of Script Object arrays is needed
you do not NEED to use them, the above specifies normal use, this is additional use
Now I also added support for passing it ScriptObject arrays... though you could just loop
this function and create all sorts of instances I figured I'd make this resource as robust
as possible... There are two types of ScriptObject arrays that it will take... "single" and
"multi"... to pass a ScriptObject array you would call do a call like
TorqueDB.addInstance("entry", "single", $testArray);
note the first value is the same, the class, the second specifies "single" or "multi" and
the last is the ScriptObject array, the code will automatically detect if its a ScriptObject
array and process it accordingly... for example

Example of "single"

$testArray = new ScriptObject();
$testArray.contents[0] = new ScriptObject();
$testArray.contents[1] = new ScriptObject();

$testArray.contents[0].contents[0] = "name";
$testArray.contents[0].contents[1] = "person1";
$testArray.contents[0].contents[2] = "text";
$testArray.contents[0].contents[3] = "BLAH BLAH BLAH";
$testArray.contents[0].contents[4] = "item";
$testArray.contents[0].contents[5] = "Sword of Fire";
$testArray.contents[1].contents[0] = "name";
$testArray.contents[1].contents[1] = "person2";
$testArray.contents[1].contents[2] = "text";
$testArray.contents[1].contents[3] = "blah blah blah";
$testArray.contents[1].contents[4] = "item";
$testArray.contents[1].contents[5] = "Sword of Death";

Example of "multi"

$testArray = new ScriptObject();
$testArray.contents[0] = new ScriptObject();
$testArray.contents[1] = new ScriptObject();

$testArray.contents[0].contents[0] = "name";
$testArray.contents[0].contents[1] = "person1";
$testArray.contents[0].contents[2] = "person2";
$testArray.contents[0].contents[3] = "person3";
$testArray.contents[0].contents[4] = "person4";
$testArray.contents[1].contents[0] = "item";
$testArray.contents[1].contents[1] = "sword1";
$testArray.contents[1].contents[2] = "sword2";
$testArray.contents[1].contents[3] = "sword3";
$testArray.contents[1].contents[4] = "sword4";


Single is set up like this
Array[0][0] - property
Array[0][1] - value
Array[0][2] - property
Array[0][3] - value
Array[1][0] - property
Array[1][1] - value
Array[1][2] - property
Array[1][3] - value
etc etc... this will create two new instances, the first array number denotes the seperation
of instances....

Multi is set up like this
Array[0][0] - property
Array[0][1] - value
Array[0][2] - value
Array[0][3] - value
Array[1][0] - property
Array[1][1] - value
Array[1][2] - value
Array[1][3] - value
Now this is quite a bit different, six different instances will be created, basically multi
is a good way to spam a bunch of simple instances, while single adds instances with multiple
properties


------Search functions

.searchByClass()

search by passing the class and returning the entire class contents
for example... I have "entry" and "item" classes... If I want to
return all entry types I would use
$entries = TorqueDB.searchByClass("entry");
then I could use
echo($entries.contents[0].contents[0]);
and the first property of the first "entry" should display in console
this will be usefull if you use this system as inventory, dialog, etc,
like a database, so you can return only the type you want


.searchByInstanceWithClass()

search by passing the class, property, and instance and returning the instance
for example... I want to get the item by the property "name" of "rod of death" and I know
its of class "item"... I would use
$testInstance = TorqueDB.searchByInstanceWithClass("item", "name", "rod of death");
then I could use
echo($testInstance.contents[0]);
and the first property of "rod of death" would be displayed in console, which would be
"rod of death"
the name we searched by...
echo($testInstance.contents[1]);
would reveal "500"


.searchByInstance()

search by passing the property, and instance and returning the instance, without passing class
note: its recommended to use the previous function, specifying the class will increase efficiency
especially if you have a lot of classes
for example... I want to get the item by the property "name" of "rod of death" without knowing
the class... note: the property instance must be unique! or it will simply return the first
match it finds
$testInstance = TorqueDB.searchByInstance("name", "rod of death");
then I could use
echo($testInstance.contents[0]);
and the first property of "rod of death" would be displayed in console, which would be
"rod of death"
the name we searched by...
echo($testInstance.contents[1]);
would reveal "500"


.searchByPropertyWithClass()

search by passing the class, property, instance, and return property and returning the
property value you specified in %returnProperty
for example... I want to get the item by the property "name" of "rod of death" and I know
its of class "item" and I want to get its "value" I would use
$testProperty = TorqueDB.searchByPropertyWithClass("item", "name", "rod of death", "value");
then I could use
echo($testProperty);
and it would reveal the "value" of "500"


.searchByProperty()

search by passing the property, instance, and return property and returning the
property value you specified in %returnProperty
for example... I want to get the item by the property "name" of "rod of death" and I know
its of class "item" and I want to get its "value" I would use
$testProperty = TorqueDB.searchByProperty("name", "rod of death", "value");
then I could use
echo($testProperty);
and it would reveal the "value" of "500"


-----------------------------------------------------------
Using these search functions... heres an example
-----------------------------------------------------------

Say you have the quest for the "rod of death"... the user wants to know its value...so you do
$testProperty = TorqueDB.searchByPropertyWithClass("item", "name", "rod of death", "value");
The value returns "500"... they like this... so they then want to see what the quest entails..
to get the text associated with the journal entry for this, you would do
$testProperty = TorqueDB.searchByPropertyWithClass("entry", "item", "rod of death", "text");
and it returns "slay the beast and return its head"
then the user asks, who do I return it to... you would get this info by
$testProperty = TorqueDB.searchByPropertyWithClass("entry", "item", "rod of death", "name");
which would return "BoB2"
this is just an example of the many ways to manipulate these functions... note I did include
search functions 'without' the class specification, though you can tell this will decrease
search efficiency

Another great use for this will be setting up a user searchable journal, item list
really its more like a database that can be used for anything... Quite nifty...
In all truth with the functions given it isnt too hard, though it won't be very smart...
(unless you add a bit of a smartness to it, like estimating what the user means, etc)
I may include GUIs and such for this by the time I release this resource, lol if you see it
included then I decided to go ahead and do that, if not then you shouldn't have much trouble
doing it yourself
I may even add functionality to search through the text of each entry, though adding searching
by item, name, or any 'property' or 'class' should be a snap with these functions



------Return functions

.returnClass()

This is for internal uses, it will return the class number used in the data arrays
by passing it the class name ... for example
$classNum = TorqueDB.returnClass("entry");
will return "0"... which then could be identified by using it in the arrays, like
echo($journalMainTags[TorqueDB.returnClass("entry")]);
should display "entry"


.returnProperty()

This is for internal uses, it will return the property number used in the data arrays
by passing a class, and property... for example
$propertyNum = TorqueDB.returnProperty("item","name");
this will return the array number


.returnInstance()

This is for internal uses, it will return the instance number used in the data arrays
by passing a class, property, and instance... for example
$instanceNum = TorqueDB.returnInstance("item","name","rod of death");
this will return the array number


.returnClassName()

This function is used if you have the internal array class Number and want to get the
class name... After some thought I decided to use the above return commands passing the
text name... like "entry" rather than having you convert it to the 0 it is in the internal
array... so I added these two function to help if you already have the number and need the
name, whether for the above functions or for another use... for example
$className = TorqueDB.returnClassName(0);
echo($className);
this should display "entry" in the console


.returnPropertyName()

Heres the second number to name function... pass it the class "text" and the property
number an it will return the property name, look above for reasons you might use this
... for example
$propertyName = TorqueDB.returnPropertyName("entry", 0);
echo($propertyName);
this should print "name" in the console







--------------------- Debug Functions---------------------------

.debug()
and
.debugInfo()

I included a couple debug functions, one that loads the file and displays the journal ... the other is

called from the debug command but can be run seperately also, debugInfo is very usefull it will display all the

contents of the arrays and counters, correctly labeled.


--------------------- String Helper Functions---------------------------


.getTagName()

Here is an internal command that is very usefull... it will get the name out of a tag... for example...

you pass it "" and it gets "entry" out of it... just a helper string function

.returnStringAfter()

Here is another internal helper string function... pass it .returnStringAfter("name=bob", "=") and it

will return "bob"...

.returnStringBefore()

Same as .returnStringAfter except it returns the string before the character

--------------------- Display Functions---------------------------

.displaySub(%lastMenu, %obj)

This function will display a sub object of the journal overlayed over the journal (as well as an image in

the right pane if you have one under that sub... In a journal example this displays the journal entry, the text,

the name, items associated with it, etc... Basically this displays an "instance"

.displayType(%lastMenu)

This will display all of the types or "classes"... so if you have an "entry" class and an "item" class

then it will display entry and item... so then you can browse down further

.displayMain(%lastMenu, %obj)

This will display the contents of a type or "class"... so if you want to display all the journal entries

this would be the function that would... note: the first property of an entry is what is display when listing

all the entries, so you can choose to make the first entry a name, a title, or whatever you choose.

About the author

Was a GG Associate and then joined GG in 2005. Lead tool dev for T2D and T3D. In 2011 joined mobile company ngmoco/DeNA and spent about 4 years working game and server tech. 2014 joined startup Merigo Games developing server technology.

#21
05/18/2005 (4:32 pm)
Quote:
--> Compiling gui/guiMessageVectorCtrl.cc
gui/guiMessageVectorCtrl.cc: In member function `virtual void
GuiMessageVectorCtrl::onMouseUp(const GuiEvent&)':
gui/guiMessageVectorCtrl.cc:819: error: `currLine' undeclared (first use this
function)
gui/guiMessageVectorCtrl.cc:819: error: (Each undeclared identifier is reported
only once for each function it appears in.)
gui/guiMessageVectorCtrl.cc: In member function `virtual void
GuiMessageVectorCtrl::onMouseMove(const GuiEvent&)':
gui/guiMessageVectorCtrl.cc:841: error: invalid conversion from `S32' to `const
char*'
gui/guiMessageVectorCtrl.cc:841: error: initializing argument 1 of `const
char* _StringTable::insert(const char*, bool)'
gui/guiMessageVectorCtrl.cc:841: error: invalid conversion from `const char*'
to `char*'
make[1]: *** [out.GCC3.DEBUG/gui/guiMessageVectorCtrl.obj] Error 1
make: *** [default] Error 2

Yuck! :(
#22
05/18/2005 (4:35 pm)
Ouch, I think I just had a slow moment... I don't think that fix on my fix will work either lol, I'm at work, I'll have to try some things at home tonight.
#23
05/18/2005 (4:40 pm)
I'm getting it cleaned up a little, gimme some time I should have a fix, only thing holding me back is that StringTable->insert() returns an S32 when in fact we need a string, I'm going to take a look a closer look at the StringTable and see if I can get somewhere with this.
#24
05/18/2005 (4:41 pm)
Thanks :) lol I did that code probably a good 5- 7 months ago (give or take)... Been cleaning up the script side and giving me a headache going back to told code.
#25
05/18/2005 (5:26 pm)
Good lord, what a bunch of hassle the string table is turning into for this purpose, can you please explain in detail what exactly the lines of code in question are trying to do, them maybe I can get this dog to bark. :)
#26
05/18/2005 (5:32 pm)
Ouch sorry to hear...

All it is is converting the "currLine" ( just the line ) to a string to be passed back to the script
#27
05/18/2005 (6:28 pm)
Remove all refferences to buffer, so the 2 main code blocks that deal with itoa should look like this...
//---------------------------------------------
   //Matthew Langley - Resource: Journal System...
   char* lineCallBackString = itoa( mMouseSpecialLine, 10 );
   Con::executef(this, 2, "clickLineCallback", lineCallBackString);
   //---------------------------------------------
char* lineCallBackString = itoa( currLine, 10 );

Add this to your guiMessageVectorCtl.cc file, down at the bottom
char* GuiMessageVectorCtrl::itoa(int val, int base){
	
	static char buf[32] = {0};
	
	int i = 30;
	
	for(; val && i ; --i, val /= base)
	
		buf[i] = "0123456789abcdef"[val % base];
	
	return &buf[i+1];
	
}

And finally make sure to declare it in guiMessageVectorCtl.h at about line 124 add
void createLineElement(LineElement&, LineWrapping&, SpecialMarkers&);
   char* itoa(int val, int base);
   void findSpecialFromCoord(const Point2I&, S32*, S32*);
#28
05/19/2005 (3:51 pm)
Just one helpful hint, don't hardcode the mod instead use, $UserMod@

For instance in file .cs you have it as
$torqueDBPath = "starter.fps/data/journalData/";

When in fact this would probably be better
$torqueDBPath = $UserMod@"/data/journalData/";


Also nowhere did you state that journalData needs to be placed in the data folder.

Other than that, great work! I'm giving it a 5 an including it as part of my MMORPG tutorial series.
#29
05/19/2005 (4:08 pm)
very good point... I definately need to polish some things

$torqueDBPath = $UserMod@"/data/journalData/";

is a very good suggestion...

today I did a tutorial using the DB in T2D and cleaned up some of the TorqueDB and uploaded a new version that has everything working as intended now...

I really need to update my description on this. I also need to modify the Tutorial for Torque as well (though the principles are the same the pictures will be different... that suggesiton will help compatability as well..

Glad you enjoyed this, love your MMORPG tutorials series.

EDIT: I did 95% of the resource a few months ago and finally had gotten a night to package it (it was one huge script file) so finally I've gotten a chance to clean it up and fix some bugs... hopefully I can take your suggestions and clean it more... maybe I can find a universal conversion method =/
#30
05/20/2005 (12:19 am)
Wow! Thats one hell of a code cleanup, great work!
#31
05/21/2005 (12:04 pm)
ok finally fixed the itoa problem... found Con::getIntArg and it works well :)
#32
05/23/2005 (10:19 am)
uploaded a new version that fixes a potentional problem with the journal system...

also modified the encrypt.cs slightly to finish off a random encryption generator... its very basic. Nothing even remotely fancy lol, just randomly picks two letters/numbers for each letter/number... it compares and checks to ensure all sets are unique... right now it wouldn't take much to set it up to load the data in with the previous encryption (from the .dso) generate a new .cs encryption file... exec it to change the encryption set to a new one, then set the .cs file to empty and run off of the .dso, so each time the game starts it generates new sets.

Or you can just dump it to plain text if you don't want to even both with the makeshift encryption lol.
#33
05/23/2005 (1:26 pm)
Dang it Matt! I just barely got things working over here :)
/sigh guess I get to do it again :)

Anyways thanks for keeping up the great work!
#34
05/23/2005 (7:23 pm)
lol, no worries, the fix in the journal is the one I've given you already :) Just finally made it to the final version I have uploaded... the encryption updates only effect encrypt.cs so you can swap that file in (that is if you ever want the simple encryption, nothing fancy, though prevents someone from simply editing the files if you plan to store save game data).
#35
05/29/2005 (8:11 pm)
Great Resource!
quick question, how do you get the number of instances (rows) for a particular class (table)? Do you have a wrapper function for this?
#36
05/29/2005 (8:54 pm)
also I am getting this error when compiling encrypt.cs (don't know if it makes a difference)

Compiling client/scripts/database/encrypt.cs...
Loading compiled script client/scripts/database/encrypt.cs.
getSubStr(...): error, starting position and desired length must be >= 0: (1, -1)
#37
08/15/2005 (12:11 pm)
Hey Matthew, great resource! Finally got a chance to try it out. I'm confused though as to what exactly it is I need to add to the .h and .cc files after Dreamer's changes and all. When I load journalDebug(); I only get "entry, item, entry" in the journal book. I also get this message in the console log a couple of times:
Con::execute - 0 has no namespace: clickLineCallback

Thanks, Nick
#38
08/23/2005 (2:11 am)
me too.
config.log
...
looking for w5 and comparing to w5
found char match of w5 with w5 which is now changed to w5
we now have... we now have...
main tag
searchmode: main
0
looking for a main
Returned Main Tag: entry
file is NOT EOF
attempting to decrypt this line: -ryvsk95f
we now have... -
looking for ry and comparing to vs
...
looking for 5f and comparing to 5f
found char match of 5f with 5f which is now changed to 5f
we now have... -name
sub tag
searchmode: sub
1
looking for a sub
Sub Tag: name
file is NOT EOF
attempting to decrypt this line: -nz5f63nz
we now have... -
looking for nz and comparing to vs
...
looking for nz and comparing to nz
found char match of nz with nz which is now changed to nz
we now have... -text
sub tag
searchmode: sub
2
looking for a sub
Sub Tag: text
file is NOT EOF
attempting to decrypt this line: -mknz5fk9
we now have... -
looking for mk and comparing to vs
...
looking for k9 and comparing to k9
found char match of k9 with k9 which is now changed to k9
we now have... -item
sub tag
searchmode: sub
3
looking for a sub
Sub Tag: item
file is NOT EOF
attempting to decrypt this line: -mkk9vs1r5f
we now have... -
looking for mk and comparing to vs
...
looking for 5f and comparing to 5f
found char match of 5f with 5f which is now changed to 5f
we now have... -image
sub tag
searchmode: sub
4
looking for a sub
Sub Tag: image
file is NOT EOF
attempting to decrypt this line:
we now have... <
we now have... looking for 5f and comparing to vs
...
looking for w5 and comparing to w5
found char match of w5 with w5 which is now changed to w5
we now have...
we now have...
main tag
searchmode: main
5
end tag
file is NOT EOF
attempting to decrypt this line:
we now have... <
looking for mk and comparing to vs
...
looking for k9 and comparing to k9
found char match of k9 with k9 which is now changed to k9
we now have... we now have...
main tag
searchmode: main
6
looking for a main
Returned Main Tag: item
file is NOT EOF
attempting to decrypt this line: -ryvsk95f
we now have... -
looking for ry and comparing to vs
...
looking for 5f and comparing to 5f
found char match of 5f with 5f which is now changed to 5f
we now have... -name
sub tag
searchmode: sub
7
looking for a sub
Sub Tag: name
file is NOT EOF
attempting to decrypt this line: -59vsjzds5f
we now have... -
looking for 59 and comparing to vs
...
looking for 5f and comparing to 5f
found char match of 5f with 5f which is now changed to 5f
we now have... -value
sub tag
searchmode: sub
8
looking for a sub
Sub Tag: value
file is NOT EOF
attempting to decrypt this line: -8owu275f46
we now have... -
looking for 8o and comparing to vs
...
looking for 46 and comparing to 46
found char match of 46 with 46 which is now changed to 46
we now have... -power
sub tag
searchmode: sub
9
looking for a sub
Sub Tag: power
file is NOT EOF
attempting to decrypt this line:

we now have... <
we now have... looking for mk and comparing to vs
...
looking for k9 and comparing to k9
found char match of k9 with k9 which is now changed to k9
we now have... we now have...
main tag
searchmode: main
10
end tag
CONFIG file scan ENDED
scanning file to find end
file loop
attempting to decrypt this line: <5frynz46w5>
we now have... <
looking for 5f and comparing to vs
...
looking for w5 and comparing to w5
found char match of w5 with w5 which is now changed to w5
we now have... we now have...
searchmode: main
11
entry
found
looking for a main
1
file loop
attempting to decrypt this line: -ryvsk95f=7qds5fginz 1rmk595fry 70w5: 70wu70
we now have... -
looking for ry and comparing to vs
...
looking for 5f and comparing to 5f
found char match of 5f with 5f which is now changed to 5f
we now have... -name
we now have... -name=
looking for 7q and comparing to vs
...
looking for wu and comparing to wu
found char match of wu with wu which is now changed to wu
we now have... -name=quest given by: bo
looking for 70 and comparing to vs
looking for 70 and comparing to 70
found char match of 70 with 70 which is now changed to 70
we now have... -name=quest given by: bob
searchmode: sub
12
looking for a sub
-name=quest given by: bob
looking for: name
found name
loading from main: 0 to sub: 0
2
file loop
attempting to decrypt this line: -nz5f63nz=7qds5fginz nz5f63nz: 1rwu 0k5f465f vsry5n 5nwu nz0kvsnz
we now have... -
looking for nz and comparing to vs
...
#39
08/23/2005 (2:18 am)
continue...
--------- closeStartupGui('Canvas.setContent(MainMenuGui);')---------
mouse0 input device created.
CLASS RETURNED = 0
adding a basic instance
creating object
CLASS RETURNED = 0
CLASS RETURNED = 0
CLASS RETURNED = 0
CLASS RETURNED = 0
CLASS RETURNED = 0
%randOne = 30
%randTwo = 5
%randCombo = 4f
found a combo set for - 0
%randOne = 1
%randTwo = 18
%randCombo = bs
found a combo set for - 1
%randOne = 34
%randTwo = 12
%randCombo = 8m
found a combo set for - 2
%randOne = 28
%randTwo = 21
%randCombo = 2v
found a combo set for - 3
%randOne = 29
%randTwo = 22
%randCombo = 3w
found a combo set for - 4
%randOne = 1
%randTwo = 19
%randCombo = bt
found a combo set for - 5
%randOne = 25
%randTwo = 27
%randCombo = z1
found a combo set for - 6
%randOne = 27
%randTwo = 17
%randCombo = 1r
found a combo set for - 7
%randOne = 10
%randTwo = 9
%randCombo = kj
found a combo set for - 8
%randOne = 11
%randTwo = 32
%randCombo = l6
found a combo set for - 9
%randOne = 29
%randTwo = 14
%randCombo = 3o
found a combo set for - 10
%randOne = 34
%randTwo = 10
%randCombo = 8k
found a combo set for - 11
%randOne = 4
%randTwo = 30
%randCombo = e4
found a combo set for - 12
%randOne = 15
%randTwo = 10
%randCombo = pk
found a combo set for - 13
%randOne = 31
%randTwo = 15
%randCombo = 5p
found a combo set for - 14
%randOne = 24
%randTwo = 23
%randCombo = yx
found a combo set for - 15
%randOne = 3
%randTwo = 33
%randCombo = d7
found a combo set for - 16
%randOne = 20
%randTwo = 6
%randCombo = ug
found a combo set for - 17
%randOne = 35
%randTwo = 13
%randCombo = 9n
found a combo set for - 18
%randOne = 23
%randTwo = 18
%randCombo = xs
found a combo set for - 19
%randOne = 12
%randTwo = 8
%randCombo = mi
found a combo set for - 20
%randOne = 3
%randTwo = 0
%randCombo = da
found a combo set for - 21
%randOne = 24
%randTwo = 22
%randCombo = yw
found a combo set for - 22
%randOne = 16
%randTwo = 1
%randCombo = qb
found a combo set for - 23
%randOne = 15
%randTwo = 33
%randCombo = p7
found a combo set for - 24
%randOne = 3
%randTwo = 13
%randCombo = dn
found a combo set for - 25
%randOne = 26
%randTwo = 11
%randCombo = 0l
found a combo set for - 26
%randOne = 1
%randTwo = 4
%randCombo = be
found a combo set for - 27
%randOne = 26
%randTwo = 15
%randCombo = 0p
found a combo set for - 28
%randOne = 7
%randTwo = 15
%randCombo = hp
found a combo set for - 29
%randOne = 18
%randTwo = 1
%randCombo = sb
found a combo set for - 30
%randOne = 33
%randTwo = 23
%randCombo = 7x
found a combo set for - 31
%randOne = 29
%randTwo = 17
%randCombo = 3r
found a combo set for - 32
%randOne = 11
%randTwo = 2
%randCombo = lc
found a combo set for - 33
%randOne = 20
%randTwo = 26
%randCombo = u0
found a combo set for - 34
%randOne = 7
%randTwo = 2
%randCombo = hc
found a combo set for - 35
$journalDisplayArray[0] = entry
$journalDisplayArray[1] = item
$journalDisplayArray[2] = entry
------------------- ARRAYS --------------------------
$journalMainTags
------------------------------------------------------------------
$journalMainTags[0] - entry
$journalMainTags[1] - item
$journalMainTags[2] - entry
------------------------------------------------------------------
$journalSubTags
------------------------------------------------------------------
$journalSubTags.contents[0].contents[0] - name
$journalSubTags.contents[0].contents[1] - text
$journalSubTags.contents[0].contents[2] - item
$journalSubTags.contents[0].contents[3] - image
$journalSubTags.contents[0].contents[4] - name
$journalSubTags.contents[0].contents[5] - NpcName
$journalSubTags.contents[0].contents[6] - Text
$journalSubTags.contents[0].contents[7] - Summary
$journalSubTags.contents[0].contents[8] - Reward
$journalSubTags.contents[0].contents[9] - Status
$journalSubTags.contents[1].contents[0] - name
$journalSubTags.contents[1].contents[1] - value
$journalSubTags.contents[1].contents[2] - power
------------------------------------------------------------------
$mainValueArray
------------------------------------------------------------------
$mainValueArray.contents[0] - 4
$mainValueArray.contents[1] - 2
------------------------------------------------------------------
$subValueArray
------------------------------------------------------------------
$subValueArray.contents[0].contents[0].contents[0] - quest given by: bob
$subValueArray.contents[0].contents[0].contents[1] - quest text: go here and do that
$subValueArray.contents[0].contents[0].contents[2] - quest item: sword of fire
$subValueArray.contents[0].contents[0].contents[3] - spacetime/client/images/back1.png
$subValueArray.contents[0].contents[1].contents[0] - bob2
$subValueArray.contents[0].contents[1].contents[1] - slay the beast and return its head
$subValueArray.contents[0].contents[1].contents[2] - rod of death
$subValueArray.contents[0].contents[1].contents[3] - spacetime/client/images/back2.png
$subValueArray.contents[0].contents[2].contents[0] - bob3
$subValueArray.contents[0].contents[2].contents[1] - go here and do that3
$subValueArray.contents[0].contents[2].contents[2] - shield of valor
$subValueArray.contents[0].contents[3].contents[0] - Quest Name
$subValueArray.contents[0].contents[3].contents[1] - Quest Text
$subValueArray.contents[0].contents[3].contents[5] - NpcName
$subValueArray.contents[0].contents[3].contents[7] - Summary
$subValueArray.contents[0].contents[3].contents[8] - Reward
$subValueArray.contents[0].contents[3].contents[9] - Status
$subValueArray.contents[1].contents[0].contents[0] - rod of death
$subValueArray.contents[1].contents[0].contents[1] - 500
$subValueArray.contents[1].contents[0].contents[2] - death
$subValueArray.contents[1].contents[1].contents[0] - staff of power
$subValueArray.contents[1].contents[1].contents[1] - 500
------------------------------------------------------------------
I think everything is OK,but can't show it in book.
Right?
#40
08/23/2005 (8:08 am)
@Nick: Sorry for the delay, meant to respond to this sooner... Dreamer helped me figure out a new way to handle returning the right values... in the resource I use "Con::getIntArg" ... what you see up in the first steps of this should work :) I changed it from the old way which only worked for Windows, so you shouldn't have to worry about that anymore...

Not exactly sure why your getting that error message though. Seems the callback to return the line you clicked isn't working right, let me double check the latest version.

@ccljyc: are you loading the gui in and bringing the gui up with the command? its coming up empty?