1
2 /* G_game.c */
3
4 #include <stdio.h>
5 #include <string.h>
6 #include "doomdef.h"
7 #include "p_local.h"
8 #include "soundst.h"
9
10 #ifdef GL_HERETIC
11 #include "gl_struct.h"
12 #endif
13
14 #ifdef GL_HERETIC
15 boolean g_bMlook=FALSE;
16 /* to be looked into later */
17 long g_bMD2=0;
18 #endif
19
20 /* Macros */
21
22 #define SVG_RAM 0
23 #define SVG_FILE 1
24 #define SAVE_GAME_TERMINATOR 0x1d
25 #define AM_STARTKEY 9
26
27 /* Functions */
28
29 boolean G_CheckDemoStatus (void);
30 void G_ReadDemoTiccmd (ticcmd_t *cmd);
31 void G_WriteDemoTiccmd (ticcmd_t *cmd);
32 void G_PlayerReborn (int player);
33 void G_InitNew (skill_t skill, int episode, int map);
34
35 void G_DoReborn (int playernum);
36
37 void G_DoLoadLevel (void);
38 void G_DoNewGame (void);
39 void G_DoLoadGame (void);
40 void G_DoPlayDemo (void);
41 void G_DoCompleted (void);
42 void G_DoVictory (void);
43 void G_DoWorldDone (void);
44 void G_DoSaveGame (void);
45
46 void D_PageTicker(void);
47 void D_AdvanceDemo(void);
48
49 struct
50 {
51 mobjtype_t type;
52 int speed[2];
53 } MonsterMissileInfo[] =
54 {
55 { MT_IMPBALL, {10, 20} },
56 { MT_MUMMYFX1, {9, 18} },
57 { MT_KNIGHTAXE, {9, 18} },
58 { MT_REDAXE, {9, 18} },
59 { MT_BEASTBALL, {12, 20} },
60 { MT_WIZFX1, {18, 24} },
61 { MT_SNAKEPRO_A, {14, 20} },
62 { MT_SNAKEPRO_B, {14, 20} },
63 { MT_HEADFX1, {13, 20} },
64 { MT_HEADFX3, {10, 18} },
65 { MT_MNTRFX1, {20, 26} },
66 { MT_MNTRFX2, {14, 20} },
67 { MT_SRCRFX1, {20, 28} },
68 { MT_SOR2FX1, {20, 28} },
69 { -1, {-1, -1} } /* Terminator */
70 };
71
72 FILE *SaveGameFP;
73 int SaveGameType;
74
75 gameaction_t gameaction;
76 gamestate_t gamestate;
77 skill_t gameskill;
78 boolean respawnmonsters;
79 int gameepisode;
80 int gamemap;
81 int prevmap;
82
83 boolean paused;
84 boolean sendpause; /* send a pause event next tic */
85 boolean sendsave; /* send a save event next tic */
86 boolean usergame; /* ok to save / end game */
87
88 boolean timingdemo; /* if true, exit with report on completion */
89 int starttime; /* for comparative timing purposes */
90
91 boolean viewactive;
92
93 boolean deathmatch; /* only if started as net death */
94 boolean respawnartifacts; /* respawning Artifacts */
95 boolean netgame; /* only true if packets are broadcast */
96 boolean playeringame[MAXPLAYERS];
97 player_t players[MAXPLAYERS];
98
99 int consoleplayer; /* player taking events and displaying */
100 int displayplayer; /* view being displayed */
101 int gametic;
102 int levelstarttic; /* gametic at level start */
103 int totalkills, totalitems, totalsecret; /* for intermission */
104
105 char demoname[32];
106 boolean demorecording;
107 boolean demoplayback;
108 byte *demobuffer, *demo_p;
109 boolean singledemo; /* quit after playing a demo from cmdline */
110
111 boolean precache = true; /* if true, load all graphics at start */
112
113 short consistancy[MAXPLAYERS][BACKUPTICS];
114
115 byte *savebuffer, *save_p;
116
117 extern char* homedir;
118
119
120 /*
121 * controls (have defaults)
122 */
123 long key_right, key_left, key_up, key_down;
124 long key_strafeleft, key_straferight;
125 long key_fire, key_use, key_strafe, key_speed;
126 long key_health, key_tomeofpower, key_egg, key_firebomb;
127 long key_flyup, key_flydown, key_flycenter;
128 long key_lookup, key_lookdown, key_lookcenter;
129 long key_invleft, key_invright, key_useartifact;
130
131 long mousebfire;
132 long mousebstrafe;
133 long mousebforward;
134 long invertmouse;
135
136 long joybfire;
137 long joybstrafe;
138 long joybuse;
139 long joybspeed;
140
141
142
143 #define MAXPLMOVE 0x32
144
145 fixed_t forwardmove[2] = {0x19, 0x32};
146 fixed_t sidemove[2] = {0x18, 0x28};
147 fixed_t angleturn[3] = {640, 1280, 320}; /* + slow turn */
148 #define SLOWTURNTICS 6
149
150 #define NUMKEYS 256
151 boolean gamekeydown[NUMKEYS];
152 int turnheld; /* for accelerative turning */
153 int lookheld;
154
155
156 boolean mousearray[4];
157 boolean *mousebuttons = &mousearray[1];
158 /* allow [-1] */
159 int mousex, mousey; /* mouse values are used once */
160 int dclicktime, dclickstate, dclicks;
161 int dclicktime2, dclickstate2, dclicks2;
162
163 int joyxmove, joyymove; /* joystick values are repeated */
164 boolean joyarray[5];
165 boolean *joybuttons = &joyarray[1]; /* allow [-1] */
166
167 int savegameslot;
168 char savedescription[32];
169
170 int inventoryTics;
171
172
173 /* =============================================================================
174 * Not used - ripped out for Heretic
175 */
176
177 /*
178 int G_CmdChecksum(ticcmd_t *cmd)
179 {
180 int i;
181 int sum;
182
183 sum = 0;
184 for(i = 0; i < sizeof(*cmd)/4-1; i++)
185 {
186 sum += ((int *)cmd)[i];
187 }
188 return(sum);
189 }
190 */
191
192 /*
193 ====================
194 =
195 = G_BuildTiccmd
196 =
197 = Builds a ticcmd from all of the available inputs or reads it from the
198 = demo buffer.
199 = If recording a demo, write it out
200 ====================
201 */
202
203 extern boolean inventory;
204 extern int curpos;
205 extern int inv_ptr;
206
207 boolean usearti = true;
208
G_BuildTiccmd(ticcmd_t * cmd)209 void G_BuildTiccmd (ticcmd_t *cmd)
210 {
211 int i;
212 boolean strafe, bstrafe;
213 int speed, tspeed, lspeed;
214 int forward, side;
215 int look, arti;
216 int flyheight;
217
218 extern boolean noartiskip;
219
220
221 memset (cmd,0,sizeof(*cmd));
222 /*
223 * cmd->consistancy =
224 * consistancy[consoleplayer][(maketic*ticdup)%BACKUPTICS];
225 */
226 cmd->consistancy =
227 consistancy[consoleplayer][maketic%BACKUPTICS];
228
229 /* printf ("cons: %i\n",cmd->consistancy); */
230
231 strafe = gamekeydown[key_strafe] || mousebuttons[mousebstrafe]
232 || joybuttons[joybstrafe];
233 speed = gamekeydown[key_speed] || joybuttons[joybspeed]
234 || joybuttons[joybspeed];
235
236 forward = side = look = arti = flyheight = 0;
237
238 /*
239 * use two stage accelerative turning on the keyboard and joystick
240 */
241 if (joyxmove < 0
242 || joyxmove > 0
243 || gamekeydown[key_right]
244 || gamekeydown[key_left])
245 turnheld += ticdup;
246 else
247 turnheld = 0;
248 if (turnheld < SLOWTURNTICS)
249 tspeed = 2; /* slow turn */
250 else
251 tspeed = speed;
252
253 if(gamekeydown[key_lookdown] || gamekeydown[key_lookup])
254 {
255 lookheld += ticdup;
256 }
257 else
258 {
259 lookheld = 0;
260 }
261 if(lookheld < SLOWTURNTICS)
262 {
263 lspeed = 1;
264 }
265 else
266 {
267 lspeed = 2;
268 }
269
270 /*
271 * let movement keys cancel each other out
272 */
273 if(strafe)
274 {
275 if (gamekeydown[key_right])
276 side += sidemove[speed];
277 if (gamekeydown[key_left])
278 side -= sidemove[speed];
279 if (joyxmove > 0)
280 side += sidemove[speed];
281 if (joyxmove < 0)
282 side -= sidemove[speed];
283 }
284 else
285 {
286 if (gamekeydown[key_right])
287 cmd->angleturn -= angleturn[tspeed];
288 if (gamekeydown[key_left])
289 cmd->angleturn += angleturn[tspeed];
290 if (joyxmove > 0)
291 cmd->angleturn -= angleturn[tspeed];
292 if (joyxmove < 0)
293 cmd->angleturn += angleturn[tspeed];
294 }
295
296 if (gamekeydown[key_up])
297 forward += forwardmove[speed];
298 if (gamekeydown[key_down])
299 forward -= forwardmove[speed];
300 if (joyymove < 0)
301 forward += forwardmove[speed];
302 if (joyymove > 0)
303 forward -= forwardmove[speed];
304 if (gamekeydown[key_straferight])
305 side += sidemove[speed];
306 if (gamekeydown[key_strafeleft])
307 side -= sidemove[speed];
308
309 /* Look up/down/center keys */
310 if(gamekeydown[key_lookup])
311 {
312 look = lspeed;
313 }
314 if(gamekeydown[key_lookdown])
315 {
316 look = -lspeed;
317 }
318 if(gamekeydown[key_lookcenter])
319 {
320 look = TOCENTER;
321 }
322
323 /* Fly up/down/drop keys */
324 if(gamekeydown[key_flyup])
325 {
326 flyheight = 5; /* note that the actual flyheight will be twice this */
327 }
328 if(gamekeydown[key_flydown])
329 {
330 flyheight = -5;
331 }
332 if(gamekeydown[key_flycenter])
333 {
334 flyheight = TOCENTER;
335 look = TOCENTER;
336 }
337
338 /* Use artifact key */
339 if(gamekeydown[key_useartifact])
340 {
341 if(gamekeydown[key_speed] && !noartiskip)
342 {
343 if(players[consoleplayer].inventory[inv_ptr].type != arti_none)
344 {
345 gamekeydown[key_useartifact] = false;
346 cmd->arti = 0xff; /* skip artifact code */
347 }
348 }
349 else
350 {
351 if(inventory)
352 {
353 players[consoleplayer].readyArtifact =
354 players[consoleplayer].inventory[inv_ptr].type;
355 inventory = false;
356 cmd->arti = 0;
357 usearti = false;
358 }
359 else if(usearti)
360 {
361 cmd->arti = players[consoleplayer].inventory[inv_ptr].type;
362 usearti = false;
363 }
364 }
365 }
366 if(gamekeydown[key_tomeofpower] && !cmd->arti
367 && !players[consoleplayer].powers[pw_weaponlevel2])
368 {
369 gamekeydown[key_tomeofpower] = false;
370 cmd->arti = arti_tomeofpower;
371 }
372 // Health by key:
373 if(gamekeydown[key_health] && !cmd->arti)
374 {
375 gamekeydown[key_health] = false;
376 cmd->arti = arti_health;
377 }
378
379 // Egg by key:
380 if(gamekeydown[key_egg] && !cmd->arti)
381 {
382 gamekeydown[key_egg] = false;
383 cmd->arti = arti_egg;
384 }
385
386 // Timebombe by key:
387 if(gamekeydown[key_firebomb] && !cmd->arti)
388 {
389 gamekeydown[key_firebomb] = false;
390 cmd->arti = arti_firebomb;
391 }
392
393
394 /*
395 * buttons
396 */
397 cmd->chatchar = CT_dequeueChatChar();
398
399 if (gamekeydown[key_fire] || mousebuttons[mousebfire]
400 || joybuttons[joybfire])
401 cmd->buttons |= BT_ATTACK;
402
403 if (gamekeydown[key_use] || joybuttons[joybuse] )
404 {
405 cmd->buttons |= BT_USE;
406 dclicks = 0; /* clear double clicks if hit use button */
407 }
408
409 for(i = 0; i < NUMWEAPONS-2; i++)
410 {
411 if(gamekeydown['1'+i])
412 {
413 cmd->buttons |= BT_CHANGE;
414 cmd->buttons |= i<<BT_WEAPONSHIFT;
415 break;
416 }
417 }
418
419 /*
420 * mouse
421 */
422 if (mousebuttons[mousebforward])
423 {
424 forward += forwardmove[speed];
425 }
426
427 /*
428 * forward double click
429 */
430 if (mousebuttons[mousebforward] != dclickstate && dclicktime > 1 )
431 {
432 dclickstate = mousebuttons[mousebforward];
433 if (dclickstate)
434 dclicks++;
435 if (dclicks == 2)
436 {
437 cmd->buttons |= BT_USE;
438 dclicks = 0;
439 }
440 else
441 dclicktime = 0;
442 }
443 else
444 {
445 dclicktime += ticdup;
446 if (dclicktime > 20)
447 {
448 dclicks = 0;
449 dclickstate = 0;
450 }
451 }
452
453 /*
454 * strafe double click
455 */
456 bstrafe = mousebuttons[mousebstrafe]
457 || joybuttons[joybstrafe];
458 if (bstrafe != dclickstate2 && dclicktime2 > 1 )
459 {
460 dclickstate2 = bstrafe;
461 if (dclickstate2)
462 dclicks2++;
463 if (dclicks2 == 2)
464 {
465 cmd->buttons |= BT_USE;
466 dclicks2 = 0;
467 }
468 else
469 dclicktime2 = 0;
470 }
471 else
472 {
473 dclicktime2 += ticdup;
474 if (dclicktime2 > 20)
475 {
476 dclicks2 = 0;
477 dclickstate2 = 0;
478 }
479 }
480
481 if (strafe)
482 {
483 side += mousex*2;
484 }
485 else
486 {
487 cmd->angleturn -= mousex*0x8;
488 }
489
490 /* rhandeev: added mouse invert option */
491 if (mouseInvert == true)
492 mousey = -mousey;
493
494 #ifndef GL_HERETIC
495 /* rhandeev: added mouse look option */
496 if (mouseLook == true)
497 {
498 static int mouse_sum = 0;
499
500 mouse_sum += mousey;
501 if (abs(mouse_sum) > 10)
502 {
503 look += mouse_sum / 10;
504 mouse_sum %= 10;
505 }
506 }
507 else
508 #endif
509
510 #ifdef GL_HERETIC /* Mlook */
511 if (g_bMlook) {
512 player_t *player;
513
514 /* lmousey+=mousey; */
515 player = &players[consoleplayer];
516 if (!invertmouse)
517 player->lookdir +=(int)((float)mousey/10.0f);
518 else
519 player->lookdir -=(int)((float)mousey/10.0f);
520 if(player->lookdir > 90)
521 player->lookdir=90;
522 if(player->lookdir < -110)
523 player->lookdir=-110;
524 /* look+=(int)((float)mousey/1.0f); */
525 }
526 else
527 #endif
528 {
529 forward += mousey;
530 }
531
532 mousex = mousey = 0;
533
534 if (forward > MAXPLMOVE)
535 forward = MAXPLMOVE;
536 else if (forward < -MAXPLMOVE)
537 forward = -MAXPLMOVE;
538 if (side > MAXPLMOVE)
539 side = MAXPLMOVE;
540 else if (side < -MAXPLMOVE)
541 side = -MAXPLMOVE;
542
543 cmd->forwardmove += forward;
544 cmd->sidemove += side;
545 if(players[consoleplayer].playerstate == PST_LIVE)
546 {
547 if(look < 0)
548 {
549 look += 16;
550 }
551 cmd->lookfly = look;
552 }
553 if(flyheight < 0)
554 {
555 flyheight += 16;
556 }
557 cmd->lookfly |= flyheight<<4;
558
559 /*
560 * special buttons
561 */
562 if (sendpause)
563 {
564 sendpause = false;
565 cmd->buttons = BT_SPECIAL | BTS_PAUSE;
566 }
567
568 if (sendsave)
569 {
570 sendsave = false;
571 cmd->buttons = BT_SPECIAL | BTS_SAVEGAME | (savegameslot<<BTS_SAVESHIFT);
572 }
573
574 }
575
576
577 /*
578 ==============
579 =
580 = G_DoLoadLevel
581 =
582 ==============
583 */
584
585 #ifdef GL_HERETIC
586 extern void GL_PurgeTextures();
587 #endif
588
G_DoLoadLevel(void)589 void G_DoLoadLevel (void)
590 {
591 int i;
592
593 #ifdef GL_HERETIC
594 /* the number for fn_vInitMemory is a dummy ! */
595 /* printf("Heretic for GLDrv: should \"fn_vSetMemoryMode(LEVEL);\"\n"); */
596 fn_vSetMemoryMode(LEVEL);
597 GL_PurgeTextures();
598 fn_vDesinitMemory();
599 fn_vInitMemory(1);
600 /* printf("Heretic for GLDrv: should \"fn_vDesinitMemory();\"\n"); */
601 /* printf("Heretic for GLDrv: should \"fn_vInitMemory(10000000);\"\n"); */
602 #endif
603
604 levelstarttic = gametic; /* for time calculation */
605 gamestate = GS_LEVEL;
606 for (i=0 ; i<MAXPLAYERS ; i++)
607 {
608 int j;
609 if (playeringame[i] && players[i].playerstate == PST_DEAD)
610 players[i].playerstate = PST_REBORN;
611 for(j=0;j<MAXPLAYERS;j++)
612 players[i].frags[j]=0;
613 /*memset (players[i].frags,0,sizeof(players[i].frags));*/
614 }
615
616 P_SetupLevel (gameepisode, gamemap, 0, gameskill);
617 displayplayer = consoleplayer; /* view the guy you are playing */
618 starttime = I_GetTime ();
619 gameaction = ga_nothing;
620 Z_CheckHeap ();
621
622 /*
623 * clear cmd building stuff
624 */
625
626 memset (gamekeydown, 0, sizeof(gamekeydown));
627 joyxmove = joyymove = 0;
628 mousex = mousey = 0;
629 sendpause = sendsave = paused = false;
630
631 memset (mousebuttons, 0, sizeof(mousebuttons));
632 memset (joybuttons, 0, sizeof(joybuttons));
633 }
634
635
636 /*
637 ===============================================================================
638 =
639 = G_Responder
640 =
641 = get info needed to make ticcmd_ts for the players
642 =
643 ===============================================================================
644 */
645
G_Responder(event_t * ev)646 boolean G_Responder(event_t *ev)
647 {
648 player_t *plr;
649 extern boolean MenuActive;
650
651 plr = &players[consoleplayer];
652 if(ev->type == ev_keyup && ev->data1 == key_useartifact)
653 { /* flag to denote that it's okay to use an artifact */
654 if(!inventory)
655 {
656 plr->readyArtifact = plr->inventory[inv_ptr].type;
657 }
658 usearti = true;
659 }
660
661 /* Check for spy mode player cycle */
662 if(gamestate == GS_LEVEL && ev->type == ev_keydown
663 && ev->data1 == KEY_F12 && !deathmatch)
664 { /* Cycle the display player */
665 do
666 {
667 displayplayer++;
668 if(displayplayer == MAXPLAYERS)
669 {
670 displayplayer = 0;
671 }
672 } while(!playeringame[displayplayer]
673 && displayplayer != consoleplayer);
674 return(true);
675 }
676
677 if(gamestate == GS_LEVEL)
678 {
679 if(CT_Responder(ev))
680 { /* Chat ate the event */
681 return(true);
682 }
683 if(SB_Responder(ev))
684 { /* Status bar ate the event */
685 return(true);
686 }
687 if(AM_Responder(ev))
688 { /* Automap ate the event */
689 return(true);
690 }
691 }
692
693 switch(ev->type)
694 {
695 case ev_keydown:
696 if(ev->data1 == key_invleft)
697 {
698 inventoryTics = 5*35;
699 if(!inventory)
700 {
701 inventory = true;
702 break;
703 }
704 inv_ptr--;
705 if(inv_ptr < 0)
706 {
707 inv_ptr = 0;
708 }
709 else
710 {
711 curpos--;
712 if(curpos < 0)
713 {
714 curpos = 0;
715 }
716 }
717 return(true);
718 }
719 if(ev->data1 == key_invright)
720 {
721 inventoryTics = 5*35;
722 if(!inventory)
723 {
724 inventory = true;
725 break;
726 }
727 inv_ptr++;
728 if(inv_ptr >= plr->inventorySlotNum)
729 {
730 inv_ptr--;
731 if(inv_ptr < 0)
732 inv_ptr = 0;
733 }
734 else
735 {
736 curpos++;
737 if(curpos > 6)
738 {
739 curpos = 6;
740 }
741 }
742 return(true);
743 }
744 if(ev->data1 == KEY_PAUSE && !MenuActive)
745 {
746 sendpause = true;
747 return(true);
748 }
749 if(ev->data1 < NUMKEYS)
750 {
751 gamekeydown[ev->data1] = true;
752 }
753 return(true); /* eat key down events */
754
755 case ev_keyup:
756 if(ev->data1 < NUMKEYS)
757 {
758 gamekeydown[ev->data1] = false;
759 }
760 return(false); /* always let key up events filter down */
761
762 case ev_mouse:
763 mousebuttons[0] = ev->data1&1;
764 mousebuttons[1] = ev->data1&2;
765 mousebuttons[2] = ev->data1&4;
766 mousex = ev->data2*mouseXSensitivity/10;
767 mousey = ev->data3*mouseYSensitivity/10;
768 return(true); /* eat events */
769
770 case ev_joystick:
771 joybuttons[0] = ev->data1&1;
772 joybuttons[1] = ev->data1&2;
773 joybuttons[2] = ev->data1&4;
774 joybuttons[3] = ev->data1&8;
775 joyxmove = ev->data2;
776 joyymove = ev->data3;
777 return(true); /* eat events */
778
779 default:
780 break;
781 }
782 return(false);
783 }
784
785 /*
786 ===============================================================================
787 =
788 = G_Ticker
789 =
790 ===============================================================================
791 */
792
G_Ticker(void)793 void G_Ticker (void)
794 {
795 int i, buf;
796 /* changed from ticcmd_t *cmd */
797 ticcmd_t *cmd=NULL;
798
799 /*
800 * do player reborns if needed
801 */
802 for (i=0 ; i<MAXPLAYERS ; i++)
803 if (playeringame[i] && players[i].playerstate == PST_REBORN)
804 G_DoReborn (i);
805
806 /*
807 * do things to change the game state
808 */
809 while (gameaction != ga_nothing)
810 {
811 switch (gameaction)
812 {
813 case ga_loadlevel:
814 G_DoLoadLevel ();
815 break;
816 case ga_newgame:
817 G_DoNewGame ();
818 break;
819 case ga_loadgame:
820 G_DoLoadGame ();
821 break;
822 case ga_savegame:
823 G_DoSaveGame ();
824 break;
825 case ga_playdemo:
826 G_DoPlayDemo ();
827 break;
828 case ga_screenshot:
829 M_ScreenShot ();
830 gameaction = ga_nothing;
831 break;
832 case ga_completed:
833 G_DoCompleted ();
834 break;
835 case ga_worlddone:
836 G_DoWorldDone();
837 break;
838 case ga_victory:
839 F_StartFinale();
840 break;
841 default:
842 break;
843 }
844 }
845
846
847 /*
848 * get commands, check consistancy, and build new consistancy check
849 */
850
851 /* buf = gametic%BACKUPTICS; */
852 buf = (gametic/ticdup)%BACKUPTICS;
853
854 for (i=0 ; i<MAXPLAYERS ; i++)
855 if (playeringame[i])
856 {
857 cmd = &players[i].cmd;
858
859 memcpy (cmd, &netcmds[i][buf], sizeof(ticcmd_t));
860
861 if (demoplayback)
862 G_ReadDemoTiccmd (cmd);
863 if (demorecording)
864 G_WriteDemoTiccmd (cmd);
865
866 if (netgame && !(gametic%ticdup) )
867 {
868 if (gametic > BACKUPTICS
869 && consistancy[i][buf] != cmd->consistancy)
870 {
871 I_Error ("consistency failure (%i should be %i)",cmd->consistancy, consistancy[i][buf]);
872 }
873 if (players[i].mo)
874 consistancy[i][buf] = players[i].mo->x;
875 else
876 consistancy[i][buf] = rndindex;
877 }
878 }
879
880 /*
881 * check for special buttons
882 */
883 for (i=0 ; i<MAXPLAYERS ; i++)
884 if (playeringame[i])
885 {
886 if (players[i].cmd.buttons & BT_SPECIAL)
887 {
888 switch (players[i].cmd.buttons & BT_SPECIALMASK)
889 {
890 case BTS_PAUSE:
891 paused ^= 1;
892 if(paused)
893 {
894 S_PauseSound();
895 }
896 else
897 {
898 S_ResumeSound();
899 }
900 break;
901
902 case BTS_SAVEGAME:
903 if (!savedescription[0])
904 {
905 if(netgame)
906 {
907 strcpy (savedescription, "NET GAME");
908 }
909 else
910 {
911 strcpy(savedescription, "SAVE GAME");
912 }
913 }
914 savegameslot =
915 (players[i].cmd.buttons & BTS_SAVEMASK)>>BTS_SAVESHIFT;
916 gameaction = ga_savegame;
917 break;
918 }
919 }
920 }
921 /* turn inventory off after a certain amount of time */
922 if(inventory && !(--inventoryTics))
923 {
924 players[consoleplayer].readyArtifact =
925 players[consoleplayer].inventory[inv_ptr].type;
926 inventory = false;
927 cmd->arti = 0;
928 }
929 /*
930 * do main actions
931 *
932 *
933 * do main actions
934 */
935 switch (gamestate)
936 {
937 case GS_LEVEL:
938 P_Ticker ();
939 SB_Ticker ();
940 AM_Ticker ();
941 CT_Ticker();
942 break;
943 case GS_INTERMISSION:
944 IN_Ticker ();
945 break;
946 case GS_FINALE:
947 F_Ticker();
948 break;
949 case GS_DEMOSCREEN:
950 D_PageTicker ();
951 break;
952 }
953 }
954
955
956 /*
957 ==============================================================================
958
959 PLAYER STRUCTURE FUNCTIONS
960
961 also see P_SpawnPlayer in P_Things
962 ==============================================================================
963 */
964
965 /*
966 ====================
967 =
968 = G_InitPlayer
969 =
970 = Called at the start
971 = Called by the game initialization functions
972 ====================
973 */
974
G_InitPlayer(int player)975 void G_InitPlayer (int player)
976 {
977 player_t *p;
978
979 /* set up the saved info */
980 p = &players[player];
981
982 /* clear everything else to defaults */
983 G_PlayerReborn (player);
984
985 }
986
987
988 /*
989 ====================
990 =
991 = G_PlayerFinishLevel
992 =
993 = Can when a player completes a level
994 ====================
995 */
996 extern int curpos;
997 extern int inv_ptr;
998 extern int playerkeys;
999
G_PlayerFinishLevel(int player)1000 void G_PlayerFinishLevel(int player)
1001 {
1002 player_t *p;
1003 int i;
1004
1005 /* // BIG HACK
1006 inv_ptr = 0;
1007 curpos = 0;
1008 */
1009
1010 /* END HACK */
1011 p = &players[player];
1012 for(i=0; i<p->inventorySlotNum; i++)
1013 {
1014 p->inventory[i].count = 1;
1015 }
1016 p->artifactCount = p->inventorySlotNum;
1017
1018 if(!deathmatch)
1019 {
1020 for(i = 0; i < 16; i++)
1021 {
1022 P_PlayerUseArtifact(p, arti_fly);
1023 }
1024 }
1025 memset(p->powers, 0, sizeof(p->powers));
1026 memset(p->keys, 0, sizeof(p->keys));
1027 playerkeys = 0;
1028 /* memset(p->inventory, 0, sizeof(p->inventory)); */
1029 if(p->chickenTics)
1030 {
1031 p->readyweapon = p->mo->special1; /* Restore weapon */
1032 p->chickenTics = 0;
1033 }
1034 p->messageTics = 0;
1035 p->lookdir = 0;
1036 p->mo->flags &= ~MF_SHADOW; /* Remove invisibility */
1037 p->extralight = 0; /* Remove weapon flashes */
1038 p->fixedcolormap = 0; /* Remove torch */
1039 p->damagecount = 0; /* No palette changes */
1040 p->bonuscount = 0;
1041 p->rain1 = NULL;
1042 p->rain2 = NULL;
1043 if(p == &players[consoleplayer])
1044 {
1045 SB_state = -1; /* refresh the status bar */
1046 }
1047 }
1048
1049 /*
1050 ====================
1051 =
1052 = G_PlayerReborn
1053 =
1054 = Called after a player dies
1055 = almost everything is cleared and initialized
1056 ====================
1057 */
1058
G_PlayerReborn(int player)1059 void G_PlayerReborn(int player)
1060 {
1061 player_t *p;
1062 int i,j;
1063 int frags[MAXPLAYERS];
1064 int killcount, itemcount, secretcount;
1065 boolean secret;
1066
1067 secret = false;
1068 for(j=0;j<MAXPLAYERS;j++)
1069 frags[j]=players[player].frags[j];
1070 /*memcpy(frags, players[player].frags, sizeof(frags));*/
1071 killcount = players[player].killcount;
1072 itemcount = players[player].itemcount;
1073 secretcount = players[player].secretcount;
1074
1075 p = &players[player];
1076 if(p->didsecret)
1077 {
1078 secret = true;
1079 }
1080 memset(p, 0, sizeof(*p));
1081
1082 for(j=0;j<MAXPLAYERS;j++)
1083 players[player].frags[j]=frags[j];
1084 /*memcpy(players[player].frags, frags, sizeof(players[player].frags));*/
1085 players[player].killcount = killcount;
1086 players[player].itemcount = itemcount;
1087 players[player].secretcount = secretcount;
1088
1089 p->usedown = p->attackdown = true; /* don't do anything immediately */
1090 p->playerstate = PST_LIVE;
1091 p->health = MAXHEALTH;
1092 p->readyweapon = p->pendingweapon = wp_goldwand;
1093 p->weaponowned[wp_staff] = true;
1094 p->weaponowned[wp_goldwand] = true;
1095 p->messageTics = 0;
1096 p->lookdir = 0;
1097 p->ammo[am_goldwand] = 50;
1098 for(i = 0; i < NUMAMMO; i++)
1099 {
1100 p->maxammo[i] = maxammo[i];
1101 }
1102 if(gamemap == 9 || secret)
1103 {
1104 p->didsecret = true;
1105 }
1106 if(p == &players[consoleplayer])
1107 {
1108 SB_state = -1; /* refresh the status bar */
1109 inv_ptr = 0; /* reset the inventory pointer */
1110 curpos = 0;
1111 }
1112 }
1113
1114 /*
1115 ====================
1116 =
1117 = G_CheckSpot
1118 =
1119 = Returns false if the player cannot be respawned at the given mapthing_t spot
1120 = because something is occupying it
1121 ====================
1122 */
1123
1124 void P_SpawnPlayer (mapthing_t *mthing);
1125
G_CheckSpot(int playernum,mapthing_t * mthing)1126 boolean G_CheckSpot (int playernum, mapthing_t *mthing)
1127 {
1128 fixed_t x,y;
1129 subsector_t *ss;
1130 unsigned an;
1131 mobj_t *mo;
1132
1133 x = mthing->x << FRACBITS;
1134 y = mthing->y << FRACBITS;
1135
1136 players[playernum].mo->flags2 &= ~MF2_PASSMOBJ;
1137 if (!P_CheckPosition (players[playernum].mo, x, y) )
1138 {
1139 players[playernum].mo->flags2 |= MF2_PASSMOBJ;
1140 return false;
1141 }
1142 players[playernum].mo->flags2 |= MF2_PASSMOBJ;
1143
1144 /* spawn a teleport fog */
1145 ss = R_PointInSubsector (x,y);
1146 an = ( ANG45 * (mthing->angle/45) ) >> ANGLETOFINESHIFT;
1147
1148 mo = P_SpawnMobj (x+20*finecosine[an], y+20*finesine[an]
1149 , ss->sector->floorheight+TELEFOGHEIGHT
1150 , MT_TFOG);
1151
1152 if (players[consoleplayer].viewz != 1)
1153 S_StartSound (mo, sfx_telept); /* don't start sound on first frame */
1154
1155 return true;
1156 }
1157
1158 /*
1159 ====================
1160 =
1161 = G_DeathMatchSpawnPlayer
1162 =
1163 = Spawns a player at one of the random death match spots
1164 = called at level load and each death
1165 ====================
1166 */
1167
G_DeathMatchSpawnPlayer(int playernum)1168 void G_DeathMatchSpawnPlayer (int playernum)
1169 {
1170 int i,j;
1171 int selections;
1172
1173 selections = deathmatch_p - deathmatchstarts;
1174 if (selections < 4)
1175 I_Error ("Only %i deathmatch spots, 4 required", selections);
1176
1177 for (j=0 ; j<20 ; j++)
1178 {
1179 i = P_Random() % selections;
1180 if (G_CheckSpot (playernum, &deathmatchstarts[i]) )
1181 {
1182 deathmatchstarts[i].type = playernum+1;
1183 P_SpawnPlayer (&deathmatchstarts[i]);
1184 return;
1185 }
1186 }
1187
1188 /* no good spot, so the player will probably get stuck */
1189 P_SpawnPlayer (&playerstarts[playernum]);
1190 }
1191
1192 /*
1193 ====================
1194 =
1195 = G_DoReborn
1196 =
1197 ====================
1198 */
1199
G_DoReborn(int playernum)1200 void G_DoReborn (int playernum)
1201 {
1202 int i;
1203
1204 if (G_CheckDemoStatus ())
1205 return;
1206 if (!netgame)
1207 gameaction = ga_loadlevel; /* reload the level from scratch */
1208 else
1209 { /* respawn at the start */
1210 players[playernum].mo->player = NULL; /* dissasociate the corpse */
1211
1212 /* spawn at random spot if in death match */
1213 if (deathmatch)
1214 {
1215 G_DeathMatchSpawnPlayer (playernum);
1216 return;
1217 }
1218
1219 if (G_CheckSpot (playernum, &playerstarts[playernum]) )
1220 {
1221 P_SpawnPlayer (&playerstarts[playernum]);
1222 return;
1223 }
1224 /* try to spawn at one of the other players spots */
1225 for (i=0 ; i<MAXPLAYERS ; i++)
1226 if (G_CheckSpot (playernum, &playerstarts[i]) )
1227 {
1228 playerstarts[i].type = playernum+1; /* fake as other player */
1229 P_SpawnPlayer (&playerstarts[i]);
1230 playerstarts[i].type = i+1; /* restore */
1231 return;
1232 }
1233 /* he's going to be inside something. Too bad. */
1234 P_SpawnPlayer (&playerstarts[playernum]);
1235 }
1236 }
1237
1238
G_ScreenShot(void)1239 void G_ScreenShot (void)
1240 {
1241 gameaction = ga_screenshot;
1242 }
1243
1244
1245 /*
1246 ====================
1247 =
1248 = G_DoCompleted
1249 =
1250 ====================
1251 */
1252
1253 boolean secretexit;
1254
G_ExitLevel(void)1255 void G_ExitLevel (void)
1256 {
1257 secretexit = false;
1258 gameaction = ga_completed;
1259 }
1260
G_SecretExitLevel(void)1261 void G_SecretExitLevel (void)
1262 {
1263 secretexit = true;
1264 gameaction = ga_completed;
1265 }
1266
G_DoCompleted(void)1267 void G_DoCompleted(void)
1268 {
1269 int i;
1270 static int afterSecret[5] = { 7, 5, 5, 5, 4 };
1271
1272 gameaction = ga_nothing;
1273 if(G_CheckDemoStatus())
1274 {
1275 return;
1276 }
1277 for(i = 0; i < MAXPLAYERS; i++)
1278 {
1279 if(playeringame[i])
1280 {
1281 G_PlayerFinishLevel(i);
1282 }
1283 }
1284 prevmap = gamemap;
1285 if(secretexit == true)
1286 {
1287 gamemap = 9;
1288 }
1289 else if(gamemap == 9)
1290 { /* Finished secret level */
1291 gamemap = afterSecret[gameepisode-1];
1292 }
1293 else if(gamemap == 8)
1294 {
1295 gameaction = ga_victory;
1296 return;
1297 }
1298 else
1299 {
1300 gamemap++;
1301 }
1302 gamestate = GS_INTERMISSION;
1303 IN_Start();
1304 }
1305
1306
1307 /*
1308 //============================================================================
1309 //
1310 // G_WorldDone
1311 //
1312 //============================================================================
1313 */
G_WorldDone(void)1314 void G_WorldDone(void)
1315 {
1316 gameaction = ga_worlddone;
1317 }
1318
1319
1320 /*
1321 //============================================================================
1322 //
1323 // G_DoWorldDone
1324 //
1325 //============================================================================
1326 */
G_DoWorldDone(void)1327 void G_DoWorldDone(void)
1328 {
1329 gamestate = GS_LEVEL;
1330 G_DoLoadLevel();
1331 gameaction = ga_nothing;
1332 viewactive = true;
1333 }
1334
1335
1336 /*
1337 //---------------------------------------------------------------------------
1338 //
1339 // PROC G_LoadGame
1340 //
1341 // Can be called by the startup code or the menu task.
1342 //
1343 //---------------------------------------------------------------------------
1344 */
1345 char savename[256];
1346
G_LoadGame(char * name)1347 void G_LoadGame(char *name)
1348 {
1349 strcpy(savename, name);
1350 gameaction = ga_loadgame;
1351 }
1352
1353
1354 /*
1355 //---------------------------------------------------------------------------
1356 //
1357 // PROC G_DoLoadGame
1358 //
1359 // Called by G_Ticker based on gameaction.
1360 //
1361 //---------------------------------------------------------------------------
1362 */
1363 #define VERSIONSIZE 16
1364
G_DoLoadGame(void)1365 void G_DoLoadGame(void)
1366 {
1367 int length;
1368 int i;
1369 int a, b, c;
1370 char vcheck[VERSIONSIZE];
1371
1372 gameaction = ga_nothing;
1373
1374 length = M_ReadFile(savename, &savebuffer);
1375 save_p = savebuffer+SAVESTRINGSIZE;
1376 /* Skip the description field */
1377 memset(vcheck, 0, sizeof(vcheck));
1378 sprintf(vcheck, "version %i", VERSION);
1379 if (strcmp (save_p, vcheck))
1380 { /* Bad version */
1381 return;
1382 }
1383 save_p += VERSIONSIZE;
1384 gameskill = *save_p++;
1385 gameepisode = *save_p++;
1386 gamemap = *save_p++;
1387 for(i = 0; i < MAXPLAYERS; i++)
1388 {
1389 playeringame[i] = *save_p++;
1390 }
1391 /* Load a base level */
1392 G_InitNew(gameskill, gameepisode, gamemap);
1393
1394 /* Create leveltime */
1395 a = *save_p++;
1396 b = *save_p++;
1397 c = *save_p++;
1398 leveltime = (a<<16)+(b<<8)+c;
1399
1400 /* De-archive all the modifications */
1401 P_UnArchivePlayers();
1402 P_UnArchiveWorld();
1403 P_UnArchiveThinkers();
1404 P_UnArchiveSpecials();
1405 #ifdef GL_HERETIC
1406 GL_UnArchiveWallImpacts();
1407 for (i=0; i<numsectors; i++)
1408 fn_vGLUpdateSector(sectors+i);
1409 #endif
1410
1411 if(*save_p != SAVE_GAME_TERMINATOR)
1412 { /* Missing savegame termination marker */
1413 I_Error("Bad savegame");
1414 }
1415 Z_Free(savebuffer);
1416 }
1417
1418
1419 /*
1420 ====================
1421 =
1422 = G_InitNew
1423 =
1424 = Can be called by the startup code or the menu task
1425 = consoleplayer, displayplayer, playeringame[] should be set
1426 ====================
1427 */
1428
1429 skill_t d_skill;
1430 int d_episode;
1431 int d_map;
1432
G_DeferedInitNew(skill_t skill,int episode,int map)1433 void G_DeferedInitNew (skill_t skill, int episode, int map)
1434 {
1435 d_skill = skill;
1436 d_episode = episode;
1437 d_map = map;
1438 gameaction = ga_newgame;
1439 }
1440
G_DoNewGame(void)1441 void G_DoNewGame (void)
1442 {
1443 G_InitNew (d_skill, d_episode, d_map);
1444 gameaction = ga_nothing;
1445 }
1446
1447 extern int skytexture;
1448
G_InitNew(skill_t skill,int episode,int map)1449 void G_InitNew(skill_t skill, int episode, int map)
1450 {
1451 int i;
1452 int speed;
1453 static char *skyLumpNames[5] =
1454 {
1455 "SKY1", "SKY2", "SKY3", "SKY1", "SKY3"
1456 };
1457
1458 if(paused)
1459 {
1460 paused = false;
1461 S_ResumeSound();
1462 }
1463 if(skill < sk_baby)
1464 skill = sk_baby;
1465 if(skill > sk_nightmare)
1466 skill = sk_nightmare;
1467 if(episode < 1)
1468 episode = 1;
1469 /* Up to 9 episodes for testing */
1470 if(episode > 9)
1471 episode = 9;
1472 if(map < 1)
1473 map = 1;
1474 if(map > 9)
1475 map = 9;
1476 M_ClearRandom();
1477 if(respawnparm)
1478 {
1479 respawnmonsters = true;
1480 }
1481 else
1482 {
1483 respawnmonsters = false;
1484 }
1485 /* Set monster missile speeds */
1486 speed = skill == sk_nightmare;
1487 for(i = 0; MonsterMissileInfo[i].type != -1; i++)
1488 {
1489 mobjinfo[MonsterMissileInfo[i].type].speed
1490 = MonsterMissileInfo[i].speed[speed]<<FRACBITS;
1491 }
1492 /* Force players to be initialized upon first level load */
1493 for(i = 0; i < MAXPLAYERS; i++)
1494 {
1495 players[i].playerstate = PST_REBORN;
1496 players[i].didsecret = false;
1497 }
1498 /* Set up a bunch of globals */
1499 usergame = true; /* will be set false if a demo */
1500 paused = false;
1501 demorecording = false;
1502 demoplayback = false;
1503 viewactive = true;
1504 gameepisode = episode;
1505 gamemap = map;
1506 gameskill = skill;
1507 viewactive = true;
1508 BorderNeedRefresh = true;
1509
1510 /* Set the sky map */
1511 if(episode > 5)
1512 {
1513 skytexture = R_TextureNumForName("SKY1");
1514 }
1515 else
1516 {
1517 skytexture = R_TextureNumForName(skyLumpNames[episode-1]);
1518 }
1519
1520 /*
1521 * give one null ticcmd_t
1522 */
1523 #if 0
1524 gametic = 0;
1525 maketic = 1;
1526 for (i=0 ; i<MAXPLAYERS ; i++)
1527 nettics[i] = 1; /* one null event for this gametic */
1528 memset (localcmds,0,sizeof(localcmds));
1529 memset (netcmds,0,sizeof(netcmds));
1530 #endif
1531 G_DoLoadLevel();
1532 }
1533
1534
1535 /*
1536 ===============================================================================
1537
1538 DEMO RECORDING
1539
1540 ===============================================================================
1541 */
1542
1543 #define DEMOMARKER 0x80
1544
G_ReadDemoTiccmd(ticcmd_t * cmd)1545 void G_ReadDemoTiccmd (ticcmd_t *cmd)
1546 {
1547 if (*demo_p == DEMOMARKER)
1548 { /* end of demo data stream */
1549 G_CheckDemoStatus ();
1550 return;
1551 }
1552 cmd->forwardmove = ((signed char)*demo_p++);
1553 cmd->sidemove = ((signed char)*demo_p++);
1554 cmd->angleturn = ((unsigned char)*demo_p++)<<8;
1555 cmd->buttons = (unsigned char)*demo_p++;
1556 cmd->lookfly = (unsigned char)*demo_p++;
1557 cmd->arti = (unsigned char)*demo_p++;
1558 }
1559
G_WriteDemoTiccmd(ticcmd_t * cmd)1560 void G_WriteDemoTiccmd (ticcmd_t *cmd)
1561 {
1562 if (gamekeydown['q']) /* press q to end demo recording */
1563 G_CheckDemoStatus ();
1564 *demo_p++ = cmd->forwardmove;
1565 *demo_p++ = cmd->sidemove;
1566 *demo_p++ = cmd->angleturn>>8;
1567 *demo_p++ = cmd->buttons;
1568 *demo_p++ = cmd->lookfly;
1569 *demo_p++ = cmd->arti;
1570 demo_p -= 6;
1571 G_ReadDemoTiccmd (cmd); /* make SURE it is exactly the same */
1572 }
1573
1574
1575
1576 /*
1577 ===================
1578 =
1579 = G_RecordDemo
1580 =
1581 ===================
1582 */
1583
G_RecordDemo(skill_t skill,int numplayers,int episode,int map,char * name)1584 void G_RecordDemo (skill_t skill, int numplayers, int episode, int map, char *name)
1585 {
1586 int i;
1587
1588 G_InitNew (skill, episode, map);
1589 usergame = false;
1590 strcpy (demoname, name);
1591 strcat (demoname, ".lmp");
1592 demobuffer = demo_p = Z_Malloc (0x20000,PU_STATIC,NULL);
1593 *demo_p++ = skill;
1594 *demo_p++ = episode;
1595 *demo_p++ = map;
1596
1597 for (i=0 ; i<MAXPLAYERS ; i++)
1598 *demo_p++ = playeringame[i];
1599
1600 demorecording = true;
1601 }
1602
1603
1604 /*
1605 ===================
1606 =
1607 = G_PlayDemo
1608 =
1609 ===================
1610 */
1611
1612 char *defdemoname;
1613
G_DeferedPlayDemo(char * name)1614 void G_DeferedPlayDemo (char *name)
1615 {
1616 defdemoname = name;
1617 gameaction = ga_playdemo;
1618 }
1619
G_DoPlayDemo(void)1620 void G_DoPlayDemo (void)
1621 {
1622 skill_t skill;
1623 int i, episode, map;
1624
1625 gameaction = ga_nothing;
1626 demobuffer = demo_p = W_CacheLumpName (defdemoname, PU_STATIC);
1627 skill = *demo_p++;
1628 episode = *demo_p++;
1629 map = *demo_p++;
1630
1631 for (i=0 ; i<MAXPLAYERS ; i++)
1632 playeringame[i] = *demo_p++;
1633
1634 #ifndef GL_HERETIC
1635 precache = false; /* don't spend a lot of time in loadlevel */
1636 #endif
1637 G_InitNew (skill, episode, map);
1638 precache = true;
1639 usergame = false;
1640 demoplayback = true;
1641 }
1642
1643
1644 /*
1645 ===================
1646 =
1647 = G_TimeDemo
1648 =
1649 ===================
1650 */
1651
G_TimeDemo(char * name)1652 void G_TimeDemo (char *name)
1653 {
1654 skill_t skill;
1655 int episode, map;
1656
1657 demobuffer = demo_p = W_CacheLumpName (name, PU_STATIC);
1658 skill = *demo_p++;
1659 episode = *demo_p++;
1660 map = *demo_p++;
1661 G_InitNew (skill, episode, map);
1662 usergame = false;
1663 demoplayback = true;
1664 timingdemo = true;
1665 singletics = true;
1666 }
1667
1668
1669 /*
1670 ===================
1671 =
1672 = G_CheckDemoStatus
1673 =
1674 = Called after a death or level completion to allow demos to be cleaned up
1675 = Returns true if a new demo loop action will take place
1676 ===================
1677 */
1678
G_CheckDemoStatus(void)1679 boolean G_CheckDemoStatus (void)
1680 {
1681 int endtime;
1682
1683 if (timingdemo)
1684 {
1685 endtime = I_GetTime ();
1686 I_Error ("timed %i gametics in %i realtics",gametic
1687 , endtime-starttime);
1688 }
1689
1690 if (demoplayback)
1691 {
1692 if (singledemo)
1693 I_Quit ();
1694
1695 Z_ChangeTag (demobuffer, PU_CACHE);
1696 demoplayback = false;
1697 D_AdvanceDemo ();
1698 return true;
1699 }
1700
1701 if (demorecording)
1702 {
1703 *demo_p++ = DEMOMARKER;
1704 M_WriteFile (demoname, demobuffer, demo_p - demobuffer);
1705 Z_Free (demobuffer);
1706 demorecording = false;
1707 I_Error ("Demo %s recorded",demoname);
1708 }
1709
1710 return false;
1711 }
1712
1713 /**************************************************************************/
1714 /**************************************************************************/
1715
1716
1717 /*
1718 //==========================================================================
1719 //
1720 // G_SaveGame
1721 //
1722 // Called by the menu task. <description> is a 24 byte text string.
1723 //
1724 //==========================================================================
1725 */
G_SaveGame(int slot,char * description)1726 void G_SaveGame(int slot, char *description)
1727 {
1728 savegameslot = slot;
1729 strcpy(savedescription, description);
1730 sendsave = true;
1731 }
1732
1733
1734 /*
1735 //==========================================================================
1736 //
1737 // G_DoSaveGame
1738 //
1739 // Called by G_Ticker based on gameaction.
1740 //
1741 //==========================================================================
1742 */
G_DoSaveGame(void)1743 void G_DoSaveGame(void)
1744 {
1745 int i;
1746 char name[100];
1747 char verString[VERSIONSIZE];
1748 char *description;
1749
1750 if(cdrom)
1751 {
1752 sprintf(name, SAVEGAMENAMECD"%d.hsg", savegameslot);
1753 }
1754 else
1755 {
1756 sprintf(name, "%s"SAVEGAMENAME"%d.hsg", homedir, savegameslot);
1757 }
1758 description = savedescription;
1759
1760 SV_Open(name);
1761 SV_Write(description, SAVESTRINGSIZE);
1762 memset(verString, 0, sizeof(verString));
1763 sprintf(verString, "version %i", VERSION);
1764 SV_Write(verString, VERSIONSIZE);
1765 SV_WriteByte(gameskill);
1766 SV_WriteByte(gameepisode);
1767 SV_WriteByte(gamemap);
1768 for(i = 0; i < MAXPLAYERS; i++)
1769 {
1770 SV_WriteByte(playeringame[i]);
1771 }
1772 SV_WriteByte(leveltime>>16);
1773 SV_WriteByte(leveltime>>8);
1774 SV_WriteByte(leveltime);
1775 P_ArchivePlayers();
1776 P_ArchiveWorld();
1777 P_ArchiveThinkers();
1778 P_ArchiveSpecials();
1779 #ifdef GL_HERETIC
1780 GL_ArchiveWallImpacts();
1781 #endif
1782 SV_Close(name);
1783
1784
1785 gameaction = ga_nothing;
1786 savedescription[0] = 0;
1787 P_SetMessage(&players[consoleplayer], TXT_GAMESAVED, true);
1788 }
1789
1790
1791 /*
1792 //==========================================================================
1793 //
1794 // SV_Open
1795 //
1796 //==========================================================================
1797 */
SV_Open(char * fileName)1798 void SV_Open(char *fileName)
1799 {
1800 MallocFailureOk = true;
1801 save_p = savebuffer = Z_Malloc(SAVEGAMESIZE, PU_STATIC, NULL);
1802 MallocFailureOk = false;
1803 if(savebuffer == NULL)
1804 { /* Not enough memory - use file save method */
1805 SaveGameType = SVG_FILE;
1806 SaveGameFP = fopen(fileName, "wb");
1807 }
1808 else
1809 {
1810 SaveGameType = SVG_RAM;
1811 }
1812 }
1813
1814
1815 /*
1816 //==========================================================================
1817 //
1818 // SV_Close
1819 //
1820 //==========================================================================
1821 */
SV_Close(char * fileName)1822 void SV_Close(char *fileName)
1823 {
1824 int length;
1825
1826 SV_WriteByte(SAVE_GAME_TERMINATOR);
1827 if(SaveGameType == SVG_RAM)
1828 {
1829 length = save_p-savebuffer;
1830 if(length > SAVEGAMESIZE)
1831 {
1832 I_Error("Savegame buffer overrun");
1833 }
1834 M_WriteFile(fileName, savebuffer, length);
1835 Z_Free(savebuffer);
1836 }
1837 else
1838 { /* SVG_FILE */
1839 fclose(SaveGameFP);
1840 }
1841 }
1842
1843
1844 /*
1845 //==========================================================================
1846 //
1847 // SV_Write
1848 //
1849 //==========================================================================
1850 */
SV_Write(void * buffer,size_t size)1851 void SV_Write(void *buffer, size_t size)
1852 {
1853 if(SaveGameType == SVG_RAM)
1854 {
1855 memcpy(save_p, buffer, size);
1856 save_p += size;
1857 }
1858 else
1859 { /* SVG_FILE */
1860 fwrite(buffer, size, 1, SaveGameFP);
1861 }
1862 }
1863
SV_WriteByte(byte val)1864 void SV_WriteByte(byte val)
1865 {
1866 SV_Write(&val, sizeof(byte));
1867 }
1868
SV_WriteWord(unsigned short val)1869 void SV_WriteWord(unsigned short val)
1870 {
1871 SV_Write(&val, sizeof(unsigned short));
1872 }
1873
SV_WriteLong(unsigned int val)1874 void SV_WriteLong(unsigned int val)
1875 {
1876 SV_Write(&val, sizeof(int));
1877 }
1878
1879
1880
1881