1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
4 // Copyright(C) 1993-1996 Id Software, Inc.
5 // Copyright(C) 2005 Simon Howard
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.
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.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
24 //-----------------------------------------------------------------------------
59 // Needs access to LFB.
72 // SKY handling - still the wrong place.
81 #define SAVEGAMESIZE 0x2c000
85 boolean
G_CheckDemoStatus (void);
86 void G_ReadDemoTiccmd (ticcmd_t
* cmd
);
87 void G_WriteDemoTiccmd (ticcmd_t
* cmd
);
88 void G_PlayerReborn (int player
);
89 void G_InitNew (skill_t skill
, int episode
, int map
);
91 void G_DoReborn (int playernum
);
93 void G_DoLoadLevel (void);
94 void G_DoNewGame (void);
95 void G_DoLoadGame (void);
96 void G_DoPlayDemo (void);
97 void G_DoCompleted (void);
98 void G_DoVictory (void);
99 void G_DoWorldDone (void);
100 void G_DoSaveGame (void);
102 // Gamestate the last time G_Ticker was called.
104 gamestate_t oldgamestate
;
106 gameaction_t gameaction
;
107 gamestate_t gamestate
;
109 boolean respawnmonsters
;
113 // If non-zero, exit the level after this number of minutes.
118 boolean sendpause
; // send a pause event next tic
119 boolean sendsave
; // send a save event next tic
120 boolean usergame
; // ok to save / end game
122 boolean timingdemo
; // if true, exit with report on completion
123 boolean nodrawers
; // for comparative timing purposes
124 boolean noblit
; // for comparative timing purposes
125 int starttime
; // for comparative timing purposes
129 boolean deathmatch
; // only if started as net death
130 boolean netgame
; // only true if packets are broadcast
131 boolean playeringame
[MAXPLAYERS
];
132 player_t players
[MAXPLAYERS
];
134 boolean turbodetected
[MAXPLAYERS
];
136 int consoleplayer
; // player taking events and displaying
137 int displayplayer
; // view being displayed
139 int levelstarttic
; // gametic at level start
140 int totalkills
, totalitems
, totalsecret
; // for intermission
143 boolean demorecording
;
144 boolean longtics
; // cph's doom 1.91 longtics hack
145 boolean lowres_turn
; // low resolution turning for longtics
146 boolean demoplayback
;
151 boolean singledemo
; // quit after playing a demo from cmdline
153 boolean precache
= true; // if true, load all graphics at start
155 boolean testcontrols
= false; // Invoked by setup to test controls
157 wbstartstruct_t wminfo
; // parms for world map / intermission
159 byte consistancy
[MAXPLAYERS
][BACKUPTICS
];
165 int key_right
= KEY_RIGHTARROW
;
166 int key_left
= KEY_LEFTARROW
;
168 int key_up
= KEY_UPARROW
;
169 int key_down
= KEY_DOWNARROW
;
170 int key_strafeleft
= ',';
171 int key_straferight
= '.';
172 int key_fire
= KEY_RCTRL
;
174 int key_strafe
= KEY_RALT
;
175 int key_speed
= KEY_RSHIFT
;
177 int key_weapon1
= '1';
178 int key_weapon2
= '2';
179 int key_weapon3
= '3';
180 int key_weapon4
= '4';
181 int key_weapon5
= '5';
182 int key_weapon6
= '6';
183 int key_weapon7
= '7';
184 int key_weapon8
= '8';
185 int key_prevweapon
= 0;
186 int key_nextweapon
= 0;
188 int key_pause
= KEY_PAUSE
;
189 int key_demo_quit
= 'q';
190 int key_spy
= KEY_F12
;
193 int mousebstrafe
= 1;
194 int mousebforward
= 2;
196 int mousebstrafeleft
= -1;
197 int mousebstraferight
= -1;
198 int mousebbackward
= -1;
201 int mousebprevweapon
= -1;
202 int mousebnextweapon
= -1;
204 // Control whether if a mouse button is double clicked, it acts like
205 // "use" has been pressed
213 int joybstrafeleft
= -1;
214 int joybstraferight
= -1;
216 int joybprevweapon
= -1;
217 int joybnextweapon
= -1;
219 // fraggle: Disallow mouse and joystick movement to cause forward/backward
220 // motion. Specified with the '-novert' command line parameter.
221 // This is an int to allow saving to config file
227 #define MAXPLMOVE (forwardmove[1])
229 #define TURBOTHRESHOLD 0x32
231 fixed_t forwardmove
[2] = {0x19, 0x32};
232 fixed_t sidemove
[2] = {0x18, 0x28};
233 fixed_t angleturn
[3] = {640, 1280, 320}; // + slow turn
235 static int *weapon_keys
[] = {
246 // Set to -1 or +1 to switch to the previous or next weapon.
248 static int next_weapon
= 0;
250 // Used for prev/next weapon keys.
255 weapontype_t weapon_num
;
256 } weapon_order_table
[] = {
257 { wp_fist
, wp_fist
},
258 { wp_chainsaw
, wp_fist
},
259 { wp_pistol
, wp_pistol
},
260 { wp_shotgun
, wp_shotgun
},
261 { wp_supershotgun
, wp_shotgun
},
262 { wp_chaingun
, wp_chaingun
},
263 { wp_missile
, wp_missile
},
264 { wp_plasma
, wp_plasma
},
268 #define SLOWTURNTICS 6
271 #define MAX_JOY_BUTTONS 20
272 #define MAX_MOUSE_BUTTONS 3
274 static boolean gamekeydown
[NUMKEYS
];
275 static int turnheld
; // for accelerative turning
277 static boolean mousearray
[MAX_MOUSE_BUTTONS
+ 1];
278 static boolean
*mousebuttons
= &mousearray
[1]; // allow [-1]
280 // mouse values are used once
284 static int dclicktime
;
285 static boolean dclickstate
;
287 static int dclicktime2
;
288 static boolean dclickstate2
;
291 // joystick values are repeated
294 static boolean joyarray
[MAX_JOY_BUTTONS
+ 1];
295 static boolean
*joybuttons
= &joyarray
[1]; // allow [-1]
297 static int savegameslot
;
298 static char savedescription
[32];
300 static int testcontrols_mousespeed
;
302 #define BODYQUESIZE 32
304 mobj_t
* bodyque
[BODYQUESIZE
];
307 int vanilla_savegame_limit
= 1;
308 int vanilla_demo_limit
= 1;
311 #define MOUSE_SPEED_BOX_WIDTH 16
312 #define COLOR_RED 0xb0
313 #define COLOR_BLACK 0x00
314 #define COLOR_WHITE 0x04
315 #define COLOR_YELLOW 0xe7
317 void G_DrawMouseSpeedBox(void)
329 // If the mouse is turned off or acceleration is turned off, don't
330 // draw the box at all.
332 if (!usemouse
|| fabs(mouse_acceleration
- 1) < 0.01)
337 // Calculate box position
339 box_x
= SCREENWIDTH
- MOUSE_SPEED_BOX_WIDTH
* 8;
340 box_y
= SCREENHEIGHT
- 9;
346 for (i
=0; i
<MOUSE_SPEED_BOX_WIDTH
; ++i
)
350 lumpname
= "M_LSLEFT";
352 else if (i
== MOUSE_SPEED_BOX_WIDTH
- 1)
354 lumpname
= "M_LSRGHT";
358 lumpname
= "M_LSCNTR";
361 V_DrawPatchDirect(x
, box_y
, 0, W_CacheLumpName(DEH_String(lumpname
),
366 // Calculate the position of the red line. This is 1/3 of the way
369 redline_x
= (MOUSE_SPEED_BOX_WIDTH
/ 3) * 8;
371 // Undo acceleration and get back the original mouse speed
373 if (testcontrols_mousespeed
< mouse_threshold
)
375 original_speed
= testcontrols_mousespeed
;
379 original_speed
= testcontrols_mousespeed
- mouse_threshold
;
380 original_speed
= (int) (original_speed
/ mouse_acceleration
);
381 original_speed
+= mouse_threshold
;
384 // Calculate line length
386 linelen
= (original_speed
* redline_x
) / mouse_threshold
;
388 // Draw horizontal "thermometer"
390 for (x
=0; x
<(MOUSE_SPEED_BOX_WIDTH
- 1) * 8; ++x
)
400 color
= COLOR_YELLOW
;
408 screens
[0][(box_y
- 4) * SCREENWIDTH
+ box_x
+ x
+ 1] = color
;
413 for (y
=box_y
- 8; y
<box_y
; ++y
)
415 screens
[0][y
* SCREENWIDTH
+ box_x
+ redline_x
] = COLOR_RED
;
419 int G_CmdChecksum (ticcmd_t
* cmd
)
424 for (i
=0 ; i
< sizeof(*cmd
)/4 - 1 ; i
++)
425 sum
+= ((int *)cmd
)[i
];
430 static boolean
WeaponSelectable(weapontype_t weapon
)
432 // Can't select a weapon if we don't own it.
434 if (!players
[consoleplayer
].weaponowned
[weapon
])
439 // Can't select the fist if we have the chainsaw, unless
440 // we also have the berserk pack.
442 if (weapon
== wp_fist
443 && players
[consoleplayer
].weaponowned
[wp_chainsaw
]
444 && !players
[consoleplayer
].powers
[pw_strength
])
452 static int G_NextWeapon(int direction
)
457 // Find index in the table.
459 if (players
[consoleplayer
].pendingweapon
== wp_nochange
)
461 weapon
= players
[consoleplayer
].readyweapon
;
465 weapon
= players
[consoleplayer
].pendingweapon
;
468 for (i
=0; i
<arrlen(weapon_order_table
); ++i
)
470 if (weapon_order_table
[i
].weapon
== weapon
)
481 i
= (i
+ arrlen(weapon_order_table
)) % arrlen(weapon_order_table
);
482 } while (!WeaponSelectable(weapon_order_table
[i
].weapon
));
484 return weapon_order_table
[i
].weapon_num
;
489 // Builds a ticcmd from all of the available inputs
490 // or reads it from the demo buffer.
491 // If recording a demo, write it out
493 void G_BuildTiccmd (ticcmd_t
* cmd
)
503 memset(cmd
, 0, sizeof(ticcmd_t
));
506 consistancy
[consoleplayer
][maketic
%BACKUPTICS
];
508 strafe
= gamekeydown
[key_strafe
] || mousebuttons
[mousebstrafe
]
509 || joybuttons
[joybstrafe
];
511 // fraggle: support the old "joyb_speed = 31" hack which
512 // allowed an autorun effect
514 speed
= key_speed
>= NUMKEYS
515 || joybspeed
>= MAX_JOY_BUTTONS
516 || gamekeydown
[key_speed
]
517 || joybuttons
[joybspeed
];
521 // use two stage accelerative turning
522 // on the keyboard and joystick
525 || gamekeydown
[key_right
]
526 || gamekeydown
[key_left
])
531 if (turnheld
< SLOWTURNTICS
)
532 tspeed
= 2; // slow turn
536 // let movement keys cancel each other out
539 if (gamekeydown
[key_right
])
541 // fprintf(stderr, "strafe right\n");
542 side
+= sidemove
[speed
];
544 if (gamekeydown
[key_left
])
546 // fprintf(stderr, "strafe left\n");
547 side
-= sidemove
[speed
];
550 side
+= sidemove
[speed
];
552 side
-= sidemove
[speed
];
557 if (gamekeydown
[key_right
])
558 cmd
->angleturn
-= angleturn
[tspeed
];
559 if (gamekeydown
[key_left
])
560 cmd
->angleturn
+= angleturn
[tspeed
];
562 cmd
->angleturn
-= angleturn
[tspeed
];
564 cmd
->angleturn
+= angleturn
[tspeed
];
567 if (gamekeydown
[key_up
])
569 // fprintf(stderr, "up\n");
570 forward
+= forwardmove
[speed
];
572 if (gamekeydown
[key_down
])
574 // fprintf(stderr, "down\n");
575 forward
-= forwardmove
[speed
];
579 forward
+= forwardmove
[speed
];
581 forward
-= forwardmove
[speed
];
583 if (gamekeydown
[key_strafeleft
]
584 || joybuttons
[joybstrafeleft
]
585 || mousebuttons
[mousebstrafeleft
])
587 side
-= sidemove
[speed
];
590 if (gamekeydown
[key_straferight
]
591 || joybuttons
[joybstraferight
]
592 || mousebuttons
[mousebstraferight
])
594 side
+= sidemove
[speed
];
598 cmd
->chatchar
= HU_dequeueChatChar();
600 if (gamekeydown
[key_fire
] || mousebuttons
[mousebfire
]
601 || joybuttons
[joybfire
])
602 cmd
->buttons
|= BT_ATTACK
;
604 if (gamekeydown
[key_use
]
605 || joybuttons
[joybuse
]
606 || mousebuttons
[mousebuse
])
608 cmd
->buttons
|= BT_USE
;
609 // clear double clicks if hit use button
613 // If the previous or next weapon button is pressed, the
614 // next_weapon variable is set to change weapons when
615 // we generate a ticcmd. Choose a new weapon.
617 if (next_weapon
!= 0)
619 i
= G_NextWeapon(next_weapon
);
620 cmd
->buttons
|= BT_CHANGE
;
621 cmd
->buttons
|= i
<< BT_WEAPONSHIFT
;
626 // Check weapon keys.
628 for (i
=0; i
<arrlen(weapon_keys
); ++i
)
630 int key
= *weapon_keys
[i
];
632 if (gamekeydown
[key
])
634 cmd
->buttons
|= BT_CHANGE
;
635 cmd
->buttons
|= i
<<BT_WEAPONSHIFT
;
642 if (mousebuttons
[mousebforward
])
644 forward
+= forwardmove
[speed
];
646 if (mousebuttons
[mousebbackward
])
648 forward
-= forwardmove
[speed
];
653 // forward double click
654 if (mousebuttons
[mousebforward
] != dclickstate
&& dclicktime
> 1 )
656 dclickstate
= mousebuttons
[mousebforward
];
661 cmd
->buttons
|= BT_USE
;
669 dclicktime
+= ticdup
;
677 // strafe double click
679 mousebuttons
[mousebstrafe
]
680 || joybuttons
[joybstrafe
];
681 if (bstrafe
!= dclickstate2
&& dclicktime2
> 1 )
683 dclickstate2
= bstrafe
;
688 cmd
->buttons
|= BT_USE
;
696 dclicktime2
+= ticdup
;
697 if (dclicktime2
> 20)
705 // fraggle: allow disabling mouse y movement
715 cmd
->angleturn
-= mousex
*0x8;
719 // No movement in the previous frame
721 testcontrols_mousespeed
= 0;
726 if (forward
> MAXPLMOVE
)
728 else if (forward
< -MAXPLMOVE
)
729 forward
= -MAXPLMOVE
;
730 if (side
> MAXPLMOVE
)
732 else if (side
< -MAXPLMOVE
)
735 cmd
->forwardmove
+= forward
;
736 cmd
->sidemove
+= side
;
742 cmd
->buttons
= BT_SPECIAL
| BTS_PAUSE
;
748 cmd
->buttons
= BT_SPECIAL
| BTS_SAVEGAME
| (savegameslot
<<BTS_SAVESHIFT
);
755 static signed short carry
= 0;
756 signed short desired_angleturn
;
758 desired_angleturn
= cmd
->angleturn
+ carry
;
760 // round angleturn to the nearest 256 unit boundary
761 // for recording demos with single byte values for turn
763 cmd
->angleturn
= (desired_angleturn
+ 128) & 0xff00;
765 // Carry forward the error from the reduced resolution to the
766 // next tic, so that successive small movements can accumulate.
768 carry
= desired_angleturn
- cmd
->angleturn
;
776 extern gamestate_t wipegamestate
;
778 void G_DoLoadLevel (void)
783 // First thing, we have a dummy sky texture name,
784 // a flat. The data is in the WAD only because
785 // we look for an actual index, instead of simply
788 skyflatnum
= R_FlatNumForName(DEH_String(SKYFLATNAME
));
790 levelstarttic
= gametic
; // for time calculation
792 if (wipegamestate
== GS_LEVEL
)
793 wipegamestate
= -1; // force a wipe
795 gamestate
= GS_LEVEL
;
797 for (i
=0 ; i
<MAXPLAYERS
; i
++)
799 turbodetected
[i
] = false;
800 if (playeringame
[i
] && players
[i
].playerstate
== PST_DEAD
)
801 players
[i
].playerstate
= PST_REBORN
;
802 memset (players
[i
].frags
,0,sizeof(players
[i
].frags
));
805 P_SetupLevel (gameepisode
, gamemap
, 0, gameskill
);
806 displayplayer
= consoleplayer
; // view the guy you are playing
807 gameaction
= ga_nothing
;
810 // clear cmd building stuff
812 memset (gamekeydown
, 0, sizeof(gamekeydown
));
813 joyxmove
= joyymove
= 0;
815 sendpause
= sendsave
= paused
= false;
816 memset (mousebuttons
, 0, sizeof(mousebuttons
));
817 memset (joybuttons
, 0, sizeof(joybuttons
));
821 players
[consoleplayer
].message
= "Press escape to quit.";
825 static void SetJoyButtons(unsigned int buttons_mask
)
829 for (i
=0; i
<MAX_JOY_BUTTONS
; ++i
)
831 int button_on
= (buttons_mask
& (1 << i
)) != 0;
833 // Detect button press:
835 if (!joybuttons
[i
] && button_on
)
839 if (i
== joybprevweapon
)
843 else if (i
== joybnextweapon
)
849 joybuttons
[i
] = button_on
;
853 static void SetMouseButtons(unsigned int buttons_mask
)
857 for (i
=0; i
<MAX_MOUSE_BUTTONS
; ++i
)
859 unsigned int button_on
= (buttons_mask
& (1 << i
)) != 0;
861 // Detect button press:
863 if (!mousebuttons
[i
] && button_on
)
865 if (i
== mousebprevweapon
)
869 else if (i
== mousebnextweapon
)
875 mousebuttons
[i
] = button_on
;
881 // Get info needed to make ticcmd_ts for the players.
883 boolean
G_Responder (event_t
* ev
)
885 // allow spy mode changes even during the demo
886 if (gamestate
== GS_LEVEL
&& ev
->type
== ev_keydown
887 && ev
->data1
== key_spy
&& (singledemo
|| !deathmatch
) )
893 if (displayplayer
== MAXPLAYERS
)
895 } while (!playeringame
[displayplayer
] && displayplayer
!= consoleplayer
);
899 // any other key pops up menu if in demos
900 if (gameaction
== ga_nothing
&& !singledemo
&&
901 (demoplayback
|| gamestate
== GS_DEMOSCREEN
)
904 if (ev
->type
== ev_keydown
||
905 (ev
->type
== ev_mouse
&& ev
->data1
) ||
906 (ev
->type
== ev_joystick
&& ev
->data1
) )
908 M_StartControlPanel ();
914 if (gamestate
== GS_LEVEL
)
917 if (devparm
&& ev
->type
== ev_keydown
&& ev
->data1
== ';')
919 G_DeathMatchSpawnPlayer (0);
923 if (HU_Responder (ev
))
924 return true; // chat ate the event
925 if (ST_Responder (ev
))
926 return true; // status window ate it
927 if (AM_Responder (ev
))
928 return true; // automap ate it
931 if (gamestate
== GS_FINALE
)
933 if (F_Responder (ev
))
934 return true; // finale ate the event
937 if (testcontrols
&& ev
->type
== ev_mouse
)
939 // If we are invoked by setup to test the controls, save the
940 // mouse speed so that we can display it on-screen.
941 // Perform a low pass filter on this so that the thermometer
942 // appears to move smoothly.
944 testcontrols_mousespeed
= abs(ev
->data2
);
947 // If the next/previous weapon keys are pressed, set the next_weapon
948 // variable to change weapons when the next ticcmd is generated.
950 if (ev
->type
== ev_keydown
&& ev
->data1
== key_prevweapon
)
954 else if (ev
->type
== ev_keydown
&& ev
->data1
== key_nextweapon
)
962 if (ev
->data1
== key_pause
)
966 else if (ev
->data1
<NUMKEYS
)
968 gamekeydown
[ev
->data1
] = true;
971 return true; // eat key down events
974 if (ev
->data1
<NUMKEYS
)
975 gamekeydown
[ev
->data1
] = false;
976 return false; // always let key up events filter down
979 SetMouseButtons(ev
->data1
);
980 mousex
= ev
->data2
*(mouseSensitivity
+5)/10;
981 mousey
= ev
->data3
*(mouseSensitivity
+5)/10;
982 return true; // eat events
985 SetJoyButtons(ev
->data1
);
986 joyxmove
= ev
->data2
;
987 joyymove
= ev
->data3
;
988 return true; // eat events
1001 // Make ticcmd_ts for the players.
1003 void G_Ticker (void)
1009 // do player reborns if needed
1010 for (i
=0 ; i
<MAXPLAYERS
; i
++)
1011 if (playeringame
[i
] && players
[i
].playerstate
== PST_REBORN
)
1014 // do things to change the game state
1015 while (gameaction
!= ga_nothing
)
1045 players
[consoleplayer
].message
= DEH_String("screen shot");
1046 gameaction
= ga_nothing
;
1053 // get commands, check consistancy,
1054 // and build new consistancy check
1055 buf
= (gametic
/ticdup
)%BACKUPTICS
;
1057 for (i
=0 ; i
<MAXPLAYERS
; i
++)
1059 if (playeringame
[i
])
1061 cmd
= &players
[i
].cmd
;
1063 memcpy (cmd
, &netcmds
[i
][buf
], sizeof(ticcmd_t
));
1066 G_ReadDemoTiccmd (cmd
);
1068 G_WriteDemoTiccmd (cmd
);
1070 // check for turbo cheats
1072 // check ~ 4 seconds whether to display the turbo message.
1073 // store if the turbo threshold was exceeded in any tics
1074 // over the past 4 seconds. offset the checking period
1075 // for each player so messages are not displayed at the
1078 if (cmd
->forwardmove
> TURBOTHRESHOLD
)
1080 turbodetected
[i
] = true;
1083 if ((gametic
& 31) == 0
1084 && ((gametic
>> 5) % MAXPLAYERS
) == i
1085 && turbodetected
[i
])
1087 static char turbomessage
[80];
1088 extern char *player_names
[4];
1089 sprintf (turbomessage
, "%s is turbo!",player_names
[i
]);
1090 players
[consoleplayer
].message
= turbomessage
;
1091 turbodetected
[i
] = false;
1094 if (netgame
&& !netdemo
&& !(gametic
%ticdup
) )
1096 if (gametic
> BACKUPTICS
1097 && consistancy
[i
][buf
] != cmd
->consistancy
)
1099 I_Error ("consistency failure (%i should be %i)",
1100 cmd
->consistancy
, consistancy
[i
][buf
]);
1103 consistancy
[i
][buf
] = players
[i
].mo
->x
;
1105 consistancy
[i
][buf
] = rndindex
;
1110 // check for special buttons
1111 for (i
=0 ; i
<MAXPLAYERS
; i
++)
1113 if (playeringame
[i
])
1115 if (players
[i
].cmd
.buttons
& BT_SPECIAL
)
1117 switch (players
[i
].cmd
.buttons
& BT_SPECIALMASK
)
1128 if (!savedescription
[0])
1129 strcpy (savedescription
, "NET GAME");
1131 (players
[i
].cmd
.buttons
& BTS_SAVEMASK
)>>BTS_SAVESHIFT
;
1132 gameaction
= ga_savegame
;
1139 // Have we just finished displaying an intermission screen?
1141 if (oldgamestate
== GS_INTERMISSION
&& gamestate
!= GS_INTERMISSION
)
1146 oldgamestate
= gamestate
;
1158 case GS_INTERMISSION
:
1174 // PLAYER STRUCTURE FUNCTIONS
1175 // also see P_SpawnPlayer in P_Things
1180 // Called at the start.
1181 // Called by the game initialization functions.
1183 void G_InitPlayer (int player
)
1187 // set up the saved info
1188 p
= &players
[player
];
1190 // clear everything else to defaults
1191 G_PlayerReborn (player
);
1198 // G_PlayerFinishLevel
1199 // Can when a player completes a level.
1201 void G_PlayerFinishLevel (int player
)
1205 p
= &players
[player
];
1207 memset (p
->powers
, 0, sizeof (p
->powers
));
1208 memset (p
->cards
, 0, sizeof (p
->cards
));
1209 p
->mo
->flags
&= ~MF_SHADOW
; // cancel invisibility
1210 p
->extralight
= 0; // cancel gun flashes
1211 p
->fixedcolormap
= 0; // cancel ir gogles
1212 p
->damagecount
= 0; // no palette changes
1219 // Called after a player dies
1220 // almost everything is cleared and initialized
1222 void G_PlayerReborn (int player
)
1226 int frags
[MAXPLAYERS
];
1231 memcpy (frags
,players
[player
].frags
,sizeof(frags
));
1232 killcount
= players
[player
].killcount
;
1233 itemcount
= players
[player
].itemcount
;
1234 secretcount
= players
[player
].secretcount
;
1236 p
= &players
[player
];
1237 memset (p
, 0, sizeof(*p
));
1239 memcpy (players
[player
].frags
, frags
, sizeof(players
[player
].frags
));
1240 players
[player
].killcount
= killcount
;
1241 players
[player
].itemcount
= itemcount
;
1242 players
[player
].secretcount
= secretcount
;
1244 p
->usedown
= p
->attackdown
= true; // don't do anything immediately
1245 p
->playerstate
= PST_LIVE
;
1246 p
->health
= deh_initial_health
; // Use dehacked value
1247 p
->readyweapon
= p
->pendingweapon
= wp_pistol
;
1248 p
->weaponowned
[wp_fist
] = true;
1249 p
->weaponowned
[wp_pistol
] = true;
1250 p
->ammo
[am_clip
] = deh_initial_bullets
;
1252 for (i
=0 ; i
<NUMAMMO
; i
++)
1253 p
->maxammo
[i
] = maxammo
[i
];
1259 // Returns false if the player cannot be respawned
1260 // at the given mapthing_t spot
1261 // because something is occupying it
1263 void P_SpawnPlayer (mapthing_t
* mthing
);
1268 mapthing_t
* mthing
)
1277 if (!players
[playernum
].mo
)
1279 // first spawn of level, before corpses
1280 for (i
=0 ; i
<playernum
; i
++)
1281 if (players
[i
].mo
->x
== mthing
->x
<< FRACBITS
1282 && players
[i
].mo
->y
== mthing
->y
<< FRACBITS
)
1287 x
= mthing
->x
<< FRACBITS
;
1288 y
= mthing
->y
<< FRACBITS
;
1290 if (!P_CheckPosition (players
[playernum
].mo
, x
, y
) )
1293 // flush an old corpse if needed
1294 if (bodyqueslot
>= BODYQUESIZE
)
1295 P_RemoveMobj (bodyque
[bodyqueslot
%BODYQUESIZE
]);
1296 bodyque
[bodyqueslot
%BODYQUESIZE
] = players
[playernum
].mo
;
1299 // spawn a teleport fog
1300 ss
= R_PointInSubsector (x
,y
);
1301 an
= ( ANG45
* (((unsigned int) mthing
->angle
)/45) ) >> ANGLETOFINESHIFT
;
1303 mo
= P_SpawnMobj (x
+20*finecosine
[an
], y
+20*finesine
[an
]
1304 , ss
->sector
->floorheight
1307 if (players
[consoleplayer
].viewz
!= 1)
1308 S_StartSound (mo
, sfx_telept
); // don't start sound on first frame
1315 // G_DeathMatchSpawnPlayer
1316 // Spawns a player at one of the random death match spots
1317 // called at level load and each death
1319 void G_DeathMatchSpawnPlayer (int playernum
)
1324 selections
= deathmatch_p
- deathmatchstarts
;
1326 I_Error ("Only %i deathmatch spots, 4 required", selections
);
1328 for (j
=0 ; j
<20 ; j
++)
1330 i
= P_Random() % selections
;
1331 if (G_CheckSpot (playernum
, &deathmatchstarts
[i
]) )
1333 deathmatchstarts
[i
].type
= playernum
+1;
1334 P_SpawnPlayer (&deathmatchstarts
[i
]);
1339 // no good spot, so the player will probably get stuck
1340 P_SpawnPlayer (&playerstarts
[playernum
]);
1346 void G_DoReborn (int playernum
)
1352 // reload the level from scratch
1353 gameaction
= ga_loadlevel
;
1357 // respawn at the start
1359 // first dissasociate the corpse
1360 players
[playernum
].mo
->player
= NULL
;
1362 // spawn at random spot if in death match
1365 G_DeathMatchSpawnPlayer (playernum
);
1369 if (G_CheckSpot (playernum
, &playerstarts
[playernum
]) )
1371 P_SpawnPlayer (&playerstarts
[playernum
]);
1375 // try to spawn at one of the other players spots
1376 for (i
=0 ; i
<MAXPLAYERS
; i
++)
1378 if (G_CheckSpot (playernum
, &playerstarts
[i
]) )
1380 playerstarts
[i
].type
= playernum
+1; // fake as other player
1381 P_SpawnPlayer (&playerstarts
[i
]);
1382 playerstarts
[i
].type
= i
+1; // restore
1385 // he's going to be inside something. Too bad.
1387 P_SpawnPlayer (&playerstarts
[playernum
]);
1392 void G_ScreenShot (void)
1394 gameaction
= ga_screenshot
;
1403 {0,30,75,120,90,165,180,180,30,165},
1404 {0,90,90,90,120,90,360,240,30,170},
1405 {0,90,45,90,150,90,90,165,30,135}
1408 // DOOM II Par Times
1411 30,90,120,120,90,150,120,120,270,90, // 1-10
1412 210,150,150,150,210,150,420,150,210,150, // 11-20
1413 240,150,180,150,150,300,330,420,300,180, // 21-30
1422 extern char* pagename
;
1424 void G_ExitLevel (void)
1427 gameaction
= ga_completed
;
1430 // Here's for the german edition.
1431 void G_SecretExitLevel (void)
1433 // IF NO WOLF3D LEVELS, NO SECRET EXIT!
1434 if ( (gamemode
== commercial
)
1435 && (W_CheckNumForName("map31")<0))
1439 gameaction
= ga_completed
;
1442 void G_DoCompleted (void)
1446 gameaction
= ga_nothing
;
1448 for (i
=0 ; i
<MAXPLAYERS
; i
++)
1449 if (playeringame
[i
])
1450 G_PlayerFinishLevel (i
); // take away cards and stuff
1455 if (gamemode
!= commercial
)
1457 // Chex Quest ends after 5 levels, rather than 8.
1459 if (gameversion
== exe_chex
)
1463 gameaction
= ga_victory
;
1472 gameaction
= ga_victory
;
1475 for (i
=0 ; i
<MAXPLAYERS
; i
++)
1476 players
[i
].didsecret
= true;
1484 && (gamemode
!= commercial
) )
1487 gameaction
= ga_victory
;
1492 && (gamemode
!= commercial
) )
1494 // exit secret level
1495 for (i
=0 ; i
<MAXPLAYERS
; i
++)
1496 players
[i
].didsecret
= true;
1501 wminfo
.didsecret
= players
[consoleplayer
].didsecret
;
1502 wminfo
.epsd
= gameepisode
-1;
1503 wminfo
.last
= gamemap
-1;
1505 // wminfo.next is 0 biased, unlike gamemap
1506 if ( gamemode
== commercial
)
1511 case 15: wminfo
.next
= 30; break;
1512 case 31: wminfo
.next
= 31; break;
1518 case 32: wminfo
.next
= 15; break;
1519 default: wminfo
.next
= gamemap
;
1525 wminfo
.next
= 8; // go to secret level
1526 else if (gamemap
== 9)
1528 // returning from secret level
1529 switch (gameepisode
)
1546 wminfo
.next
= gamemap
; // go to next level
1549 wminfo
.maxkills
= totalkills
;
1550 wminfo
.maxitems
= totalitems
;
1551 wminfo
.maxsecret
= totalsecret
;
1552 wminfo
.maxfrags
= 0;
1553 if ( gamemode
== commercial
)
1554 wminfo
.partime
= TICRATE
*cpars
[gamemap
-1];
1556 wminfo
.partime
= TICRATE
*pars
[gameepisode
][gamemap
];
1557 wminfo
.pnum
= consoleplayer
;
1559 for (i
=0 ; i
<MAXPLAYERS
; i
++)
1561 wminfo
.plyr
[i
].in
= playeringame
[i
];
1562 wminfo
.plyr
[i
].skills
= players
[i
].killcount
;
1563 wminfo
.plyr
[i
].sitems
= players
[i
].itemcount
;
1564 wminfo
.plyr
[i
].ssecret
= players
[i
].secretcount
;
1565 wminfo
.plyr
[i
].stime
= leveltime
;
1566 memcpy (wminfo
.plyr
[i
].frags
, players
[i
].frags
1567 , sizeof(wminfo
.plyr
[i
].frags
));
1570 gamestate
= GS_INTERMISSION
;
1572 automapactive
= false;
1581 void G_WorldDone (void)
1583 gameaction
= ga_worlddone
;
1586 players
[consoleplayer
].didsecret
= true;
1588 if ( gamemode
== commercial
)
1606 void G_DoWorldDone (void)
1608 gamestate
= GS_LEVEL
;
1609 gamemap
= wminfo
.next
+1;
1611 gameaction
= ga_nothing
;
1618 // G_InitFromSavegame
1619 // Can be called by the startup code or the menu task.
1621 extern boolean setsizeneeded
;
1622 void R_ExecuteSetViewSize (void);
1626 void G_LoadGame (char* name
)
1628 strcpy (savename
, name
);
1629 gameaction
= ga_loadgame
;
1632 #define VERSIONSIZE 16
1635 void G_DoLoadGame (void)
1639 gameaction
= ga_nothing
;
1641 save_stream
= fopen(savename
, "rb");
1643 if (save_stream
== NULL
)
1648 savegame_error
= false;
1650 if (!P_ReadSaveGameHeader())
1652 fclose(save_stream
);
1656 savedleveltime
= leveltime
;
1658 // load a base level
1659 G_InitNew (gameskill
, gameepisode
, gamemap
);
1661 leveltime
= savedleveltime
;
1663 // dearchive all the modifications
1664 P_UnArchivePlayers ();
1665 P_UnArchiveWorld ();
1666 P_UnArchiveThinkers ();
1667 P_UnArchiveSpecials ();
1669 if (!P_ReadSaveGameEOF())
1670 I_Error ("Bad savegame");
1672 fclose(save_stream
);
1675 R_ExecuteSetViewSize ();
1677 // draw the pattern into the back screen
1678 R_FillBackScreen ();
1684 // Called by the menu task.
1685 // Description is a 24 byte text string
1692 savegameslot
= slot
;
1693 strcpy (savedescription
, description
);
1697 void G_DoSaveGame (void)
1699 char *savegame_file
;
1700 char *temp_savegame_file
;
1702 temp_savegame_file
= P_TempSaveGameFile();
1703 savegame_file
= P_SaveGameFile(savegameslot
);
1705 // Open the savegame file for writing. We write to a temporary file
1706 // and then rename it at the end if it was successfully written.
1707 // This prevents an existing savegame from being overwritten by
1708 // a corrupted one, or if a savegame buffer overrun occurs.
1710 save_stream
= fopen(temp_savegame_file
, "wb");
1712 if (save_stream
== NULL
)
1717 savegame_error
= false;
1719 P_WriteSaveGameHeader(savedescription
);
1721 P_ArchivePlayers ();
1723 P_ArchiveThinkers ();
1724 P_ArchiveSpecials ();
1726 P_WriteSaveGameEOF();
1728 // Enforce the same savegame size limit as in Vanilla Doom,
1729 // except if the vanilla_savegame_limit setting is turned off.
1731 if (vanilla_savegame_limit
&& ftell(save_stream
) > SAVEGAMESIZE
)
1733 I_Error ("Savegame buffer overrun");
1736 // Finish up, close the savegame file.
1738 fclose(save_stream
);
1740 // Now rename the temporary savegame file to the actual savegame
1741 // file, overwriting the old savegame if there was one there.
1743 remove(savegame_file
);
1744 rename(temp_savegame_file
, savegame_file
);
1746 gameaction
= ga_nothing
;
1747 strcpy(savedescription
, "");
1749 players
[consoleplayer
].message
= DEH_String(GGSAVED
);
1751 // draw the pattern into the back screen
1752 R_FillBackScreen ();
1758 // Can be called by the startup code or the menu task,
1759 // consoleplayer, displayplayer, playeringame[] should be set.
1772 d_episode
= episode
;
1774 gameaction
= ga_newgame
;
1778 void G_DoNewGame (void)
1780 demoplayback
= false;
1784 playeringame
[1] = playeringame
[2] = playeringame
[3] = 0;
1785 respawnparm
= false;
1789 G_InitNew (d_skill
, d_episode
, d_map
);
1790 gameaction
= ga_nothing
;
1793 // The sky texture to be used instead of the F_SKY1 dummy.
1794 extern int skytexture
;
1803 char *skytexturename
;
1813 if (skill
> sk_nightmare
)
1814 skill
= sk_nightmare
;
1817 // This was quite messy with SPECIAL and commented parts.
1818 // Supposedly hacks to make the latest edition work.
1819 // It might not work properly.
1823 if ( gamemode
== retail
)
1828 else if ( gamemode
== shareware
)
1831 episode
= 1; // only start episode 1 on shareware
1845 && ( gamemode
!= commercial
) )
1850 if (skill
== sk_nightmare
|| respawnparm
)
1851 respawnmonsters
= true;
1853 respawnmonsters
= false;
1855 if (fastparm
|| (skill
== sk_nightmare
&& gameskill
!= sk_nightmare
) )
1857 for (i
=S_SARG_RUN1
; i
<=S_SARG_PAIN2
; i
++)
1858 states
[i
].tics
>>= 1;
1859 mobjinfo
[MT_BRUISERSHOT
].speed
= 20*FRACUNIT
;
1860 mobjinfo
[MT_HEADSHOT
].speed
= 20*FRACUNIT
;
1861 mobjinfo
[MT_TROOPSHOT
].speed
= 20*FRACUNIT
;
1863 else if (skill
!= sk_nightmare
&& gameskill
== sk_nightmare
)
1865 for (i
=S_SARG_RUN1
; i
<=S_SARG_PAIN2
; i
++)
1866 states
[i
].tics
<<= 1;
1867 mobjinfo
[MT_BRUISERSHOT
].speed
= 15*FRACUNIT
;
1868 mobjinfo
[MT_HEADSHOT
].speed
= 10*FRACUNIT
;
1869 mobjinfo
[MT_TROOPSHOT
].speed
= 10*FRACUNIT
;
1873 // force players to be initialized upon first level load
1874 for (i
=0 ; i
<MAXPLAYERS
; i
++)
1875 players
[i
].playerstate
= PST_REBORN
;
1877 usergame
= true; // will be set false if a demo
1879 demoplayback
= false;
1880 automapactive
= false;
1882 gameepisode
= episode
;
1888 // Set the sky to use.
1890 // Note: This IS broken, but it is how Vanilla Doom behaves.
1891 // See http://doom.wikia.com/wiki/Sky_never_changes_in_Doom_II.
1893 // Because we set the sky here at the start of a game, not at the
1894 // start of a level, the sky texture never changes unless we
1895 // restore from a saved game. This was fixed before the Doom
1896 // source release, but this IS the way Vanilla DOS Doom behaves.
1898 if (gamemode
== commercial
)
1901 skytexturename
= "SKY1";
1902 else if (gamemap
< 21)
1903 skytexturename
= "SKY2";
1905 skytexturename
= "SKY3";
1909 switch (gameepisode
)
1913 skytexturename
= "SKY1";
1916 skytexturename
= "SKY2";
1919 skytexturename
= "SKY3";
1921 case 4: // Special Edition sky
1922 skytexturename
= "SKY4";
1927 skytexturename
= DEH_String(skytexturename
);
1929 skytexture
= R_TextureNumForName(skytexturename
);
1939 #define DEMOMARKER 0x80
1942 void G_ReadDemoTiccmd (ticcmd_t
* cmd
)
1944 if (*demo_p
== DEMOMARKER
)
1946 // end of demo data stream
1947 G_CheckDemoStatus ();
1950 cmd
->forwardmove
= ((signed char)*demo_p
++);
1951 cmd
->sidemove
= ((signed char)*demo_p
++);
1953 // If this is a longtics demo, read back in higher resolution
1957 cmd
->angleturn
= *demo_p
++;
1958 cmd
->angleturn
|= (*demo_p
++) << 8;
1962 cmd
->angleturn
= ((unsigned char) *demo_p
++)<<8;
1965 cmd
->buttons
= (unsigned char)*demo_p
++;
1968 // Increase the size of the demo buffer to allow unlimited demos
1970 static void IncreaseDemoBuffer(void)
1973 byte
*new_demobuffer
;
1977 // Find the current size
1979 current_length
= demoend
- demobuffer
;
1981 // Generate a new buffer twice the size
1982 new_length
= current_length
* 2;
1984 new_demobuffer
= Z_Malloc(new_length
, PU_STATIC
, 0);
1985 new_demop
= new_demobuffer
+ (demo_p
- demobuffer
);
1987 // Copy over the old data
1989 memcpy(new_demobuffer
, demobuffer
, current_length
);
1991 // Free the old buffer and point the demo pointers at the new buffer.
1995 demobuffer
= new_demobuffer
;
1997 demoend
= demobuffer
+ new_length
;
2000 void G_WriteDemoTiccmd (ticcmd_t
* cmd
)
2004 if (gamekeydown
[key_demo_quit
]) // press q to end demo recording
2005 G_CheckDemoStatus ();
2007 demo_start
= demo_p
;
2009 *demo_p
++ = cmd
->forwardmove
;
2010 *demo_p
++ = cmd
->sidemove
;
2012 // If this is a longtics demo, record in higher resolution
2016 *demo_p
++ = (cmd
->angleturn
& 0xff);
2017 *demo_p
++ = (cmd
->angleturn
>> 8) & 0xff;
2021 *demo_p
++ = cmd
->angleturn
>> 8;
2024 *demo_p
++ = cmd
->buttons
;
2026 // reset demo pointer back
2027 demo_p
= demo_start
;
2029 if (demo_p
> demoend
- 16)
2031 if (vanilla_demo_limit
)
2034 G_CheckDemoStatus ();
2039 // Vanilla demo limit disabled: unlimited
2042 IncreaseDemoBuffer();
2046 G_ReadDemoTiccmd (cmd
); // make SURE it is exactly the same
2054 void G_RecordDemo (char* name
)
2060 strcpy (demoname
, name
);
2061 strcat (demoname
, ".lmp");
2069 // Specify the demo buffer size (KiB)
2072 i
= M_CheckParm ("-maxdemo");
2073 if (i
&& i
<myargc
-1)
2074 maxsize
= atoi(myargv
[i
+1])*1024;
2075 demobuffer
= Z_Malloc (maxsize
,PU_STATIC
,NULL
);
2076 demoend
= demobuffer
+ maxsize
;
2078 demorecording
= true;
2082 void G_BeginRecording (void)
2089 // Record a high resolution "Doom 1.91" demo.
2092 longtics
= M_CheckParm("-longtics") != 0;
2094 // If not recording a longtics demo, record in low res
2096 lowres_turn
= !longtics
;
2098 demo_p
= demobuffer
;
2100 // Save the right version code for this demo
2104 *demo_p
++ = DOOM_191_VERSION
;
2108 *demo_p
++ = DOOM_VERSION
;
2111 *demo_p
++ = gameskill
;
2112 *demo_p
++ = gameepisode
;
2113 *demo_p
++ = gamemap
;
2114 *demo_p
++ = deathmatch
;
2115 *demo_p
++ = respawnparm
;
2116 *demo_p
++ = fastparm
;
2117 *demo_p
++ = nomonsters
;
2118 *demo_p
++ = consoleplayer
;
2120 for (i
=0 ; i
<MAXPLAYERS
; i
++)
2121 *demo_p
++ = playeringame
[i
];
2131 void G_DeferedPlayDemo (char* name
)
2134 gameaction
= ga_playdemo
;
2137 // Generate a string describing a demo version
2139 static char *DemoVersionDescription(int version
)
2141 static char resultbuf
[16];
2150 return "v1.6/v1.666";
2152 return "v1.7/v1.7a";
2161 // Unknown version. Perhaps this is a pre-v1.4 IWAD? If the version
2162 // byte is in the range 0-4 then it can be a v1.0-v1.2 demo.
2164 if (version
>= 0 && version
<= 4)
2166 return "v1.0/v1.1/v1.2";
2170 sprintf(resultbuf
, "%i.%i (unknown)", version
/ 100, version
% 100);
2175 void G_DoPlayDemo (void)
2178 int i
, episode
, map
;
2181 gameaction
= ga_nothing
;
2182 demobuffer
= demo_p
= W_CacheLumpName (defdemoname
, PU_STATIC
);
2184 demoversion
= *demo_p
++;
2186 if (demoversion
== DOOM_VERSION
)
2190 else if (demoversion
== DOOM_191_VERSION
)
2192 // demo recorded with cph's modified "v1.91" doom exe
2197 char *message
= "Demo is from a different game version!\n"
2198 "(read %i, should be %i)\n"
2200 "*** You may need to upgrade your version "
2201 "of Doom to v1.9. ***\n"
2202 " See: http://doomworld.com/files/patches.shtml\n"
2203 " This appears to be %s.";
2205 I_Error(message
, demoversion
, DOOM_VERSION
,
2206 DemoVersionDescription(demoversion
));
2210 episode
= *demo_p
++;
2212 deathmatch
= *demo_p
++;
2213 respawnparm
= *demo_p
++;
2214 fastparm
= *demo_p
++;
2215 nomonsters
= *demo_p
++;
2216 consoleplayer
= *demo_p
++;
2218 for (i
=0 ; i
<MAXPLAYERS
; i
++)
2219 playeringame
[i
] = *demo_p
++;
2224 // Play back a demo recorded in a netgame with a single player.
2227 if (playeringame
[1] || M_CheckParm("-netdemo") > 0)
2233 // don't spend a lot of time in loadlevel
2235 G_InitNew (skill
, episode
, map
);
2237 starttime
= I_GetTime ();
2240 demoplayback
= true;
2246 void G_TimeDemo (char* name
)
2251 // Disable rendering the screen entirely.
2254 nodrawers
= M_CheckParm ("-nodraw");
2259 // Disable blitting the screen.
2262 noblit
= M_CheckParm ("-noblit");
2267 gameaction
= ga_playdemo
;
2276 = Called after a death or level completion to allow demos to be cleaned up
2277 = Returns true if a new demo loop action will take place
2281 boolean
G_CheckDemoStatus (void)
2290 endtime
= I_GetTime ();
2291 realtics
= endtime
- starttime
;
2292 fps
= ((float) gametic
* TICRATE
) / realtics
;
2294 // Prevent recursive calls
2296 demoplayback
= false;
2298 I_Error ("timed %i gametics in %i realtics (%f fps)",
2299 gametic
, realtics
, fps
);
2304 W_ReleaseLumpName(defdemoname
);
2305 demoplayback
= false;
2309 playeringame
[1] = playeringame
[2] = playeringame
[3] = 0;
2310 respawnparm
= false;
2325 *demo_p
++ = DEMOMARKER
;
2326 M_WriteFile (demoname
, demobuffer
, demo_p
- demobuffer
);
2327 Z_Free (demobuffer
);
2328 demorecording
= false;
2329 I_Error ("Demo %s recorded",demoname
);