ok well here it is simplified:
first, no need to write your own file format since you can work around it and it would take too much time, so your setup will be something like: (assuming it's a game involving levels, for example)
Game folder
|_>Game exe
|_>DLLs
|_>txt readme etc.
|_>Data folder
|. |_>Global.FORMAT
|. |_>Maps folder
|. |. |_>Map1.FORMAT
|. |. |_>Map2.FORMAT
|. |_>Media folder
|. |. |_>Soldier.x
|. |. |_>Car.x
|. |. |_>SomeTexture.jpg
names may be whatever you want, FORMAT is also an extension of your choice (it doesn't really matter at all, txt or ASDG, it will be the exact same data inside, it's just a sign to windows so it can choose what application to open the file with)
so it goes like this, exe runs, lead media for menu or whatever, and load Global.FORMAT, not necessary but you may use such thing to apply some global changes through patches, like um, dunno, it could include the number of levels or some info regarding characters and so on, then at loading levels, your exe will open up a map in the maps folder, this file will merely include info such as, for every object: name,position,angle,script/AI name, physics data(static/dynamic),etc.., you will read all these lines of objects and save them all in an array or better yet, an std::vector(dynamic array), and then right after you finished reading your file, close it and loop through your array or std::vector, and for each one, load the object with the specified name(or check if it's already loaded from a previous level, could save loading time), then link it with a script written in your code since you know the script name.
now from the C++ side, it should look like this:
//forward declaration
class Object;
class BaseScript //base class for all scripts, all of them should inherit from this
{
public:
//....
virtual void Update ( float fDeltaTime, Object* const obj ) = 0;
virtual bool OnDamageTaken ( float fDamage ) {return true;} //for example, a derived script could implement this to specify how it reacts to taking damage, could take actions such as playing animation or return false upon death or whatever you want
//etc.. functions that each script would need
virtual const char* GetName ( void ) const = 0; //leaves should implement this to specify a name to be registered
};
class SoldierScript : public BaseScript
{
public:
virtual void Update ( float fDeltaTime, Object* const obj ) //implements updating for all types of soldiers
{
}
};
class ArmoredSoldierScript : public SoldierScript //leaf class
{
public:
void Update ( float fDeltaTime, Object* const obj ) //should call SoldierScript::Update ( fDeltaTime ); then continue actions that only applies to this kind of soldiers
{
}
const char* GetName ( void ) const { return "ArmoredSoldierAI"; };
};
class VehicleSoldierScript : public SoldierScript //leaf class
{
public:
void Update ( float fDeltaTime, Object* const obj ) //should call SoldierScript::Update ( fDeltaTime ); then continue actions that only applies to this kind of soldiers
{
}
const char* GetName ( void ) const { return "VehicleSoldierAI"; };
};
void InitializeScripts ( std::vector<BaseScript*>* scriptArray ) //should be called at the beginning to recognize all our scripts
{
//should include this same line for every leaf script we have
scriptArray->push_back ( new ArmoredSoldierScript ( ) );
scriptArray->push_back ( new VehicleSoldierScript ( ) );
}
class Object
{
int _GDKObjID; //the id for the object in DarkGDK
BaseScript* _script;
public:
Object ( BaseScript* script ) //constructor should allocate a free ID
{
_GDKObjID = GetFreeObjectID ( );
_script = script;
}
unsigned int GetID ( void ) const
{
return _GDKObjID;
}
void Create ( const char* filename )
{
//load the object, note that the name does not include the file path, so combine it with the path
char fullPath[256];
strcpy ( fullPath, "Data\\Media\\" );
strcat ( fullPath, filename );
dbLoadObject ( _GDKObjID, filename );
}
void Update ( float fDeltaTime )
{
if ( _script )
_script->Update ( fDeltaTime, this ); //update the script and tell it that WE (this point) is the user of it
}
};
struct MapEntity
{
char* objectFileName;
char* objectScriptName;
float x, y, z;
float angX, angY, angZ;
//...whatever needs to be read
};
void LoadMapFile ( const char* name, std::vector<MapEntity>* pOutEntities )
{
//read the file, parse it and store every entry in the vector
//like:
MapEntity entity;
while ( !EOF ) //end of file reached?
{
//read line
//parse it
//store parsed data in entity
pOutEntities->push_back ( entity );
}
}
std::vector<BaseScript*> scriptV;
std::vector<Object*> objectV;
void DarkGDK ( void )
{
InitializeScripts ( &scriptV );
//...
//...
//loading a map
std::vector<MapEntity> entities;
LoadMapFile ( "Data\\Maps\\Map1.FORMAT", &entities );
//ok now entities data had been loaded, create them:
for ( unsigned int i = 0; i < entities.size ( ); i++ )
{
BaseScript* entityScript = NULL;
//first, find the script
for ( unsigned int j = 0; j < scriptV.size ( ); j++ )
{
if ( strcmp ( entities[i].objectScriptName, scriptV[j]->GetName ( ) ) == 0 )
{
//script found, use it
entityScript = scriptV[j];
break;
}
}
//create a new object, we got the script
Object* obj = new Object ( entityScript );
obj->Create ( entities[i].objectFileName );
dbPositionObject ( obj->GetID ( ), entities[i].x, entities[i].y, entities[i].z );
dbRotateObject ( obj->GetID ( ), entities[i].angX, entities[i].angY, entities[i].angZ );
objectV.push_back ( obj );
}
while ( LoopGDK ( ) )
{
//update all objects, they will update their script and might do some extra per-object work
for ( int i = 0; i < objectV.size ( ); i++ )
objectV[i]->Update ( fDeltaTime );
//...
//...
dbSync ( );
}
}
i added everything in one file, should look simpler when you split it, but thats it, the very basic structure of the code
now for the actual patching all you're gonna do is add or remove entries from map files, add new map files or add/remove media, and you can alter the Global.FORMAT file to apply some global changes of your choice (your exe should include some code to parse that, too)
if you have any questions feel free to ask, im a pretty bad teacher and the code above is probably confusing :/