// Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id:$ // // Copyright (C) 1993-1996 by id Software, Inc. // // This source is available for distribution and/or modification // only under the terms of the DOOM Source Code License as // published by id Software. All rights reserved. // // The source is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License // for more details. // // $Log:$ // // DESCRIPTION: // Archiving: SaveGame I/O. // //----------------------------------------------------------------------------- #include "i_system.h" #include "z_zone.h" #include "p_local.h" // State. #include "doomstat.h" #include "r_state.h" #include "m_random.h" #include "p_saveg.h" #include "p_acs.h" #include "s_sndseq.h" #include "v_palett.h" extern button_t *buttonlist; byte *save_p; // // P_ArchivePlayers // void P_ArchivePlayers (void) { int i, j; player_t *dest; CheckSaveGame (sizeof(player_t) * MAXPLAYERS); // killough for (i = 0; i < MAXPLAYERS; i++) { if (!playeringame[i]) continue; PADSAVEP(); dest = (player_t *)save_p; memcpy (dest,&players[i],sizeof(player_t)); save_p += sizeof(player_t); for (j = 0; j < NUMPSPRITES; j++) { if (dest->psprites[j].state) { dest->psprites[j].state = (state_t *)(dest->psprites[j].state-states); } } } } // // P_UnArchivePlayers // void P_UnArchivePlayers (void) { int i, j; userinfo_t userinfo; for (i = 0; i < MAXPLAYERS; i++) { if (!playeringame[i]) continue; PADSAVEP(); memcpy (&userinfo, &players[i].userinfo, sizeof(userinfo)); // [RH] remember userinfo memcpy (&players[i], save_p, sizeof(player_t)); save_p += sizeof(player_t); memcpy (&players[i].userinfo, &userinfo, sizeof(userinfo)); players[i].mo = NULL; // will be set when unarc thinker players[i].attacker = NULL; for (j = 0; j < NUMPSPRITES; j++) { if (players[i]. psprites[j].state) { players[i]. psprites[j].state = &states[ (int)players[i].psprites[j].state ]; } } } } // // P_ArchiveWorld // void P_ArchiveWorld (void) { int i; int j; sector_t* sec; line_t* li; side_t* si; short* put; // killough 3/22/98: fix bug caused by hoisting save_p too early size_t size = (sizeof(unsigned int)*2+sizeof(float)+sizeof(short)*10)*numsectors + (sizeof(short)*8)*numlines + 4; for (i = 0; i < numlines; i++) { if (lines[i].sidenum[0] != -1) size += sizeof(short)*5; if (lines[i].sidenum[1] != -1) size += sizeof(short)*5; } CheckSaveGame (size); // killough PADSAVEP(); // killough 3/22/98 // do sectors for (i = 0, sec = sectors; i < numsectors; i++, sec++) { put = (short *)save_p; put[0] = (short)(sec->floorheight >> FRACBITS); put[1] = (short)(sec->ceilingheight >> FRACBITS); put[2] = sec->floorpic; put[3] = sec->ceilingpic; put[4] = sec->lightlevel; put[5] = sec->special; put[6] = sec->tag; put[7] = sec->damage; // [RH] put[8] = sec->mod; // [RH] put[9] = sec->seqType; // [RH] save_p = (byte *)(put + 10); // [RH] PADSAVEP(); // [RH] *((float *)save_p)++ = sec->gravity; // [RH] *((unsigned int *)save_p)++ = sec->colormap->color; // [RH] *((unsigned int *)save_p)++ = sec->colormap->fade; // [RH]; } put = (short *)save_p; // [RH] // do lines for (i = 0, li = lines; i < numlines; i++, li++) { put[0] = li->flags; put[1] = (li->special << 8) | li->lucency; // [RH] put[2] = li->id; // [RH] memcpy (put + 3, li->args, sizeof(li->args)); // [RH] put += 3 + sizeof(li->args)/sizeof(*put); for (j = 0; j < 2; j++) { if (li->sidenum[j] == -1) continue; si = &sides[li->sidenum[j]]; *put++ = (short)(si->textureoffset >> FRACBITS); *put++ = (short)(si->rowoffset >> FRACBITS); *put++ = si->toptexture; *put++ = si->bottomtexture; *put++ = si->midtexture; } } save_p = (byte *)put; } // // P_UnArchiveWorld // void P_UnArchiveWorld (void) { int i; int j; sector_t* sec; line_t* li; side_t* si; short* get; PADSAVEP(); // killough 3/22/98 // do sectors for (i=0, sec = sectors ; ifloorheight = get[0] << FRACBITS; sec->ceilingheight = get[1] << FRACBITS; sec->floorpic = get[2]; sec->ceilingpic = get[3]; sec->lightlevel = get[4]; sec->special = get[5]; sec->tag = get[6]; sec->damage = get[7]; // [RH] sec->mod = get[8]; // [RH] sec->seqType = get[9]; // [RH] save_p = (byte *)(get + 10); // [RH] PADSAVEP(); sec->gravity = *((float *)save_p)++; // [RH] color = *((unsigned int *)save_p)++; // [RH] fade = *((unsigned int *)save_p)++; // [RH] sec->colormap = GetSpecialLights ( RPART(color), GPART(color), BPART(color), RPART(fade), GPART(fade), BPART(fade)); // [RH] sec->ceilingdata = 0; //jff 2/22/98 now three thinker fields not two sec->floordata = 0; sec->lightingdata = 0; sec->soundtarget = 0; } get = (short *)save_p; // [RH] // do lines for (i=0, li = lines ; iflags = *get++; li->special = *get >> 8; // [RH] li->lucency = (*get++) & 255; // [RH] li->id = *get++; // [RH] memcpy (li->args, get, sizeof(li->args)); // [RH] get += sizeof(li->args) / sizeof(*get); for (j=0 ; j<2 ; j++) { if (li->sidenum[j] == -1) continue; si = &sides[li->sidenum[j]]; si->textureoffset = *get++ << FRACBITS; si->rowoffset = *get++ << FRACBITS; si->toptexture = *get++; si->bottomtexture = *get++; si->midtexture = *get++; } } save_p = (byte *)get; } // // Thinkers // typedef enum { tc_end, tc_mobj } thinkerclass_t; // // P_ArchiveThinkers // // 2/14/98 killough: substantially modified to fix savegame bugs void P_ArchiveThinkers (void) { thinker_t *th; size_t size = 0; CheckSaveGame (sizeof brain); // killough 3/26/98: Save boss brain state memcpy (save_p, &brain, sizeof brain); save_p += sizeof brain; // killough 2/14/98: // count the number of thinkers, and mark each one with its index, using // the prev field as a placeholder, since it can be restored later. for (th = thinkercap.next ; th != &thinkercap ; th=th->next) if (th->function.acp1 == (actionf_p1) P_MobjThinker) th->prev = (thinker_t *) ++size; // check that enough room is available in savegame buffer CheckSaveGame (size*(sizeof(mobj_t)+4)); // killough 2/14/98 // save off the current thinkers for (th = thinkercap.next ; th != &thinkercap ; th=th->next) if (th->function.acp1 == (actionf_p1) P_MobjThinker) { mobj_t *mobj; *save_p++ = tc_mobj; PADSAVEP(); mobj = (mobj_t *)save_p; memcpy (mobj, th, sizeof(*mobj)); save_p += sizeof(*mobj); mobj->state = (state_t *)(mobj->state - states); // killough 2/14/98: convert pointers into indices. // Fixes many savegame problems, by properly saving // target and tracer fields. Note: we store NULL if // the thinker pointed to by these fields is not a // mobj thinker. if (mobj->target) mobj->target = mobj->target->thinker.function.acp1 == (actionf_p1) P_MobjThinker ? (mobj_t *) mobj->target->thinker.prev : NULL; if (mobj->tracer) mobj->tracer = mobj->tracer->thinker.function.acp1 == (actionf_p1) P_MobjThinker ? (mobj_t *) mobj->tracer->thinker.prev : NULL; if (mobj->goal) // [RH] Save goal properly mobj->goal = mobj->goal->thinker.function.acp1 == (actionf_p1) P_MobjThinker ? (mobj_t *) mobj->goal->thinker.prev : NULL; // killough 2/14/98: new field: save last known enemy. Prevents // monsters from going to sleep after killing monsters and not // seeing player anymore. if (mobj->lastenemy) mobj->lastenemy = mobj->lastenemy->thinker.function.acp1 == (actionf_p1) P_MobjThinker ? (mobj_t *) mobj->lastenemy->thinker.prev : NULL; // killough 2/14/98: end changes if (mobj->player) mobj->player = (player_t *)((mobj->player-players) + 1); } // add a terminating marker *save_p++ = tc_end; // [RH] Don't restore mobj prev fields until the end of // P_ArchiveThinkers() since we need the indices for // archiving ACS scripts, too. // killough 2/14/98: end changes } // // P_UnArchiveThinkers // // 2/14/98 killough: substantially modified to fix savegame bugs // [RH] Moved this out of P_UnArchiveThinkers so that it's visible // within P_UnArchiveScripts() and P_UnArchiveSpecials(). static mobj_t **mobj_p; // killough 2/14/98: Translation table // [RH] Added keepPlayers. When true, the player mobjs that were // spawned during level initialization are kept instead of // being replaced by the ones in the snapshot. void P_UnArchiveThinkers (BOOL keepPlayers) { thinker_t *th; size_t size; // killough 2/14/98: size of or index into table // killough 3/26/98: Load boss brain state memcpy (&brain, save_p, sizeof brain); save_p += sizeof brain; // remove all the current thinkers P_ClearTidHashes (); // [RH] Clear the tid hash table for (th = thinkercap.next; th != &thinkercap; ) { thinker_t *next = th->next; if (th->function.acp1 == (actionf_p1) P_MobjThinker) { if (!keepPlayers || !(((mobj_t *)th)->player)) P_RemoveMobj ((mobj_t *) th); } else { // time to remove it th->next->prev = th->prev; th->prev->next = th->next; Z_Free (th); } th = next; } // if (!keepPlayers) // P_InitThinkers (); // killough 2/14/98: count number of thinkers by skipping through them { byte *sp = save_p; // save pointer and skip header for (size = 1; *save_p++ == tc_mobj; size++) // killough 2/14/98 { // skip all entries, adding up count PADSAVEP(); save_p += sizeof(mobj_t); } if (*--save_p != tc_end) I_Error ("Unknown tclass %i in snapshot", *save_p); // first table entry special: 0 maps to NULL *(mobj_p = Z_Malloc(size * sizeof *mobj_p, PU_STATIC, 0)) = 0; // table of pointers save_p = sp; // restore save pointer } // read in saved thinkers for (size = 1; *save_p++ == tc_mobj; size++) // killough 2/14/98 { mobj_t *mobj = Z_Malloc(sizeof(mobj_t), PU_LEVEL, NULL); // killough 2/14/98 -- insert pointers to thinkers into table, in order: mobj_p[size] = mobj; PADSAVEP(); memcpy (mobj, save_p, sizeof(mobj_t)); save_p += sizeof(mobj_t); mobj->state = states + (int) mobj->state; if (mobj->player) { // [RH] If we're re-entering a level as part of movement in a hub, // use the currently existing player mobj instead of the one // in the snapshot. Note that this doesn't work with voodoo // dolls, and fixing that isn't worth the trouble since // scripting offers everything voodoo dolls did and more. if (keepPlayers) { Z_Free (mobj); mobj_p[size] = players[(int)mobj->player - 1].mo; continue; } (mobj->player = &players[(int) mobj->player - 1]) -> mo = mobj; mobj->player->camera = mobj; // [RH] Reset the camera to the player's viewpoint } P_SetThingPosition (mobj); mobj->info = &mobjinfo[mobj->type]; // killough 2/28/98: // Fix for falling down into a wall after savegame loaded: // mobj->floorz = mobj->subsector->sector->floorheight; // mobj->ceilingz = mobj->subsector->sector->ceilingheight; mobj->thinker.function.acp1 = (actionf_p1) P_MobjThinker; P_AddThinker (&mobj->thinker); P_AddMobjToHash (mobj); // [RH] Add mobj to tid hash table } // killough 2/14/98: adjust target and tracer fields, plus // lastenemy field, to correctly point to mobj thinkers. // NULL entries automatically handled by first table entry. for (th = thinkercap.next ; th != &thinkercap ; th=th->next) { if (th->function.acp1 != (actionf_p1) P_MobjThinker) continue; if (!keepPlayers || !((mobj_t *)th)->player) { ((mobj_t *) th)->target = mobj_p[(size_t)((mobj_t *)th)->target]; ((mobj_t *) th)->tracer = mobj_p[(size_t)((mobj_t *)th)->tracer]; ((mobj_t *) th)->lastenemy = mobj_p[(size_t)((mobj_t *)th)->lastenemy]; ((mobj_t *) th)->goal = // [RH] restore goal mobj_p[(size_t)((mobj_t *)th)->goal]; } } // killough 3/26/98: Spawn icon landings: if (gamemode == commercial) P_SpawnBrainTargets(); } // // P_ArchiveSpecials // enum { tc_ceiling, tc_door, tc_floor, tc_plat, tc_flash, tc_strobe, tc_glow, tc_fireflicker, tc_elevator, //jff 2/22/98 new elevator type thinker tc_scroll, // killough 3/7/98: new scroll effect thinker tc_glow2, tc_waggle, tc_phased, tc_pillar, tc_friction, // phares 3/18/98: new friction effect thinker tc_pusher, // phares 3/22/98: new push/pull effect thinker tc_rotate_poly, tc_move_poly, tc_poly_door, tc_flicker, tc_endspecials } specials_e; // // Things to handle: // // T_MoveCeiling, (ceiling_t: sector_t * swizzle), - active list // T_VerticalDoor, (vldoor_t: sector_t * swizzle), // T_MoveFloor, (floormove_t: sector_t * swizzle), // T_LightFlash, (lightflash_t: sector_t * swizzle), // T_StrobeFlash, (strobe_t: sector_t *), // T_Glow, (glow_t: sector_t *), // T_PlatRaise, (plat_t: sector_t *), - active list // T_FireFlicker (fireflicker_t: sector_t * swizzle), // [RH] Was missed in original code // T_Flicker (flicker_t: sector_t * swizzle) // [RH] Hexen flicker // T_MoveElevator, (plat_t: sector_t *), - active list // jff 2/22/98 // T_Scroll // killough 3/7/98 // T_Glow2, (glow2_t: sector_t *) // [RH] // T_Waggle, (floorWaggle_t: sector_t *) // [RH] // T_PhasedLight, (phased_t: sector_t *) // [RH] // T_Pillar (pillar_t: sector_t *) // [RH] // T_Pusher (pusher_t: mobj_t *) // phares 3/22/98 // T_RotatePoly // T_MovePoly // T_PolyDoor // void P_ArchiveSpecials (void) { thinker_t* th; ceiling_t* ceiling; vldoor_t* door; floormove_t* floor; plat_t* plat; lightflash_t* flash; strobe_t* strobe; glow_t* glow; button_t* button; quake_t* quake; size_t size = 0; // killough // save off the current thinkers (memory size calculation -- killough) // [RH] save buttonlist, too button = buttonlist; while (button) { size += sizeof(*button); button = button->next; } size++; // [RH] Also save earthquakes quake = ActiveQuakes; while (quake) { size += sizeof(*quake); quake = quake->next; } size++; for (th = thinkercap.next ; th != &thinkercap ; th=th->next) if (th->function.acv == (actionf_v)NULL) { // [RH] Search for active plat or ceiling list that's in-stasis for (ceiling = activeceilings; ceiling; ceiling = ceiling->next) if (ceiling == (ceiling_t *)th) break; if (ceiling) { size += 4+sizeof(ceiling_t); goto end; } for (plat = activeplats; plat; plat = plat->next) if (plat == (plat_t *)th) break; if (plat) { size += 4+sizeof(plat_t); goto end; } end:; } else size += th->function.acp1==(actionf_p1)T_MoveCeiling ? 4+sizeof(ceiling_t) : th->function.acp1==(actionf_p1)T_VerticalDoor ? 4+sizeof(vldoor_t) : th->function.acp1==(actionf_p1)T_MoveFloor ? 4+sizeof(floormove_t): th->function.acp1==(actionf_p1)T_PlatRaise ? 4+sizeof(plat_t) : th->function.acp1==(actionf_p1)T_LightFlash ? 4+sizeof(lightflash_t): th->function.acp1==(actionf_p1)T_StrobeFlash ? 4+sizeof(strobe_t) : th->function.acp1==(actionf_p1)T_Glow ? 4+sizeof(glow_t) : th->function.acp1==(actionf_p1)T_Glow2 ? 4+sizeof(glow2_t) : th->function.acp1==(actionf_p1)T_FireFlicker ? 4+sizeof(fireflicker_t): th->function.acp1==(actionf_p1)T_Flicker ? 4+sizeof(fireflicker_t): th->function.acp1==(actionf_p1)T_PhasedLight ? 4+sizeof(phased_t) : th->function.acp1==(actionf_p1)T_MoveElevator ? 4+sizeof(elevator_t): th->function.acp1==(actionf_p1)T_Scroll ? 4+sizeof(scroll_t) : th->function.acp1==(actionf_p1)T_FloorWaggle ? 4+sizeof(floorWaggle_t): th->function.acp1==(actionf_p1)T_Pillar ? 4+sizeof(pillar_t) : th->function.acp1==(actionf_p1)T_Pusher ? 4+sizeof(pusher_t) : 0; CheckSaveGame(size); // killough // [RH] save the active buttons button = buttonlist; if (button) { *save_p++ = 1; while (button) { button_t *but; PADSAVEP(); memcpy (save_p, button, sizeof(*button)); but = (button_t *)save_p; save_p += sizeof(*button); but->line = (line_t *)(but->line ? but->line - lines : -1); button = button->next; } } else { *save_p++ = 0; } // [RH] save the active quakes quake = ActiveQuakes; if (quake) { *save_p++ = 1; while (quake) { quake_t *q; PADSAVEP(); memcpy (save_p, quake, sizeof(*quake)); q = (quake_t *)save_p; q->quakespot = (mobj_t *)q->quakespot->thinker.prev; save_p += sizeof(*quake); quake = quake->next; } } else { *save_p++ = 0; } // save off the current thinkers for (th = thinkercap.next ; th != &thinkercap ; th=th->next) { if (th->function.acv == (actionf_v)NULL) { // [RH] Use the linked list for both plats and ceilings for (ceiling = activeceilings; ceiling; ceiling = ceiling->next) if (ceiling == (ceiling_t *)th) goto ceiling; for (plat = activeplats; plat; plat = plat->next) if (plat == (plat_t *)th) goto plat; continue; } if (th->function.acp1 == (actionf_p1)T_MoveCeiling) { ceiling: *save_p++ = tc_ceiling; PADSAVEP(); ceiling = (ceiling_t *)save_p; memcpy (ceiling, th, sizeof(*ceiling)); save_p += sizeof(*ceiling); ceiling->sector = (sector_t *)(ceiling->sector - sectors); } else if (th->function.acp1 == (actionf_p1)T_VerticalDoor) { *save_p++ = tc_door; PADSAVEP(); door = (vldoor_t *)save_p; memcpy (door, th, sizeof(*door)); save_p += sizeof(*door); door->sector = (sector_t *)(door->sector - sectors); } else if (th->function.acp1 == (actionf_p1)T_MoveFloor) { *save_p++ = tc_floor; PADSAVEP(); floor = (floormove_t *)save_p; memcpy (floor, th, sizeof(*floor)); save_p += sizeof(*floor); floor->sector = (sector_t *)(floor->sector - sectors); } else if (th->function.acp1 == (actionf_p1)T_PlatRaise) { plat: *save_p++ = tc_plat; PADSAVEP(); plat = (plat_t *)save_p; memcpy (plat, th, sizeof(*plat)); save_p += sizeof(*plat); plat->sector = (sector_t *)(plat->sector - sectors); } else if (th->function.acp1 == (actionf_p1)T_LightFlash) { *save_p++ = tc_flash; PADSAVEP(); flash = (lightflash_t *)save_p; memcpy (flash, th, sizeof(*flash)); save_p += sizeof(*flash); flash->sector = (sector_t *)(flash->sector - sectors); } else if (th->function.acp1 == (actionf_p1)T_StrobeFlash) { *save_p++ = tc_strobe; PADSAVEP(); strobe = (strobe_t *)save_p; memcpy (strobe, th, sizeof(*strobe)); save_p += sizeof(*strobe); strobe->sector = (sector_t *)(strobe->sector - sectors); } else if (th->function.acp1 == (actionf_p1)T_Glow) { *save_p++ = tc_glow; PADSAVEP(); glow = (glow_t *)save_p; memcpy (glow, th, sizeof(*glow)); save_p += sizeof(*glow); glow->sector = (sector_t *)(glow->sector - sectors); } else if (th->function.acp1 == (actionf_p1)T_FireFlicker) { fireflicker_t *flick; // [RH] Save FireFlicker thinkers. The Linux code missed these. *save_p++ = tc_fireflicker; PADSAVEP(); flick = (fireflicker_t *)save_p; memcpy (flick, th, sizeof(*flick)); save_p += sizeof(*flick); flick->sector = (sector_t *)(flick->sector - sectors); } else if (th->function.acp1 == (actionf_p1)T_Flicker) { fireflicker_t *flick; *save_p++ = tc_flicker; PADSAVEP(); flick = (fireflicker_t *)save_p; memcpy (flick, th, sizeof(*flick)); save_p += sizeof(*flick); flick->sector = (sector_t *)(flick->sector - sectors); } else if (th->function.acp1 == (actionf_p1)T_MoveElevator) { //jff 2/22/98 new case for elevators elevator_t *elevator; //jff 2/22/98 *save_p++ = tc_elevator; PADSAVEP(); elevator = (elevator_t *)save_p; memcpy (elevator, th, sizeof(*elevator)); save_p += sizeof(*elevator); elevator->sector = (sector_t *)(elevator->sector - sectors); } else if (th->function.acp1 == (actionf_p1) T_Scroll) { // killough 3/7/98: Scroll effect thinkers *save_p++ = tc_scroll; memcpy (save_p, th, sizeof(scroll_t)); save_p += sizeof(scroll_t); continue; } else if (th->function.acp1 == (actionf_p1) T_Glow2) { // [RH] Second type of glowing sectors glow2_t *glow; *save_p++ = tc_glow2; PADSAVEP(); glow = (glow2_t *)save_p; memcpy (glow, th, sizeof(*glow)); save_p += sizeof(*glow); glow->sector = (sector_t *)(glow->sector - sectors); } else if (th->function.acp1 == (actionf_p1) T_FloorWaggle) { // [RH] Waggling floors floorWaggle_t *waggle; *save_p++ = tc_waggle; PADSAVEP(); waggle = (floorWaggle_t *)save_p; memcpy (waggle, th, sizeof(*waggle)); save_p += sizeof(*waggle); waggle->sector = (sector_t *)(waggle->sector - sectors); } else if (th->function.acp1 == (actionf_p1) T_PhasedLight) { // [RH] Phased lighting phased_t *phased; *save_p++ = tc_phased; PADSAVEP(); phased = (phased_t *)save_p; memcpy (phased, th, sizeof(*phased)); save_p += sizeof(*phased); phased->sector = (sector_t *)(phased->sector - sectors); } else if (th->function.acp1 == (actionf_p1) T_Pillar) { // [RH] Pillar builders pillar_t *pillar; *save_p++ = tc_pillar; PADSAVEP(); pillar = (pillar_t *)save_p; memcpy (pillar, th, sizeof(*pillar)); save_p += sizeof(*pillar); pillar->sector = (sector_t *)(pillar->sector - sectors); } else if (th->function.acp1 == (actionf_p1) T_Pusher) { // phares 3/22/98: Push/Pull effect thinkers pusher_t *pusher; *save_p++ = tc_pusher; PADSAVEP(); pusher = (pusher_t *)save_p; memcpy (save_p, th, sizeof(*pusher)); save_p += sizeof(*pusher); pusher->source = (mobj_t *) pusher->source->thinker.prev; // [RH] remember source continue; } else if (th->function.acp1 == (actionf_p1) T_RotatePoly) { *save_p++ = tc_rotate_poly; memcpy (save_p, th, sizeof(polyevent_t)); save_p += sizeof(polyevent_t); continue; } else if (th->function.acp1 == (actionf_p1) T_MovePoly) { *save_p++ = tc_move_poly; memcpy (save_p, th, sizeof(polyevent_t)); save_p += sizeof(polyevent_t); continue; } else if (th->function.acp1 == (actionf_p1) T_PolyDoor) { *save_p++ = tc_poly_door; memcpy (save_p, th, sizeof(polydoor_t)); save_p += sizeof(polydoor_t); } } // add a terminating marker *save_p++ = tc_endspecials; } // // P_UnArchiveSpecials // void P_UnArchiveSpecials (void) { byte tclass; ceiling_t* ceiling; vldoor_t* door; floormove_t* floor; plat_t* plat; lightflash_t* flash; strobe_t* strobe; glow_t* glow; fireflicker_t* flick; // [RH] read the active buttons if (*save_p++) { button_t *button, **buttonptr; buttonptr = &buttonlist; do { button = Z_Malloc (sizeof(*button), PU_LEVEL, 0); PADSAVEP(); memcpy (button, save_p, sizeof(*button)); button->line = (((int)button->line) == -1) ? NULL : &lines[(int)button->line]; *buttonptr = button; buttonptr = &button->next; save_p += sizeof(*button); } while (*buttonptr); } // [RH] read the active quakes if (*save_p++) { quake_t *quake, **quakeptr; quakeptr = &ActiveQuakes; do { quake = Z_Malloc (sizeof(*quake), PU_LEVEL, 0); PADSAVEP(); memcpy (quake, save_p, sizeof(*quake)); *quakeptr = quake; quake->quakespot = (mobj_t *)mobj_p[(size_t)(quake->quakespot)]; quakeptr = &quake->next; save_p += sizeof(*quake); } while (*quakeptr); } // read in saved thinkers while (1) { tclass = *save_p++; switch (tclass) { case tc_endspecials: return; // end of list case tc_ceiling: PADSAVEP(); ceiling = Z_Malloc (sizeof(*ceiling), PU_LEVEL, NULL); memcpy (ceiling, save_p, sizeof(*ceiling)); save_p += sizeof(*ceiling); ceiling->sector = §ors[(int)ceiling->sector]; ceiling->sector->ceilingdata = ceiling; //jff 2/22/98 if (ceiling->thinker.function.acp1) ceiling->thinker.function.acp1 = (actionf_p1)T_MoveCeiling; P_AddThinker (&ceiling->thinker); P_AddActiveCeiling(ceiling); break; case tc_door: PADSAVEP(); door = Z_Malloc (sizeof(*door), PU_LEVEL, NULL); memcpy (door, save_p, sizeof(*door)); save_p += sizeof(*door); door->sector = §ors[(int)door->sector]; door->sector->ceilingdata = door; //jff 2/22/98 door->thinker.function.acp1 = (actionf_p1)T_VerticalDoor; P_AddThinker (&door->thinker); break; case tc_floor: PADSAVEP(); floor = Z_Malloc (sizeof(*floor), PU_LEVEL, NULL); memcpy (floor, save_p, sizeof(*floor)); save_p += sizeof(*floor); floor->sector = §ors[(int)floor->sector]; floor->sector->floordata = floor; //jff 2/22/98 floor->thinker.function.acp1 = (actionf_p1)T_MoveFloor; P_AddThinker (&floor->thinker); break; case tc_plat: PADSAVEP(); plat = Z_Malloc (sizeof(*plat), PU_LEVEL, NULL); memcpy (plat, save_p, sizeof(*plat)); save_p += sizeof(*plat); plat->sector = §ors[(int)plat->sector]; plat->sector->floordata = plat; //jff 2/22/98 if (plat->thinker.function.acp1) plat->thinker.function.acp1 = (actionf_p1)T_PlatRaise; P_AddThinker (&plat->thinker); P_AddActivePlat(plat); break; case tc_flash: PADSAVEP(); flash = Z_Malloc (sizeof(*flash), PU_LEVEL, NULL); memcpy (flash, save_p, sizeof(*flash)); save_p += sizeof(*flash); flash->sector = §ors[(int)flash->sector]; flash->thinker.function.acp1 = (actionf_p1)T_LightFlash; P_AddThinker (&flash->thinker); break; case tc_strobe: PADSAVEP(); strobe = Z_Malloc (sizeof(*strobe), PU_LEVEL, NULL); memcpy (strobe, save_p, sizeof(*strobe)); save_p += sizeof(*strobe); strobe->sector = §ors[(int)strobe->sector]; strobe->thinker.function.acp1 = (actionf_p1)T_StrobeFlash; P_AddThinker (&strobe->thinker); break; case tc_glow: PADSAVEP(); glow = Z_Malloc (sizeof(*glow), PU_LEVEL, NULL); memcpy (glow, save_p, sizeof(*glow)); save_p += sizeof(*glow); glow->sector = §ors[(int)glow->sector]; glow->thinker.function.acp1 = (actionf_p1)T_Glow; P_AddThinker (&glow->thinker); break; case tc_fireflicker: // [RH] Restore fireflicker thinkers PADSAVEP(); flick = Z_Malloc (sizeof(*flick), PU_LEVEL, NULL); memcpy (flick, save_p, sizeof(*flick)); save_p += sizeof(*flick); flick->sector = §ors[(int)flick->sector]; flick->thinker.function.acp1 = (actionf_p1)T_FireFlicker; P_AddThinker (&flick->thinker); break; case tc_flicker: PADSAVEP(); flick = Z_Malloc (sizeof(*flick), PU_LEVEL, NULL); memcpy (flick, save_p, sizeof(*flick)); save_p += sizeof(*flick); flick->sector = §ors[(int)flick->sector]; flick->thinker.function.acp1 = (actionf_p1)T_Flicker; P_AddThinker (&flick->thinker); break; //jff 2/22/98 new case for elevators case tc_elevator: PADSAVEP(); { elevator_t *elevator = Z_Malloc (sizeof(*elevator), PU_LEVEL, NULL); memcpy (elevator, save_p, sizeof(*elevator)); save_p += sizeof(*elevator); elevator->sector = §ors[(int)elevator->sector]; elevator->sector->floordata = elevator; //jff 2/22/98 elevator->sector->ceilingdata = elevator; //jff 2/22/98 elevator->thinker.function.acp1 = (actionf_p1) T_MoveElevator; P_AddThinker (&elevator->thinker); break; } case tc_scroll: // killough 3/7/98: scroll effect thinkers { scroll_t *scroll = Z_Malloc (sizeof(scroll_t), PU_LEVEL, NULL); memcpy (scroll, save_p, sizeof(scroll_t)); save_p += sizeof(scroll_t); scroll->thinker.function.acp1 = (actionf_p1) T_Scroll; P_AddThinker(&scroll->thinker); break; } case tc_glow2: // [RH] Another style of glowing light { glow2_t *glow2 = Z_Malloc (sizeof(*glow2), PU_LEVEL, NULL); PADSAVEP(); memcpy (glow2, save_p, sizeof(*glow2)); save_p += sizeof(*glow2); glow2->sector = §ors[(int)glow2->sector]; glow2->thinker.function.acp1 = (actionf_p1)T_Glow2; P_AddThinker (&glow2->thinker); break; } case tc_waggle: // [RH] Waggling floors { floorWaggle_t *waggle = Z_Malloc (sizeof(*waggle), PU_LEVEL, NULL); PADSAVEP(); memcpy (waggle, save_p, sizeof(*waggle)); save_p += sizeof(*waggle); waggle->sector = §ors[(int)waggle->sector]; waggle->sector->floordata = waggle; waggle->thinker.function.acp1 = (actionf_p1)T_FloorWaggle; P_AddThinker (&waggle->thinker); break; } case tc_phased: // [RH] Phased lighting { phased_t *phased = Z_Malloc (sizeof(*phased), PU_LEVEL, NULL); PADSAVEP(); memcpy (phased, save_p, sizeof(*phased)); save_p += sizeof(*phased); phased->sector = §ors[(int)phased->sector]; phased->thinker.function.acp1 = (actionf_p1)T_PhasedLight; P_AddThinker (&phased->thinker); break; } case tc_pillar: // [RH] Pillar builders { pillar_t *pillar = Z_Malloc (sizeof(*pillar), PU_LEVEL, NULL); PADSAVEP(); memcpy (pillar, save_p, sizeof(*pillar)); save_p += sizeof(*pillar); pillar->sector = §ors[(int)pillar->sector]; pillar->thinker.function.acp1 = (actionf_p1)T_Pillar; P_AddThinker (&pillar->thinker); break; } case tc_pusher: // phares 3/22/98: new Push/Pull effect thinkers { pusher_t *pusher = Z_Malloc (sizeof(pusher_t), PU_LEVEL, NULL); PADSAVEP(); memcpy (pusher, save_p, sizeof(pusher_t)); save_p += sizeof(pusher_t); pusher->thinker.function.acp1 = (actionf_p1) T_Pusher; pusher->source = mobj_p[(size_t)pusher->source]; // [RH] remember source P_AddThinker (&pusher->thinker); break; } case tc_rotate_poly: { polyevent_t *event = Z_Malloc (sizeof(polyevent_t), PU_LEVEL, NULL); memcpy (event, save_p, sizeof(polyevent_t)); save_p += sizeof(polyevent_t); event->thinker.function.acp1 = (actionf_p1) T_RotatePoly; P_AddThinker (&event->thinker); } break; case tc_move_poly: { polyevent_t *event = Z_Malloc (sizeof(polyevent_t), PU_LEVEL, NULL); memcpy (event, save_p, sizeof(polyevent_t)); save_p += sizeof(polyevent_t); event->thinker.function.acp1 = (actionf_p1) T_MovePoly; P_AddThinker (&event->thinker); } break; case tc_poly_door: { polyevent_t *door = Z_Malloc (sizeof(polydoor_t), PU_LEVEL, NULL); memcpy (door, save_p, sizeof(polydoor_t)); save_p += sizeof(polydoor_t); door->thinker.function.acp1 = (actionf_p1) T_PolyDoor; P_AddThinker (&door->thinker); } break; default: I_Error ("P_UnarchiveSpecials: Unknown tclass %i in snapshot", tclass); } } } // [RH] Save the state of the random number generator void P_ArchiveRNGState (void) { CheckSaveGame (sizeof(rng)); memcpy (save_p, &rng, sizeof(rng)); save_p += sizeof(rng); } // [RH] Load the state of the random number generator void P_UnArchiveRNGState (void) { memcpy (&rng, save_p, sizeof(rng)); save_p += sizeof(rng); } typedef enum { tc_script = 98, tc_scrend } scriptclass_t; // [RH] Store state of all running scripts void P_ArchiveScripts (void) { script_t *scr = Scripts, *script; while (scr) { CheckSaveGame (sizeof(*scr)); *save_p++ = tc_script; PADSAVEP(); script = (script_t *)save_p; memcpy (script, scr, sizeof(*script)); save_p += sizeof(*script); script->pc = (int *)(script->pc - (int *)level.behavior); script->activationline = (line_t *) (script->activationline ? script->activationline - lines : -1); if (script->activator) script->activator = script->activator->thinker.function.acp1 == (actionf_p1) P_MobjThinker ? (mobj_t *) script->activator->thinker.prev : NULL; if (RunningScripts[script->script] != scr) script->script += 2000; // ACS_ExecuteAlways script scr = script->next; } *save_p++ = tc_scrend; // killough 2/14/98: restore prev pointers { thinker_t *prev = &thinkercap, *th; for (th = thinkercap.next ; th != &thinkercap ; prev=th, th=th->next) th->prev = prev; } } // [RH] Restore state of all running scripts void P_UnArchiveScripts (void) { script_t **scripttail = &Scripts, *scriptprev = NULL; P_ClearScripts (); Z_FreeTags (PU_LEVACS, PU_LEVACS); while (*save_p++ == tc_script) { script_t *script = Z_Malloc (sizeof(*script), PU_LEVACS, NULL); PADSAVEP(); memcpy (script, save_p, sizeof(*script)); save_p += sizeof(*script); script->pc = ((int)script->pc) + (int *)level.behavior; script->activationline = (((int)script->activationline) == -1) ? NULL : &lines[(int)script->activationline]; script->activator = mobj_p[(size_t)script->activator]; if (script->script < 2000) RunningScripts[script->script] = script; else script->script -= 2000; *scripttail = script; scripttail = &script->next; script->prev = scriptprev; scriptprev = script; } *scripttail = NULL; LastScript = scriptprev; Z_Free (mobj_p); // free translation table if (*(save_p - 1) != tc_scrend) I_Error ("Unknown sclass %d in snapshot", *(save_p - 1)); } //========================================================================== // // ArchiveSounds // //========================================================================== #define ASEG_SOUNDS 109 void P_ArchiveSounds (void) { seqnode_t *node; sector_t *sec; int difference; int i; WriteLong (ASEG_SOUNDS, &save_p); // Save the sound sequences WriteLong (ActiveSequences, &save_p); for (node = SequenceListHead; node; node = node->next) { WriteString (Sequences[node->sequence]->name, &save_p); WriteLong (node->delayTics, &save_p); WriteLong (*((int *)&node->volume), &save_p); WriteLong (SN_GetSequenceOffset (node->sequence, node->sequencePtr), &save_p); WriteString (S_sfx[node->currentSoundID].name, &save_p); for (i = 0; i < po_NumPolyobjs; i++) { if (node->mobj == (mobj_t *)&polyobjs[i].startSpot) { break; } } if (i == po_NumPolyobjs) { // Sound is attached to a sector, not a polyobj sec = R_PointInSubsector(node->mobj->x, node->mobj->y)->sector; difference = (int)((byte *)sec -(byte *)§ors[0])/sizeof(sector_t); WriteLong (0, &save_p); // 0 -- sector sound origin } else { WriteLong (1, &save_p); // 1 -- polyobj sound origin difference = i; } WriteLong (difference, &save_p); } } //========================================================================== // // UnarchiveSounds // //========================================================================== void P_UnArchiveSounds (void) { int i; int numSequences; int delayTics; int volTemp; float volume; int seqOffset; int polySnd; int secNum; char *soundName; char *sequenceName; mobj_t *sndMobj; i = ReadLong (&save_p); if (i != ASEG_SOUNDS) I_Error ("Sound sequence marker missing"); // Reload and restart all sound sequences numSequences = ReadLong (&save_p); i = 0; while (i < numSequences) { sequenceName = ReadString (&save_p); delayTics = ReadLong (&save_p); volTemp = ReadLong (&save_p); volume = *((float *)&volTemp); seqOffset = ReadLong (&save_p); soundName = ReadString (&save_p); polySnd = ReadLong (&save_p); secNum = ReadLong (&save_p); if (!polySnd) { sndMobj = (mobj_t *)§ors[secNum].soundorg; } else { sndMobj = (mobj_t *)&polyobjs[secNum].startSpot; } SN_StartSequenceName (sndMobj, sequenceName); SN_ChangeNodeData (i, seqOffset, delayTics, volume, S_FindSound (soundName)); free (soundName); free (sequenceName); i++; } } //========================================================================== // // ArchivePolyobjs // //========================================================================== #define ASEG_POLYOBJS 104 void P_ArchivePolyobjs (void) { int i; WriteLong (ASEG_POLYOBJS, &save_p); WriteLong (po_NumPolyobjs, &save_p); for(i = 0; i < po_NumPolyobjs; i++) { WriteLong (polyobjs[i].tag, &save_p); WriteLong (polyobjs[i].angle, &save_p); WriteLong (polyobjs[i].startSpot.x, &save_p); WriteLong (polyobjs[i].startSpot.y, &save_p); } } //========================================================================== // // UnarchivePolyobjs // //========================================================================== void P_UnArchivePolyobjs (void) { int i, data; fixed_t deltaX; fixed_t deltaY; data = ReadLong (&save_p); if (data != ASEG_POLYOBJS) I_Error ("Polyobject marker missing"); if (ReadLong (&save_p) != po_NumPolyobjs) { I_Error ("UnarchivePolyobjs: Bad polyobj count"); } for (i = 0; i < po_NumPolyobjs; i++) { if (ReadLong (&save_p) != polyobjs[i].tag) { I_Error ("UnarchivePolyobjs: Invalid polyobj tag"); } PO_RotatePolyobj (polyobjs[i].tag, (angle_t)ReadLong (&save_p)); deltaX = ReadLong (&save_p) - polyobjs[i].startSpot.x; deltaY = ReadLong (&save_p) - polyobjs[i].startSpot.y; PO_MovePolyobj (polyobjs[i].tag, deltaX, deltaY); } }