1820 lines
37 KiB
C
1820 lines
37 KiB
C
|
|
||
|
// G_game.c
|
||
|
|
||
|
#include <string.h>
|
||
|
#include "DoomDef.h"
|
||
|
#include "P_local.h"
|
||
|
#include "soundst.h"
|
||
|
|
||
|
// Macros
|
||
|
|
||
|
#define SAVE_GAME_TERMINATOR 0x1d
|
||
|
|
||
|
// Functions
|
||
|
|
||
|
boolean G_CheckDemoStatus (void);
|
||
|
void G_ReadDemoTiccmd (ticcmd_t *cmd);
|
||
|
void G_WriteDemoTiccmd (ticcmd_t *cmd);
|
||
|
void G_PlayerReborn (int player);
|
||
|
void G_InitNew (skill_t skill, int episode, int map);
|
||
|
|
||
|
void G_DoReborn (int playernum);
|
||
|
|
||
|
void G_DoLoadLevel (void);
|
||
|
void G_DoNewGame (void);
|
||
|
void G_DoLoadGame (void);
|
||
|
void G_DoPlayDemo (void);
|
||
|
void G_DoCompleted (void);
|
||
|
void G_DoVictory (void);
|
||
|
void G_DoWorldDone (void);
|
||
|
void G_DoSaveGame (void);
|
||
|
|
||
|
void D_PageTicker(void);
|
||
|
void D_AdvanceDemo(void);
|
||
|
|
||
|
struct
|
||
|
{
|
||
|
mobjtype_t type;
|
||
|
int speed[2];
|
||
|
} MonsterMissileInfo[] =
|
||
|
{
|
||
|
{ MT_IMPBALL, 10, 20 },
|
||
|
{ MT_MUMMYFX1, 9, 18 },
|
||
|
{ MT_KNIGHTAXE, 9, 18 },
|
||
|
{ MT_REDAXE, 9, 18 },
|
||
|
{ MT_BEASTBALL, 12, 20 },
|
||
|
{ MT_WIZFX1, 18, 24 },
|
||
|
{ MT_SNAKEPRO_A, 14, 20 },
|
||
|
{ MT_SNAKEPRO_B, 14, 20 },
|
||
|
{ MT_HEADFX1, 13, 20 },
|
||
|
{ MT_HEADFX3, 10, 18 },
|
||
|
{ MT_MNTRFX1, 20, 26 },
|
||
|
{ MT_MNTRFX2, 14, 20 },
|
||
|
{ MT_SRCRFX1, 20, 28 },
|
||
|
{ MT_SOR2FX1, 20, 28 },
|
||
|
{ -1, -1, -1 } // Terminator
|
||
|
};
|
||
|
|
||
|
gameaction_t gameaction;
|
||
|
gamestate_t gamestate;
|
||
|
skill_t gameskill;
|
||
|
boolean respawnmonsters;
|
||
|
int gameepisode;
|
||
|
int gamemap;
|
||
|
int prevmap;
|
||
|
|
||
|
boolean paused;
|
||
|
boolean sendpause; // send a pause event next tic
|
||
|
boolean sendsave; // send a save event next tic
|
||
|
boolean usergame; // ok to save / end game
|
||
|
|
||
|
boolean timingdemo; // if true, exit with report on completion
|
||
|
int starttime; // for comparative timing purposes
|
||
|
|
||
|
boolean viewactive;
|
||
|
|
||
|
boolean deathmatch; // only if started as net death
|
||
|
boolean netgame; // only true if packets are broadcast
|
||
|
boolean playeringame[MAXPLAYERS];
|
||
|
player_t players[MAXPLAYERS];
|
||
|
|
||
|
int consoleplayer; // player taking events and displaying
|
||
|
int displayplayer; // view being displayed
|
||
|
int gametic;
|
||
|
int levelstarttic; // gametic at level start
|
||
|
int totalkills, totalitems, totalsecret; // for intermission
|
||
|
|
||
|
char demoname[32];
|
||
|
boolean demorecording;
|
||
|
boolean demoplayback;
|
||
|
byte *demobuffer, *demo_p;
|
||
|
boolean singledemo; // quit after playing a demo from cmdline
|
||
|
|
||
|
boolean precache = true; // if true, load all graphics at start
|
||
|
|
||
|
short consistancy[MAXPLAYERS][BACKUPTICS];
|
||
|
|
||
|
byte *savebuffer, *save_p;
|
||
|
|
||
|
|
||
|
//
|
||
|
// controls (have defaults)
|
||
|
//
|
||
|
int key_right, key_left, key_up, key_down;
|
||
|
int key_strafeleft, key_straferight;
|
||
|
int key_fire, key_use, key_strafe, key_speed;
|
||
|
int key_flyup, key_flydown, key_flycenter;
|
||
|
int key_lookup, key_lookdown, key_lookcenter;
|
||
|
int key_invleft, key_invright, key_useartifact;
|
||
|
|
||
|
int mousebfire;
|
||
|
int mousebstrafe;
|
||
|
int mousebforward;
|
||
|
|
||
|
int joybfire;
|
||
|
int joybstrafe;
|
||
|
int joybuse;
|
||
|
int joybspeed;
|
||
|
|
||
|
|
||
|
|
||
|
#define MAXPLMOVE 0x32
|
||
|
|
||
|
fixed_t forwardmove[2] = {0x19, 0x32};
|
||
|
fixed_t sidemove[2] = {0x18, 0x28};
|
||
|
fixed_t angleturn[3] = {640, 1280, 320}; // + slow turn
|
||
|
#define SLOWTURNTICS 6
|
||
|
|
||
|
#define NUMKEYS 256
|
||
|
boolean gamekeydown[NUMKEYS];
|
||
|
int turnheld; // for accelerative turning
|
||
|
int lookheld;
|
||
|
|
||
|
|
||
|
boolean mousearray[4];
|
||
|
boolean *mousebuttons = &mousearray[1];
|
||
|
// allow [-1]
|
||
|
int mousex, mousey; // mouse values are used once
|
||
|
int dclicktime, dclickstate, dclicks;
|
||
|
int dclicktime2, dclickstate2, dclicks2;
|
||
|
|
||
|
int joyxmove, joyymove; // joystick values are repeated
|
||
|
boolean joyarray[5];
|
||
|
boolean *joybuttons = &joyarray[1]; // allow [-1]
|
||
|
|
||
|
int savegameslot;
|
||
|
char savedescription[32];
|
||
|
|
||
|
int inventoryTics;
|
||
|
|
||
|
#ifdef __WATCOMC__
|
||
|
extern externdata_t *i_ExternData;
|
||
|
#endif
|
||
|
|
||
|
//=============================================================================
|
||
|
// Not used - ripped out for Heretic
|
||
|
/*
|
||
|
int G_CmdChecksum(ticcmd_t *cmd)
|
||
|
{
|
||
|
int i;
|
||
|
int sum;
|
||
|
|
||
|
sum = 0;
|
||
|
for(i = 0; i < sizeof(*cmd)/4-1; i++)
|
||
|
{
|
||
|
sum += ((int *)cmd)[i];
|
||
|
}
|
||
|
return(sum);
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
====================
|
||
|
=
|
||
|
= G_BuildTiccmd
|
||
|
=
|
||
|
= Builds a ticcmd from all of the available inputs or reads it from the
|
||
|
= demo buffer.
|
||
|
= If recording a demo, write it out
|
||
|
====================
|
||
|
*/
|
||
|
|
||
|
extern boolean inventory;
|
||
|
extern int curpos;
|
||
|
extern int inv_ptr;
|
||
|
|
||
|
extern int isCyberPresent; // is CyberMan present?
|
||
|
boolean usearti = true;
|
||
|
void I_ReadCyberCmd (ticcmd_t *cmd);
|
||
|
|
||
|
void G_BuildTiccmd (ticcmd_t *cmd)
|
||
|
{
|
||
|
int i;
|
||
|
boolean strafe, bstrafe;
|
||
|
int speed, tspeed, lspeed;
|
||
|
int forward, side;
|
||
|
int look, arti;
|
||
|
int flyheight;
|
||
|
|
||
|
extern boolean noartiskip;
|
||
|
|
||
|
#ifdef __WATCOMC__
|
||
|
int angleDelta;
|
||
|
static int oldAngle;
|
||
|
extern int newViewAngleOff;
|
||
|
static int externInvKey;
|
||
|
extern boolean automapactive;
|
||
|
event_t ev;
|
||
|
#endif
|
||
|
|
||
|
|
||
|
memset (cmd,0,sizeof(*cmd));
|
||
|
cmd->consistancy =
|
||
|
consistancy[consoleplayer][(maketic*ticdup)%BACKUPTICS];
|
||
|
if (isCyberPresent)
|
||
|
I_ReadCyberCmd (cmd);
|
||
|
|
||
|
//printf ("cons: %i\n",cmd->consistancy);
|
||
|
|
||
|
strafe = gamekeydown[key_strafe] || mousebuttons[mousebstrafe]
|
||
|
|| joybuttons[joybstrafe];
|
||
|
speed = gamekeydown[key_speed] || joybuttons[joybspeed]
|
||
|
|| joybuttons[joybspeed];
|
||
|
#ifdef __WATCOMC__
|
||
|
if(useexterndriver)
|
||
|
{
|
||
|
speed |= (i_ExternData->buttons&EBT_SPEED);
|
||
|
strafe |= (i_ExternData->buttons&EBT_STRAFE);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
forward = side = look = arti = flyheight = 0;
|
||
|
|
||
|
//
|
||
|
// use two stage accelerative turning on the keyboard and joystick
|
||
|
//
|
||
|
if (joyxmove < 0 || joyxmove > 0
|
||
|
|| gamekeydown[key_right] || gamekeydown[key_left])
|
||
|
turnheld += ticdup;
|
||
|
else
|
||
|
turnheld = 0;
|
||
|
if (turnheld < SLOWTURNTICS)
|
||
|
tspeed = 2; // slow turn
|
||
|
else
|
||
|
tspeed = speed;
|
||
|
|
||
|
if(gamekeydown[key_lookdown] || gamekeydown[key_lookup])
|
||
|
{
|
||
|
lookheld += ticdup;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
lookheld = 0;
|
||
|
}
|
||
|
if(lookheld < SLOWTURNTICS)
|
||
|
{
|
||
|
lspeed = 3;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
lspeed = 5;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// let movement keys cancel each other out
|
||
|
//
|
||
|
if(strafe)
|
||
|
{
|
||
|
if (gamekeydown[key_right])
|
||
|
side += sidemove[speed];
|
||
|
if (gamekeydown[key_left])
|
||
|
side -= sidemove[speed];
|
||
|
if (joyxmove > 0)
|
||
|
side += sidemove[speed];
|
||
|
if (joyxmove < 0)
|
||
|
side -= sidemove[speed];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (gamekeydown[key_right])
|
||
|
cmd->angleturn -= angleturn[tspeed];
|
||
|
if (gamekeydown[key_left])
|
||
|
cmd->angleturn += angleturn[tspeed];
|
||
|
if (joyxmove > 0)
|
||
|
cmd->angleturn -= angleturn[tspeed];
|
||
|
if (joyxmove < 0)
|
||
|
cmd->angleturn += angleturn[tspeed];
|
||
|
}
|
||
|
|
||
|
if (gamekeydown[key_up])
|
||
|
forward += forwardmove[speed];
|
||
|
if (gamekeydown[key_down])
|
||
|
forward -= forwardmove[speed];
|
||
|
if (joyymove < 0)
|
||
|
forward += forwardmove[speed];
|
||
|
if (joyymove > 0)
|
||
|
forward -= forwardmove[speed];
|
||
|
if (gamekeydown[key_straferight])
|
||
|
side += sidemove[speed];
|
||
|
if (gamekeydown[key_strafeleft])
|
||
|
side -= sidemove[speed];
|
||
|
|
||
|
// Look up/down/center keys
|
||
|
if(gamekeydown[key_lookup])
|
||
|
{
|
||
|
look = lspeed;
|
||
|
}
|
||
|
if(gamekeydown[key_lookdown])
|
||
|
{
|
||
|
look = -lspeed;
|
||
|
}
|
||
|
if(gamekeydown[key_lookcenter])
|
||
|
{
|
||
|
look = TOCENTER;
|
||
|
}
|
||
|
|
||
|
#ifdef __WATCOMC__
|
||
|
if(useexterndriver && i_ExternData->buttons&EBT_CENTERVIEW)
|
||
|
{
|
||
|
look = TOCENTER;
|
||
|
}
|
||
|
if(useexterndriver && look != TOCENTER && (gamestate == GS_LEVEL ||
|
||
|
gamestate == GS_INTERMISSION))
|
||
|
{
|
||
|
if(i_ExternData->moveForward)
|
||
|
{
|
||
|
forward += i_ExternData->moveForward;
|
||
|
if(speed)
|
||
|
{
|
||
|
forward <<= 1;
|
||
|
}
|
||
|
}
|
||
|
if(i_ExternData->angleTurn)
|
||
|
{
|
||
|
if(strafe)
|
||
|
{
|
||
|
side += i_ExternData->angleTurn;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
cmd->angleturn += i_ExternData->angleTurn;
|
||
|
}
|
||
|
}
|
||
|
if(i_ExternData->moveSideways)
|
||
|
{
|
||
|
side += i_ExternData->moveSideways;
|
||
|
if(speed)
|
||
|
{
|
||
|
side <<= 1;
|
||
|
}
|
||
|
}
|
||
|
if(i_ExternData->pitch)
|
||
|
{
|
||
|
angleDelta = i_ExternData->pitch-oldAngle;
|
||
|
if(abs(angleDelta < 14))
|
||
|
{
|
||
|
look = angleDelta/2;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
look = 7*(angleDelta > 0 ? 1 : -1);
|
||
|
}
|
||
|
if(look == TOCENTER)
|
||
|
{
|
||
|
look++;
|
||
|
}
|
||
|
oldAngle += look;
|
||
|
}
|
||
|
if(i_ExternData->flyDirection)
|
||
|
{
|
||
|
if(i_ExternData->flyDirection > 0)
|
||
|
{
|
||
|
flyheight = 5;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
flyheight = -5;
|
||
|
}
|
||
|
}
|
||
|
if(abs(newViewAngleOff-i_ExternData->angleHead) < 3000)
|
||
|
{
|
||
|
newViewAngleOff = i_ExternData->angleHead;
|
||
|
}
|
||
|
if(i_ExternData->buttons&EBT_FIRE)
|
||
|
{
|
||
|
cmd->buttons |= BT_ATTACK;
|
||
|
}
|
||
|
if(i_ExternData->buttons&EBT_OPENDOOR)
|
||
|
{
|
||
|
cmd->buttons |= BT_USE;
|
||
|
}
|
||
|
if(i_ExternData->buttons&EBT_PAUSE)
|
||
|
{
|
||
|
sendpause ^= 1;
|
||
|
}
|
||
|
if(externInvKey&EBT_USEARTIFACT)
|
||
|
{
|
||
|
ev.type = ev_keyup;
|
||
|
ev.data1 = key_useartifact;
|
||
|
D_PostEvent(&ev);
|
||
|
externInvKey &= ~EBT_USEARTIFACT;
|
||
|
}
|
||
|
else if(i_ExternData->buttons&EBT_USEARTIFACT)
|
||
|
{
|
||
|
externInvKey |= EBT_USEARTIFACT;
|
||
|
ev.type = ev_keydown;
|
||
|
ev.data1 = key_useartifact;
|
||
|
D_PostEvent(&ev);
|
||
|
}
|
||
|
if(externInvKey&EBT_INVENTORYRIGHT)
|
||
|
{
|
||
|
ev.type = ev_keyup;
|
||
|
ev.data1 = key_invright;
|
||
|
D_PostEvent(&ev);
|
||
|
externInvKey &= ~EBT_INVENTORYRIGHT;
|
||
|
}
|
||
|
else if(i_ExternData->buttons&EBT_INVENTORYRIGHT)
|
||
|
{
|
||
|
externInvKey |= EBT_INVENTORYRIGHT;
|
||
|
ev.type = ev_keydown;
|
||
|
ev.data1 = key_invright;
|
||
|
D_PostEvent(&ev);
|
||
|
}
|
||
|
if(externInvKey&EBT_INVENTORYLEFT)
|
||
|
{
|
||
|
ev.type = ev_keyup;
|
||
|
ev.data1 = key_invleft;
|
||
|
D_PostEvent(&ev);
|
||
|
externInvKey &= ~EBT_INVENTORYLEFT;
|
||
|
}
|
||
|
else if(i_ExternData->buttons&EBT_INVENTORYLEFT)
|
||
|
{
|
||
|
externInvKey |= EBT_INVENTORYLEFT;
|
||
|
ev.type = ev_keydown;
|
||
|
ev.data1 = key_invleft;
|
||
|
D_PostEvent(&ev);
|
||
|
}
|
||
|
if(i_ExternData->buttons&EBT_FLYDROP)
|
||
|
{
|
||
|
flyheight = TOCENTER;
|
||
|
}
|
||
|
if(i_ExternData->buttons&EBT_MAP && gamestate == GS_LEVEL)
|
||
|
{
|
||
|
if(automapactive)
|
||
|
{
|
||
|
AM_Stop();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
AM_Start();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// Fly up/down/drop keys
|
||
|
if(gamekeydown[key_flyup])
|
||
|
{
|
||
|
flyheight = 5; // note that the actual flyheight will be twice this
|
||
|
}
|
||
|
if(gamekeydown[key_flydown])
|
||
|
{
|
||
|
flyheight = -5;
|
||
|
}
|
||
|
if(gamekeydown[key_flycenter])
|
||
|
{
|
||
|
flyheight = TOCENTER;
|
||
|
look = TOCENTER;
|
||
|
}
|
||
|
|
||
|
// Use artifact key
|
||
|
if(gamekeydown[key_useartifact])
|
||
|
{
|
||
|
if(gamekeydown[key_speed] && !noartiskip)
|
||
|
{
|
||
|
if(players[consoleplayer].inventory[inv_ptr].type != arti_none)
|
||
|
{
|
||
|
gamekeydown[key_useartifact] = false;
|
||
|
cmd->arti = 0xff; // skip artifact code
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if(inventory)
|
||
|
{
|
||
|
players[consoleplayer].readyArtifact =
|
||
|
players[consoleplayer].inventory[inv_ptr].type;
|
||
|
inventory = false;
|
||
|
cmd->arti = 0;
|
||
|
usearti = false;
|
||
|
}
|
||
|
else if(usearti)
|
||
|
{
|
||
|
cmd->arti = players[consoleplayer].inventory[inv_ptr].type;
|
||
|
usearti = false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if(gamekeydown[127] && !cmd->arti
|
||
|
&& !players[consoleplayer].powers[pw_weaponlevel2])
|
||
|
{
|
||
|
gamekeydown[127] = false;
|
||
|
cmd->arti = arti_tomeofpower;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// buttons
|
||
|
//
|
||
|
cmd->chatchar = CT_dequeueChatChar();
|
||
|
|
||
|
if (gamekeydown[key_fire] || mousebuttons[mousebfire]
|
||
|
|| joybuttons[joybfire])
|
||
|
cmd->buttons |= BT_ATTACK;
|
||
|
|
||
|
if (gamekeydown[key_use] || joybuttons[joybuse] )
|
||
|
{
|
||
|
cmd->buttons |= BT_USE;
|
||
|
dclicks = 0; // clear double clicks if hit use button
|
||
|
}
|
||
|
|
||
|
for(i = 0; i < NUMWEAPONS-2; i++)
|
||
|
{
|
||
|
if(gamekeydown['1'+i])
|
||
|
{
|
||
|
cmd->buttons |= BT_CHANGE;
|
||
|
cmd->buttons |= i<<BT_WEAPONSHIFT;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// mouse
|
||
|
//
|
||
|
if (mousebuttons[mousebforward])
|
||
|
forward += forwardmove[speed];
|
||
|
|
||
|
//
|
||
|
// forward double click
|
||
|
//
|
||
|
if (mousebuttons[mousebforward] != dclickstate && dclicktime > 1 )
|
||
|
{
|
||
|
dclickstate = mousebuttons[mousebforward];
|
||
|
if (dclickstate)
|
||
|
dclicks++;
|
||
|
if (dclicks == 2)
|
||
|
{
|
||
|
cmd->buttons |= BT_USE;
|
||
|
dclicks = 0;
|
||
|
}
|
||
|
else
|
||
|
dclicktime = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
dclicktime += ticdup;
|
||
|
if (dclicktime > 20)
|
||
|
{
|
||
|
dclicks = 0;
|
||
|
dclickstate = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// strafe double click
|
||
|
//
|
||
|
bstrafe = mousebuttons[mousebstrafe]
|
||
|
|| joybuttons[joybstrafe];
|
||
|
if (bstrafe != dclickstate2 && dclicktime2 > 1 )
|
||
|
{
|
||
|
dclickstate2 = bstrafe;
|
||
|
if (dclickstate2)
|
||
|
dclicks2++;
|
||
|
if (dclicks2 == 2)
|
||
|
{
|
||
|
cmd->buttons |= BT_USE;
|
||
|
dclicks2 = 0;
|
||
|
}
|
||
|
else
|
||
|
dclicktime2 = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
dclicktime2 += ticdup;
|
||
|
if (dclicktime2 > 20)
|
||
|
{
|
||
|
dclicks2 = 0;
|
||
|
dclickstate2 = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (strafe)
|
||
|
{
|
||
|
side += mousex*2;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
cmd->angleturn -= mousex*0x8;
|
||
|
}
|
||
|
forward += mousey;
|
||
|
mousex = mousey = 0;
|
||
|
|
||
|
if (forward > MAXPLMOVE)
|
||
|
forward = MAXPLMOVE;
|
||
|
else if (forward < -MAXPLMOVE)
|
||
|
forward = -MAXPLMOVE;
|
||
|
if (side > MAXPLMOVE)
|
||
|
side = MAXPLMOVE;
|
||
|
else if (side < -MAXPLMOVE)
|
||
|
side = -MAXPLMOVE;
|
||
|
|
||
|
cmd->forwardmove += forward;
|
||
|
cmd->sidemove += side;
|
||
|
if(players[consoleplayer].playerstate == PST_LIVE)
|
||
|
{
|
||
|
if(look < 0)
|
||
|
{
|
||
|
look += 16;
|
||
|
}
|
||
|
cmd->lookfly = look;
|
||
|
}
|
||
|
if(flyheight < 0)
|
||
|
{
|
||
|
flyheight += 16;
|
||
|
}
|
||
|
cmd->lookfly |= flyheight<<4;
|
||
|
|
||
|
//
|
||
|
// special buttons
|
||
|
//
|
||
|
if (sendpause)
|
||
|
{
|
||
|
sendpause = false;
|
||
|
cmd->buttons = BT_SPECIAL | BTS_PAUSE;
|
||
|
}
|
||
|
|
||
|
if (sendsave)
|
||
|
{
|
||
|
sendsave = false;
|
||
|
cmd->buttons = BT_SPECIAL | BTS_SAVEGAME | (savegameslot<<BTS_SAVESHIFT);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
=
|
||
|
= G_DoLoadLevel
|
||
|
=
|
||
|
==============
|
||
|
*/
|
||
|
|
||
|
void G_DoLoadLevel (void)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
levelstarttic = gametic; // for time calculation
|
||
|
gamestate = GS_LEVEL;
|
||
|
for (i=0 ; i<MAXPLAYERS ; i++)
|
||
|
{
|
||
|
if (playeringame[i] && players[i].playerstate == PST_DEAD)
|
||
|
players[i].playerstate = PST_REBORN;
|
||
|
memset (players[i].frags,0,sizeof(players[i].frags));
|
||
|
}
|
||
|
|
||
|
P_SetupLevel (gameepisode, gamemap, 0, gameskill);
|
||
|
displayplayer = consoleplayer; // view the guy you are playing
|
||
|
starttime = I_GetTime ();
|
||
|
gameaction = ga_nothing;
|
||
|
Z_CheckHeap ();
|
||
|
|
||
|
//
|
||
|
// clear cmd building stuff
|
||
|
//
|
||
|
|
||
|
memset (gamekeydown, 0, sizeof(gamekeydown));
|
||
|
joyxmove = joyymove = 0;
|
||
|
mousex = mousey = 0;
|
||
|
sendpause = sendsave = paused = false;
|
||
|
memset (mousebuttons, 0, sizeof(mousebuttons));
|
||
|
memset (joybuttons, 0, sizeof(joybuttons));
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
===============================================================================
|
||
|
=
|
||
|
= G_Responder
|
||
|
=
|
||
|
= get info needed to make ticcmd_ts for the players
|
||
|
=
|
||
|
===============================================================================
|
||
|
*/
|
||
|
|
||
|
boolean G_Responder(event_t *ev)
|
||
|
{
|
||
|
player_t *plr;
|
||
|
extern boolean MenuActive;
|
||
|
|
||
|
plr = &players[consoleplayer];
|
||
|
if(ev->type == ev_keyup && ev->data1 == key_useartifact)
|
||
|
{ // flag to denote that it's okay to use an artifact
|
||
|
if(!inventory)
|
||
|
{
|
||
|
plr->readyArtifact = plr->inventory[inv_ptr].type;
|
||
|
}
|
||
|
usearti = true;
|
||
|
}
|
||
|
|
||
|
// Check for spy mode player cycle
|
||
|
if(gamestate == GS_LEVEL && ev->type == ev_keydown
|
||
|
&& ev->data1 == KEY_F12 && !deathmatch)
|
||
|
{ // Cycle the display player
|
||
|
do
|
||
|
{
|
||
|
displayplayer++;
|
||
|
if(displayplayer == MAXPLAYERS)
|
||
|
{
|
||
|
displayplayer = 0;
|
||
|
}
|
||
|
} while(!playeringame[displayplayer]
|
||
|
&& displayplayer != consoleplayer);
|
||
|
return(true);
|
||
|
}
|
||
|
|
||
|
if(gamestate == GS_LEVEL)
|
||
|
{
|
||
|
if(CT_Responder(ev))
|
||
|
{ // Chat ate the event
|
||
|
return(true);
|
||
|
}
|
||
|
if(SB_Responder(ev))
|
||
|
{ // Status bar ate the event
|
||
|
return(true);
|
||
|
}
|
||
|
if(AM_Responder(ev))
|
||
|
{ // Automap ate the event
|
||
|
return(true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
switch(ev->type)
|
||
|
{
|
||
|
case ev_keydown:
|
||
|
if(ev->data1 == key_invleft)
|
||
|
{
|
||
|
inventoryTics = 5*35;
|
||
|
if(!inventory)
|
||
|
{
|
||
|
inventory = true;
|
||
|
break;
|
||
|
}
|
||
|
inv_ptr--;
|
||
|
if(inv_ptr < 0)
|
||
|
{
|
||
|
inv_ptr = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
curpos--;
|
||
|
if(curpos < 0)
|
||
|
{
|
||
|
curpos = 0;
|
||
|
}
|
||
|
}
|
||
|
return(true);
|
||
|
}
|
||
|
if(ev->data1 == key_invright)
|
||
|
{
|
||
|
inventoryTics = 5*35;
|
||
|
if(!inventory)
|
||
|
{
|
||
|
inventory = true;
|
||
|
break;
|
||
|
}
|
||
|
inv_ptr++;
|
||
|
if(inv_ptr >= plr->inventorySlotNum)
|
||
|
{
|
||
|
inv_ptr--;
|
||
|
if(inv_ptr < 0)
|
||
|
inv_ptr = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
curpos++;
|
||
|
if(curpos > 6)
|
||
|
{
|
||
|
curpos = 6;
|
||
|
}
|
||
|
}
|
||
|
return(true);
|
||
|
}
|
||
|
if(ev->data1 == KEY_PAUSE && !MenuActive)
|
||
|
{
|
||
|
sendpause = true;
|
||
|
return(true);
|
||
|
}
|
||
|
if(ev->data1 < NUMKEYS)
|
||
|
{
|
||
|
gamekeydown[ev->data1] = true;
|
||
|
}
|
||
|
return(true); // eat key down events
|
||
|
|
||
|
case ev_keyup:
|
||
|
if(ev->data1 < NUMKEYS)
|
||
|
{
|
||
|
gamekeydown[ev->data1] = false;
|
||
|
}
|
||
|
return(false); // always let key up events filter down
|
||
|
|
||
|
case ev_mouse:
|
||
|
mousebuttons[0] = ev->data1&1;
|
||
|
mousebuttons[1] = ev->data1&2;
|
||
|
mousebuttons[2] = ev->data1&4;
|
||
|
mousex = ev->data2*(mouseSensitivity+5)/10;
|
||
|
mousey = ev->data3*(mouseSensitivity+5)/10;
|
||
|
return(true); // eat events
|
||
|
|
||
|
case ev_joystick:
|
||
|
joybuttons[0] = ev->data1&1;
|
||
|
joybuttons[1] = ev->data1&2;
|
||
|
joybuttons[2] = ev->data1&4;
|
||
|
joybuttons[3] = ev->data1&8;
|
||
|
joyxmove = ev->data2;
|
||
|
joyymove = ev->data3;
|
||
|
return(true); // eat events
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
return(false);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============================================================================
|
||
|
=
|
||
|
= G_Ticker
|
||
|
=
|
||
|
===============================================================================
|
||
|
*/
|
||
|
|
||
|
void G_Ticker (void)
|
||
|
{
|
||
|
int i, buf;
|
||
|
ticcmd_t *cmd;
|
||
|
|
||
|
//
|
||
|
// do player reborns if needed
|
||
|
//
|
||
|
for (i=0 ; i<MAXPLAYERS ; i++)
|
||
|
if (playeringame[i] && players[i].playerstate == PST_REBORN)
|
||
|
G_DoReborn (i);
|
||
|
|
||
|
//
|
||
|
// do things to change the game state
|
||
|
//
|
||
|
while (gameaction != ga_nothing)
|
||
|
{
|
||
|
switch (gameaction)
|
||
|
{
|
||
|
case ga_loadlevel:
|
||
|
G_DoLoadLevel ();
|
||
|
break;
|
||
|
case ga_newgame:
|
||
|
G_DoNewGame ();
|
||
|
break;
|
||
|
case ga_loadgame:
|
||
|
G_DoLoadGame ();
|
||
|
break;
|
||
|
case ga_savegame:
|
||
|
G_DoSaveGame ();
|
||
|
break;
|
||
|
case ga_playdemo:
|
||
|
G_DoPlayDemo ();
|
||
|
break;
|
||
|
case ga_screenshot:
|
||
|
M_ScreenShot ();
|
||
|
gameaction = ga_nothing;
|
||
|
break;
|
||
|
case ga_completed:
|
||
|
G_DoCompleted ();
|
||
|
break;
|
||
|
case ga_worlddone:
|
||
|
G_DoWorldDone();
|
||
|
break;
|
||
|
case ga_victory:
|
||
|
F_StartFinale();
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// get commands, check consistancy, and build new consistancy check
|
||
|
//
|
||
|
buf = gametic%BACKUPTICS;
|
||
|
|
||
|
for (i=0 ; i<MAXPLAYERS ; i++)
|
||
|
if (playeringame[i])
|
||
|
{
|
||
|
cmd = &players[i].cmd;
|
||
|
|
||
|
memcpy (cmd, &netcmds[i][buf], sizeof(ticcmd_t));
|
||
|
|
||
|
if (demoplayback)
|
||
|
G_ReadDemoTiccmd (cmd);
|
||
|
if (demorecording)
|
||
|
G_WriteDemoTiccmd (cmd);
|
||
|
|
||
|
if (netgame && !(gametic%ticdup) )
|
||
|
{
|
||
|
if (gametic > BACKUPTICS
|
||
|
&& consistancy[i][buf] != cmd->consistancy)
|
||
|
{
|
||
|
I_Error ("consistency failure (%i should be %i)",cmd->consistancy, consistancy[i][buf]);
|
||
|
}
|
||
|
if (players[i].mo)
|
||
|
consistancy[i][buf] = players[i].mo->x;
|
||
|
else
|
||
|
consistancy[i][buf] = rndindex;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// check for special buttons
|
||
|
//
|
||
|
for (i=0 ; i<MAXPLAYERS ; i++)
|
||
|
if (playeringame[i])
|
||
|
{
|
||
|
if (players[i].cmd.buttons & BT_SPECIAL)
|
||
|
{
|
||
|
switch (players[i].cmd.buttons & BT_SPECIALMASK)
|
||
|
{
|
||
|
case BTS_PAUSE:
|
||
|
paused ^= 1;
|
||
|
if(paused)
|
||
|
{
|
||
|
S_PauseSound();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
S_ResumeSound();
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case BTS_SAVEGAME:
|
||
|
if (!savedescription[0])
|
||
|
{
|
||
|
if(netgame)
|
||
|
{
|
||
|
strcpy (savedescription, "NET GAME");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
strcpy(savedescription, "SAVE GAME");
|
||
|
}
|
||
|
}
|
||
|
savegameslot =
|
||
|
(players[i].cmd.buttons & BTS_SAVEMASK)>>BTS_SAVESHIFT;
|
||
|
gameaction = ga_savegame;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// turn inventory off after a certain amount of time
|
||
|
if(inventory && !(--inventoryTics))
|
||
|
{
|
||
|
players[consoleplayer].readyArtifact =
|
||
|
players[consoleplayer].inventory[inv_ptr].type;
|
||
|
inventory = false;
|
||
|
cmd->arti = 0;
|
||
|
}
|
||
|
//
|
||
|
// do main actions
|
||
|
//
|
||
|
//
|
||
|
// do main actions
|
||
|
//
|
||
|
switch (gamestate)
|
||
|
{
|
||
|
case GS_LEVEL:
|
||
|
P_Ticker ();
|
||
|
SB_Ticker ();
|
||
|
AM_Ticker ();
|
||
|
CT_Ticker();
|
||
|
break;
|
||
|
case GS_INTERMISSION:
|
||
|
IN_Ticker ();
|
||
|
break;
|
||
|
case GS_FINALE:
|
||
|
F_Ticker();
|
||
|
break;
|
||
|
case GS_DEMOSCREEN:
|
||
|
D_PageTicker ();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
==============================================================================
|
||
|
|
||
|
PLAYER STRUCTURE FUNCTIONS
|
||
|
|
||
|
also see P_SpawnPlayer in P_Things
|
||
|
==============================================================================
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
====================
|
||
|
=
|
||
|
= G_InitPlayer
|
||
|
=
|
||
|
= Called at the start
|
||
|
= Called by the game initialization functions
|
||
|
====================
|
||
|
*/
|
||
|
|
||
|
void G_InitPlayer (int player)
|
||
|
{
|
||
|
player_t *p;
|
||
|
|
||
|
// set up the saved info
|
||
|
p = &players[player];
|
||
|
|
||
|
// clear everything else to defaults
|
||
|
G_PlayerReborn (player);
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
====================
|
||
|
=
|
||
|
= G_PlayerFinishLevel
|
||
|
=
|
||
|
= Can when a player completes a level
|
||
|
====================
|
||
|
*/
|
||
|
extern int curpos;
|
||
|
extern int inv_ptr;
|
||
|
extern int playerkeys;
|
||
|
|
||
|
void G_PlayerFinishLevel(int player)
|
||
|
{
|
||
|
player_t *p;
|
||
|
int i;
|
||
|
|
||
|
/* // BIG HACK
|
||
|
inv_ptr = 0;
|
||
|
curpos = 0;
|
||
|
*/
|
||
|
// END HACK
|
||
|
p = &players[player];
|
||
|
for(i=0; i<p->inventorySlotNum; i++)
|
||
|
{
|
||
|
p->inventory[i].count = 1;
|
||
|
}
|
||
|
p->artifactCount = p->inventorySlotNum;
|
||
|
|
||
|
if(!deathmatch)
|
||
|
{
|
||
|
for(i = 0; i < 16; i++)
|
||
|
{
|
||
|
P_PlayerUseArtifact(p, arti_fly);
|
||
|
}
|
||
|
}
|
||
|
memset(p->powers, 0, sizeof(p->powers));
|
||
|
memset(p->keys, 0, sizeof(p->keys));
|
||
|
playerkeys = 0;
|
||
|
// memset(p->inventory, 0, sizeof(p->inventory));
|
||
|
if(p->chickenTics)
|
||
|
{
|
||
|
p->readyweapon = p->mo->special1; // Restore weapon
|
||
|
p->chickenTics = 0;
|
||
|
}
|
||
|
p->messageTics = 0;
|
||
|
p->lookdir = 0;
|
||
|
p->mo->flags &= ~MF_SHADOW; // Remove invisibility
|
||
|
p->extralight = 0; // Remove weapon flashes
|
||
|
p->fixedcolormap = 0; // Remove torch
|
||
|
p->damagecount = 0; // No palette changes
|
||
|
p->bonuscount = 0;
|
||
|
p->rain1 = NULL;
|
||
|
p->rain2 = NULL;
|
||
|
if(p == &players[consoleplayer])
|
||
|
{
|
||
|
SB_state = -1; // refresh the status bar
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
====================
|
||
|
=
|
||
|
= G_PlayerReborn
|
||
|
=
|
||
|
= Called after a player dies
|
||
|
= almost everything is cleared and initialized
|
||
|
====================
|
||
|
*/
|
||
|
|
||
|
void G_PlayerReborn(int player)
|
||
|
{
|
||
|
player_t *p;
|
||
|
int i;
|
||
|
int frags[MAXPLAYERS];
|
||
|
int killcount, itemcount, secretcount;
|
||
|
boolean secret;
|
||
|
|
||
|
secret = false;
|
||
|
memcpy(frags, players[player].frags, sizeof(frags));
|
||
|
killcount = players[player].killcount;
|
||
|
itemcount = players[player].itemcount;
|
||
|
secretcount = players[player].secretcount;
|
||
|
|
||
|
p = &players[player];
|
||
|
if(p->didsecret)
|
||
|
{
|
||
|
secret = true;
|
||
|
}
|
||
|
memset(p, 0, sizeof(*p));
|
||
|
|
||
|
memcpy(players[player].frags, frags, sizeof(players[player].frags));
|
||
|
players[player].killcount = killcount;
|
||
|
players[player].itemcount = itemcount;
|
||
|
players[player].secretcount = secretcount;
|
||
|
|
||
|
p->usedown = p->attackdown = true; // don't do anything immediately
|
||
|
p->playerstate = PST_LIVE;
|
||
|
p->health = MAXHEALTH;
|
||
|
p->readyweapon = p->pendingweapon = wp_goldwand;
|
||
|
p->weaponowned[wp_staff] = true;
|
||
|
p->weaponowned[wp_goldwand] = true;
|
||
|
p->messageTics = 0;
|
||
|
p->lookdir = 0;
|
||
|
p->ammo[am_goldwand] = 50;
|
||
|
for(i = 0; i < NUMAMMO; i++)
|
||
|
{
|
||
|
p->maxammo[i] = maxammo[i];
|
||
|
}
|
||
|
if(gamemap == 9 || secret)
|
||
|
{
|
||
|
p->didsecret = true;
|
||
|
}
|
||
|
if(p == &players[consoleplayer])
|
||
|
{
|
||
|
SB_state = -1; // refresh the status bar
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
====================
|
||
|
=
|
||
|
= G_CheckSpot
|
||
|
=
|
||
|
= Returns false if the player cannot be respawned at the given mapthing_t spot
|
||
|
= because something is occupying it
|
||
|
====================
|
||
|
*/
|
||
|
|
||
|
void P_SpawnPlayer (mapthing_t *mthing);
|
||
|
|
||
|
boolean G_CheckSpot (int playernum, mapthing_t *mthing)
|
||
|
{
|
||
|
fixed_t x,y;
|
||
|
subsector_t *ss;
|
||
|
unsigned an;
|
||
|
mobj_t *mo;
|
||
|
|
||
|
x = mthing->x << FRACBITS;
|
||
|
y = mthing->y << FRACBITS;
|
||
|
|
||
|
players[playernum].mo->flags2 &= ~MF2_PASSMOBJ;
|
||
|
if (!P_CheckPosition (players[playernum].mo, x, y) )
|
||
|
{
|
||
|
players[playernum].mo->flags2 |= MF2_PASSMOBJ;
|
||
|
return false;
|
||
|
}
|
||
|
players[playernum].mo->flags2 |= MF2_PASSMOBJ;
|
||
|
|
||
|
// spawn a teleport fog
|
||
|
ss = R_PointInSubsector (x,y);
|
||
|
an = ( ANG45 * (mthing->angle/45) ) >> ANGLETOFINESHIFT;
|
||
|
|
||
|
mo = P_SpawnMobj (x+20*finecosine[an], y+20*finesine[an]
|
||
|
, ss->sector->floorheight+TELEFOGHEIGHT
|
||
|
, MT_TFOG);
|
||
|
|
||
|
if (players[consoleplayer].viewz != 1)
|
||
|
S_StartSound (mo, sfx_telept); // don't start sound on first frame
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
====================
|
||
|
=
|
||
|
= G_DeathMatchSpawnPlayer
|
||
|
=
|
||
|
= Spawns a player at one of the random death match spots
|
||
|
= called at level load and each death
|
||
|
====================
|
||
|
*/
|
||
|
|
||
|
void G_DeathMatchSpawnPlayer (int playernum)
|
||
|
{
|
||
|
int i,j;
|
||
|
int selections;
|
||
|
|
||
|
selections = deathmatch_p - deathmatchstarts;
|
||
|
if (selections < 4)
|
||
|
I_Error ("Only %i deathmatch spots, 4 required", selections);
|
||
|
|
||
|
for (j=0 ; j<20 ; j++)
|
||
|
{
|
||
|
i = P_Random() % selections;
|
||
|
if (G_CheckSpot (playernum, &deathmatchstarts[i]) )
|
||
|
{
|
||
|
deathmatchstarts[i].type = playernum+1;
|
||
|
P_SpawnPlayer (&deathmatchstarts[i]);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// no good spot, so the player will probably get stuck
|
||
|
P_SpawnPlayer (&playerstarts[playernum]);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
====================
|
||
|
=
|
||
|
= G_DoReborn
|
||
|
=
|
||
|
====================
|
||
|
*/
|
||
|
|
||
|
void G_DoReborn (int playernum)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
if (G_CheckDemoStatus ())
|
||
|
return;
|
||
|
if (!netgame)
|
||
|
gameaction = ga_loadlevel; // reload the level from scratch
|
||
|
else
|
||
|
{ // respawn at the start
|
||
|
players[playernum].mo->player = NULL; // dissasociate the corpse
|
||
|
|
||
|
// spawn at random spot if in death match
|
||
|
if (deathmatch)
|
||
|
{
|
||
|
G_DeathMatchSpawnPlayer (playernum);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (G_CheckSpot (playernum, &playerstarts[playernum]) )
|
||
|
{
|
||
|
P_SpawnPlayer (&playerstarts[playernum]);
|
||
|
return;
|
||
|
}
|
||
|
// try to spawn at one of the other players spots
|
||
|
for (i=0 ; i<MAXPLAYERS ; i++)
|
||
|
if (G_CheckSpot (playernum, &playerstarts[i]) )
|
||
|
{
|
||
|
playerstarts[i].type = playernum+1; // fake as other player
|
||
|
P_SpawnPlayer (&playerstarts[i]);
|
||
|
playerstarts[i].type = i+1; // restore
|
||
|
return;
|
||
|
}
|
||
|
// he's going to be inside something. Too bad.
|
||
|
P_SpawnPlayer (&playerstarts[playernum]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void G_ScreenShot (void)
|
||
|
{
|
||
|
gameaction = ga_screenshot;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
====================
|
||
|
=
|
||
|
= G_DoCompleted
|
||
|
=
|
||
|
====================
|
||
|
*/
|
||
|
|
||
|
boolean secretexit;
|
||
|
|
||
|
void G_ExitLevel (void)
|
||
|
{
|
||
|
secretexit = false;
|
||
|
gameaction = ga_completed;
|
||
|
}
|
||
|
|
||
|
void G_SecretExitLevel (void)
|
||
|
{
|
||
|
secretexit = true;
|
||
|
gameaction = ga_completed;
|
||
|
}
|
||
|
|
||
|
void G_DoCompleted(void)
|
||
|
{
|
||
|
int i;
|
||
|
static int afterSecret[3] = { 7, 5, 5 };
|
||
|
|
||
|
gameaction = ga_nothing;
|
||
|
if(G_CheckDemoStatus())
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
for(i = 0; i < MAXPLAYERS; i++)
|
||
|
{
|
||
|
if(playeringame[i])
|
||
|
{
|
||
|
G_PlayerFinishLevel(i);
|
||
|
}
|
||
|
}
|
||
|
prevmap = gamemap;
|
||
|
if(secretexit == true)
|
||
|
{
|
||
|
gamemap = 9;
|
||
|
}
|
||
|
else if(gamemap == 9)
|
||
|
{ // Finished secret level
|
||
|
gamemap = afterSecret[gameepisode-1];
|
||
|
}
|
||
|
else if(gamemap == 8)
|
||
|
{
|
||
|
gameaction = ga_victory;
|
||
|
return;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
gamemap++;
|
||
|
}
|
||
|
gamestate = GS_INTERMISSION;
|
||
|
IN_Start();
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
//
|
||
|
// G_WorldDone
|
||
|
//
|
||
|
//============================================================================
|
||
|
|
||
|
void G_WorldDone(void)
|
||
|
{
|
||
|
gameaction = ga_worlddone;
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
//
|
||
|
// G_DoWorldDone
|
||
|
//
|
||
|
//============================================================================
|
||
|
|
||
|
void G_DoWorldDone(void)
|
||
|
{
|
||
|
gamestate = GS_LEVEL;
|
||
|
G_DoLoadLevel();
|
||
|
gameaction = ga_nothing;
|
||
|
viewactive = true;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
//
|
||
|
// PROC G_LoadGame
|
||
|
//
|
||
|
// Can be called by the startup code or the menu task.
|
||
|
//
|
||
|
//---------------------------------------------------------------------------
|
||
|
|
||
|
char savename[256];
|
||
|
|
||
|
void G_LoadGame(char *name)
|
||
|
{
|
||
|
strcpy(savename, name);
|
||
|
gameaction = ga_loadgame;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
//
|
||
|
// PROC G_DoLoadGame
|
||
|
//
|
||
|
// Called by G_Ticker based on gameaction.
|
||
|
//
|
||
|
//---------------------------------------------------------------------------
|
||
|
|
||
|
#define VERSIONSIZE 16
|
||
|
|
||
|
void G_DoLoadGame(void)
|
||
|
{
|
||
|
int length;
|
||
|
int i;
|
||
|
int a, b, c;
|
||
|
char vcheck[VERSIONSIZE];
|
||
|
|
||
|
gameaction = ga_nothing;
|
||
|
|
||
|
length = M_ReadFile(savename, &savebuffer);
|
||
|
save_p = savebuffer+SAVESTRINGSIZE;
|
||
|
// Skip the description field
|
||
|
memset(vcheck, 0, sizeof(vcheck));
|
||
|
sprintf(vcheck, "version %i", VERSION);
|
||
|
if (strcmp (save_p, vcheck))
|
||
|
{ // Bad version
|
||
|
return;
|
||
|
}
|
||
|
save_p += VERSIONSIZE;
|
||
|
gameskill = *save_p++;
|
||
|
gameepisode = *save_p++;
|
||
|
gamemap = *save_p++;
|
||
|
for(i = 0; i < MAXPLAYERS; i++)
|
||
|
{
|
||
|
playeringame[i] = *save_p++;
|
||
|
}
|
||
|
// Load a base level
|
||
|
G_InitNew(gameskill, gameepisode, gamemap);
|
||
|
|
||
|
// Create leveltime
|
||
|
a = *save_p++;
|
||
|
b = *save_p++;
|
||
|
c = *save_p++;
|
||
|
leveltime = (a<<16)+(b<<8)+c;
|
||
|
|
||
|
// De-archive all the modifications
|
||
|
P_UnArchivePlayers();
|
||
|
P_UnArchiveWorld();
|
||
|
P_UnArchiveThinkers();
|
||
|
P_UnArchiveSpecials();
|
||
|
|
||
|
if(*save_p != SAVE_GAME_TERMINATOR)
|
||
|
{ // Missing savegame termination marker
|
||
|
I_Error("Bad savegame");
|
||
|
}
|
||
|
Z_Free(savebuffer);
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
//
|
||
|
// PROC G_SaveGame
|
||
|
//
|
||
|
// Called by the menu task. <description> is a 24 byte text string.
|
||
|
//
|
||
|
//---------------------------------------------------------------------------
|
||
|
|
||
|
void G_SaveGame(int slot, char *description)
|
||
|
{
|
||
|
savegameslot = slot;
|
||
|
strcpy(savedescription, description);
|
||
|
sendsave = true;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
//
|
||
|
// PROC G_DoSaveGame
|
||
|
//
|
||
|
// Called by G_Ticker based on gameaction.
|
||
|
//
|
||
|
//---------------------------------------------------------------------------
|
||
|
|
||
|
void G_DoSaveGame(void)
|
||
|
{
|
||
|
char name[100];
|
||
|
char name2[VERSIONSIZE];
|
||
|
char *description;
|
||
|
int length;
|
||
|
int i;
|
||
|
|
||
|
if(cdrom)
|
||
|
{
|
||
|
sprintf(name, SAVEGAMENAMECD"%d.hsg", savegameslot);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sprintf(name, SAVEGAMENAME"%d.hsg", savegameslot);
|
||
|
}
|
||
|
description = savedescription;
|
||
|
|
||
|
// Allocate save game buffer
|
||
|
save_p = savebuffer = Z_Malloc(SAVEGAMESIZE, PU_STATIC, NULL);
|
||
|
|
||
|
memcpy(save_p, description, SAVESTRINGSIZE);
|
||
|
save_p += SAVESTRINGSIZE;
|
||
|
memset(name2, 0, sizeof(name2));
|
||
|
sprintf(name2, "version %i",VERSION);
|
||
|
memcpy(save_p, name2, VERSIONSIZE);
|
||
|
save_p += VERSIONSIZE;
|
||
|
|
||
|
*save_p++ = gameskill;
|
||
|
*save_p++ = gameepisode;
|
||
|
*save_p++ = gamemap;
|
||
|
for(i = 0; i < MAXPLAYERS; i++)
|
||
|
{
|
||
|
*save_p++ = playeringame[i];
|
||
|
}
|
||
|
*save_p++ = leveltime>>16;
|
||
|
*save_p++ = leveltime>>8;
|
||
|
*save_p++ = leveltime;
|
||
|
|
||
|
P_ArchivePlayers();
|
||
|
P_ArchiveWorld();
|
||
|
P_ArchiveThinkers();
|
||
|
P_ArchiveSpecials();
|
||
|
|
||
|
// Send a savegame termination marker
|
||
|
*save_p++ = SAVE_GAME_TERMINATOR;
|
||
|
|
||
|
length = save_p-savebuffer;
|
||
|
if(length > SAVEGAMESIZE)
|
||
|
{
|
||
|
I_Error("Savegame buffer overrun");
|
||
|
}
|
||
|
M_WriteFile(name, savebuffer, length);
|
||
|
gameaction = ga_nothing;
|
||
|
savedescription[0] = 0;
|
||
|
Z_Free(savebuffer);
|
||
|
P_SetMessage(&players[consoleplayer], TXT_GAMESAVED, true);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
====================
|
||
|
=
|
||
|
= G_InitNew
|
||
|
=
|
||
|
= Can be called by the startup code or the menu task
|
||
|
= consoleplayer, displayplayer, playeringame[] should be set
|
||
|
====================
|
||
|
*/
|
||
|
|
||
|
skill_t d_skill;
|
||
|
int d_episode;
|
||
|
int d_map;
|
||
|
|
||
|
void G_DeferedInitNew (skill_t skill, int episode, int map)
|
||
|
{
|
||
|
d_skill = skill;
|
||
|
d_episode = episode;
|
||
|
d_map = map;
|
||
|
gameaction = ga_newgame;
|
||
|
}
|
||
|
|
||
|
void G_DoNewGame (void)
|
||
|
{
|
||
|
G_InitNew (d_skill, d_episode, d_map);
|
||
|
gameaction = ga_nothing;
|
||
|
}
|
||
|
|
||
|
extern int skytexture;
|
||
|
|
||
|
void G_InitNew(skill_t skill, int episode, int map)
|
||
|
{
|
||
|
int i;
|
||
|
int speed;
|
||
|
|
||
|
if(paused)
|
||
|
{
|
||
|
paused = false;
|
||
|
S_ResumeSound();
|
||
|
}
|
||
|
if(skill < sk_baby)
|
||
|
skill = sk_baby;
|
||
|
if(skill > sk_nightmare)
|
||
|
skill = sk_nightmare;
|
||
|
if(episode < 1)
|
||
|
episode = 1;
|
||
|
// Up to 9 episodes for testing
|
||
|
if(episode > 9)
|
||
|
episode = 9;
|
||
|
if(map < 1)
|
||
|
map = 1;
|
||
|
if(map > 9)
|
||
|
map = 9;
|
||
|
M_ClearRandom();
|
||
|
if(respawnparm)
|
||
|
{
|
||
|
respawnmonsters = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
respawnmonsters = false;
|
||
|
}
|
||
|
// Set monster missile speeds
|
||
|
speed = skill == sk_nightmare;
|
||
|
for(i = 0; MonsterMissileInfo[i].type != -1; i++)
|
||
|
{
|
||
|
mobjinfo[MonsterMissileInfo[i].type].speed
|
||
|
= MonsterMissileInfo[i].speed[speed]<<FRACBITS;
|
||
|
}
|
||
|
// Force players to be initialized upon first level load
|
||
|
for(i = 0; i < MAXPLAYERS; i++)
|
||
|
{
|
||
|
players[i].playerstate = PST_REBORN;
|
||
|
}
|
||
|
// Set up a bunch of globals
|
||
|
usergame = true; // will be set false if a demo
|
||
|
paused = false;
|
||
|
demorecording = false;
|
||
|
demoplayback = false;
|
||
|
viewactive = true;
|
||
|
gameepisode = episode;
|
||
|
gamemap = map;
|
||
|
gameskill = skill;
|
||
|
viewactive = true;
|
||
|
BorderNeedRefresh = true;
|
||
|
|
||
|
// Set the sky map
|
||
|
switch(episode)
|
||
|
{
|
||
|
case 1:
|
||
|
skytexture = R_TextureNumForName("SKY1");
|
||
|
break;
|
||
|
case 2:
|
||
|
skytexture = R_TextureNumForName("SKY2");
|
||
|
break;
|
||
|
case 3:
|
||
|
skytexture = R_TextureNumForName("SKY3");
|
||
|
break;
|
||
|
default:
|
||
|
skytexture = R_TextureNumForName("SKY1");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// give one null ticcmd_t
|
||
|
//
|
||
|
#if 0
|
||
|
gametic = 0;
|
||
|
maketic = 1;
|
||
|
for (i=0 ; i<MAXPLAYERS ; i++)
|
||
|
nettics[i] = 1; // one null event for this gametic
|
||
|
memset (localcmds,0,sizeof(localcmds));
|
||
|
memset (netcmds,0,sizeof(netcmds));
|
||
|
#endif
|
||
|
G_DoLoadLevel();
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
===============================================================================
|
||
|
|
||
|
DEMO RECORDING
|
||
|
|
||
|
===============================================================================
|
||
|
*/
|
||
|
|
||
|
#define DEMOMARKER 0x80
|
||
|
|
||
|
void G_ReadDemoTiccmd (ticcmd_t *cmd)
|
||
|
{
|
||
|
if (*demo_p == DEMOMARKER)
|
||
|
{ // end of demo data stream
|
||
|
G_CheckDemoStatus ();
|
||
|
return;
|
||
|
}
|
||
|
cmd->forwardmove = ((signed char)*demo_p++);
|
||
|
cmd->sidemove = ((signed char)*demo_p++);
|
||
|
cmd->angleturn = ((unsigned char)*demo_p++)<<8;
|
||
|
cmd->buttons = (unsigned char)*demo_p++;
|
||
|
cmd->lookfly = (unsigned char)*demo_p++;
|
||
|
cmd->arti = (unsigned char)*demo_p++;
|
||
|
}
|
||
|
|
||
|
void G_WriteDemoTiccmd (ticcmd_t *cmd)
|
||
|
{
|
||
|
if (gamekeydown['q']) // press q to end demo recording
|
||
|
G_CheckDemoStatus ();
|
||
|
*demo_p++ = cmd->forwardmove;
|
||
|
*demo_p++ = cmd->sidemove;
|
||
|
*demo_p++ = cmd->angleturn>>8;
|
||
|
*demo_p++ = cmd->buttons;
|
||
|
*demo_p++ = cmd->lookfly;
|
||
|
*demo_p++ = cmd->arti;
|
||
|
demo_p -= 6;
|
||
|
G_ReadDemoTiccmd (cmd); // make SURE it is exactly the same
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
=
|
||
|
= G_RecordDemo
|
||
|
=
|
||
|
===================
|
||
|
*/
|
||
|
|
||
|
void G_RecordDemo (skill_t skill, int numplayers, int episode, int map, char *name)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
G_InitNew (skill, episode, map);
|
||
|
usergame = false;
|
||
|
strcpy (demoname, name);
|
||
|
strcat (demoname, ".lmp");
|
||
|
demobuffer = demo_p = Z_Malloc (0x20000,PU_STATIC,NULL);
|
||
|
*demo_p++ = skill;
|
||
|
*demo_p++ = episode;
|
||
|
*demo_p++ = map;
|
||
|
|
||
|
for (i=0 ; i<MAXPLAYERS ; i++)
|
||
|
*demo_p++ = playeringame[i];
|
||
|
|
||
|
demorecording = true;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
=
|
||
|
= G_PlayDemo
|
||
|
=
|
||
|
===================
|
||
|
*/
|
||
|
|
||
|
char *defdemoname;
|
||
|
|
||
|
void G_DeferedPlayDemo (char *name)
|
||
|
{
|
||
|
defdemoname = name;
|
||
|
gameaction = ga_playdemo;
|
||
|
}
|
||
|
|
||
|
void G_DoPlayDemo (void)
|
||
|
{
|
||
|
skill_t skill;
|
||
|
int i, episode, map;
|
||
|
|
||
|
gameaction = ga_nothing;
|
||
|
demobuffer = demo_p = W_CacheLumpName (defdemoname, PU_STATIC);
|
||
|
skill = *demo_p++;
|
||
|
episode = *demo_p++;
|
||
|
map = *demo_p++;
|
||
|
|
||
|
for (i=0 ; i<MAXPLAYERS ; i++)
|
||
|
playeringame[i] = *demo_p++;
|
||
|
|
||
|
precache = false; // don't spend a lot of time in loadlevel
|
||
|
G_InitNew (skill, episode, map);
|
||
|
precache = true;
|
||
|
usergame = false;
|
||
|
demoplayback = true;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
=
|
||
|
= G_TimeDemo
|
||
|
=
|
||
|
===================
|
||
|
*/
|
||
|
|
||
|
void G_TimeDemo (char *name)
|
||
|
{
|
||
|
skill_t skill;
|
||
|
int episode, map;
|
||
|
|
||
|
demobuffer = demo_p = W_CacheLumpName (name, PU_STATIC);
|
||
|
skill = *demo_p++;
|
||
|
episode = *demo_p++;
|
||
|
map = *demo_p++;
|
||
|
G_InitNew (skill, episode, map);
|
||
|
usergame = false;
|
||
|
demoplayback = true;
|
||
|
timingdemo = true;
|
||
|
singletics = true;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
===================
|
||
|
=
|
||
|
= G_CheckDemoStatus
|
||
|
=
|
||
|
= Called after a death or level completion to allow demos to be cleaned up
|
||
|
= Returns true if a new demo loop action will take place
|
||
|
===================
|
||
|
*/
|
||
|
|
||
|
boolean G_CheckDemoStatus (void)
|
||
|
{
|
||
|
int endtime;
|
||
|
|
||
|
if (timingdemo)
|
||
|
{
|
||
|
endtime = I_GetTime ();
|
||
|
I_Error ("timed %i gametics in %i realtics",gametic
|
||
|
, endtime-starttime);
|
||
|
}
|
||
|
|
||
|
if (demoplayback)
|
||
|
{
|
||
|
if (singledemo)
|
||
|
I_Quit ();
|
||
|
|
||
|
Z_ChangeTag (demobuffer, PU_CACHE);
|
||
|
demoplayback = false;
|
||
|
D_AdvanceDemo ();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (demorecording)
|
||
|
{
|
||
|
*demo_p++ = DEMOMARKER;
|
||
|
M_WriteFile (demoname, demobuffer, demo_p - demobuffer);
|
||
|
Z_Free (demobuffer);
|
||
|
demorecording = false;
|
||
|
I_Error ("Demo %s recorded",demoname);
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
|