Announcement: Be excellent to each other.


Caravel Forum : Caravel Boards : Development : Game Handler Systems (What's the best way?)
Page 1 of 2
2
New Topic New Poll Post Reply
Poster Message
coppro
Level: Smitemaster
Rank Points: 1308
Registered: 11-24-2005
IP: Logged
icon Game Handler Systems (0)  
So, my friend and I have decided to start working on a text-based game, grid-style, not command-line style (whether it will ever see the light of day is a different question), and here's what we've figured out so far about the main engine, based on my prior experience with programming:

So, each thing in the game that is displayed on the map is an instance of CObject. CObjects are basically owned by CRoom (it's supposed to be the only thing to call the constructor, it has direct access to some important variables and set-method access to the rest), and so a question arose. Should the room handle most of the processing, or should we derive classes to do so? Currently, the idea is that monsters will get their own class because they may be much more complex, but the majority of the handling, such as for a traps, is to be processed by the room. So, is this a good path to take for the coding? The object class is the only thing I have actually coded other than I/O manipulators (although I can't wait to do the actual I/O code, as I came up with a really clever way to make colors really easy to use), and it's looking pretty sparse, seeing as it is designed for the inanimate as well as the animate.

So, is there a better way?
11-24-2006 at 03:27 AM
View Profile Show all user's posts Quote Reply
halyavin
Level: Delver
Rank Points: 52
Registered: 02-20-2006
IP: Logged
icon Re: Game Handler Systems (+1)  
DROD have only classes for monsters and room. You can try to add objects for traps in you have too complex code for them.
11-24-2006 at 12:41 PM
View Profile Send Private Message to User Show all user's posts Quote Reply
ErikH2000
Level: Legendary Smitemaster
Avatar
Rank Points: 2794
Registered: 02-04-2003
IP: Logged
icon Re: Game Handler Systems (+2)  
halyavin wrote:
DROD have only classes for monsters and room. You can try to add objects for traps in you have too complex code for them.
Yeah, that's right.

Making a class out of every element with interfaces to operate on them isn't good for performance, and the need for accessing object interfaces can be cumbersome for "Forest-for-the-Trees" operations that involve working with many objects at once. However, you've got modern computing power at your disposal and a text-based game probably isn't very demanding. So maybe you do want to try a pure object design. It really depends what you are trying to do.

The type of thing which warrants object encapsulation of tile-based elements is different code that runs in response to the same event. In the case of monsters, you have the "room update" event which triggers different code running in each monster element. Some behaviors of elements aren't really about running element-specific code. For example, the obstacle-ness of a tile needs to be determined by whatever is stepping onto it. It doesn't make as much sense to have CWall.IsObstacle(ThingThatIsSteppingOntoWall) as CMonster.DoesSquareContainObstacle(Square).

If you have some more "physics" in the game, then using objects for elements becomes more attractive. Suppose that you could kick a crate across a room and it would move in that direction until it hit something else, perhaps breaking that other thing or causing that other thing to be set in motion. Then it would make sense to have some object methods like CObject.OnReceiveForce(FromObject) so that the object itself would decide if it wants to fly across the room in response or break into pieces.

So I think probably you want to have most of your game elements stored as non-objects or at least as methodless structs. But certain game designs do favor making all the game elements objects.

-Erik

____________________________
The Godkiller - Chapter 1 available now on Steam. It's a DROD-like puzzle adventure game.
dev journals | twitch stream | youtube archive (NSFW)
11-24-2006 at 06:47 PM
View Profile Send Email to User Show all user's posts This architect's holds Quote Reply
coppro
Level: Smitemaster
Rank Points: 1308
Registered: 11-24-2005
IP: Logged
icon Re: Game Handler Systems (0)  
Well, I've decided to use metakit in a very DROD-like way to handle my data storage, but I've got another question. How does one make it so that one database can handle more than one type of stuff? For instance, drod2_0.dat has multiple instances of views with columns with the same name as columns in other views - I was under the impression that views are just ways to access data, as opposed to data itself. Is this false?

Oh, and in case you're wondering, I've decided that the architecture lends itself best to one CObject, which all things (even floors and walls) are instances of, and then CMonster would be a derived class, along with CItem, and then each monster would derive it's own class from CMonster for stuff like AI handling. The system is going to be primarily event-based. Once I work it out, the monsters won't actually interact with each other, but through the room interface, allowing the room to tweak anything that it needs to based on the current room's characteristics.

[Last edited by coppro at 12-10-2006 08:31 PM]
12-10-2006 at 08:27 PM
View Profile Show all user's posts Quote Reply
ErikH2000
Level: Legendary Smitemaster
Avatar
Rank Points: 2794
Registered: 02-04-2003
IP: Logged
icon Re: Game Handler Systems (+1)  
coppro wrote:
Well, I've decided to use metakit in a very DROD-like way to handle my data storage, but I've got another question. How does one make it so that one database can handle more than one type of stuff?
Just create a view for each type of item you want to keep track of. You might want to read up on database normalization, but I've found there are a few simple rules that go a long way:

* Each view (aka "table") should only contain one type of item, i.e. create a "Rooms" view and a "Demos" view, but don't create a "Rooms and Demos" view.
* Each row (aka "record) of a table should contain data for just one instance of that type.

For instance, drod2_0.dat has multiple instances of views with columns with the same name as columns in other views - I was under the impression that views are just ways to access data, as opposed to data itself. Is this false?
Yeah, that's false. In common database terminology, what Jean-Claude Whippler calls "views" would instead be called "tables". I'm not sure why he didn't call them "tables", but I think it has something to do with his underlying storage implementation which put all the values of one field in contiguous memory. I found the "view" term confusing when I started as well.

In the DROD code, when you see the name of one field appearing in multiple views, there are still independant values stored in each view. Often we use a foreign key in one view to reference primary keys in another view. But when I say "reference", I don't mean anything magical is happening down in Metakit--we just store a value in row(s) of one view that matches the value stored in row(s) of a second view, and our application code knows that the row(s) from the second view are associated with row(s) from the first view. Metakit itself is not aware of any association between the two views, however, despite the identical naming of fields. I think we also have some fields with generic field names like "Description" that show up in multiple views--these aren't keys and don't indicate a row relationship. As a naming convention, we suffixed fields with "ID" to indicate they are keys.

If row association ideas sound confusing, I recommend looking up "foreign key" and "primary key". These are general database concepts--nothing specific to DROD or Metakit.

Oh, and in case you're wondering, I've decided that the architecture lends itself best to one CObject, which all things (even floors and walls) are instances of, and then CMonster would be a derived class, along with CItem, and then each monster would derive it's own class from CMonster for stuff like AI handling.
Yep, that's the purist route. Probably a little overwrought, but it shouldn't cause you any problems and following a consistent design has its own rewards.

-Erik

____________________________
The Godkiller - Chapter 1 available now on Steam. It's a DROD-like puzzle adventure game.
dev journals | twitch stream | youtube archive (NSFW)
12-10-2006 at 11:38 PM
View Profile Send Email to User Show all user's posts This architect's holds Quote Reply
coppro
Level: Smitemaster
Rank Points: 1308
Registered: 11-24-2005
IP: Logged
icon Re: Game Handler Systems (0)  
Okay. I think it was something along the point where it said that you can reconfigure views or something similar that made me think about that. As for the bit on the class system, it makes output a lot easier, but it will add overhead (something that I know will inevitably bite me later, but for now I'm not too worried). Thanks for the help! Now, I wonder when the Ubuntu package maintainer's going to fix the problem with metakit. I'm doing my development on Linux, and this is one of the three big things on my list (the other two are recalibrating the process handling structure and adding items in). Also, not that I'd expect you to know, but for some reason whenever I use cursor-positioning functions it does some funny stuff, for instance it sometimes scrolls down so that the line I was working on is now the top one. I know I could force it to change the buffer to be the right size, but that doesn't seem ideal. Any ideas?

EDIT: I also noticed when examining the DROD code that you store your strings as binary data. Is this just for the Unicode support?

[Last edited by coppro at 12-11-2006 12:30 AM]
12-11-2006 at 12:19 AM
View Profile Show all user's posts Quote Reply
Syntax
Level: Smitemaster
Rank Points: 1218
Registered: 05-12-2005
IP: Logged
icon Re: Game Handler Systems (+1)  
Quick addition to the db normalisation concept:
A pure normalised database will not store the same unrelated data twice. For example, if you had a table with a users score for each room solved, you could also store the users password and address in the same table. This is fine. But it's not normalised, which means it will take a huge amount of space. Each new score would store the user details each time. You would have:
User   | Score | Password       | Adress
Syntax | 123   | easytoremember | Some street
Syntax | 56    | easytoremember | Some street
Other  | 741   | easytoremember | Some street

Normalising this would be recognising you have unrelated data (usually identified by repetion). For example, the above table may be called "Scores". If there was to be a password and/or address in there it should be directly linked to the score itself. In this case it's not, hence this table lends itself to be normalised. The important concept behind normalisation is that every row has to be uniquely identifiable. In the case above, I could have 2 scores in different rooms with 123 move score. Adding an ID such as level and room would solve this (as long as only 1 demo can be stored per room).
User   | Score | Password       | Adress      | Room
Syntax | 123   | easytoremember | Some street | JtRH (1N, 1W)
Syntax | 56    | easytoremember | Some street | Machina (17S, 2W)
Other  | 741   | easytoremember | Some street | Ninja (1N, 166E)

Each row is now identifiable. In order to normalise a table you'll need an *external* identifier. In this case, you could use a combination of all columns (any fewer an you may get identical matches). So you add a *primary key*. This is something which uniquely identifies each row. As you see above, a row can only be identified by considering the entire row.

So we and an id. This will be unique for each row:
ID |User   | Score | Password       | Adress      | Room
1  |Syntax | 123   | easytoremember | Some street | JtRH (1N, 1W)
2  |Syntax | 56    | easytoremember | Some street | Machina (17S, 2W)
3  |Other  | 741   | nicetoremember | Some street | Ninja (1N, 166E)

Now we have a distinct way of referencing each row, without considering the whole row. In my example this is not necessary to do, but it's a good guideline as you'll now see. How do we avoid this password/address duplication?

Create a new User table, again with it's own identical identifier:
ID | Name   | Password       | Address
1  | Syntax | easytoremember | Some street
2  | Other  | nicetoremember | Some street

And now, can identify each user uniquely - this format could even allow 2 players with the same usernames if we wanted to. In this case, for example, we have 2 users with the same address as they have separate ids.

One more step to normalisiation. We'll change the first table to this:

So we and an id. This will be unique for each row:
ID |User   | Score | UserID      | Room
1  |Syntax | 123   | 1           | JtRH (1N, 1W)
2  |Syntax | 56    | 1           | Machina (17S, 2W)
3  |Other  | 741   | 2           | Ninja (1N, 166E)

This representation of date is now a lot more compact. A user could have 5000 high scores yet only once would the user details be stored, and you'd only have 5000 UserIDs stored instead which is a lot of space gained.

In this sort of example, the IDs are called "primary keys" as these are the unique identifiers. It is possible, and sometimes better to have cross-column primary keys, but a good rule of thumb is to always assign a primary key in case you later want to reference that table.

The referencing column (UserID in this case) is called the "foreign key".

Be careful not to over-normalise (you could for example store every possible address in the world in a seperate table and use an ID as above to reference it). DB design is the art of understanding the data, above all, and normalising redundancy without compromising efficiency and storage.

[Last edited by Syntax at 12-11-2006 12:24 AM]
12-11-2006 at 12:23 AM
View Profile Send Private Message to User Show all user's posts Quote Reply
coppro
Level: Smitemaster
Rank Points: 1308
Registered: 11-24-2005
IP: Logged
icon Re: Game Handler Systems (0)  
Ah. So don't make every object store a copy of the room that it's in. Got it.

No, really, that's actually useful. I just realized that if each object that wasn't a monster had a lot of zeros for all of the monster data, that would be useless. Instead I can have monsters be a seperate view and then use one field that's a zero if the object isn't a monster.

Also, do subviews actually allow for storage of more data, or is it just for sorting. e.g. If I have 'Rooms' as a view, would I use 'Objects' as a subview so that each room can store it's contents, or would I make Objects a seperate view linked by secondary IDs?
12-11-2006 at 12:36 AM
View Profile Show all user's posts Quote Reply
ErikH2000
Level: Legendary Smitemaster
Avatar
Rank Points: 2794
Registered: 02-04-2003
IP: Logged
icon Re: Game Handler Systems (0)  
coppro wrote:
Also, do subviews actually allow for storage of more data, or is it just for sorting. e.g. If I have 'Rooms' as a view, would I use 'Objects' as a subview so that each room can store it's contents, or would I make Objects a seperate view linked by secondary IDs?
You could do it either way. I understand that there is a performance penalty in Metakit for writing views that have subviews. Also if you have any operations that need to work on objects in multiple rooms at once, i.e. "remove all the monsters from all the rooms", then you definitely want to avoid subviews.

The only real reason I can see to use subviews is that it's a little more intuitive to look at the structure and see how data is stored. I think it's best to avoid using subviews.

-Erik

____________________________
The Godkiller - Chapter 1 available now on Steam. It's a DROD-like puzzle adventure game.
dev journals | twitch stream | youtube archive (NSFW)
12-11-2006 at 01:56 AM
View Profile Send Email to User Show all user's posts This architect's holds Quote Reply
coppro
Level: Smitemaster
Rank Points: 1308
Registered: 11-24-2005
IP: Logged
icon Re: Game Handler Systems (0)  
Well, the package people still haven't updated metakit - very annoying. I'd settle for an out-of-date package but broken ones don't cut it. In the meanwhile though, I was writing an event handler, and the following doesn't work:

bool CRoom::AddEvent( const Event event ) {
  ObjIter o;
  static bool running = false;
  static bool base = false;
  static vector<Event> queue;
  if ( running ) {
    queue.push_back( event );
    return false;
    }
  queue.clear();
  running = true;
  for ( o = objects.begin(); o != objects.end(); ++o )
  if ( ( *o ) ->GetEvents() & event.code ) ( *o ) ->HandleEvent( &event );
  running = false;
  if ( !base && !queue.empty () ) {
    base = true;
    while ( !queue.empty() ) {
      AddEvent( queue[ 0 ] );
      queue.erase( queue.begin() );
      }
    base = false;
    }
  return true;
  }


Basically, when the function is called, it checks to see whether any objects care about the event, and if they do, then call their handlers. If an event is added during the process (as a general rule, they shouldn't be, but it's better to be prepared in case they eventually do), then it should be queued up, so that different monsters don't have differing concepts of what occurred. The only problem is that if another one is queued, it gets stuck in an infinite loop processing the queue.
12-14-2006 at 11:27 PM
View Profile Show all user's posts Quote Reply
schep
Level: Smitemaster
Avatar
Rank Points: 864
Registered: 03-01-2005
IP: Logged
icon Re: Game Handler Systems (+1)  
I'm not quite following where the infinite loop comes into it, but it looks as though calling AddEvent recursively from the 'base' section would clear your queue too early. How about:
bool CRoom::AddEvent( const Event& event ) {
  static list<Event> queue;
  bool handle = queue.empty(); // delay if other events going
  queue.push_back(event);
  while (handle && !queue.empty()) {
    const Event& qev = queue.front();
    for (ObjIter o=objects.begin(); o!=objects.end(); ++o) {
      if ((*o)->GetEvents() & qev.code)
        (*o)->HandleEvent(qev);
    }
    queue.pop_front();
  }
  return handle;
}

The change from vector to list has nothing to do with the correctness, it's just that seeing vector::erase() makes me unhappy. I'd also consider changing that event queue to a static member of class CRoom, but there are pros and cons to that.

[Last edited by schep at 12-15-2006 01:20 AM : oh yeah, return value]
12-15-2006 at 01:19 AM
View Profile Send Private Message to User Send Email to User Show all user's posts This architect's holds Quote Reply
coppro
Level: Smitemaster
Rank Points: 1308
Registered: 11-24-2005
IP: Logged
icon Re: Game Handler Systems (0)  
schep wrote:
I'm not quite following where the infinite loop comes into it, but it looks as though calling AddEvent recursively from the 'base' section would clear your queue too early. How about:
bool CRoom::AddEvent( const Event& event ) {
  static list<Event> queue;
  bool handle = queue.empty(); // delay if other events going
  queue.push_back(event);
  while (handle && !queue.empty()) {
    const Event& qev = queue.front();
    for (ObjIter o=objects.begin(); o!=objects.end(); ++o) {
      if ((*o)->GetEvents() & qev.code)
        (*o)->HandleEvent(qev);
    }
    queue.pop_front();
  }
  return handle;
}

The change from vector to list has nothing to do with the correctness, it's just that seeing vector::erase() makes me unhappy. I'd also consider changing that event queue to a static member of class CRoom, but there are pros and cons to that.

Well, it may actually be vector::erase(). If only it had a push_front or pop_front. But a list is in fact a good idea, I just switched to one, and a bit of ironing made it work. Although I do call vector::erase() at other points in the program - I don't see a good way to allow for the random-access needed. Your code seems like a more efficient way of handling things, but it doesn't actually work. Sorry. Good idea though. I guess that's what comes of designing a function all at once instead of in pieces.

Thanks for the help!
12-15-2006 at 02:06 AM
View Profile Show all user's posts Quote Reply
schep
Level: Smitemaster
Avatar
Rank Points: 864
Registered: 03-01-2005
IP: Logged
icon Re: Game Handler Systems (+1)  
coppro wrote:
Although I do call vector::erase() at other points in the program - I don't see a good way to allow for the random-access needed.
Sure, if you use at() or operator[] more than erase(), vector may be a good choice. It's just that in this one example, I could see all of the container's scope and it wasn't even using such things.
12-15-2006 at 02:16 AM
View Profile Send Private Message to User Send Email to User Show all user's posts This architect's holds Quote Reply
coppro
Level: Smitemaster
Rank Points: 1308
Registered: 11-24-2005
IP: Logged
icon Re: Game Handler Systems (0)  
One thing I absolutely hate is when you accidentally overload a virtual function in a base class - there should be some sort of warning about that, because there's no other indication that there is any problem whatsoever. Also, is there any actual advantage to using this->x over just plain x, or is it just for clarity?

EDIT: Oh, and I *heart* Doxygen.

[Last edited by coppro at 12-15-2006 05:15 AM]
12-15-2006 at 05:13 AM
View Profile Show all user's posts Quote Reply
schep
Level: Smitemaster
Avatar
Rank Points: 864
Registered: 03-01-2005
IP: Logged
icon Re: Game Handler Systems (0)  
this->x and x are equivalent (assuming there's not also some local variable called x or something weird like that). Likewise for this->f() and f(). It's a style choice - I usually omit the 'this' part, but it doesn't really matter to me.
12-15-2006 at 01:08 PM
View Profile Send Private Message to User Send Email to User Show all user's posts This architect's holds Quote Reply
Syntax
Level: Smitemaster
Rank Points: 1218
Registered: 05-12-2005
IP: Logged
icon Re: Game Handler Systems (+1)  
coppro wrote:
Also, is there any actual advantage to using this->x over just plain x, or is it just for clarity?
I always use this->x notation as it clarifies which var you're actually using. But yeah, as schep said it's a preference matter. I just think code should be as specific as possible... avoids hard to detect bugs.
12-15-2006 at 05:51 PM
View Profile Send Private Message to User Show all user's posts Quote Reply
coppro
Level: Smitemaster
Rank Points: 1308
Registered: 11-24-2005
IP: Logged
icon Re: Game Handler Systems (0)  
Well, it seems I'm destined to come back here a lot. I just wrote a function to display a menu screen, but it keeps randomly crashing. The system is complaining about use of free() and malloc(), but I never use them (rather new[] and delete[]). It fails upon calling the dynamic allocation or deleting it. The former happens in the third call of the program, without fail. The latter is more random. However, the interesting thing is that I use exactly the same syntax there as in a different function, which to date has never failed.

Here's the beginning section of code:

int DrawMenu( string menu ) {
  char * tok = new char [ menu.length() ];
  char * t;
  int line = 0;      // current line
  int startLine = 0; // starting line on the screen
  int topLine = 0;   // topmost displayed line.
  vector<string> lines;

  strcpy( tok, menu.c_str() );
  t = strtok( tok, "\n\0" );

  while ( t ) {
    lines.push_back( t );
    t = strtok( NULL, "\n\0" );
    }


And there is a delete[] tok at the end. For some reason, however, this system doesn't work in this function. Here is the full text of the error:

** glibc detected *** ./textthing: malloc(): memory corruption (fast): 0x08086ff0 ***
======= Backtrace: =========
/lib/tls/i686/cmov/libc.so.6[0xb7da088f]
/lib/tls/i686/cmov/libc.so.6(malloc+0x7f)[0xb7da183f]
/usr/lib/libstdc++.so.6(_Znwj+0x27)[0xb7f514b7]
/usr/lib/libstdc++.so.6(_ZNSs4_Rep9_S_createEjjRKSaIcE+0x6b)[0xb7f2c87b]
/usr/lib/libstdc++.so.6[0xb7f2da65]
/usr/lib/libstdc++.so.6(_ZNSsC1EPKcRKSaIcE+0x47)[0xb7f2dc17]
./textthing[0x8052da3]
./textthing[0x804e88b]
./textthing[0x804a4b7]
./textthing[0x804ddb0]
./textthing[0x804e005]
/lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xdc)[0xb7d4e8cc]
./textthing(__gxx_personality_v0+0xa9)[0x80498b1]
======= Memory map: ========
08048000-0805a000 r-xp 00000000 08:01 2329377    /home/coppro/C++/textthing/debug/src/textthing
0805a000-0805b000 rw-p 00011000 08:01 2329377    /home/coppro/C++/textthing/debug/src/textthing
0805b000-0809d000 rw-p 0805b000 00:00 0          [heap]
b7c00000-b7c21000 rw-p b7c00000 00:00 0
b7c21000-b7d00000 ---p b7c21000 00:00 0
b7d38000-b7d39000 rw-p b7d38000 00:00 0
b7d39000-b7e66000 r-xp 00000000 08:01 1352474    /lib/tls/i686/cmov/libc-2.4.so
b7e66000-b7e68000 r--p 0012c000 08:01 1352474    /lib/tls/i686/cmov/libc-2.4.so
b7e68000-b7e6a000 rw-p 0012e000 08:01 1352474    /lib/tls/i686/cmov/libc-2.4.so
b7e6a000-b7e6e000 rw-p b7e6a000 00:00 0
b7e6e000-b7e78000 r-xp 00000000 08:01 1319395    /lib/libgcc_s.so.1
b7e78000-b7e79000 rw-p 00009000 08:01 1319395    /lib/libgcc_s.so.1
b7e79000-b7e9d000 r-xp 00000000 08:01 1352482    /lib/tls/i686/cmov/libm-2.4.so
b7e9d000-b7e9f000 rw-p 00023000 08:01 1352482    /lib/tls/i686/cmov/libm-2.4.so
b7e9f000-b7f73000 r-xp 00000000 08:01 2557531    /usr/lib/libstdc++.so.6.0.8
b7f73000-b7f76000 r--p 000d4000 08:01 2557531    /usr/lib/libstdc++.so.6.0.8
b7f76000-b7f78000 rw-p 000d7000 08:01 2557531    /usr/lib/libstdc++.so.6.0.8
b7f78000-b7f7e000 rw-p b7f78000 00:00 0
b7f91000-b7f95000 rw-p b7f91000 00:00 0
b7f95000-b7fae000 r-xp 00000000 08:01 1319350    /lib/ld-2.4.so
b7fae000-b7fb0000 rw-p 00018000 08:01 1319350    /lib/ld-2.4.so
bfcbe000-bfcd3000 rw-p bfcbe000 00:00 0          [stack]
ffffe000-fffff000 ---p 00000000 00:00 0          [vdso]
Aborted
Press Enter to continue!


Any ideas whatsoever on what's causing it/how to fix?

[Last edited by coppro at 12-20-2006 06:09 AM]
12-20-2006 at 06:07 AM
View Profile Show all user's posts Quote Reply
Schik
Level: Legendary Smitemaster
Avatar
Rank Points: 5381
Registered: 02-04-2003
IP: Logged
icon Re: Game Handler Systems (+2)  
coppro wrote:
int DrawMenu( string menu ) {
Okay, this isn't the problem but I'd change "string menu" to "const string& menu" to avoid another string copy on each function call.
  char * tok = new char [ menu.length() ];
You should add 1 to the length there for the NULL.

____________________________
The greatness of a nation and its moral progress can be judged by the way it treats its animals.
--Mahatma Gandhi
12-20-2006 at 06:34 AM
View Profile Send Private Message to User Send Email to User Show all user's posts High Scores Quote Reply
coppro
Level: Smitemaster
Rank Points: 1308
Registered: 11-24-2005
IP: Logged
icon Re: Game Handler Systems (0)  
That solved the problem. For some reason, though, this problem wasn't occurring in a different function with exactly the same syntax. Don't know why. Thanks a bunch though.
12-20-2006 at 02:15 PM
View Profile Show all user's posts Quote Reply
schep
Level: Smitemaster
Avatar
Rank Points: 864
Registered: 03-01-2005
IP: Logged
icon Re: Game Handler Systems (0)  
On many compilers, C++ 'new' actually calls malloc() behind the scenes. You should never assume this, but it explains why you were seeing malloc() mentioned.

And in general, heap corruption when you do something wrong with your new-ed memory is a hard bug to spot. It probably won't crash the first time you write to a bad address, just blow everything up later when it's least convenient. It could even seem to work fine for years, and then some tiny change will break things. So if you haven't already, immediately change that "other function" to be correct. And if you come across similar problems again, you could check out valgrind, which is really good at spotting this sort of problem.

[Last edited by schep at 12-20-2006 03:11 PM : why did I think he was Windows?]
12-20-2006 at 03:08 PM
View Profile Send Private Message to User Send Email to User Show all user's posts This architect's holds Quote Reply
Schik
Level: Legendary Smitemaster
Avatar
Rank Points: 5381
Registered: 02-04-2003
IP: Logged
icon Re: Game Handler Systems (+1)  
If you want to just use std::string to do the splitting, and avoid the C string functions...

int DrawMenu( const string& menu ) {
  size_type cur = 0, last = 0;
  vector<string> lines;

  cur = menu.find_first_of("\n\0");

  while ( cur  != string::npos ) {
    lines.push_back( menu.substr(last, cur-last+1) );
    last = cur;
    cur = menu.find_first_of("\n\0", last+1);
  }
  ... draw the menu, presumably
}
This might need some debugging. I just wrote it here, I didn't even see if it compiles, much less test it.

____________________________
The greatness of a nation and its moral progress can be judged by the way it treats its animals.
--Mahatma Gandhi
12-20-2006 at 03:18 PM
View Profile Send Private Message to User Send Email to User Show all user's posts High Scores Quote Reply
schep
Level: Smitemaster
Avatar
Rank Points: 864
Registered: 03-01-2005
IP: Logged
icon Re: Game Handler Systems (+1)  
Schik wrote:
This might need some debugging.
Yeah, nice idea, but no function is going to understand the argument "\n\0". Also, I'm going to move some of those +1 parts around.

So for something equally uncompiled and untested, but possibly better, just do the final line manually:
int DrawMenu( const string& menu ) {
  size_type cur = 0, last = 0;
  vector<string> lines;

  cur = menu.find_first_of('\n');

  while ( cur  != string::npos ) {
    lines.push_back( menu.substr(last, cur-last) );
    last = cur+1;
    cur = menu.find_first_of('\n', last);
  }
  lines.push_back( menu.substr(last) );

  ... draw the menu, presumably
}

You can also have a string object with contents "\n\0" and that actually should work, but this seems easier.

[Last edited by schep at 12-20-2006 03:37 PM : final fix this time, really]
12-20-2006 at 03:28 PM
View Profile Send Private Message to User Send Email to User Show all user's posts This architect's holds Quote Reply
Schik
Level: Legendary Smitemaster
Avatar
Rank Points: 5381
Registered: 02-04-2003
IP: Logged
icon Re: Game Handler Systems (0)  
schep wrote:
Yeah, nice idea, but no function is going to understand the argument "\n\0". Also, I'm going to move some of those +1 parts around.
Yikes. Yeah, I just copied that from his, not thinking. Although... I think it would actually work, but it's definitely not how I would do it if I were thinking.
   lines.push_back( menu.substr(last) );
Oops, nice catch.

____________________________
The greatness of a nation and its moral progress can be judged by the way it treats its animals.
--Mahatma Gandhi
12-20-2006 at 04:17 PM
View Profile Send Private Message to User Send Email to User Show all user's posts High Scores Quote Reply
coppro
Level: Smitemaster
Rank Points: 1308
Registered: 11-24-2005
IP: Logged
icon Re: Game Handler Systems (0)  
Ah, there we go. I didn't think of using that.

I'm probably going to get a lot more advice if/when the code actually goes public. But that's okay, I've never made any sort of game, so this one already beats anything else I've written.
12-20-2006 at 11:19 PM
View Profile Show all user's posts Quote Reply
trick
Level: Legendary Smitemaster
Rank Points: 2580
Registered: 04-12-2003
IP: Logged
icon Re: Game Handler Systems (0)  
I, er, couldn't resist trying for a one-liner.
size_type cur = 0, last = 0;
vector<string> lines;

do lines.push_back(menu.substr(last, (cur = menu_find_first_of('\n', last)) == string::npos ? cur : cur - last));
while ((last = cur + 1) - 1 != string::npos);

(Untested ..)

~ Gerry
12-20-2006 at 11:29 PM
View Profile Send Private Message to User Send Email to User Show all user's posts Quote Reply
coppro
Level: Smitemaster
Rank Points: 1308
Registered: 11-24-2005
IP: Logged
icon Re: Game Handler Systems (0)  
There's an easier way:

size_typecur=0,last=0;vector<string> lines;cur=menu.find_first_of('\n');while(cur!= string::npos){lines.push_back(menu.substr(last,cur-last));last=cur+1;cur=menu.find_first_of('\n',last);}lines.push_back(menu.substr(last));


Multiple statements can go on the same line. (whitespace removed to ensure non-readbility)

Oh and schep, this time I do use random-access later in the function. ;)

[Last edited by coppro at 12-20-2006 11:33 PM]
12-20-2006 at 11:33 PM
View Profile Show all user's posts Quote Reply
coppro
Level: Smitemaster
Rank Points: 1308
Registered: 11-24-2005
IP: Logged
icon Re: Game Handler Systems (0)  
Here was the code I used:

  vector<string> lines;
  int cur = 0, last = 0;

  cur = menu.find_first_of( '\n' );

  while ( cur != string::npos ) {
    lines.push_back( menu.substr( last, cur - last ));
    last = menu.find_first_not_of( '\n', cur + 1);
    cur = menu.find_first_of( '\n', last );
    }

  if (last != string::npos )
    lines.push_back( menu.substr(last));


I also completely refined the message prompt - I think I cut the code in three.

[Last edited by coppro at 12-21-2006 12:52 AM]
12-21-2006 at 12:24 AM
View Profile Show all user's posts Quote Reply
coppro
Level: Smitemaster
Rank Points: 1308
Registered: 11-24-2005
IP: Logged
icon Re: Game Handler Systems (0)  
I went and compiled metakit myself, and now I need to know how to make it load data. Is GetAs() the correct function in all instances, or do you just use View() when attempting to load? If not, how do you know when to use GetAs()?

EDIT: Using View() works fine with no data present, but for some reason changes aren't persisting. Why?

EDIT2: GetAs() works finer. My problem was using an (i = view.Find() == 0) without braces surrounding the first statement. I think that Mike and I have something in common: Operator precedence hates us.

[Last edited by coppro at 01-06-2007 02:22 AM]
01-05-2007 at 07:10 PM
View Profile Show all user's posts Quote Reply
ErikH2000
Level: Legendary Smitemaster
Avatar
Rank Points: 2794
Registered: 02-04-2003
IP: Logged
icon Re: Game Handler Systems (0)  
coppro wrote:
I went and compiled metakit myself, and now I need to know how to make it load data. Is GetAs() the correct function in all instances, or do you just use View() when attempting to load? If not, how do you know when to use GetAs()?
If I remember right, View doesn't change the structure of an existing view, but GetAs can add fields to the view if the structure you specify differs. Myself, I prefer the fields to be specified in one place and not accidentally added.
EDIT: Using View() works fine with no data present, but for some reason changes aren't persisting. Why?
Did you remember to call the commit() member of your storage object?

-Erik

____________________________
The Godkiller - Chapter 1 available now on Steam. It's a DROD-like puzzle adventure game.
dev journals | twitch stream | youtube archive (NSFW)
01-06-2007 at 04:07 PM
View Profile Send Email to User Show all user's posts This architect's holds Quote Reply
coppro
Level: Smitemaster
Rank Points: 1308
Registered: 11-24-2005
IP: Logged
icon Re: Game Handler Systems (0)  
Well, I wrote a little function allowing the database to be opened, and then any other data functions called inside would in turn call the function, incrementing a counter. When they finished, they would call a close function, decrementing it. There's also a function to cause a rollback if an error occurred. But if you don't call the cancel function, it won't commit. That was a problem for a while, but then I added a function so that when the program exits, it commits. Still didn't work. Then I did something else, and it did work. But I'm using constants for my view structure, so I'm not really worried about redefining it. Now it's working, so it's time to go clean up my code and starting writing real save/load functions. Just out of curiosity, can GetAs() not remove a field from a view?
01-06-2007 at 04:28 PM
View Profile Show all user's posts Quote Reply
Page 1 of 2
2
New Topic New Poll Post Reply
Caravel Forum : Caravel Boards : Development : Game Handler Systems (What's the best way?)
Surf To:


Forum Rules:
Can I post a new topic? No
Can I reply? No
Can I read? Yes
HTML Enabled? No
UBBC Enabled? Yes
Words Filter Enable? No

Contact Us | CaravelGames.com

Powered by: tForum tForumHacks Edition b0.98.8
Originally created by Toan Huynh (Copyright © 2000)
Enhanced by the tForumHacks team and the Caravel team.