1 /*
2 ** p_saveg.cpp
3 ** Code for serializing the world state in an archive
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 1998-2006 Randy Heit
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 ** notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 ** notice, this list of conditions and the following disclaimer in the
17 ** documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 ** derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 */
34
35 #include "i_system.h"
36 #include "p_local.h"
37
38 // State.
39 #include "dobject.h"
40 #include "doomstat.h"
41 #include "r_state.h"
42 #include "m_random.h"
43 #include "p_saveg.h"
44 #include "s_sndseq.h"
45 #include "v_palette.h"
46 #include "a_sharedglobal.h"
47 #include "r_data/r_interpolate.h"
48 #include "g_level.h"
49 #include "po_man.h"
50 #include "p_setup.h"
51 #include "r_data/colormaps.h"
52 #include "farchive.h"
53 #include "p_lnspec.h"
54 #include "p_acs.h"
55 #include "p_terrain.h"
56
57 static void CopyPlayer (player_t *dst, player_t *src, const char *name);
58 static void ReadOnePlayer (FArchive &arc, bool skipload);
59 static void ReadMultiplePlayers (FArchive &arc, int numPlayers, int numPlayersNow, bool skipload);
60 static void SpawnExtraPlayers ();
61
operator <<(FArchive & arc,FLinkedSector & link)62 inline FArchive &operator<< (FArchive &arc, FLinkedSector &link)
63 {
64 arc << link.Sector << link.Type;
65 return arc;
66 }
67
68 //
69 // P_ArchivePlayers
70 //
P_SerializePlayers(FArchive & arc,bool skipload)71 void P_SerializePlayers (FArchive &arc, bool skipload)
72 {
73 BYTE numPlayers, numPlayersNow;
74 int i;
75
76 // Count the number of players present right now.
77 for (numPlayersNow = 0, i = 0; i < MAXPLAYERS; ++i)
78 {
79 if (playeringame[i])
80 {
81 ++numPlayersNow;
82 }
83 }
84
85 if (arc.IsStoring())
86 {
87 // Record the number of players in this save.
88 arc << numPlayersNow;
89
90 // Record each player's name, followed by their data.
91 for (i = 0; i < MAXPLAYERS; ++i)
92 {
93 if (playeringame[i])
94 {
95 arc.WriteString (players[i].userinfo.GetName());
96 players[i].Serialize (arc);
97 }
98 }
99 }
100 else
101 {
102 arc << numPlayers;
103
104 // If there is only one player in the game, they go to the
105 // first player present, no matter what their name.
106 if (numPlayers == 1)
107 {
108 ReadOnePlayer (arc, skipload);
109 }
110 else
111 {
112 ReadMultiplePlayers (arc, numPlayers, numPlayersNow, skipload);
113 }
114 if (!skipload && numPlayersNow > numPlayers)
115 {
116 SpawnExtraPlayers ();
117 }
118 // Redo pitch limits, since the spawned player has them at 0.
119 players[consoleplayer].SendPitchLimits();
120 }
121 }
122
ReadOnePlayer(FArchive & arc,bool skipload)123 static void ReadOnePlayer (FArchive &arc, bool skipload)
124 {
125 int i;
126 char *name = NULL;
127 bool didIt = false;
128
129 arc << name;
130
131 for (i = 0; i < MAXPLAYERS; ++i)
132 {
133 if (playeringame[i])
134 {
135 if (!didIt)
136 {
137 didIt = true;
138 player_t playerTemp;
139 playerTemp.Serialize (arc);
140 if (!skipload)
141 {
142 CopyPlayer (&players[i], &playerTemp, name);
143 }
144 }
145 else
146 {
147 if (players[i].mo != NULL)
148 {
149 players[i].mo->Destroy();
150 players[i].mo = NULL;
151 }
152 }
153 }
154 }
155 delete[] name;
156 }
157
ReadMultiplePlayers(FArchive & arc,int numPlayers,int numPlayersNow,bool skipload)158 static void ReadMultiplePlayers (FArchive &arc, int numPlayers, int numPlayersNow, bool skipload)
159 {
160 // For two or more players, read each player into a temporary array.
161 int i, j;
162 char **nametemp = new char *[numPlayers];
163 player_t *playertemp = new player_t[numPlayers];
164 BYTE *tempPlayerUsed = new BYTE[numPlayers];
165 BYTE playerUsed[MAXPLAYERS];
166
167 for (i = 0; i < numPlayers; ++i)
168 {
169 nametemp[i] = NULL;
170 arc << nametemp[i];
171 playertemp[i].Serialize (arc);
172 tempPlayerUsed[i] = 0;
173 }
174 for (i = 0; i < MAXPLAYERS; ++i)
175 {
176 playerUsed[i] = playeringame[i] ? 0 : 2;
177 }
178
179 if (!skipload)
180 {
181 // Now try to match players from the savegame with players present
182 // based on their names. If two players in the savegame have the
183 // same name, then they are assigned to players in the current game
184 // on a first-come, first-served basis.
185 for (i = 0; i < numPlayers; ++i)
186 {
187 for (j = 0; j < MAXPLAYERS; ++j)
188 {
189 if (playerUsed[j] == 0 && stricmp(players[j].userinfo.GetName(), nametemp[i]) == 0)
190 { // Found a match, so copy our temp player to the real player
191 Printf ("Found player %d (%s) at %d\n", i, nametemp[i], j);
192 CopyPlayer (&players[j], &playertemp[i], nametemp[i]);
193 playerUsed[j] = 1;
194 tempPlayerUsed[i] = 1;
195 break;
196 }
197 }
198 }
199
200 // Any players that didn't have matching names are assigned to existing
201 // players on a first-come, first-served basis.
202 for (i = 0; i < numPlayers; ++i)
203 {
204 if (tempPlayerUsed[i] == 0)
205 {
206 for (j = 0; j < MAXPLAYERS; ++j)
207 {
208 if (playerUsed[j] == 0)
209 {
210 Printf ("Assigned player %d (%s) to %d (%s)\n", i, nametemp[i], j, players[j].userinfo.GetName());
211 CopyPlayer (&players[j], &playertemp[i], nametemp[i]);
212 playerUsed[j] = 1;
213 tempPlayerUsed[i] = 1;
214 break;
215 }
216 }
217 }
218 }
219
220 // Make sure any extra players don't have actors spawned yet. Happens if the players
221 // present now got the same slots as they had in the save, but there are not as many
222 // as there were in the save.
223 for (j = 0; j < MAXPLAYERS; ++j)
224 {
225 if (playerUsed[j] == 0)
226 {
227 if (players[j].mo != NULL)
228 {
229 players[j].mo->Destroy();
230 players[j].mo = NULL;
231 }
232 }
233 }
234
235 // Remove any temp players that were not used. Happens if there are fewer players
236 // than there were in the save, and they got shuffled.
237 for (i = 0; i < numPlayers; ++i)
238 {
239 if (tempPlayerUsed[i] == 0)
240 {
241 playertemp[i].mo->Destroy();
242 playertemp[i].mo = NULL;
243 }
244 }
245 }
246
247 delete[] tempPlayerUsed;
248 delete[] playertemp;
249 for (i = 0; i < numPlayers; ++i)
250 {
251 delete[] nametemp[i];
252 }
253 delete[] nametemp;
254 }
255
CopyPlayer(player_t * dst,player_t * src,const char * name)256 static void CopyPlayer (player_t *dst, player_t *src, const char *name)
257 {
258 // The userinfo needs to be saved for real players, but it
259 // needs to come from the save for bots.
260 userinfo_t uibackup;
261 userinfo_t uibackup2;
262
263 uibackup.TransferFrom(dst->userinfo);
264 uibackup2.TransferFrom(src->userinfo);
265
266 int chasecam = dst->cheats & CF_CHASECAM; // Remember the chasecam setting
267 bool attackdown = dst->attackdown;
268 bool usedown = dst->usedown;
269
270
271 *dst = *src; // To avoid memory leaks at this point the userinfo in src must be empty which is taken care of by the TransferFrom call above.
272
273 dst->cheats |= chasecam;
274
275 if (dst->Bot != NULL)
276 {
277 botinfo_t *thebot = bglobal.botinfo;
278 while (thebot && stricmp (name, thebot->name))
279 {
280 thebot = thebot->next;
281 }
282 if (thebot)
283 {
284 thebot->inuse = BOTINUSE_Yes;
285 }
286 bglobal.botnum++;
287 dst->userinfo.TransferFrom(uibackup2);
288 }
289 else
290 {
291 dst->userinfo.TransferFrom(uibackup);
292 }
293 // Validate the skin
294 dst->userinfo.SkinNumChanged(R_FindSkin(skins[dst->userinfo.GetSkin()].name, dst->CurrentPlayerClass));
295
296 // Make sure the player pawn points to the proper player struct.
297 if (dst->mo != NULL)
298 {
299 dst->mo->player = dst;
300 }
301 // These 2 variables may not be overwritten.
302 dst->attackdown = attackdown;
303 dst->usedown = usedown;
304 }
305
SpawnExtraPlayers()306 static void SpawnExtraPlayers ()
307 {
308 // If there are more players now than there were in the savegame,
309 // be sure to spawn the extra players.
310 int i;
311
312 if (deathmatch)
313 {
314 return;
315 }
316
317 for (i = 0; i < MAXPLAYERS; ++i)
318 {
319 if (playeringame[i] && players[i].mo == NULL)
320 {
321 players[i].playerstate = PST_ENTER;
322 P_SpawnPlayer(&playerstarts[i], i, (level.flags2 & LEVEL2_PRERAISEWEAPON) ? SPF_WEAPONFULLYUP : 0);
323 }
324 }
325 }
326
327 //
328 // P_ArchiveWorld
329 //
P_SerializeWorld(FArchive & arc)330 void P_SerializeWorld (FArchive &arc)
331 {
332 int i, j;
333 sector_t *sec;
334 line_t *li;
335 zone_t *zn;
336
337 // do sectors
338 for (i = 0, sec = sectors; i < numsectors; i++, sec++)
339 {
340 arc << sec->floorplane
341 << sec->ceilingplane;
342 if (SaveVersion < 3223)
343 {
344 BYTE bytelight;
345 arc << bytelight;
346 sec->lightlevel = bytelight;
347 }
348 else
349 {
350 arc << sec->lightlevel;
351 }
352 arc << sec->special;
353 if (SaveVersion < 4523)
354 {
355 short tag;
356 arc << tag;
357 }
358 arc << sec->soundtraversed
359 << sec->seqType
360 << sec->friction
361 << sec->movefactor
362 << sec->floordata
363 << sec->ceilingdata
364 << sec->lightingdata
365 << sec->stairlock
366 << sec->prevsec
367 << sec->nextsec
368 << sec->planes[sector_t::floor]
369 << sec->planes[sector_t::ceiling]
370 << sec->heightsec
371 << sec->bottommap << sec->midmap << sec->topmap
372 << sec->gravity;
373 if (SaveVersion >= 4530)
374 {
375 P_SerializeTerrain(arc, sec->terrainnum[0]);
376 P_SerializeTerrain(arc, sec->terrainnum[1]);
377 }
378 if (SaveVersion >= 4529)
379 {
380 arc << sec->damageamount;
381 }
382 else
383 {
384 short dmg;
385 arc << dmg;
386 sec->damageamount = dmg;
387 }
388 if (SaveVersion >= 4528)
389 {
390 arc << sec->damageinterval
391 << sec->leakydamage
392 << sec->damagetype;
393 }
394 else
395 {
396 short damagemod;
397 arc << damagemod;
398 sec->damagetype = MODtoDamageType(damagemod);
399 if (sec->damageamount < 20)
400 {
401 sec->leakydamage = 0;
402 sec->damageinterval = 32;
403 }
404 else if (sec->damageamount < 50)
405 {
406 sec->leakydamage = 5;
407 sec->damageinterval = 32;
408 }
409 else
410 {
411 sec->leakydamage = 256;
412 sec->damageinterval = 1;
413 }
414 }
415
416 arc << sec->SoundTarget
417 << sec->SecActTarget
418 << sec->sky
419 << sec->MoreFlags
420 << sec->Flags
421 << sec->SkyBoxes[sector_t::floor] << sec->SkyBoxes[sector_t::ceiling]
422 << sec->ZoneNumber;
423 if (SaveVersion < 4529)
424 {
425 short secretsector;
426 arc << secretsector;
427 if (secretsector) sec->Flags |= SECF_WASSECRET;
428 P_InitSectorSpecial(sec, sec->special, true);
429 }
430 arc << sec->interpolations[0]
431 << sec->interpolations[1]
432 << sec->interpolations[2]
433 << sec->interpolations[3]
434 << sec->SeqName;
435
436 sec->e->Serialize(arc);
437 if (arc.IsStoring ())
438 {
439 arc << sec->ColorMap->Color
440 << sec->ColorMap->Fade;
441 BYTE sat = sec->ColorMap->Desaturate;
442 arc << sat;
443 }
444 else
445 {
446 PalEntry color, fade;
447 BYTE desaturate;
448 arc << color << fade
449 << desaturate;
450 sec->ColorMap = GetSpecialLights (color, fade, desaturate);
451 }
452 }
453
454 // do lines
455 for (i = 0, li = lines; i < numlines; i++, li++)
456 {
457 arc << li->flags
458 << li->activation
459 << li->special
460 << li->Alpha;
461
462 if (SaveVersion < 4523)
463 {
464 int id;
465 arc << id;
466 }
467 if (P_IsACSSpecial(li->special))
468 {
469 P_SerializeACSScriptNumber(arc, li->args[0], false);
470 }
471 else
472 {
473 arc << li->args[0];
474 }
475 arc << li->args[1] << li->args[2] << li->args[3] << li->args[4];
476
477 for (j = 0; j < 2; j++)
478 {
479 if (li->sidedef[j] == NULL)
480 continue;
481
482 side_t *si = li->sidedef[j];
483 arc << si->textures[side_t::top]
484 << si->textures[side_t::mid]
485 << si->textures[side_t::bottom]
486 << si->Light
487 << si->Flags
488 << si->LeftSide
489 << si->RightSide
490 << si->Index;
491 DBaseDecal::SerializeChain (arc, &si->AttachedDecals);
492 }
493 }
494
495 // do zones
496 arc << numzones;
497
498 if (arc.IsLoading())
499 {
500 if (zones != NULL)
501 {
502 delete[] zones;
503 }
504 zones = new zone_t[numzones];
505 }
506
507 for (i = 0, zn = zones; i < numzones; ++i, ++zn)
508 {
509 arc << zn->Environment;
510 }
511 }
512
Serialize(FArchive & arc)513 void extsector_t::Serialize(FArchive &arc)
514 {
515 arc << FakeFloor.Sectors
516 << Midtex.Floor.AttachedLines
517 << Midtex.Floor.AttachedSectors
518 << Midtex.Ceiling.AttachedLines
519 << Midtex.Ceiling.AttachedSectors
520 << Linked.Floor.Sectors
521 << Linked.Ceiling.Sectors;
522 }
523
operator <<(FArchive & arc,side_t::part & p)524 FArchive &operator<< (FArchive &arc, side_t::part &p)
525 {
526 arc << p.xoffset << p.yoffset << p.interpolation << p.texture
527 << p.xscale << p.yscale;// << p.Light;
528 return arc;
529 }
530
operator <<(FArchive & arc,sector_t::splane & p)531 FArchive &operator<< (FArchive &arc, sector_t::splane &p)
532 {
533 arc << p.xform.xoffs << p.xform.yoffs << p.xform.xscale << p.xform.yscale
534 << p.xform.angle << p.xform.base_yoffs << p.xform.base_angle
535 << p.Flags << p.Light << p.Texture << p.TexZ << p.alpha;
536 return arc;
537 }
538
539
540 //
541 // Thinkers
542 //
543
544 //
545 // P_ArchiveThinkers
546 //
547
P_SerializeThinkers(FArchive & arc,bool hubLoad)548 void P_SerializeThinkers (FArchive &arc, bool hubLoad)
549 {
550 DImpactDecal::SerializeTime (arc);
551 DThinker::SerializeAll (arc, hubLoad);
552 }
553
554 //==========================================================================
555 //
556 // ArchiveSounds
557 //
558 //==========================================================================
559
P_SerializeSounds(FArchive & arc)560 void P_SerializeSounds (FArchive &arc)
561 {
562 S_SerializeSounds (arc);
563 DSeqNode::SerializeSequences (arc);
564 char *name = NULL;
565 BYTE order;
566
567 if (arc.IsStoring ())
568 {
569 order = S_GetMusic (&name);
570 }
571 arc << name << order;
572 if (arc.IsLoading ())
573 {
574 if (!S_ChangeMusic (name, order))
575 if (level.cdtrack == 0 || !S_ChangeCDMusic (level.cdtrack, level.cdid))
576 S_ChangeMusic (level.Music, level.musicorder);
577 }
578 delete[] name;
579 }
580
581 //==========================================================================
582 //
583 // ArchivePolyobjs
584 //
585 //==========================================================================
586 #define ASEG_POLYOBJS 104
587
P_SerializePolyobjs(FArchive & arc)588 void P_SerializePolyobjs (FArchive &arc)
589 {
590 int i;
591 FPolyObj *po;
592
593 if (arc.IsStoring ())
594 {
595 int seg = ASEG_POLYOBJS;
596 arc << seg << po_NumPolyobjs;
597 for(i = 0, po = polyobjs; i < po_NumPolyobjs; i++, po++)
598 {
599 arc << po->tag << po->angle << po->StartSpot.x <<
600 po->StartSpot.y << po->interpolation;
601 }
602 }
603 else
604 {
605 int data;
606 angle_t angle;
607 fixed_t deltaX, deltaY;
608
609 arc << data;
610 if (data != ASEG_POLYOBJS)
611 I_Error ("Polyobject marker missing");
612
613 arc << data;
614 if (data != po_NumPolyobjs)
615 {
616 I_Error ("UnarchivePolyobjs: Bad polyobj count");
617 }
618 for (i = 0, po = polyobjs; i < po_NumPolyobjs; i++, po++)
619 {
620 arc << data;
621 if (data != po->tag)
622 {
623 I_Error ("UnarchivePolyobjs: Invalid polyobj tag");
624 }
625 arc << angle;
626 po->RotatePolyobj (angle, true);
627 arc << deltaX << deltaY << po->interpolation;
628 deltaX -= po->StartSpot.x;
629 deltaY -= po->StartSpot.y;
630 po->MovePolyobj (deltaX, deltaY, true);
631 }
632 }
633 }
634
635 //==========================================================================
636 //
637 // RecalculateDrawnSubsectors
638 //
639 // In case the subsector data is unusable this function tries to reconstruct
640 // if from the linedefs' ML_MAPPED info.
641 //
642 //==========================================================================
643
RecalculateDrawnSubsectors()644 void RecalculateDrawnSubsectors()
645 {
646 for(int i=0;i<numsubsectors;i++)
647 {
648 subsector_t *sub = &subsectors[i];
649 for(unsigned int j=0;j<sub->numlines;j++)
650 {
651 if (sub->firstline[j].linedef != NULL &&
652 (sub->firstline[j].linedef->flags & ML_MAPPED))
653 {
654 sub->flags |= SSECF_DRAWN;
655 }
656 }
657 }
658 }
659
660 //==========================================================================
661 //
662 // ArchiveSubsectors
663 //
664 //==========================================================================
665
P_SerializeSubsectors(FArchive & arc)666 void P_SerializeSubsectors(FArchive &arc)
667 {
668 int num_verts, num_subs, num_nodes;
669 BYTE by;
670
671 if (arc.IsStoring())
672 {
673 if (hasglnodes)
674 {
675 arc << numvertexes << numsubsectors << numnodes; // These are only for verification
676 for(int i=0;i<numsubsectors;i+=8)
677 {
678 by = 0;
679 for(int j=0;j<8;j++)
680 {
681 if (i+j<numsubsectors && (subsectors[i+j].flags & SSECF_DRAWN))
682 {
683 by |= (1<<j);
684 }
685 }
686 arc << by;
687 }
688 }
689 else
690 {
691 int v = 0;
692 arc << v << v << v;
693 }
694 }
695 else
696 {
697 arc << num_verts << num_subs << num_nodes;
698 if (num_verts != numvertexes ||
699 num_subs != numsubsectors ||
700 num_nodes != numnodes)
701 {
702 // Nodes don't match - we can't use this info
703 for(int i=0;i<num_subs;i+=8)
704 {
705 // Skip the subsector info.
706 arc << by;
707 }
708 if (hasglnodes)
709 {
710 RecalculateDrawnSubsectors();
711 }
712 return;
713 }
714 else
715 {
716 for(int i=0;i<numsubsectors;i+=8)
717 {
718 arc << by;
719 for(int j=0;j<8;j++)
720 {
721 if ((by & (1<<j)) && i+j<numsubsectors)
722 {
723 subsectors[i+j].flags |= SSECF_DRAWN;
724 }
725 }
726 }
727 }
728 }
729 }
730