From: amitp@owlnet.rice.edu (Amit Jayant Patel)
Subject: Object-oriented game system
Reply-To: amitp@owlnet.rice.edu
Organization: Rice University
Date: Tue, 13 Oct 1992 15:41:51 GMT

In article <Bw1M74.1o7@agora.rain.com>, donp@agora.rain.com (Don Park) writes:
|> amitp@owlnet.rice.edu (Amit J Patel) writes:
|> 
|> >where I can put some objects in and let them run.  I think this will
|> >be fun!
|> 
|> I think you have some good ideas as to how to make objects interact
|> with the world. My problem is how to effectiveely save their state
|> on a moments notice and reload to where the program left off and
|> restore the state of all objects.
|> 

Can't we just save the binary to disk?  Oops, almost... let's see, with
C++, we can be nasty and do this:

	void * vtbl_lookup[100];

Then for each type of object, assign it an 'object number' and put its
vtbl pointer (yes, yes, it's very unportable) into the array; then save
its object number and the chunk of memory assigned to the object (other
than the vtable pointer) to disk.  

To restore, look at the object number, create a new object of the proper
type, and pluck the disk image for the object and put it into memory
(past the vtable pointer).  

Yes, it's evil & unportable ... :(

We also have to make sure our objects contain no pointers... but if each
object contains its location & damage & all that, I think it should work...
Here's something similar that I once wrote for a simple game:

	enum type { tMONSTER, tNPC, tDONPARK };

	struct Location
	{
	   int x, y;
	};

	struct Object
	{
	   Location loc;

	   virtual type ObjectType() = 0;
	   virtual void Act() {}
	   virtual int SizeOf() = 0;
	};

	class Monster: public Object
	{
	   virtual type ObjectType() { return tMONSTER; }
	   virtual int SizeOf() { return sizeof(Monster); }
	};

	class NPC: public Object
	{
	   int strength, intelligence, health;
	   virtual type ObjectType() { return tNPC; }
	   virtual int SizeOf() { return sizeof(NPC); }
	}

	class DonPark: public NPC
	{
	   int power;
	   virtual type ObjectType() { return tDONPARK; }
	   virtual void Act() { cout << "Let's play!\n"; }
	   virtual int SizeOf() { return sizeof(DonPark); }
	};

Then we'd have a list of objects in each sector:

	List<Object> objects;
	ListIterator<Object> them(objects);

	while( them )
	{
	   them.first() -> Act();
	   them++;
	}

And to save (here's where it gets messy):
	
	List<Object> objects;
	ListIterator<Object> them(objects);

	OutputFile f = "objects.dat";

	while( them )
	{
	   Object* obj = them.first();
	   them++;

	   f.write( &obj->ObjectType(), sizeof(type) );
	   char* raw = (char *)(&obj->loc);
	   f.write( raw, obj->SizeOf() - (raw - (char *)obj) );
	}

Wasn't that nasty?  Loading:

	List<Object> objects;
	InputFile f = "objects.dat";

	while( !f.eof() )
	{
	   type objtype;
	   f.read( &objtype, sizeof(type) );

	   Object* obj = CreateObjectOfType( objtype );

	   // ich grep blech
	   f.read( &(obj->loc), obj->SizeOf() - 
		   ((char *)(&obj->loc) - (char *)obj) );

	   objects.insert( obj );
	}

where CreateObjectOfType looks at the objtype and creates an appropriate
object (it could be a huge switch(), or it could use the vtbl_lookup
array mentioned at the top)...

I don't think Turbo Pascal w/objects or C++ is very well suited for
this game.  Here's what I want to see in the game language:

  (1)	The _system_ should maintain the "enum" type and keep it
	consistent across compiles (if you insert a new object, saved
	games should not die because the numbers are off by one).  This
	could be done by keeping a table of strings(object names) ->
	enum numbers in each saved game, so if an older saved game is
	loaded, the translation could be made.

  (2)	The language should automatically give us ObjectType()
	and SizeOf(); they're all the same, right?  I've been
	doing it with some macros, but it's ugly.  :(

  (3)	The system shouldn't make us use the vtable, because it's
	unportable -- if it's a new language, we can write a portable
	way to access the vtable.

  (4)	[Ok, I'm starting to dream here]  The game system should 
	swap objects in & out automatically.

  (5)	[More dreaming]  The .OBJs for the objects should not even
	be linked in to the game.  They should remain out in a directory
	somewhere so that 

	  (a) 	They can be swapped out as necessary
	  (b)	If we have a bug in the NPC routines, all we have
		to do is ship a new NPC.DLL file out to the users
		to fix the bug

  (6)	[Combine with 4]  The system should not even give us the
	notion of separate "memory" and "disk"; when it swaps
	objects out, it's just saving them, and whenever we use
	the objects, it's swapping them in & loading.  We could
	just say "run this object", and it would transparently
	load the object, run it, and save it.  (Saving would be
	cached for speed)  [this would even replace 3, since our
	only use for the vtable was for saving & loading]

  (7)	[Ok, I'm way overboard]  It might be fun to have a multi-
	tasking kernel in there so that objects can just roam around
	on their own instead of being controlled by a main loop.
	They could put themselves to 'sleep' most of the time for
	efficiency, and the game system would keep a list of objects
	that need to be woken up at various times, and then this
	would all be part of the process scheduler.  We would need
	interprocess communications to handle things like players
	attacking each other or people picking things up.  :)

zzzz
<wake up>
What was I thinking?  There's no way we could do that... or is there?
Something like TADS/ADVSYS, except for object-oriented ultima-style
games?

	:) :)

			Amit
			the
			dreamer


-- 
/\\ Amit J Patel, amitp@owlnet.rice.edu
\\/ I think I'm dreaming of a neat game system