29.6 The ''cCritter'' serialize

All the tricks we've discussed come into play with the cCritter::Serialize. A critter has a cCritter* _ptarget field that's a reference pointer, so when you save and reload the game file, which means saving and loading all of the critters, then the copy of the target critter is not going to be loaded into the exact same area of memory, and the old _ptarget pointer will no longer be correct. That's why we have the _targetindex field in the cCritter.

This reference pointer problem comes back in even stronger form with the armed critters, who have pointers to their bullets. You can look at the Serialize code inside the critterarmed.cpp file to see one way to work things out. In particular look for the fixPointerRefs methods.

The cCritter also has member pointer fields. As we explained in the last section, in order to load the pointer _psprite and _plistener fields of cCritter, we must first delete them and then use the overloaded operator>> to overwrite them with new pointers.

Note also that the CArray _forcearray has to be handled with Serialize and that, to avoid having a possible memory leak, you have to empty it out before you read into it. Here's some relevant code.

void cCritter::Serialize(CArchive &ar) 
{ 
    CObject::Serialize(ar); 
        //Call the base class method to save the CRuntimeClass info. 
    if (ar.IsStoring()) //Writing data. 
    { 
        _forcearray.Serialize(ar); 
            //Generally you cant use << with a CArray. 
//Sprite 
        ar << _psprite << 
//Listener 
        _plistener << 
//Personal variables 
        _age << _lasthit_age<< _oldrecentlydamaged << _health << 
            _shieldflag << 
        //........ETCETERA.......... 
//Pointer index variables. 
        if (_pownerbiota) 
            _targetindex = _pownerbiota->_index(_ptarget); 
                //Prepare for a pointer reference. 
        else 
            _targetindex = cBiota::NOINDEX; 
        ar << _targetindex; 
} 
else //Reading data. 
{ 
        clearForcearray(); /* We have to empty out the array before 
            reading into it or we'll have a memory leak in case 
            something's in it. */ 
        delete _psprite; 
            /* always delete a pointer before reading into it or you 
                have a leak. */ 
        delete _plistener; 
            /* always delete a pointer before reading into it or you 
                have a leak. */ 
        _forcearray.Serialize(ar); /* Read in. You usually can't use >> 
            with a CArray. */ 
//Sprite 
        ar >> _psprite >> /* Uses CreateObject to creates a new 
            cSprite* object of the correct child class, copies the new 
            object's fields out of the file, and places the pointer to 
            the new object in _psprite */ 
//Listener and force 
        _plistener >> // See the comment just above. 
//Personal variables 
        _age >> _lasthit_age >> _oldrecentlydamaged >> _health >> 
            _shieldflag >> 
        //........ETCETERA.......... 
//Index for pointer reference variable. 
        ar >> _targetindex; /* cBiota::Serialize will call 
            cCritter::FixPointerRefs to replace this index 
            by a pointer. */ 
    _ptarget = NULL; /* The cBiota::Serialize will call 
        all pcritter->fixPointerRefs for each critter to fix the 
        _ptarget, and also fix any pointer refs in the forces. */ 
    } 
} 

The cCritter::fixPointerRefs can only be called after all of the cCritter have been loaded and added to the cBiota array, with this array installed as the _pownerbiota of each cCritter. The fixPointerRefs does the following, in part.

void cCritter::fixPointerRefs() 
{ 
    if (!_pownerbiota) 
        return; 
    else 
        _ptarget = _pownerbiota->GetAt(_targetindex); 
} 


    Part I: Software Engineering and Computer Games
    Part II: Software Engineering and Computer Games Reference