1 //----------------------------------------------------------------------------
2 // EDGE New SaveGame Handling (Things)
3 //----------------------------------------------------------------------------
4 //
5 // Copyright (c) 1999-2009 The EDGE Team.
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 //
17 //----------------------------------------------------------------------------
18 //
19 // See the file "docs/save_sys.txt" for a complete description of the
20 // new savegame system.
21 //
22 // This file handles:
23 // mobj_t [MOBJ]
24 // spawnspot_t [SPWN]
25 // iteminque_t [ITMQ]
26 //
27
28 #include "i_defs.h"
29
30 #include "p_setup.h"
31 #include "sv_chunk.h"
32 #include "sv_main.h"
33 #include "z_zone.h"
34
35
36 #undef SF
37 #define SF SVFIELD
38
39
40 // forward decls.
41 int SV_MobjCountElems(void);
42 int SV_MobjFindElem(mobj_t *elem);
43 void * SV_MobjGetElem(int index);
44 void SV_MobjCreateElems(int num_elems);
45 void SV_MobjFinaliseElems(void);
46
47 int SV_ItemqCountElems(void);
48 int SV_ItemqFindElem(iteminque_t *elem);
49 void * SV_ItemqGetElem(int index);
50 void SV_ItemqCreateElems(int num_elems);
51 void SV_ItemqFinaliseElems(void);
52
53 bool SR_MobjGetPlayer(void *storage, int index, void *extra);
54 bool SR_MobjGetMobj(void *storage, int index, void *extra);
55 bool SR_MobjGetType(void *storage, int index, void *extra);
56 bool SR_MobjGetState(void *storage, int index, void *extra);
57 bool SR_MobjGetSpawnPoint(void *storage, int index, void *extra);
58 bool SR_MobjGetAttack(void *storage, int index, void *extra);
59
60 void SR_MobjPutPlayer(void *storage, int index, void *extra);
61 void SR_MobjPutMobj(void *storage, int index, void *extra);
62 void SR_MobjPutType(void *storage, int index, void *extra);
63 void SR_MobjPutState(void *storage, int index, void *extra);
64 void SR_MobjPutSpawnPoint(void *storage, int index, void *extra);
65 void SR_MobjPutAttack(void *storage, int index, void *extra);
66
67 //----------------------------------------------------------------------------
68 //
69 // MOBJ STRUCTURE AND ARRAY
70 //
71 static mobj_t sv_dummy_mobj;
72
73 #define SV_F_BASE sv_dummy_mobj
74
75 static savefield_t sv_fields_mobj[] =
76 {
77 SF(x, "x", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
78 SF(y, "y", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
79 SF(z, "z", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
80 SF(angle, "angle", 1, SVT_ANGLE, SR_GetAngle, SR_PutAngle),
81 SF(floorz, "floorz", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
82 SF(ceilingz, "ceilingz", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
83 SF(dropoffz, "dropoffz", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
84 SF(radius, "radius", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
85 SF(height, "height", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
86 SF(mom, "mom", 1, SVT_VEC3, SR_GetVec3, SR_PutVec3),
87 SF(health, "health", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
88 SF(speed, "speed", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
89 SF(fuse, "fuse", 1, SVT_INT, SR_GetInt, SR_PutInt),
90 SF(info, "info", 1, SVT_STRING, SR_MobjGetType, SR_MobjPutType),
91 SF(state, "state", 1, SVT_STRING, SR_MobjGetState, SR_MobjPutState),
92 SF(next_state, "next_state", 1, SVT_STRING, SR_MobjGetState, SR_MobjPutState),
93 SF(tics, "tics", 1, SVT_INT, SR_GetInt, SR_PutInt),
94 SF(flags, "flags", 1, SVT_INT, SR_GetInt, SR_PutInt),
95 SF(extendedflags, "extendedflags", 1, SVT_INT, SR_GetInt, SR_PutInt),
96 SF(hyperflags, "hyperflags", 1, SVT_INT, SR_GetInt, SR_PutInt),
97 SF(movedir, "movedir", 1, SVT_INT, SR_GetInt, SR_PutInt),
98 SF(movecount, "movecount", 1, SVT_INT, SR_GetInt, SR_PutInt),
99 SF(reactiontime, "reactiontime", 1, SVT_INT, SR_GetInt, SR_PutInt),
100 SF(threshold, "threshold", 1, SVT_INT, SR_GetInt, SR_PutInt),
101 SF(model_skin, "model_skin", 1, SVT_INT, SR_GetInt, SR_PutInt),
102 SF(tag, "tag", 1, SVT_INT, SR_GetInt, SR_PutInt),
103 SF(side, "side", 1, SVT_INT, SR_GetInt, SR_PutInt),
104 SF(player, "player", 1, SVT_INDEX("players"),
105 SR_MobjGetPlayer, SR_MobjPutPlayer),
106 SF(spawnpoint, "spawnpoint", 1, SVT_STRUCT("spawnpoint_t"),
107 SR_MobjGetSpawnPoint, SR_MobjPutSpawnPoint),
108 SF(origheight, "origheight", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
109 SF(visibility, "visibility", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
110 SF(vis_target, "vis_target", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
111 SF(vertangle, "vertangle", 1, SVT_FLOAT, SR_GetAngleFromSlope, SR_PutAngleToSlope),
112 SF(spreadcount, "spreadcount", 1, SVT_INT, SR_GetInt, SR_PutInt),
113 SF(currentattack, "currentattack", 1, SVT_STRING, SR_MobjGetAttack, SR_MobjPutAttack),
114 SF(source, "source", 1, SVT_INDEX("mobjs"), SR_MobjGetMobj, SR_MobjPutMobj),
115 SF(target, "target", 1, SVT_INDEX("mobjs"), SR_MobjGetMobj, SR_MobjPutMobj),
116 SF(tracer, "tracer", 1, SVT_INDEX("mobjs"), SR_MobjGetMobj, SR_MobjPutMobj),
117 SF(supportobj, "supportobj", 1, SVT_INDEX("mobjs"), SR_MobjGetMobj, SR_MobjPutMobj),
118 SF(above_mo, "above_mo", 1, SVT_INDEX("mobjs"), SR_MobjGetMobj, SR_MobjPutMobj),
119 SF(below_mo, "below_mo", 1, SVT_INDEX("mobjs"), SR_MobjGetMobj, SR_MobjPutMobj),
120 SF(ride_dx, "ride_dx", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
121 SF(ride_dy, "ride_dy", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
122 SF(on_ladder, "on_ladder", 1, SVT_INT, SR_GetInt, SR_PutInt),
123 SF(path_trigger, "path_trigger", 1, SVT_STRING,
124 SR_TriggerGetScript, SR_TriggerPutScript),
125 SF(dlight.r, "dlight_qty", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
126 SF(dlight.target, "dlight_target", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
127 SF(dlight.color, "dlight_color", 1, SVT_RGBCOL, SR_GetRGB, SR_PutRGB),
128 SF(shot_count, "shot_count", 1, SVT_INT, SR_GetInt, SR_PutInt),
129
130 // NOT HERE:
131 // subsector & region: these are regenerated.
132 // next,prev,snext,sprev,bnext,bprev: links are regenerated.
133 // tunnel_hash: would be meaningless, and not important.
134 // lastlookup: being reset to zero won't hurt.
135 // ...
136
137 SVFIELD_END
138 };
139
140 savestruct_t sv_struct_mobj =
141 {
142 NULL, // link in list
143 "mobj_t", // structure name
144 "mobj", // start marker
145 sv_fields_mobj, // field descriptions
146 SVDUMMY, // dummy base
147 true, // define_me
148 NULL // pointer to known struct
149 };
150
151 #undef SV_F_BASE
152
153 savearray_t sv_array_mobj =
154 {
155 NULL, // link in list
156 "mobjs", // array name
157 &sv_struct_mobj, // array type
158 true, // define_me
159 true, // allow_hub
160
161 SV_MobjCountElems, // count routine
162 SV_MobjGetElem, // index routine
163 SV_MobjCreateElems, // creation routine
164 SV_MobjFinaliseElems, // finalisation routine
165
166 NULL, // pointer to known array
167 0 // loaded size
168 };
169
170
171 //----------------------------------------------------------------------------
172 //
173 // SPAWNPOINT STRUCTURE
174 //
175 static spawnpoint_t sv_dummy_spawnpoint;
176
177 #define SV_F_BASE sv_dummy_spawnpoint
178
179 static savefield_t sv_fields_spawnpoint[] =
180 {
181 SF(x, "x", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
182 SF(y, "y", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
183 SF(z, "z", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
184 SF(angle, "angle", 1, SVT_ANGLE, SR_GetAngle, SR_PutAngle),
185 SF(vertangle, "slope", 1, SVT_FLOAT, SR_GetAngleFromSlope, SR_PutAngleToSlope),
186 SF(info, "info", 1, SVT_STRING, SR_MobjGetType, SR_MobjPutType),
187 SF(flags, "flags", 1, SVT_INT, SR_GetInt, SR_PutInt),
188
189 SVFIELD_END
190 };
191
192 savestruct_t sv_struct_spawnpoint =
193 {
194 NULL, // link in list
195 "spawnpoint_t", // structure name
196 "spwn", // start marker
197 sv_fields_spawnpoint, // field descriptions
198 SVDUMMY, // dummy base
199 true, // define_me
200 NULL // pointer to known struct
201 };
202
203 #undef SV_F_BASE
204
205
206 //----------------------------------------------------------------------------
207 //
208 // ITEMINQUE STRUCTURE AND ARRAY
209 //
210 static iteminque_t sv_dummy_iteminque;
211
212 #define SV_F_BASE sv_dummy_iteminque
213
214 static savefield_t sv_fields_iteminque[] =
215 {
216 SF(spawnpoint, "spawnpoint", 1, SVT_STRUCT("spawnpoint_t"),
217 SR_MobjGetSpawnPoint, SR_MobjPutSpawnPoint),
218 SF(time, "time", 1, SVT_INT, SR_GetInt, SR_PutInt),
219
220 // NOT HERE:
221 // next,prev: links are regenerated.
222
223 SVFIELD_END
224 };
225
226 savestruct_t sv_struct_iteminque =
227 {
228 NULL, // link in list
229 "iteminque_t", // structure name
230 "itmq", // start marker
231 sv_fields_iteminque, // field descriptions
232 SVDUMMY, // dummy base
233 true, // define_me
234 NULL // pointer to known struct
235 };
236
237 #undef SV_F_BASE
238
239 savearray_t sv_array_iteminque =
240 {
241 NULL, // link in list
242 "itemquehead", // array name
243 &sv_struct_iteminque, // array type
244 true, // define_me
245 true, // allow_hub
246
247 SV_ItemqCountElems, // count routine
248 SV_ItemqGetElem, // index routine
249 SV_ItemqCreateElems, // creation routine
250 SV_ItemqFinaliseElems, // finalisation routine
251
252 NULL, // pointer to known array
253 0 // loaded size
254 };
255
256
257 //----------------------------------------------------------------------------
258
259 //
260 // SV_MobjCountElems
261 //
SV_MobjCountElems(void)262 int SV_MobjCountElems(void)
263 {
264 mobj_t *cur;
265 int count=0;
266
267 for (cur=mobjlisthead; cur; cur=cur->next)
268 count++;
269
270 return count;
271 }
272
273 //
274 // SV_MobjGetElem
275 //
276 // The index here starts at 0.
277 //
SV_MobjGetElem(int index)278 void *SV_MobjGetElem(int index)
279 {
280 mobj_t *cur;
281
282 for (cur=mobjlisthead; cur && index > 0; cur=cur->next)
283 index--;
284
285 if (!cur)
286 I_Error("LOADGAME: Invalid Mobj: %d\n", index);
287
288 SYS_ASSERT(index == 0);
289
290 return cur;
291 }
292
293 //
294 // SV_MobjFindElem
295 //
296 // Returns the index number (starts at 0 here).
297 //
SV_MobjFindElem(mobj_t * elem)298 int SV_MobjFindElem(mobj_t *elem)
299 {
300 mobj_t *cur;
301 int index;
302
303 for (cur=mobjlisthead, index=0; cur && cur != elem; cur=cur->next)
304 index++;
305
306 if (!cur)
307 I_Error("LOADGAME: No such MobjPtr: %p\n", elem);
308
309 return index;
310 }
311
312 //
313 // SV_MobjCreateElems
314 //
SV_MobjCreateElems(int num_elems)315 void SV_MobjCreateElems(int num_elems)
316 {
317 // free existing mobjs
318 if (mobjlisthead)
319 P_RemoveAllMobjs();
320
321 SYS_ASSERT(mobjlisthead == NULL);
322
323 for (; num_elems > 0; num_elems--)
324 {
325 mobj_t *cur = Z_New(mobj_t, 1);
326
327 Z_Clear(cur, mobj_t, 1);
328
329 cur->next = mobjlisthead;
330 cur->prev = NULL;
331
332 if (mobjlisthead)
333 mobjlisthead->prev = cur;
334
335 mobjlisthead = cur;
336
337 // initialise defaults
338 cur->info = NULL;
339 cur->state = cur->next_state = states+1;
340
341 cur->model_skin = 1;
342 cur->model_last_frame = -1;
343 }
344 }
345
346 //
347 // SV_MobjFinaliseElems
348 //
SV_MobjFinaliseElems(void)349 void SV_MobjFinaliseElems(void)
350 {
351 mobj_t *mo;
352
353 for (mo=mobjlisthead; mo; mo=mo->next)
354 {
355 if (! mo->info)
356 mo->info = mobjtypes.Lookup(0); // template
357
358 P_SetThingPosition(mo);
359
360 // handle reference counts
361
362 #define REF_COUNT_FIELD(field) \
363 if (mo->field) mo->field->refcount++;
364
365 REF_COUNT_FIELD(tracer);
366 REF_COUNT_FIELD(source);
367 REF_COUNT_FIELD(target);
368 REF_COUNT_FIELD(supportobj);
369 REF_COUNT_FIELD(above_mo);
370 REF_COUNT_FIELD(below_mo);
371
372 #undef REF_COUNT_FIELD
373
374 // sanity checks
375 }
376 }
377
378
379 //----------------------------------------------------------------------------
380
381 //
382 // SV_ItemqCountElems
383 //
SV_ItemqCountElems(void)384 int SV_ItemqCountElems(void)
385 {
386 iteminque_t *cur;
387 int count=0;
388
389 for (cur=itemquehead; cur; cur=cur->next)
390 count++;
391
392 return count;
393 }
394
395 //
396 // SV_ItemqGetElem
397 //
398 // The index value starts at 0.
399 //
SV_ItemqGetElem(int index)400 void *SV_ItemqGetElem(int index)
401 {
402 iteminque_t *cur;
403
404 for (cur=itemquehead; cur && index > 0; cur=cur->next)
405 index--;
406
407 if (!cur)
408 I_Error("LOADGAME: Invalid ItemInQue: %d\n", index);
409
410 SYS_ASSERT(index == 0);
411 return cur;
412 }
413
414 //
415 // SV_ItemqFindElem
416 //
417 // Returns the index number (starts at 0 here).
418 //
SV_ItemqFindElem(iteminque_t * elem)419 int SV_ItemqFindElem(iteminque_t *elem)
420 {
421 iteminque_t *cur;
422 int index;
423
424 for (cur=itemquehead, index=0; cur && cur != elem; cur=cur->next)
425 index++;
426
427 if (!cur)
428 I_Error("LOADGAME: No such ItemInQue ptr: %p\n", elem);
429
430 return index;
431 }
432
433 //
434 // SV_ItemqCreateElems
435 //
SV_ItemqCreateElems(int num_elems)436 void SV_ItemqCreateElems(int num_elems)
437 {
438 P_RemoveItemsInQue();
439
440 itemquehead = NULL;
441
442 for (; num_elems > 0; num_elems--)
443 {
444 iteminque_t *cur = Z_New(iteminque_t, 1);
445
446 Z_Clear(cur, iteminque_t, 1);
447
448 cur->next = itemquehead;
449 cur->prev = NULL;
450
451 if (itemquehead)
452 itemquehead->prev = cur;
453
454 itemquehead = cur;
455
456 // initialise defaults: leave blank
457 }
458 }
459
460 //
461 // SV_ItemqFinaliseElems
462 //
SV_ItemqFinaliseElems(void)463 void SV_ItemqFinaliseElems(void)
464 {
465 iteminque_t *cur, *next;
466
467 // remove any dead wood
468 for (cur = itemquehead; cur; cur = next)
469 {
470 next = cur->next;
471
472 if (cur->spawnpoint.info)
473 continue;
474
475 I_Warning("LOADGAME: discarding empty ItemInQue\n");
476
477 if (next)
478 next->prev = cur->prev;
479
480 if (cur->prev)
481 cur->prev->next = next;
482 else
483 itemquehead = next;
484
485 Z_Free(cur);
486 }
487 }
488
489
490 //----------------------------------------------------------------------------
491
SR_MobjGetPlayer(void * storage,int index,void * extra)492 bool SR_MobjGetPlayer(void *storage, int index, void *extra)
493 {
494 player_t ** dest = (player_t **)storage + index;
495
496 int swizzle = SV_GetInt();
497
498 *dest = (swizzle == 0) ? NULL : (player_t*)SV_PlayerGetElem(swizzle - 1);
499 return true;
500 }
501
SR_MobjPutPlayer(void * storage,int index,void * extra)502 void SR_MobjPutPlayer(void *storage, int index, void *extra)
503 {
504 player_t *elem = ((player_t **)storage)[index];
505
506 int swizzle = (elem == NULL) ? 0 : SV_PlayerFindElem(elem) + 1;
507
508 SV_PutInt(swizzle);
509 }
510
SR_MobjGetMobj(void * storage,int index,void * extra)511 bool SR_MobjGetMobj(void *storage, int index, void *extra)
512 {
513 mobj_t ** dest = (mobj_t **)storage + index;
514
515 int swizzle = SV_GetInt();
516
517 *dest = (swizzle == 0) ? NULL : (mobj_t*)SV_MobjGetElem(swizzle - 1);
518 return true;
519 }
520
SR_MobjPutMobj(void * storage,int index,void * extra)521 void SR_MobjPutMobj(void *storage, int index, void *extra)
522 {
523 mobj_t *elem = ((mobj_t **)storage)[index];
524
525 int swizzle;
526
527 swizzle = (elem == NULL) ? 0 : SV_MobjFindElem(elem) + 1;
528 SV_PutInt(swizzle);
529 }
530
SR_MobjGetType(void * storage,int index,void * extra)531 bool SR_MobjGetType(void *storage, int index, void *extra)
532 {
533 mobjtype_c ** dest = (mobjtype_c **)storage + index;
534
535 const char *name = SV_GetString();
536
537 if (! name)
538 {
539 *dest = NULL;
540 return true;
541 }
542
543 // special handling for projectiles (attacks)
544 if (strncmp(name, "atk:", 4) == 0)
545 {
546 const atkdef_c *atk = atkdefs.Lookup(name+4);
547
548 if (atk)
549 *dest = (mobjtype_c *)atk->atk_mobj;
550 }
551 else
552 *dest = (mobjtype_c *)mobjtypes.Lookup(name);
553
554 if (! *dest)
555 {
556 // Note: a missing 'info' field will be fixed up later
557 I_Warning("LOADGAME: no such thing type '%s'\n", name);
558 }
559
560 SV_FreeString(name);
561 return true;
562 }
563
SR_MobjPutType(void * storage,int index,void * extra)564 void SR_MobjPutType(void *storage, int index, void *extra)
565 {
566 mobjtype_c *info = ((mobjtype_c **)storage)[index];
567
568 SV_PutString((info == NULL) ? NULL : info->name.c_str());
569 }
570
SR_MobjGetSpawnPoint(void * storage,int index,void * extra)571 bool SR_MobjGetSpawnPoint(void *storage, int index, void *extra)
572 {
573 spawnpoint_t *dest = (spawnpoint_t *)storage + index;
574
575 if (sv_struct_spawnpoint.counterpart)
576 return SV_LoadStruct(dest, sv_struct_spawnpoint.counterpart);
577
578 return true; // presumably
579 }
580
SR_MobjPutSpawnPoint(void * storage,int index,void * extra)581 void SR_MobjPutSpawnPoint(void *storage, int index, void *extra)
582 {
583 spawnpoint_t *src = (spawnpoint_t *)storage + index;
584
585 SV_SaveStruct(src, &sv_struct_spawnpoint);
586 }
587
SR_MobjGetAttack(void * storage,int index,void * extra)588 bool SR_MobjGetAttack(void *storage, int index, void *extra)
589 {
590 atkdef_c ** dest = (atkdef_c **)storage + index;
591
592 const char *name = SV_GetString();
593
594 // Intentional Const Override
595 *dest = (name == NULL) ? NULL : (atkdef_c *)atkdefs.Lookup(name);
596
597 SV_FreeString(name);
598 return true;
599 }
600
SR_MobjPutAttack(void * storage,int index,void * extra)601 void SR_MobjPutAttack(void *storage, int index, void *extra)
602 {
603 atkdef_c *info = ((atkdef_c **)storage)[index];
604
605 SV_PutString((info == NULL) ? NULL : info->name.c_str());
606 }
607
608
609 //----------------------------------------------------------------------------
610
611 //
612 // SR_MobjGetState
613 //
SR_MobjGetState(void * storage,int index,void * extra)614 bool SR_MobjGetState(void *storage, int index, void *extra)
615 {
616 state_t ** dest = (state_t **)storage + index;
617
618 char buffer[256];
619 char *base_p, *off_p;
620 int base, offset;
621
622 const char *swizzle;
623 const mobj_t *mo = (mobj_t *) sv_current_elem;
624 const mobjtype_c *actual;
625
626 SYS_ASSERT(mo);
627
628 swizzle = SV_GetString();
629
630 if (!swizzle || !mo->info)
631 {
632 *dest = NULL;
633 return true;
634 }
635
636 Z_StrNCpy(buffer, swizzle, 256-1);
637 SV_FreeString(swizzle);
638
639 // separate string at `:' characters
640
641 base_p = strchr(buffer, ':');
642
643 if (base_p == NULL || base_p[0] == 0)
644 I_Error("Corrupt savegame: bad state 1/2: `%s'\n", buffer);
645
646 *base_p++ = 0;
647
648 off_p = strchr(base_p, ':');
649
650 if (off_p == NULL || off_p[0] == 0)
651 I_Error("Corrupt savegame: bad state 2/2: `%s'\n", base_p);
652
653 *off_p++ = 0;
654
655 // find thing that contains the state
656 actual = mo->info;
657
658 if (buffer[0] != '*')
659 {
660 // Do we care about those in the disabled group?
661 actual = mobjtypes.Lookup(buffer);
662 if (!actual)
663 I_Error("LOADGAME: no such thing %s for state %s:%s\n",
664 buffer, base_p, off_p);
665 }
666
667 // find base state
668 offset = strtol(off_p, NULL, 0) - 1;
669
670 base = DDF_StateFindLabel(actual->state_grp, base_p, true /* quiet */);
671
672 if (! base)
673 {
674 I_Warning("LOADGAME: no such label `%s' for state.\n", base_p);
675 offset = 0;
676
677 if (actual->idle_state)
678 base = actual->idle_state;
679 else if (actual->spawn_state)
680 base = actual->spawn_state;
681 else if (actual->meander_state)
682 base = actual->meander_state;
683 else if (actual->state_grp.size() > 0)
684 base = actual->state_grp[0].first;
685 else
686 base = 1;
687 }
688
689 #if 0
690 L_WriteDebug("Unswizzled state `%s:%s:%s' -> %d\n",
691 buffer, base_p, off_p, base + offset);
692 #endif
693
694 *dest = states + base + offset;
695
696 return true;
697 }
698
699 //
700 // SR_MobjPutState
701 //
702 // The format of the string is:
703 //
704 // THING `:' BASE `:' OFFSET
705 //
706 // where THING is usually just "*" for the current thing, but can
707 // refer to another ddf thing (e.g. "IMP"). BASE is the nearest
708 // labelled state (e.g. "SPAWN"), or "*" as offset from the thing's
709 // first state (unlikely to be needed). OFFSET is the integer offset
710 // from the base state (e.g. "5"), which BTW starts at 1 (like the ddf
711 // format).
712 //
713 // Alternatively, the string can be NULL, which means the state
714 // pointer should be NULL.
715 //
716 // P.S: we go to all this trouble to try and get reasonable behaviour
717 // when loading with different DDF files than what we saved with.
718 // Typical example: a new item, monster or weapon gets added to our
719 // DDF files causing all state numbers to be shifted upwards.
720 //
SR_MobjPutState(void * storage,int index,void * extra)721 void SR_MobjPutState(void *storage, int index, void *extra)
722 {
723 state_t *S = ((state_t **)storage)[index];
724
725 char swizzle[256];
726
727 int s_num, base;
728
729 const mobj_t *mo = (mobj_t *) sv_current_elem;
730 const mobjtype_c *actual;
731
732 SYS_ASSERT(mo);
733
734 if (S == NULL || !mo->info)
735 {
736 SV_PutString(NULL);
737 return;
738 }
739
740 // object has no states ?
741 if (mo->info->state_grp.empty())
742 {
743 I_Warning("SAVEGAME: object [%s] has no states !!\n", mo->info->name.c_str());
744 SV_PutString(NULL);
745 return;
746 }
747
748 // get state number, check if valid
749 s_num = (int)(S - states);
750
751 if (s_num < 0 || s_num >= num_states)
752 {
753 I_Warning("SAVEGAME: object [%s] is in invalid state %d\n",
754 mo->info->name.c_str(), s_num);
755
756 if (mo->info->idle_state)
757 s_num = mo->info->idle_state;
758 else if (mo->info->spawn_state)
759 s_num = mo->info->spawn_state;
760 else if (mo->info->meander_state)
761 s_num = mo->info->meander_state;
762 else
763 {
764 SV_PutString("*:*:1");
765 return;
766 }
767 }
768
769 // state gone AWOL into another object ?
770 actual = mo->info;
771
772 if (! DDF_StateGroupHasState(actual->state_grp, s_num))
773 {
774 I_Warning("SAVEGAME: object [%s] is in AWOL state %d\n",
775 mo->info->name.c_str(), s_num);
776
777 epi::array_iterator_c it;
778
779 // look for real object
780 for (it = mobjtypes.GetBaseIterator(); it.IsValid(); it++)
781 {
782 actual = ITERATOR_TO_TYPE(it, mobjtype_c*);
783
784 if (DDF_StateGroupHasState(actual->state_grp, s_num))
785 break;
786 }
787
788 if (it.IsValid())
789 {
790 I_Warning("-- ARGH: state %d cannot be found !!\n", s_num);
791 SV_PutString("*:*:1");
792 return;
793 }
794
795 if (! actual->name)
796 {
797 I_Warning("-- OOPS: state %d found in unnamed object !!\n", s_num);
798 SV_PutString("*:*:1");
799 return;
800 }
801 }
802
803 // find the nearest base state
804 base = s_num;
805
806 while (! states[base].label &&
807 DDF_StateGroupHasState(actual->state_grp, base-1))
808 {
809 base--;
810 }
811
812 sprintf(swizzle, "%s:%s:%d",
813 (actual == mo->info) ? "*" : actual->name.c_str(),
814 states[base].label ? states[base].label : "*",
815 1 + s_num - base);
816
817 #if 0
818 L_WriteDebug("Swizzled state %d of [%s] -> `%s'\n",
819 s_num, mo->info->name, swizzle);
820 #endif
821
822 SV_PutString(swizzle);
823 }
824
825
826 //--- editor settings ---
827 // vi:ts=4:sw=4:noexpandtab
828