1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276 |
- // Filename:- g_savegame.cpp
- //
- // leave this line at the top for all g_xxxx.cpp files...
- #include "g_headers.h"
- #include "IcarusInterface.h"
- #include "Q3_Interface.h"
- #include "g_local.h"
- #include "fields.h"
- #include "objectives.h"
- #include "../cgame/cg_camera.h"
- #include "../qcommon/sstring.h"
- extern void OBJ_LoadTacticalInfo(void);
- extern void G_LoadSave_WriteMiscData(void);
- extern void G_LoadSave_ReadMiscData(void);
- extern void G_ReloadSaberData( gentity_t *ent );
- extern void FX_Read( void );
- extern void FX_Write( void );
- static const save_field_t savefields_gEntity[] =
- {
- {strFOFS(client), F_GCLIENT},
- {strFOFS(owner), F_GENTITY},
- {strFOFS(classname), F_STRING},
- {strFOFS(model), F_STRING},
- {strFOFS(model2), F_STRING},
- // {strFOFS(model3), F_STRING}, - MCG
- {strFOFS(nextTrain), F_GENTITY},
- {strFOFS(prevTrain), F_GENTITY},
- {strFOFS(message), F_STRING},
- {strFOFS(target), F_STRING},
- {strFOFS(target2), F_STRING},
- {strFOFS(target3), F_STRING},
- {strFOFS(target4), F_STRING},
- {strFOFS(targetJump), F_STRING},
- {strFOFS(targetname), F_STRING},
- {strFOFS(team), F_STRING},
- {strFOFS(roff), F_STRING},
- // {strFOFS(target_ent), F_GENTITY}, - MCG
- {strFOFS(chain), F_GENTITY},
- {strFOFS(enemy), F_GENTITY},
- {strFOFS(activator), F_GENTITY},
- {strFOFS(teamchain), F_GENTITY},
- {strFOFS(teammaster), F_GENTITY},
- {strFOFS(item), F_ITEM},
- {strFOFS(NPC_type), F_STRING},
- {strFOFS(closetarget), F_STRING},
- {strFOFS(opentarget), F_STRING},
- {strFOFS(paintarget), F_STRING},
- {strFOFS(NPC_targetname), F_STRING},
- {strFOFS(NPC_target), F_STRING},
- {strFOFS(ownername), F_STRING},
- {strFOFS(lastEnemy), F_GENTITY},
- {strFOFS(behaviorSet), F_BEHAVIORSET},
- {strFOFS(script_targetname),F_STRING},
- {strFOFS(m_iIcarusID), F_NULL},
- {strFOFS(NPC), F_BOOLPTR},
- {strFOFS(soundSet), F_STRING},
- {strFOFS(cameraGroup), F_STRING},
- {strFOFS(parms), F_BOOLPTR},
- {strFOFS(m_pVehicle), F_BOOLPTR},
- {NULL, 0, F_IGNORE}
- };
- static const save_field_t savefields_gNPC[] =
- {
- // {strNPCOFS(pendingEnemy), F_GENTITY},
- {strNPCOFS(touchedByPlayer), F_GENTITY},
- {strNPCOFS(aimingBeam), F_GENTITY},
- {strNPCOFS(eventOwner), F_GENTITY},
- {strNPCOFS(coverTarg), F_GENTITY},
- {strNPCOFS(tempGoal), F_GENTITY},
- {strNPCOFS(goalEntity), F_GENTITY},
- {strNPCOFS(lastGoalEntity), F_GENTITY},
- {strNPCOFS(eventualGoal), F_GENTITY},
- {strNPCOFS(captureGoal), F_GENTITY},
- {strNPCOFS(defendEnt), F_GENTITY},
- {strNPCOFS(greetEnt), F_GENTITY},
- {strNPCOFS(group), F_GROUP},
- {strNPCOFS(blockedEntity), F_GENTITY},
- {strNPCOFS(blockedTargetEntity),F_GENTITY},
- {strNPCOFS(jumpTarget), F_GENTITY},
- {strNPCOFS(watchTarget), F_GENTITY},
- {NULL, 0, F_IGNORE}
- };
- static const save_field_t savefields_LevelLocals[] =
- {
- {strLLOFS(locationHead), F_GENTITY},
- {strLLOFS(alertEvents), F_ALERTEVENT},
- {strLLOFS(groups), F_AIGROUPS},
- {strLLOFS(knownAnimFileSets),F_ANIMFILESETS},
- {NULL, 0, F_IGNORE}
- };
- static const save_field_t savefields_gVHIC[] =
- {
- {strVHICOFS(m_pPilot), F_GENTITY},
- {strVHICOFS(m_pOldPilot), F_GENTITY},
- {strVHICOFS(m_pDroidUnit), F_GENTITY},
- {strVHICOFS(m_pParentEntity), F_GENTITY},
-
- //m_ppPassengers //!ptr array?!
- {strVHICOFS(m_pVehicleInfo), F_VEHINFO}, //!another ptr! store name field instead and re-hook on load?
- {NULL, 0, F_IGNORE}
- };
- static const save_field_t savefields_gClient[] =
- {
- // sabers are stomped over by specific code elsewhere, it seems, but the first two fields MUST be saved
- // or it crashes on reload
- {strCLOFS(ps.saber[0].name),F_STRING},
- /* {strCLOFS(ps.saber[0].model),F_STRING},
- {strCLOFS(ps.saber[0].skin),F_STRING},
- {strCLOFS(ps.saber[0].brokenSaber1),F_STRING},
- {strCLOFS(ps.saber[0].brokenSaber2),F_STRING},
- */
- {strCLOFS(ps.saber[1].name),F_STRING},
- /* {strCLOFS(ps.saber[1].model),F_STRING},
- {strCLOFS(ps.saber[1].skin),F_STRING},
- {strCLOFS(ps.saber[1].brokenSaber1),F_STRING},
- {strCLOFS(ps.saber[1].brokenSaber2),F_STRING},
- */
- {strCLOFS(leader), F_GENTITY},
- {strCLOFS(clientInfo.customBasicSoundDir),F_STRING},
- {strCLOFS(clientInfo.customCombatSoundDir),F_STRING},
- {strCLOFS(clientInfo.customExtraSoundDir),F_STRING},
- {strCLOFS(clientInfo.customJediSoundDir),F_STRING},
- {NULL, 0, F_IGNORE}
- };
- list<sstring_t> *strList = NULL;
- /////////// char * /////////////
- //
- //
- static int GetStringNum(const char *psString)
- {
- assert( psString != (char *)0xcdcdcdcd );
- // NULL ptrs I'll write out as a strlen of -1...
- //
- if (!psString)
- {
- return -1;
- }
- strList->push_back( psString );
- return strlen(psString) + 1; // this gives us the chunk length for the reader later
- }
- static char *GetStringPtr(int iStrlen, char *psOriginal/*may be NULL*/)
- {
- if (iStrlen != -1)
- {
- char sString[768]; // arb, inc if nec.
- sString[0]=0;
- assert(iStrlen+1<=sizeof(sString));
-
- gi.ReadFromSaveGame('STRG', sString, iStrlen);
- #ifndef _XBOX // TAG_G_ALLOC is always blown away, we can never recycle
- if (psOriginal && gi.bIsFromZone(psOriginal, TAG_G_ALLOC)) {
- if (!strcmp(psOriginal,sString))
- {//it's a legal ptr and they're the same so let's just reuse it instead of free/alloc
- return psOriginal;
- }
- gi.Free(psOriginal);
- }
- #endif
- return G_NewString(sString);
- }
- return NULL;
- }
- //
- //
- ////////////////////////////////
- /////////// gentity_t * ////////
- //
- //
- static int GetGEntityNum(gentity_t* ent)
- {
- assert( ent != (gentity_t *) 0xcdcdcdcd);
- if (ent == NULL)
- {
- return -1;
- }
- // note that I now validate the return value (to avoid triggering asserts on re-load) because of the
- // way that the level_locals_t alertEvents struct contains a count of which ones are valid, so I'm guessing
- // that some of them aren't (valid)...
- //
- int iReturnIndex = ent - g_entities;
- if (iReturnIndex < 0 || iReturnIndex >= MAX_GENTITIES)
- {
- iReturnIndex = -1; // will get a NULL ptr on reload
- }
- return iReturnIndex;
- }
- static gentity_t *GetGEntityPtr(int iEntNum)
- {
- if (iEntNum == -1)
- {
- return NULL;
- }
- assert(iEntNum >= 0);
- assert(iEntNum < MAX_GENTITIES);
- return (g_entities + iEntNum);
- }
- //
- //
- ////////////////////////////////
- static int GetGroupNumber(AIGroupInfo_t *pGroup)
- {
- assert( pGroup != (AIGroupInfo_t *) 0xcdcdcdcd);
- if (pGroup == NULL)
- {
- return -1;
- }
- int iReturnIndex = pGroup - level.groups;
- if (iReturnIndex < 0 || iReturnIndex >= (sizeof(level.groups) / sizeof(level.groups[0])) )
- {
- iReturnIndex = -1; // will get a NULL ptr on reload
- }
- return iReturnIndex;
- }
- static AIGroupInfo_t *GetGroupPtr(int iGroupNum)
- {
- if (iGroupNum == -1)
- {
- return NULL;
- }
- assert(iGroupNum >= 0);
- assert(iGroupNum < (sizeof(level.groups) / sizeof(level.groups[0])));
- return (level.groups + iGroupNum);
- }
- /////////// gclient_t * ////////
- //
- //
- static int GetGClientNum(gclient_t *c, gentity_t *ent)
- {
- // unfortunately, I now need to see if this is a 'real' client (and therefore resolve to an enum), or
- // whether it's one of the NPC or SP_misc_weapon_shooter
- //
- assert(c != (gclient_t *)0xcdcdcdcd);
- if (c == NULL)
- {
- return -1;
- }
-
- if (ent->s.number < MAX_CLIENTS)
- { // regular client...
- return (c - level.clients);
- }
- else
- { // this must be an NPC or weapon_shooter, so mark it as special...
- return -2; // yeuch, but distinguishes it from a valid 0 index, or -1 for client==NULL
- }
- }
- static gclient_t *GetGClientPtr(int c)
- {
- if (c == -1)
- {
- return NULL;
- }
- if (c == -2)
- {
- return (gclient_t *) -2; // preserve this value so that I know to load in one of Mike's private NPC clients later
- }
- assert(c >= 0);
- assert(c < level.maxclients);
- return (level.clients + c);
- }
- //
- //
- ////////////////////////////////
- /////////// gitem_t * //////////
- //
- //
- static int GetGItemNum (gitem_t *pItem)
- {
- assert(pItem != (gitem_t*) 0xcdcdcdcd);
-
- if (pItem == NULL)
- {
- return -1;
- }
-
- return pItem - bg_itemlist;
- }
- static gitem_t *GetGItemPtr(int iItem)
- {
- if (iItem == -1)
- {
- return NULL;
- }
- assert(iItem >= 0);
- assert(iItem < bg_numItems);
- return &bg_itemlist[iItem];
- }
- //
- //
- ////////////////////////////////
- /////////// vehicleInfo_t * //////////
- //
- //
- static int GetVehicleInfoNum(vehicleInfo_t *pVehicleInfo)
- {
- assert(pVehicleInfo != (vehicleInfo_t*) 0xcdcdcdcd);
-
- if (pVehicleInfo == NULL)
- {
- return -1;
- }
-
- return pVehicleInfo - g_vehicleInfo;
- }
- static vehicleInfo_t *GetVehicleInfoPtr(int iVehicleIndex)
- {
- if (iVehicleIndex == -1)
- {
- return NULL;
- }
- assert(iVehicleIndex > 0);
- assert(iVehicleIndex < numVehicles);
- return &g_vehicleInfo[iVehicleIndex];
- }
- //
- //
- ////////////////////////////////
- static void EnumerateField(const save_field_t *pField, const byte *pbBase)
- {
- void *pv = (void *)(pbBase + pField->iOffset);
- switch (pField->eFieldType)
- {
- case F_STRING:
- *(int *)pv = GetStringNum(*(char **)pv);
- break;
- case F_GENTITY:
- *(int *)pv = GetGEntityNum(*(gentity_t **)pv);
- break;
- case F_GROUP:
- *(int *)pv = GetGroupNumber(*(AIGroupInfo_t **)pv);
- break;
- case F_GCLIENT:
- *(int *)pv = GetGClientNum(*(gclient_t **)pv, (gentity_t *) pbBase);
- break;
- case F_ITEM:
- *(int *)pv = GetGItemNum(*(gitem_t **)pv);
- break;
- case F_VEHINFO:
- *(int *)pv = GetVehicleInfoNum(*(vehicleInfo_t **)pv);
- break;
- case F_BEHAVIORSET:
- {
- const char **p = (const char **) pv;
- for (int i=0; i<NUM_BSETS; i++)
- {
- pv = &p[i]; // since you can't ++ a void ptr
- *(int *)pv = GetStringNum(*(char **)pv);
- }
- }
- break;
- /*MCG
- case F_BODYQUEUE:
- {
- gentity_t **p = (gentity_t **) pv;
- for (int i=0; i<BODY_QUEUE_SIZE; i++)
- {
- pv = &p[i]; // since you can't ++ a void ptr
- *(int *)pv = GetGEntityNum(*(gentity_t **)pv);
- }
- }
- break;
- */
- case F_ALERTEVENT: // convert all gentity_t ptrs in an alertEvent array into indexes...
- {
- alertEvent_t* p = (alertEvent_t *) pv;
- for (int i=0; i<MAX_ALERT_EVENTS; i++)
- {
- p[i].owner = (gentity_t *) GetGEntityNum(p[i].owner);
- }
- }
- break;
- case F_AIGROUPS: // convert to ptrs within this into indexes...
- {
- AIGroupInfo_t* p = (AIGroupInfo_t *) pv;
- for (int i=0; i<MAX_FRAME_GROUPS; i++)
- {
- p[i].enemy = (gentity_t *) GetGEntityNum(p[i].enemy);
- p[i].commander = (gentity_t *) GetGEntityNum(p[i].commander);
- }
- }
- break;
- case F_ANIMFILESETS:
- {
- animFileSet_t* p = (animFileSet_t *) pv;
- for (int i=0; i<MAX_ANIM_FILES; i++)
- {
- for ( int j=0; j<MAX_ANIM_EVENTS; j++ )
- {
- const char *ptAnimEventStringData = p[i].torsoAnimEvents[j].stringData;
- *(int *)(&p[i].torsoAnimEvents[j].stringData) = GetStringNum( ptAnimEventStringData );
- const char *plAnimEventStringData = p[i].legsAnimEvents[j].stringData;
- *(int *)(&p[i].legsAnimEvents[j].stringData) = GetStringNum( plAnimEventStringData );
- }
- }
- }
- break;
- case F_BOOLPTR:
- *(qboolean *)pv = !!(*(int *)pv);
- break;
- // These are pointers that are always recreated
- case F_NULL:
- *(void **)pv = NULL;
- break;
- case F_IGNORE:
- break;
- default:
- G_Error ("EnumerateField: unknown field type");
- break;
- }
- }
- static void EnumerateFields(const save_field_t *pFields, const byte *pbData, unsigned long ulChid, int iLen)
- {
- strList = new list<sstring_t>;
- // enumerate all the fields...
- //
- if (pFields)
- {
- for (const save_field_t *pField = pFields; pField->psName; pField++)
- {
- assert(pField->iOffset < iLen);
- EnumerateField(pField, pbData);
- }
- }
-
- // save out raw data...
- //
- gi.AppendToSaveGame(ulChid, pbData, iLen);
- // save out any associated strings..
- //
- list<sstring_t>::iterator it = strList->begin();
- for (unsigned int i=0; i<strList->size(); i++, ++it)
- {
- gi.AppendToSaveGame('STRG', (void *)(*it).c_str(), (*it).length() + 1);
- }
-
- delete strList;
- strList = NULL;
- }
- static void EvaluateField(const save_field_t *pField, byte *pbBase, byte *pbOriginalRefData/* may be NULL*/)
- {
- void *pv = (void *)(pbBase + pField->iOffset);
- void *pvOriginal = (void *)(pbOriginalRefData + pField->iOffset);
- switch (pField->eFieldType)
- {
- case F_STRING:
- *(char **)pv = GetStringPtr(*(int *)pv, pbOriginalRefData?*(char**)pvOriginal:NULL);
- break;
- case F_GENTITY:
- *(gentity_t **)pv = GetGEntityPtr(*(int *)pv);
- break;
- case F_GROUP:
- *(AIGroupInfo_t **)pv = GetGroupPtr(*(int *)pv);
- break;
- case F_GCLIENT:
- *(gclient_t **)pv = GetGClientPtr(*(int *)pv);
- break;
- case F_ITEM:
- *(gitem_t **)pv = GetGItemPtr(*(int *)pv);
- break;
- case F_VEHINFO:
- *(vehicleInfo_t **)pv = GetVehicleInfoPtr(*(int *)pv);
- break;
- case F_BEHAVIORSET:
- {
- char **p = (char **) pv;
- char **pO= (char **) pvOriginal;
- for (int i=0; i<NUM_BSETS; i++, p++, pO++)
- {
- *p = GetStringPtr(*(int *)p, pbOriginalRefData?*(char **)pO:NULL);
- }
- }
- break;
- /*MCG
- case F_BODYQUEUE:
- {
- gentity_t **p = (gentity_t **) pv;
- for (int i=0; i<BODY_QUEUE_SIZE; i++, p++)
- {
- *p = GetGEntityPtr(*(int *)p);
- }
- }
- break;
- */
- case F_ALERTEVENT:
- {
- alertEvent_t* p = (alertEvent_t *) pv;
- for (int i=0; i<MAX_ALERT_EVENTS; i++)
- {
- p[i].owner = GetGEntityPtr((int)(p[i].owner));
- }
- }
- break;
- case F_AIGROUPS: // convert to ptrs within this into indexes...
- {
- AIGroupInfo_t* p = (AIGroupInfo_t *) pv;
- for (int i=0; i<MAX_FRAME_GROUPS; i++)
- {
- p[i].enemy = GetGEntityPtr((int)(p[i].enemy));
- p[i].commander = GetGEntityPtr((int)(p[i].commander));
- }
- }
- break;
- case F_ANIMFILESETS:
- {
- animFileSet_t* p = (animFileSet_t *) pv;
- char *pO;
- for (int i=0; i<MAX_ANIM_FILES; i++)
- {
- for ( int j=0; j<MAX_ANIM_EVENTS; j++ )
- {
- pO = pbOriginalRefData ? level.knownAnimFileSets[i].torsoAnimEvents[j].stringData : NULL;
- p[i].torsoAnimEvents[j].stringData = GetStringPtr((int)p[i].torsoAnimEvents[j].stringData, pO);
- pO = pbOriginalRefData ? level.knownAnimFileSets[i].legsAnimEvents[j].stringData : NULL;
- p[i].legsAnimEvents[j].stringData = GetStringPtr((int)p[i].legsAnimEvents[j].stringData, pO);
- }
- }
- }
- break;
- // // These fields are patched in when their relevant owners are loaded
- case F_BOOLPTR:
- case F_NULL:
- break;
- case F_IGNORE:
- break;
- default:
- G_Error ("EvaluateField: unknown field type");
- break;
- }
- }
- // copy of function in sv_savegame
- static LPCSTR SG_GetChidText(unsigned long chid)
- {
- static char chidtext[5];
- *(unsigned long *)chidtext = BigLong(chid);
- chidtext[4] = 0;
- return chidtext;
- }
- static void EvaluateFields(const save_field_t *pFields, byte *pbData, byte *pbOriginalRefData, unsigned long ulChid, int iSize, qboolean bOkToSizeMisMatch)
- {
- int iReadSize = gi.ReadFromSaveGame(ulChid, pbData, bOkToSizeMisMatch?0:iSize);
- if (iReadSize != iSize)
- {
- // handle any chunks that are ok to change length (typically this is a last minute hack,
- // so hopefully we won't need it any more... ;-)
- //
- switch (ulChid)
- {
- // example chunk handler...
- //
- case 'GCLI':
- /* assert(iSize>iReadSize);
- memset(&pbData[iReadSize], 0, iSize-iReadSize); // zero out new objectives that weren't in old-format save file
- break;
- */
- default:
- // won't return...
- //
- G_Error(va("EvaluateFields(): variable-sized chunk '%s' without handler!",SG_GetChidText(ulChid)));
- break;
- }
- }
-
- if (pFields)
- {
- for (const save_field_t *pField = pFields; pField->psName; pField++)
- {
- EvaluateField(pField, pbData, pbOriginalRefData);
- }
- }
- }
- /*
- ==============
- WriteLevelLocals
- All pointer variables (except function pointers) must be handled specially.
- ==============
- */
- static void WriteLevelLocals ()
- {
- level_locals_t *temp = (level_locals_t *)gi.Malloc(sizeof(level_locals_t), TAG_TEMP_WORKSPACE, qfalse);
- *temp = level; // copy out all data into a temp space
- EnumerateFields(savefields_LevelLocals, (byte *)temp, 'LVLC', LLOFS(LEVEL_LOCALS_T_SAVESTOP)); // sizeof(temp));
- gi.Free(temp);
- }
- /*
- ==============
- ReadLevelLocals
- All pointer variables (except function pointers) must be handled specially.
- ==============
- */
- static void ReadLevelLocals ()
- {
- // preserve client ptr either side of the load, because clients are already saved/loaded through Read/Writegame...
- //
- gclient_t *pClients = level.clients; // save clients
- level_locals_t *temp = (level_locals_t *)gi.Malloc(sizeof(level_locals_t), TAG_TEMP_WORKSPACE, qfalse);
- *temp = level; // struct copy
- EvaluateFields(savefields_LevelLocals, (byte *)temp, (byte *)&level, 'LVLC', LLOFS(LEVEL_LOCALS_T_SAVESTOP),qfalse); // sizeof(level_locals_t));
- level = *temp; // struct copy
- level.clients = pClients; // restore clients
- gi.Free(temp);
- }
- static void WriteGEntities(qboolean qbAutosave)
- {
- int iCount = 0;
- for (int i=0; i<(qbAutosave?1:globals.num_entities); i++)
- {
- gentity_t* ent = &g_entities[i];
- if ( ent->inuse )
- {
- iCount++;
- }
- }
- gi.AppendToSaveGame('NMED', &iCount, sizeof(iCount));
- for (i=0; i<(qbAutosave?1:globals.num_entities); i++)
- {
- gentity_t* ent = &g_entities[i];
- if ( ent->inuse)
- {
- gi.AppendToSaveGame('EDNM', (void *)&i, sizeof(i));
- qboolean qbLinked = ent->linked;
- gi.unlinkentity( ent );
- gentity_t tempEnt = *ent; // make local copy
- tempEnt.linked = qbLinked;
- if (qbLinked)
- {
- gi.linkentity( ent );
- }
- EnumerateFields(savefields_gEntity, (byte *)&tempEnt, 'GENT', sizeof(tempEnt));
- // now for any fiddly bits that would be rather awkward to build into the enumerator...
- //
- if (tempEnt.NPC)
- {
- gNPC_t npc = *ent->NPC; // NOT *tempEnt.NPC; !! :-)
- EnumerateFields(savefields_gNPC, (byte *)&npc, 'GNPC', sizeof(npc));
- }
- if (tempEnt.client == (gclient_t *)-2) // I know, I know...
- {
- gclient_t client = *ent->client; // NOT *tempEnt.client!!
- EnumerateFields(savefields_gClient, (byte *)&client, 'GCLI', sizeof(client));
- }
- if (tempEnt.parms)
- {
- gi.AppendToSaveGame('PARM', ent->parms, sizeof(*ent->parms));
- }
-
- if (tempEnt.m_pVehicle)
- {
- Vehicle_t vehicle = *ent->m_pVehicle; // NOT *tempEnt.m_pVehicle!!
- EnumerateFields(savefields_gVHIC, (byte *)&vehicle, 'VHIC', sizeof(vehicle));
- }
-
- // the scary ghoul2 saver stuff... (fingers crossed)
- //
- gi.G2API_SaveGhoul2Models(tempEnt.ghoul2);
- tempEnt.ghoul2.kill(); // this handle was shallow copied from an ent. We don't want it destroyed
- }
- }
- //Write out all entity timers
- TIMER_Save();//WriteEntityTimers();
- if (!qbAutosave)
- {
- //Save out ICARUS information
- IIcarusInterface::GetIcarus()->Save();
- // this marker needs to be here, it lets me know if Icarus doesn't load everything back later,
- // which has happened, and doesn't always show up onscreen until certain game situations.
- // This saves time debugging, and makes things easier to track.
- //
- static int iBlah = 1234;
- gi.AppendToSaveGame('ICOK', &iBlah, sizeof(iBlah));
- }
- if (!qbAutosave )//really shouldn't need to write these bits at all, just restore them from the ents...
- {
- WriteInUseBits();
- }
- }
- static void ReadGEntities(qboolean qbAutosave)
- {
- int iCount;
-
- gi.ReadFromSaveGame('NMED', (void *)&iCount, sizeof(iCount));
- int iPreviousEntRead = -1;
- for (int i=0; i<iCount; i++)
- {
- int iEntIndex;
- gi.ReadFromSaveGame('EDNM', (void *)&iEntIndex, sizeof(iEntIndex));
- if (iEntIndex >= globals.num_entities)
- {
- globals.num_entities = iEntIndex + 1;
- }
- if (iPreviousEntRead != iEntIndex-1)
- {
- for (int j=iPreviousEntRead+1; j!=iEntIndex; j++)
- {
- if ( g_entities[j].inuse ) // not actually necessary
- {
- G_FreeEntity(&g_entities[j]);
- }
- }
- }
- iPreviousEntRead = iEntIndex;
- // slightly naff syntax here, but makes a few ops clearer later...
- //
- gentity_t entity;
- gentity_t* pEntOriginal = &entity;
- gentity_t* pEnt = &g_entities[iEntIndex];
- *pEntOriginal = *pEnt; // struct copy, so we can refer to original
-
- pEntOriginal->ghoul2.kill();
- gi.unlinkentity(pEnt);
- Quake3Game()->FreeEntity( pEnt );
-
- //
- // sneaky: destroy the ghoul2 object within this struct before binary-loading over the top of it...
- //
- gi.G2API_LoadSaveCodeDestructGhoul2Info(pEnt->ghoul2);
- pEnt->ghoul2.kill();
- EvaluateFields(savefields_gEntity, (byte *)pEnt, (byte *)pEntOriginal, 'GENT', sizeof(*pEnt),qfalse);
- pEnt->ghoul2.kill();
- // now for any fiddly bits...
- //
- if (pEnt->NPC) // will be qtrue/qfalse
- {
- gNPC_t tempNPC;
- EvaluateFields(savefields_gNPC, (byte *)&tempNPC,(byte *)pEntOriginal->NPC, 'GNPC', sizeof (*pEnt->NPC),qfalse);
-
- // so can we pinch the original's one or do we have to alloc a new one?...
- //
- if (pEntOriginal->NPC)
- {
- // pinch this G_Alloc handle...
- //
- pEnt->NPC = pEntOriginal->NPC;
- }
- else
- {
- // original didn't have one (hmmm...), so make a new one...
- //
- //assert(0); // I want to know about this, though not in release
- pEnt->NPC = (gNPC_t *) G_Alloc(sizeof(*pEnt->NPC));
- }
- // copy over the one we've just loaded...
- //
- *pEnt->NPC = tempNPC; // struct copy
- //FIXME: do we need to do these too?
- /*
- if ( pEnt->s.number )
- {//not player
- G_LoadAnimFileSet( *pEnt, *pEnt->NPC_type );
- G_SetSkin( *pEnt, *pEnt->NPC_type, NULL );// it probably wasn't the default skin, do we need this at all?
- }
- */
- }
- if (pEnt->client == (gclient_t*) -2) // one of Mike G's NPC clients?
- {
- gclient_t tempGClient;
- EvaluateFields(savefields_gClient, (byte *)&tempGClient, (byte *)pEntOriginal->client, 'GCLI', sizeof(*pEnt->client),qfalse);
- // can we pinch the original's client handle or do we have to alloc a new one?...
- //
- if (pEntOriginal->client)
- {
- // pinch this G_Alloc handle...
- //
- pEnt->client = pEntOriginal->client;
- }
- else
- {
- // original didn't have one (hmmm...) so make a new one...
- //
- pEnt->client = (gclient_t *) G_Alloc(sizeof(*pEnt->client));
- }
- // copy over the one we've just loaded....
- //
- *pEnt->client = tempGClient; // struct copy
- if ( pEnt->s.number )
- {//not player
- G_ReloadSaberData( pEnt );
- }
- }
- // Some Icarus thing... (probably)
- //
- if (pEnt->parms) // will be qtrue/qfalse
- {
- parms_t tempParms;
-
- gi.ReadFromSaveGame('PARM', &tempParms, sizeof(tempParms));
- // so can we pinch the original's one or do we have to alloc a new one?...
- //
- if (pEntOriginal->parms)
- {
- // pinch this G_Alloc handle...
- //
- pEnt->parms = pEntOriginal->parms;
- }
- else
- {
- // original didn't have one, so make a new one...
- //
- pEnt->parms = (parms_t *) G_Alloc(sizeof(*pEnt->parms));
- }
- // copy over the one we've just loaded...
- //
- *pEnt->parms = tempParms; // struct copy
- }
- if (pEnt->m_pVehicle) // will be qtrue/qfalse
- {
- Vehicle_t tempVehicle;
-
- EvaluateFields(savefields_gVHIC, (byte *)&tempVehicle,(byte *)pEntOriginal->m_pVehicle, 'VHIC', sizeof (*pEnt->m_pVehicle),qfalse);
- // so can we pinch the original's one or do we have to alloc a new one?...
- //
- if (pEntOriginal->m_pVehicle)
- {
- // pinch this G_Alloc handle...
- //
- pEnt->m_pVehicle = pEntOriginal->m_pVehicle;
- }
- else
- {
- // original didn't have one, so make a new one...
- //
- pEnt->m_pVehicle = (Vehicle_t *) gi.Malloc( sizeof(Vehicle_t), TAG_G_ALLOC, qfalse );
- }
- // copy over the one we've just loaded...
- //
- *pEnt->m_pVehicle = tempVehicle; // struct copy
- }
- // the scary ghoul2 stuff... (fingers crossed)
- //
- {
- char *pGhoul2Data = NULL;
- gi.ReadFromSaveGame('GHL2', 0, 0, (void**)&pGhoul2Data);
- gi.G2API_LoadGhoul2Models(pEnt->ghoul2, pGhoul2Data); // if it's going to crash anywhere... <g>
- gi.Free(pGhoul2Data);
- }
- // gi.unlinkentity (pEntOriginal);
- // ICARUS_FreeEnt( pEntOriginal );
- // *pEntOriginal = *pEnt; // struct copy
- // qboolean qbLinked = pEntOriginal->linked;
- // pEntOriginal->linked = qfalse;
- // if (qbLinked)
- // {
- // gi.linkentity (pEntOriginal);
- // }
- // because the sytem stores sfx_t handles directly instead of the set, we have to reget the set's sfx_t...
- //
- if (pEnt->s.eType == ET_MOVER && pEnt->s.loopSound>0)
- {
- if ( VALIDSTRING( pEnt->soundSet ))
- {
- extern int BMS_MID; // from g_mover
- pEnt->s.loopSound = CAS_GetBModelSound( pEnt->soundSet, BMS_MID );
- if (pEnt->s.loopSound == -1)
- {
- pEnt->s.loopSound = 0;
- }
- }
- }
- // NPCs and other ents store waypoints that aren't valid after a load
- pEnt->waypoint = 0;
- // Hazard troopers store a troop value that isn't valid either:
- if( pEnt->NPC )
- pEnt->NPC->troop = 0;
- qboolean qbLinked = pEnt->linked;
- pEnt->linked = qfalse;
- if (qbLinked)
- {
- gi.linkentity (pEnt);
- }
- }
- //Read in all the entity timers
- TIMER_Load();//ReadEntityTimers();
- if (!qbAutosave)
- {
- // now zap any g_ents that were inuse when the level was loaded, but are no longer in use in the saved version
- // that we've just loaded...
- //
- for (i=iPreviousEntRead+1; i<globals.num_entities; i++)
- {
- if ( g_entities[i].inuse ) // not actually necessary
- {
- G_FreeEntity(&g_entities[i]);
- }
- }
- //Load ICARUS information
- Quake3Game()->ClearEntityList();
- IIcarusInterface::GetIcarus()->Load();
- // check that Icarus has loaded everything it saved out by having a marker chunk after it...
- //
- static int iBlah = 1234;
- gi.ReadFromSaveGame('ICOK', &iBlah, sizeof(iBlah));
- }
- if (!qbAutosave)
- {
- ReadInUseBits();//really shouldn't need to read these bits in at all, just restore them from the ents...
- }
- }
- void WriteLevel(qboolean qbAutosave)
- {
- if (!qbAutosave) //-always save the client
- {
- // write out one client - us!
- //
- assert(level.maxclients == 1); // I'll need to know if this changes, otherwise I'll need to change the way ReadGame works
- gclient_t client = level.clients[0];
- EnumerateFields(savefields_gClient, (byte *)&client, 'GCLI', sizeof(client));
- WriteLevelLocals(); // level_locals_t level
- }
- OBJ_SaveObjectiveData();
- FX_Write();
- /////////////
- WriteGEntities(qbAutosave);
- Quake3Game()->VariableSave();
- G_LoadSave_WriteMiscData();
- extern void CG_WriteTheEvilCGHackStuff(void);
- CG_WriteTheEvilCGHackStuff();
- // (Do NOT put any write-code below this line)
- //
- // put out an end-marker so that the load code can check everything was read in...
- //
- static int iDONE = 1234;
- gi.AppendToSaveGame('DONE', &iDONE, sizeof(iDONE));
- }
- void ReadLevel(qboolean qbAutosave, qboolean qbLoadTransition)
- {
- if ( qbLoadTransition )
- {
- // I STRONGLY SUSPECT THAT THIS WILL JUST ERR_DROP BECAUSE OF THE LOAD SWAPPING OF THE CHUNK-ORDER
- // BELOW BETWEEN OBJECTIVES AND LEVEL_LOCALS, SO I'M GUESSING THIS IS SOME OLD EF1 JUNK?
- // IN ANY CASE, LET'S MAKE SURE... // -ste (no idea who wrote the comment stuff below, did it ever work?)
- //
- assert(0);
- //
- //loadtransitions do not need to read the objectives and client data from the level they're going to
- //In a loadtransition, client data is carried over on the server and will be stomped later anyway.
- //The objective info (in client->sess data), however, is read in from G_ReadSessionData which is called before this func,
- //we do NOT want to stomp that session data when doing a load transition
-
- //However, we should still save this info out because these savegames may need to be
- //loaded normally later- perhaps if you die and need to respawn, perhaps as some kind
- //of emergency savegame for resuming, etc.
- //SO: We read it in, but throw it away.
-
- //Read & throw away gclient info
- gclient_t junkClient;
- EvaluateFields(savefields_gClient, (byte *)&junkClient, (byte *)&level.clients[0], 'GCLI', sizeof(*level.clients), qfalse);
- //Read & throw away objective info
- objectives_t junkObj[MAX_MISSION_OBJ];
- gi.ReadFromSaveGame('OBJT', (void *) &junkObj, 0);
- ReadLevelLocals(); // level_locals_t level
- }
- else
- {
- if (!qbAutosave )//always load the client unless it's an autosave
- {
- assert(level.maxclients == 1); // I'll need to know if this changes, otherwise I'll need to change the way things work
-
- gclient_t GClient;
- EvaluateFields(savefields_gClient, (byte *)&GClient, (byte *)&level.clients[0], 'GCLI', sizeof(*level.clients), qfalse);
- level.clients[0] = GClient; // struct copy
- ReadLevelLocals(); // level_locals_t level
- }
-
- OBJ_LoadObjectiveData();//loads mission objectives AND tactical info
- }
- FX_Read();
- /////////////
- ReadGEntities(qbAutosave);
- Quake3Game()->VariableLoad();
- G_LoadSave_ReadMiscData();
- extern void CG_ReadTheEvilCGHackStuff(void);
- CG_ReadTheEvilCGHackStuff();
- // (Do NOT put any read-code below this line)
- //
- // check that the whole file content was loaded by specifically requesting an end-marker...
- //
- static int iDONE = 1234;
- gi.ReadFromSaveGame('DONE', &iDONE, sizeof(iDONE));
- }
- extern int killPlayerTimer;
- qboolean GameAllowedToSaveHere(void)
- {
- return (!in_camera&&!killPlayerTimer);
- }
- //////////////////// eof /////////////////////
- #if 0
- // !!!!!!!!!!!!!!!!!! loadsave affecting structure !!!!!!!!!!!!!!!!!!!!!!!
- struct Vehicle_t
- {
- // The entity who pilots/drives this vehicle.
- // NOTE: This is redundant (since m_pParentEntity->owner _should_ be the pilot). This makes things clearer though.
- gentity_t *m_pPilot;
- int m_iPilotTime; //if spawnflag to die without pilot and this < level.time then die.
- qboolean m_bHasHadPilot; //qtrue once the vehicle gets its first pilot
- // The passengers of this vehicle.
- gentity_t **m_ppPassengers;
- // The number of passengers currently in this vehicle.
- int m_iNumPassengers;
- //the droid unit NPC for this vehicle, if any
- gentity_t *m_pDroidUnit;
- // The entity from which this NPC comes from.
- gentity_t *m_pParentEntity;
- // If not zero, how long to wait before we can do anything with the vehicle (we're getting on still).
- // -1 = board from left, -2 = board from right, -3 = jump/quick board. -4 & -5 = throw off existing pilot
- int m_iBoarding;
- // Used to check if we've just started the boarding process
- bool m_bWasBoarding;
- // The speed the vehicle maintains while boarding occurs (often zero)
- vec3_t m_vBoardingVelocity;
- // Time modifier (must only be used in ProcessMoveCommands() and ProcessOrientCommands() and is updated in Update()).
- float m_fTimeModifier;
- // Ghoul2 Animation info.
- // NOTE: Since each vehicle has their own model instance, these bolts must be local to each vehicle as well.
- int m_iLeftWingBone;
- int m_iRightWingBone;
- //int m_iDriverTag;
- int m_iExhaustTag[MAX_VEHICLE_EXHAUSTS];
- int m_iMuzzleTag[MAX_VEHICLE_MUZZLES];
- int m_iDroidUnitTag;
- int m_iGunnerViewTag[MAX_VEHICLE_TURRETS];//Where to put the view origin of the gunner (index)
- // This vehicles weapon muzzles.
- Muzzle m_Muzzles[MAX_VEHICLE_MUZZLES];
- // The user commands structure.
- usercmd_t m_ucmd;
- // The direction an entity will eject from the vehicle towards.
- int m_EjectDir;
- // Flags that describe the vehicles behavior.
- unsigned long m_ulFlags;
- // NOTE: Vehicle Type ID, Orientation, and Armor MUST be transmitted over the net.
- // Current angles of this vehicle.
- vec3_t m_vOrientation;
- // How long you have strafed left or right (increments every frame that you strafe to right, decrements every frame you strafe left)
- int m_fStrafeTime;
- // Previous angles of this vehicle.
- vec3_t m_vPrevOrientation;
- // When control is lost on a speeder, current angular velocity is stored here and applied until landing
- float m_vAngularVelocity;
- vec3_t m_vFullAngleVelocity;
- // Current armor and shields of your vehicle (explodes if armor to 0).
- int m_iArmor; //hull strength - STAT_HEALTH on NPC
- int m_iShields; //energy shielding - STAT_ARMOR on NPC
- // Timer for all cgame-FX...? ex: exhaust?
- int m_iLastFXTime;
- // When to die.
- int m_iDieTime;
- // This pointer is to a valid VehicleInfo (which could be an animal, speeder, fighter, whatever). This
- // contains the functions actually used to do things to this specific kind of vehicle as well as shared
- // information (max speed, type, etc...).
- vehicleInfo_t *m_pVehicleInfo;
- // This trace tells us if we're within landing height.
- trace_t m_LandTrace;
- //bitflag of surfaces that have broken off
- int m_iRemovedSurfaces;
- // the last time this vehicle fired a turbo burst
- int m_iTurboTime;
- //how long it should drop like a rock for after freed from SUSPEND
- int m_iDropTime;
- int m_iSoundDebounceTimer;
- //last time we incremented the shields
- int lastShieldInc;
- //so we don't hold it down and toggle it back and forth
- qboolean linkWeaponToggleHeld;
- //info about our weapons (linked, ammo, etc.)
- vehWeaponStatus_t weaponStatus[MAX_VEHICLE_WEAPONS];
- vehTurretStatus_t turretStatus[MAX_VEHICLE_TURRETS];
- //the guy who was previously the pilot
- gentity_t* m_pOldPilot;
- // don't need these in mp
- int m_safeJumpMountTime;
- float m_safeJumpMountRightDot;
- };
- #endif
|