diff options
Diffstat (limited to 'src/strife/g_game.c')
-rw-r--r-- | src/strife/g_game.c | 2454 |
1 files changed, 2454 insertions, 0 deletions
diff --git a/src/strife/g_game.c b/src/strife/g_game.c new file mode 100644 index 00000000..19f3e1fe --- /dev/null +++ b/src/strife/g_game.c @@ -0,0 +1,2454 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 1993-1996 Id Software, Inc. +// Copyright(C) 2005 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: none +// +//----------------------------------------------------------------------------- + +#include <string.h> +#include <stdlib.h> +#include <math.h> + +#include "doomdef.h" +#include "doomkeys.h" +#include "doomstat.h" + +#include "deh_main.h" +#include "deh_misc.h" +#include "z_zone.h" +#include "f_finale.h" +#include "m_argv.h" +#include "m_controls.h" +#include "m_misc.h" +#include "m_menu.h" +#include "m_saves.h" // STRIFE +#include "m_random.h" +#include "i_system.h" +#include "i_timer.h" +#include "i_video.h" +#include "p_setup.h" +#include "p_saveg.h" +#include "p_tick.h" +#include "d_main.h" +#include "wi_stuff.h" +#include "hu_stuff.h" +#include "st_stuff.h" +#include "am_map.h" + +// Needs access to LFB. +#include "v_video.h" + +#include "w_wad.h" + +#include "p_local.h" + +#include "s_sound.h" + +// Data. +#include "dstrings.h" +#include "sounds.h" + +// SKY handling - still the wrong place. +#include "r_data.h" +#include "r_sky.h" + +#include "p_dialog.h" // villsa [STRIFE] + +#include "g_game.h" + + +#define SAVEGAMESIZE 0x2c000 + +void G_ReadDemoTiccmd (ticcmd_t* cmd); +void G_WriteDemoTiccmd (ticcmd_t* cmd); +void G_PlayerReborn (int player); + +void G_DoReborn (int playernum); + +void G_DoLoadLevel (void); +void G_DoNewGame (void); +void G_DoPlayDemo (void); +void G_DoCompleted (void); +void G_DoVictory (void); +void G_DoWorldDone (void); +void G_DoSaveGame (char *path); + +// Gamestate the last time G_Ticker was called. + +gamestate_t oldgamestate; + +gameaction_t gameaction; +gamestate_t gamestate; +skill_t gameskill = 2; // [STRIFE] Default value set to 2. +boolean respawnmonsters; +//int gameepisode; +int gamemap; + +// haleyjd 08/24/10: [STRIFE] New variables +int destmap; // current destination map when exiting +int riftdest; // destination spot for player +angle_t riftangle; // player angle saved during exit + +// If non-zero, exit the level after this number of minutes. + +int timelimit; + +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 +boolean nodrawers; // for comparative timing purposes +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]; + +boolean turbodetected[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; +boolean demorecording; +boolean longtics; // cph's doom 1.91 longtics hack +boolean lowres_turn; // low resolution turning for longtics +boolean demoplayback; +boolean netdemo; +byte* demobuffer; +byte* demo_p; +byte* demoend; +boolean singledemo; // quit after playing a demo from cmdline + +boolean precache = true; // if true, load all graphics at start + +boolean testcontrols = false; // Invoked by setup to test controls + +wbstartstruct_t wminfo; // parms for world map / intermission + +byte consistancy[MAXPLAYERS][BACKUPTICS]; + +#define MAXPLMOVE (forwardmove[1]) + +#define TURBOTHRESHOLD 0x32 + +fixed_t forwardmove[2] = {0x19, 0x32}; +fixed_t sidemove[2] = {0x18, 0x28}; +fixed_t angleturn[3] = {640, 1280, 320}; // + slow turn + +int mouse_fire_countdown = 0; // villsa [STRIFE] + +static int *weapon_keys[] = { + &key_weapon1, + &key_weapon2, + &key_weapon3, + &key_weapon4, + &key_weapon5, + &key_weapon6, + &key_weapon7, + &key_weapon8 +}; + +// Set to -1 or +1 to switch to the previous or next weapon. + +static int next_weapon = 0; + +// Used for prev/next weapon keys. +// STRIFE-TODO: Check this table makes sense. + +static const struct +{ + weapontype_t weapon; + weapontype_t weapon_num; +} weapon_order_table[] = { + { wp_fist, wp_fist }, + { wp_poisonbow, wp_elecbow }, + { wp_elecbow, wp_elecbow }, + { wp_rifle, wp_rifle }, + { wp_missile, wp_missile }, + { wp_wpgrenade, wp_hegrenade }, + { wp_hegrenade, wp_hegrenade }, + { wp_flame, wp_flame }, + { wp_torpedo, wp_mauler }, + { wp_mauler, wp_mauler }, + { wp_sigil, wp_sigil }, +}; + +#define SLOWTURNTICS 6 + +#define NUMKEYS 256 +#define MAX_JOY_BUTTONS 20 + +static boolean gamekeydown[NUMKEYS]; +static int turnheld; // for accelerative turning + +static boolean mousearray[MAX_MOUSE_BUTTONS + 1]; +static boolean *mousebuttons = &mousearray[1]; // allow [-1] + +// mouse values are used once +int mousex; +int mousey; + +static int dclicktime; +static boolean dclickstate; +static int dclicks; +static int dclicktime2; +static boolean dclickstate2; +static int dclicks2; + +// joystick values are repeated +static int joyxmove; +static int joyymove; +static boolean joyarray[MAX_JOY_BUTTONS + 1]; +static boolean *joybuttons = &joyarray[1]; // allow [-1] + +static int savegameslot = 6; // [STRIFE] initialized to 6 +static char savedescription[32]; + +int testcontrols_mousespeed; + +#define BODYQUESIZE 32 + +mobj_t* bodyque[BODYQUESIZE]; +//int bodyqueslot; [STRIFE] unused + +int vanilla_savegame_limit = 1; +int vanilla_demo_limit = 1; + + +int G_CmdChecksum (ticcmd_t* cmd) +{ + size_t i; + int sum = 0; + + for (i=0 ; i< sizeof(*cmd)/4 - 1 ; i++) + sum += ((int *)cmd)[i]; + + return sum; +} + +static boolean WeaponSelectable(weapontype_t weapon) +{ + player_t *player; + + player = &players[consoleplayer]; + + // Can't select a weapon if we don't own it. + + if (!player->weaponowned[weapon]) + { + return false; + } + + // Can't use registered-only weapons in demo mode: + + if (isdemoversion && !weaponinfo[weapon].availabledemo) + { + return false; + } + + // Special rules for switching to alternate versions of weapons. + // These must match the weapon-switching rules in P_PlayerThink() + + if (weapon == wp_torpedo + && player->ammo[weaponinfo[am_cell].ammo] < 30) + { + return false; + } + + if (player->ammo[weaponinfo[weapon].ammo] == 0) + { + return false; + } + + return true; +} + +static int G_NextWeapon(int direction) +{ + weapontype_t weapon; + int i; + + // Find index in the table. + + if (players[consoleplayer].pendingweapon == wp_nochange) + { + weapon = players[consoleplayer].readyweapon; + } + else + { + weapon = players[consoleplayer].pendingweapon; + } + + for (i=0; i<arrlen(weapon_order_table); ++i) + { + if (weapon_order_table[i].weapon == weapon) + { + break; + } + } + + // Switch weapon. + + do + { + i += direction; + i = (i + arrlen(weapon_order_table)) % arrlen(weapon_order_table); + } while (!WeaponSelectable(weapon_order_table[i].weapon)); + + return weapon_order_table[i].weapon_num; +} + +// +// 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 +// +void G_BuildTiccmd (ticcmd_t* cmd, int maketic) +{ + int i; + boolean strafe; + boolean bstrafe; + int speed; + int tspeed; + int forward; + int side; + + memset(cmd, 0, sizeof(ticcmd_t)); + + cmd->consistancy = + consistancy[consoleplayer][maketic%BACKUPTICS]; + + // villsa [STRIFE] look up key + if(gamekeydown[key_lookup]) + cmd->buttons2 |= BT2_LOOKUP; + + // villsa [STRIFE] look down key + if(gamekeydown[key_lookdown]) + cmd->buttons2 |= BT2_LOOKDOWN; + + // villsa [STRIFE] inventory use key + if(gamekeydown[key_invuse]) + { + player_t* player = &players[consoleplayer]; + if(player->numinventory > 0) + { + cmd->buttons2 |= BT2_INVUSE; + cmd->inventory = player->inventory[player->inventorycursor].sprite; + } + } + + // villsa [STRIFE] inventory drop key + if(gamekeydown[key_invdrop]) + { + player_t* player = &players[consoleplayer]; + if(player->numinventory > 0) + { + cmd->buttons2 |= BT2_INVDROP; + cmd->inventory = player->inventory[player->inventorycursor].sprite; + } + } + + // villsa [STRIFE] use medkit + if(gamekeydown[key_usehealth]) + cmd->buttons2 |= BT2_HEALTH; + + + + strafe = gamekeydown[key_strafe] || mousebuttons[mousebstrafe] + || joybuttons[joybstrafe]; + + // fraggle: support the old "joyb_speed = 31" hack which + // allowed an autorun effect + + speed = key_speed >= NUMKEYS + || joybspeed >= MAX_JOY_BUTTONS + || gamekeydown[key_speed] + || joybuttons[joybspeed]; + + forward = side = 0; + + // villsa [STRIFE] running causes centerview to occur + if(speed) + cmd->buttons2 |= BT2_CENTERVIEW; + + // villsa [STRIFE] disable running if low on health + if (players[consoleplayer].health <= 15) + speed = 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; + + // let movement keys cancel each other out + if (strafe) + { + if (gamekeydown[key_right]) + { + // fprintf(stderr, "strafe right\n"); + side += sidemove[speed]; + } + if (gamekeydown[key_left]) + { + // fprintf(stderr, "strafe left\n"); + 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]) + { + // fprintf(stderr, "up\n"); + forward += forwardmove[speed]; + } + if (gamekeydown[key_down]) + { + // fprintf(stderr, "down\n"); + forward -= forwardmove[speed]; + } + + if (joyymove < 0) + forward += forwardmove[speed]; + if (joyymove > 0) + forward -= forwardmove[speed]; + + if (gamekeydown[key_strafeleft] + || joybuttons[joybstrafeleft] + || mousebuttons[mousebstrafeleft]) + { + side -= sidemove[speed]; + } + + if (gamekeydown[key_straferight] + || joybuttons[joybstraferight] + || mousebuttons[mousebstraferight]) + { + side += sidemove[speed]; + } + + // buttons + cmd->chatchar = HU_dequeueChatChar(); + + // villsa [STRIFE] - add mouse button support for jump + if(gamekeydown[key_jump] || mousebuttons[mousebjump]) + cmd->buttons2 |= BT2_JUMP; + + // villsa [STRIFE]: Moved mousebuttons[mousebfire] to below + if (gamekeydown[key_fire] || joybuttons[joybfire]) + cmd->buttons |= BT_ATTACK; + + // villsa [STRIFE] + if(mousebuttons[mousebfire]) + { + if(mouse_fire_countdown <= 0) + cmd->buttons |= BT_ATTACK; + else + --mouse_fire_countdown; + } + + if (gamekeydown[key_use] + || joybuttons[joybuse] + || mousebuttons[mousebuse]) + { + cmd->buttons |= BT_USE; + // clear double clicks if hit use button + dclicks = 0; + } + + // If the previous or next weapon button is pressed, the + // next_weapon variable is set to change weapons when + // we generate a ticcmd. Choose a new weapon. + + if (next_weapon != 0) + { + i = G_NextWeapon(next_weapon); + cmd->buttons |= BT_CHANGE; + cmd->buttons |= i << BT_WEAPONSHIFT; + next_weapon = 0; + } + else + { + // Check weapon keys. + + for (i=0; i<arrlen(weapon_keys); ++i) + { + int key = *weapon_keys[i]; + + if (gamekeydown[key]) + { + cmd->buttons |= BT_CHANGE; + cmd->buttons |= i<<BT_WEAPONSHIFT; + break; + } + } + } + + // mouse + if (mousebuttons[mousebforward]) + { + forward += forwardmove[speed]; + } + if (mousebuttons[mousebbackward]) + { + forward -= forwardmove[speed]; + } + + if (dclick_use) + { + // 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; + } + } + } + + forward += mousey; + + if (strafe) + side += mousex*2; + else + cmd->angleturn -= mousex*0x8; + + if (mousex == 0) + { + // No movement in the previous frame + + testcontrols_mousespeed = 0; + } + + 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; + + // 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); + } + + // low-res turning + + if (lowres_turn) + { + static signed short carry = 0; + signed short desired_angleturn; + + desired_angleturn = cmd->angleturn + carry; + + // round angleturn to the nearest 256 unit boundary + // for recording demos with single byte values for turn + + cmd->angleturn = (desired_angleturn + 128) & 0xff00; + + // Carry forward the error from the reduced resolution to the + // next tic, so that successive small movements can accumulate. + + carry = desired_angleturn - cmd->angleturn; + } +} + + +// +// G_DoLoadLevel +// +void G_DoLoadLevel (void) +{ + int i; + + // haleyjd 10/03/10: [STRIFE] This is not done here. + //skyflatnum = R_FlatNumForName(DEH_String(SKYFLATNAME)); + + levelstarttic = gametic; // for time calculation + + if (wipegamestate == GS_LEVEL) + wipegamestate = -1; // force a wipe + + gamestate = GS_LEVEL; + + for (i=0 ; i<MAXPLAYERS ; i++) + { + turbodetected[i] = false; + + // haleyjd 20110204 [STRIFE]: PST_REBORN if players[i].health <= 0 + if (playeringame[i] && (players[i].playerstate == PST_DEAD || players[i].health <= 0)) + players[i].playerstate = PST_REBORN; + memset (players[i].frags,0,sizeof(players[i].frags)); + } + + P_SetupLevel (gamemap, 0, gameskill); + displayplayer = consoleplayer; // view the guy you are playing + starttime = I_GetTime(); // haleyjd 20110204 [STRIFE] + 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)); + + if (testcontrols) + { + players[consoleplayer].message = "Press escape to quit."; + } + + P_DialogLoad(); // villsa [STRIFE] +} + +static void SetJoyButtons(unsigned int buttons_mask) +{ + int i; + + for (i=0; i<MAX_JOY_BUTTONS; ++i) + { + int button_on = (buttons_mask & (1 << i)) != 0; + + // Detect button press: + + if (!joybuttons[i] && button_on) + { + // Weapon cycling: + + if (i == joybprevweapon) + { + next_weapon = -1; + } + else if (i == joybnextweapon) + { + next_weapon = 1; + } + } + + joybuttons[i] = button_on; + } +} + +static void SetMouseButtons(unsigned int buttons_mask) +{ + int i; + + for (i=0; i<MAX_MOUSE_BUTTONS; ++i) + { + unsigned int button_on = (buttons_mask & (1 << i)) != 0; + + // Detect button press: + + if (!mousebuttons[i] && button_on) + { + if (i == mousebprevweapon) + { + next_weapon = -1; + } + else if (i == mousebnextweapon) + { + next_weapon = 1; + } + } + + mousebuttons[i] = button_on; + } +} + +// +// G_Responder +// Get info needed to make ticcmd_ts for the players. +// +boolean G_Responder (event_t* ev) +{ + // allow spy mode changes even during the demo + if (gamestate == GS_LEVEL && ev->type == ev_keydown + && ev->data1 == key_spy && (singledemo || !gameskill) ) // [STRIFE]: o_O + { + // spy mode + do + { + displayplayer++; + if (displayplayer == MAXPLAYERS) + displayplayer = 0; + } while (!playeringame[displayplayer] && displayplayer != consoleplayer); + return true; + } + + // any other key pops up menu if in demos + if (gameaction == ga_nothing && !singledemo && + (demoplayback || gamestate == GS_DEMOSCREEN) + ) + { + if (ev->type == ev_keydown || + (ev->type == ev_mouse && ev->data1) || + (ev->type == ev_joystick && ev->data1) ) + { + if(devparm && ev->data1 == 'g') + D_PageTicker(); // [STRIFE]: wat? o_O + else + M_StartControlPanel (); + return true; + } + return false; + } + + if (gamestate == GS_LEVEL) + { +#if 0 + if (devparm && ev->type == ev_keydown && ev->data1 == ';') + { + G_DeathMatchSpawnPlayer (0); + return true; + } +#endif + if (HU_Responder (ev)) + return true; // chat ate the event + if (ST_Responder (ev)) + return true; // status window ate it + if (AM_Responder (ev)) + return true; // automap ate it + } + + if (gamestate == GS_FINALE) + { + if (F_Responder (ev)) + return true; // finale ate the event + } + + if (testcontrols && ev->type == ev_mouse) + { + // If we are invoked by setup to test the controls, save the + // mouse speed so that we can display it on-screen. + // Perform a low pass filter on this so that the thermometer + // appears to move smoothly. + + testcontrols_mousespeed = abs(ev->data2); + } + + // If the next/previous weapon keys are pressed, set the next_weapon + // variable to change weapons when the next ticcmd is generated. + + if (ev->type == ev_keydown && ev->data1 == key_prevweapon) + { + next_weapon = -1; + } + else if (ev->type == ev_keydown && ev->data1 == key_nextweapon) + { + next_weapon = 1; + } + + switch (ev->type) + { + case ev_keydown: + if (ev->data1 == key_pause) + { + sendpause = true; + } + else 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: + SetMouseButtons(ev->data1); + mousex = ev->data2*(mouseSensitivity+5)/10; + mousey = ev->data3*(mouseSensitivity+5)/10; + return true; // eat events + + case ev_joystick: + SetJoyButtons(ev->data1); + joyxmove = ev->data2; + joyymove = ev->data3; + return true; // eat events + + default: + break; + } + + return false; +} + +// +// G_Ticker +// Make ticcmd_ts for the players. +// +void G_Ticker (void) +{ + int i; + int 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(true); + M_SaveMoveHereToMap(); // [STRIFE] + M_ReadMisObj(); + break; + case ga_savegame: + M_SaveMoveMapToHere(); // [STRIFE] + M_SaveMisObj(savepath); + G_DoSaveGame(savepath); + break; + case ga_playdemo: + G_DoPlayDemo (); + break; + case ga_completed: + G_DoCompleted (); + break; + case ga_victory: + F_StartFinale (); + break; + case ga_worlddone: + G_DoWorldDone (); + break; + case ga_screenshot: + V_ScreenShot("STRIFE%02i.pcx"); // [STRIFE] file name, message + players[consoleplayer].message = DEH_String("STRIFE by Rogue entertainment"); + gameaction = ga_nothing; + break; + case ga_nothing: + break; + } + } + + // get commands, check consistancy, + // and build new consistancy check + buf = (gametic/ticdup)%BACKUPTICS; + + // STRIFE-TODO: pnameprefixes bullcrap + + for (i=0 ; i<MAXPLAYERS ; i++) + { + if (playeringame[i]) + { + cmd = &players[i].cmd; + + memcpy (cmd, &netcmds[i], sizeof(ticcmd_t)); + + if (demoplayback) + G_ReadDemoTiccmd (cmd); + if (demorecording) + G_WriteDemoTiccmd (cmd); + + // check for turbo cheats + + // check ~ 4 seconds whether to display the turbo message. + // store if the turbo threshold was exceeded in any tics + // over the past 4 seconds. offset the checking period + // for each player so messages are not displayed at the + // same time. + + if (cmd->forwardmove > TURBOTHRESHOLD) + { + turbodetected[i] = true; + } + + if ((gametic & 31) == 0 + && ((gametic >> 5) % MAXPLAYERS) == i + && turbodetected[i]) + { + static char turbomessage[80]; + extern char player_names[8][16]; + sprintf (turbomessage, "%s is turbo!", player_names[i]); + players[consoleplayer].message = turbomessage; + turbodetected[i] = false; + } + + if (netgame && !netdemo && !(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 (!character_name[0]) // [STRIFE] + strcpy (character_name, "NET GAME"); + savegameslot = + (players[i].cmd.buttons & BTS_SAVEMASK)>>BTS_SAVESHIFT; + gameaction = ga_savegame; + break; + } + } + } + } + + // Have we just finished displaying an intermission screen? + // haleyjd 08/23/10: [STRIFE] No intermission. + /* + if (oldgamestate == GS_INTERMISSION && gamestate != GS_INTERMISSION) + { + WI_End(); + } + */ + + oldgamestate = gamestate; + + // do main actions + switch (gamestate) + { + case GS_LEVEL: + P_Ticker (); + ST_Ticker (); + AM_Ticker (); + HU_Ticker (); + break; + + // haleyjd 08/23/10: [STRIFE] No intermission. + /* + case GS_INTERMISSION: + WI_Ticker (); + break; + */ + case GS_UNKNOWN: // STRIFE-TODO: What is this? is it ever used?? + F_WaitTicker(); + 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. +// +// [STRIFE] No such function. +/* +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. +// +// [STRIFE] No such function. The equivalent to this logic was moved into +// G_DoCompleted. +/* +void G_PlayerFinishLevel (int player) +{ + player_t* p; + + p = &players[player]; + + memset (p->powers, 0, sizeof (p->powers)); + memset (p->cards, 0, sizeof (p->cards)); + p->mo->flags &= ~MF_SHADOW; // cancel invisibility + p->extralight = 0; // cancel gun flashes + p->fixedcolormap = 0; // cancel ir gogles + p->damagecount = 0; // no palette changes + p->bonuscount = 0; +} +*/ + +// +// G_PlayerReborn +// Called after a player dies +// almost everything is cleared and initialized +// +// [STRIFE] Small changes for allegiance, inventory, health auto-use, and +// mission objective. +// +void G_PlayerReborn (int player) +{ + player_t* p; + int i; + int frags[MAXPLAYERS]; + int killcount; + int allegiance; + + killcount = players[player].killcount; + allegiance = players[player].allegiance; // [STRIFE] + + memcpy(frags,players[player].frags,sizeof(frags)); + + p = &players[player]; + memset (p, 0, sizeof(*p)); + + memcpy(p->frags, frags, sizeof(p->frags)); + + p->usedown = true; // don't do anything immediately + p->attackdown = true; + p->inventorydown = true; // villsa [STRIFE] + p->playerstate = PST_LIVE; + p->health = deh_initial_health; // Use dehacked value + p->readyweapon = wp_fist; // villsa [STRIFE] default to fists + p->pendingweapon = wp_fist; // villsa [STRIFE] default to fists + p->weaponowned[wp_fist] = true; // villsa [STRIFE] default to fists + p->cheats |= CF_AUTOHEALTH; // villsa [STRIFE] + p->killcount = killcount; + p->allegiance = allegiance; // villsa [STRIFE] + p->centerview = true; // villsa [STRIFE] + + for(i = 0; i < NUMAMMO; i++) + p->maxammo[i] = maxammo[i]; + + // [STRIFE] clear inventory + for(i = 0; i < 32; i++) + p->inventory[i].type = NUMMOBJTYPES; + + // villsa [STRIFE]: Default objective + strncpy(mission_objective, DEH_String("Find help"), OBJECTIVE_LEN); +} + +// +// G_CheckSpot +// Returns false if the player cannot be respawned +// at the given mapthing_t spot +// because something is occupying it +// +// [STRIFE] Changed to eliminate body queue and an odd error message was added. +// +void P_SpawnPlayer (mapthing_t* mthing); + +boolean +G_CheckSpot +( int playernum, + mapthing_t* mthing ) +{ + fixed_t x; + fixed_t y; + subsector_t* ss; + unsigned an; + mobj_t* mo; + int i; + + if (!players[playernum].mo) + { + // [STRIFE] weird error message added here: + if(leveltime > 0) + players[playernum].message = DEH_String("you didn't have a body!"); + + // first spawn of level, before corpses + for (i=0 ; i<playernum ; i++) + if (players[i].mo->x == mthing->x << FRACBITS + && players[i].mo->y == mthing->y << FRACBITS) + return false; + return true; + } + + x = mthing->x << FRACBITS; + y = mthing->y << FRACBITS; + + if (!P_CheckPosition (players[playernum].mo, x, y) ) + return false; + + // flush an old corpse if needed + // [STRIFE] player corpses remove themselves after a short time, so + // evidently this wasn't needed. + /* + if (bodyqueslot >= BODYQUESIZE) + P_RemoveMobj (bodyque[bodyqueslot%BODYQUESIZE]); + bodyque[bodyqueslot%BODYQUESIZE] = players[playernum].mo; + bodyqueslot++; + */ + + // spawn a teleport fog + ss = R_PointInSubsector (x,y); + an = ( ANG45 * (((unsigned int) mthing->angle)/45) ) >> ANGLETOFINESHIFT; + + mo = P_SpawnMobj (x+20*finecosine[an], y+20*finesine[an] + , ss->sector->floorheight + , 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 +// +// [STRIFE]: Modified exit message to match binary. +// +void G_DeathMatchSpawnPlayer (int playernum) +{ + int i,j; + int selections; + + selections = deathmatch_p - deathmatchstarts; + if (selections < 4) + I_Error ("Only %i deathmatch spots, at least 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_LoadPath +// +// haleyjd 20101003: [STRIFE] New function +// Sets loadpath based on the map and "savepathtemp" +// +void G_LoadPath(int map) +{ + char mapbuf[33]; + + memset(mapbuf, 0, sizeof(mapbuf)); + sprintf(mapbuf, "%d", map); + + // haleyjd: free if already set, and use M_SafeFilePath + if(loadpath) + Z_Free(loadpath); + loadpath = M_SafeFilePath(savepathtemp, mapbuf); +} + +// +// G_DoReborn +// +void G_DoReborn (int playernum) +{ + int i; + + if (!netgame) + { + // reload the level from scratch + // [STRIFE] Reborn level load + G_LoadPath(gamemap); + gameaction = ga_loadgame; + } + else + { + // respawn at the start + + // first dissasociate the corpse + // [STRIFE] Checks for NULL first + if(players[playernum].mo) + players[playernum].mo->player = NULL; + + // 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]); + } +} + +// +// G_ScreenShot +// +// [STRIFE] Verified unmodified +// +void G_ScreenShot (void) +{ + gameaction = ga_screenshot; +} + +// haleyjd 20100823: [STRIFE] Removed par times. + +// +// G_DoCompleted +// +//boolean secretexit; +extern char* pagename; + +// +// G_RiftExitLevel +// +// haleyjd 20100824: [STRIFE] New function +// * Called from some exit linedefs to exit to a specific riftspot in the +// given destination map. +// +void G_RiftExitLevel(int map, int spot, angle_t angle) +{ + gameaction = ga_completed; + + // special handling for post-Sigil map changes + if(players[0].weaponowned[wp_sigil]) + { + if(map == 3) // Front Base -> Abandoned Front Base + map = 30; + if(map == 7) // Castle -> New Front Base + map = 10; + } + + // no rifting in deathmatch games + if(deathmatch) + spot = 0; + + riftangle = angle; + riftdest = spot; + destmap = map; +} + +// +// G_Exit2 +// +// haleyjd 20101003: [STRIFE] New function. +// No xrefs to this, doesn't seem to be used. Could have gotten inlined +// somewhere but I haven't seen it. +// +void G_Exit2(int dest, angle_t angle) +{ + riftdest = dest; + gameaction = ga_completed; + riftangle = angle; + destmap = gamemap; +} + +// +// G_ExitLevel +// +// haleyjd 20100824: [STRIFE]: +// * Default to next map in numeric order; init destmap and riftdest. +// +void G_ExitLevel (int dest) +{ + if(dest == 0) + dest = gamemap + 1; + destmap = dest; + riftdest = 0; + gameaction = ga_completed; +} + +/* +// haleyjd 20100823: [STRIFE] No secret exits in Strife. +// Here's for the german edition. +void G_SecretExitLevel (void) +{ + // IF NO WOLF3D LEVELS, NO SECRET EXIT! + if ( (gamemode == commercial) + && (W_CheckNumForName("map31")<0)) + secretexit = false; + else + secretexit = true; + gameaction = ga_completed; +} +*/ + +// +// G_StartFinale +// +// haleyjd 20100921: [STRIFE] New function. +// This replaced G_SecretExitLevel in Strife. I don't know that it's actually +// used anywhere in the game, but it *is* usable in mods via linetype 124, +// W1 Start Finale. +// +void G_StartFinale(void) +{ + gameaction = ga_victory; +} + +// +// G_DoCompleted +// +// haleyjd 20100823: [STRIFE]: +// * Removed G_PlayerFinishLevel and just sets some powerup states. +// * Removed Chex, as not relevant to Strife. +// * Removed DOOM level transfer logic +// * Removed intermission code. +// * Added setting gameaction to ga_worlddone. +// +void G_DoCompleted (void) +{ + int i; + + // deal with powerup states + for(i = 0; i < MAXPLAYERS; i++) + { + if(playeringame[i]) + { + // [STRIFE] restore pw_allmap power from mapstate cache + if(destmap < 40) + players[i].powers[pw_allmap] = players[i].mapstate[destmap]; + + // Shadowarmor doesn't persist between maps in netgames + if(netgame) + players[i].powers[pw_invisibility] = 0; + } + } + + stonecold = false; // villsa [STRIFE] + + if (automapactive) + AM_Stop (); + + // [STRIFE] HUB SAVE + if(!deathmatch) + G_DoSaveGame(savepathtemp); + + gameaction = ga_worlddone; +} + + +// haleyjd 20100824: [STRIFE] No secret exits. +/* +// +// G_WorldDone +// +void G_WorldDone (void) +{ + gameaction = ga_worlddone; + + if (secretexit) + players[consoleplayer].didsecret = true; + + if ( gamemode == commercial ) + { + switch (gamemap) + { + case 15: + case 31: + if (!secretexit) + break; + case 6: + case 11: + case 20: + case 30: + F_StartFinale (); + break; + } + } +} +*/ + +// +// G_RiftPlayer +// +// haleyjd 20100824: [STRIFE] New function +// Teleports the player to the appropriate rift spot. +// +void G_RiftPlayer(void) +{ + if(riftdest) + { + P_TeleportMove(players[0].mo, + riftSpots[riftdest - 1].x << FRACBITS, + riftSpots[riftdest - 1].y << FRACBITS); + players[0].mo->angle = riftangle; + players[0].mo->health = players[0].health; + } +} + +// +// G_RiftCheat +// +// haleyjd 20100824: [STRIFE] New function +// Called from the cheat code to jump to a rift spot. +// +boolean G_RiftCheat(int riftSpotNum) +{ + return P_TeleportMove(players[0].mo, + riftSpots[riftSpotNum - 1].x << FRACBITS, + riftSpots[riftSpotNum - 1].y << FRACBITS); +} + +// +// G_DoWorldDone +// +// haleyjd 20100824: [STRIFE] Added destmap -> gamemap set. +// +void G_DoWorldDone (void) +{ + int temp_leveltime = leveltime; + boolean temp_shadow = false; + boolean temp_mvis = false; + + gamestate = GS_LEVEL; + gamemap = destmap; + + // [STRIFE] HUB LOAD + G_LoadPath(destmap); + if (!deathmatch) + { + // Remember Shadowarmor across hub loads + if(players[0].mo->flags & MF_SHADOW) + temp_shadow = true; + if(players[0].mo->flags & MF_MVIS) + temp_mvis = true; + } + G_DoLoadGame(false); + + // [STRIFE] leveltime carries over between maps + leveltime = temp_leveltime; + + if(!deathmatch) + { + // [STRIFE]: transfer saved powerups + players[0].mo->flags &= ~(MF_SHADOW|MF_MVIS); + if(temp_shadow) + players[0].mo->flags |= MF_SHADOW; + if(temp_mvis) + players[0].mo->flags |= MF_MVIS; + + // [STRIFE] HUB SAVE + G_RiftPlayer(); + G_DoSaveGame(savepathtemp); + M_SaveMisObj(savepathtemp); + } + + gameaction = ga_nothing; + viewactive = true; +} + +// +// G_DoWorldDone2 +// +// haleyjd 20101003: [STRIFE] New function. No xrefs; unused. +// +void G_DoWorldDone2(void) +{ + gamestate = GS_LEVEL; + gameaction = ga_nothing; + viewactive = true; +} + +// +// G_ReadCurrent +// +// haleyjd 20101003: [STRIFE] New function. +// Reads the "CURRENT" file from the given path and then sets it to +// gamemap. +// +void G_ReadCurrent(const char *path) +{ + char *temppath = NULL; + byte *buffer = NULL; + + temppath = M_SafeFilePath(path, "\\current"); + + if(M_ReadFile(temppath, &buffer) <= 0) + gameaction = ga_newgame; + else + { + // haleyjd 20110211: do endian-correct read + gamemap = (((int)buffer[0]) | + ((int)buffer[1] << 8) | + ((int)buffer[2] << 16) | + ((int)buffer[3] << 24)); + gameaction = ga_loadgame; + Z_Free(buffer); + } + + Z_Free(temppath); + + G_LoadPath(gamemap); +} + +// +// G_InitFromSavegame +// Can be called by the startup code or the menu task. +// +extern boolean setsizeneeded; +void R_ExecuteSetViewSize (void); + +char savename[256]; + +// [STRIFE]: No such function. +/* +void G_LoadGame (char* name) +{ + strcpy (savename, name); + gameaction = ga_loadgame; +} +*/ + +// haleyjd 20100928: [STRIFE] VERSIONSIZE == 8 +#define VERSIONSIZE 8 + +void G_DoLoadGame (boolean userload) +{ + int savedleveltime; + + gameaction = ga_nothing; + + save_stream = fopen(loadpath, "rb"); + + // [STRIFE] If the file does not exist, G_DoLoadLevel is called. + if (save_stream == NULL) + { + G_DoLoadLevel(); + return; + } + + savegame_error = false; + + if (!P_ReadSaveGameHeader()) + { + fclose(save_stream); + return; + } + + // haleyjd: A comment would be good here, fraggle... + // Evidently this is a Choco-ism, necessitated by reading the savegame + // header *before* calling G_DoLoadLevel. + savedleveltime = leveltime; + + // load a base level + + // STRIFE-TODO: ???? + if(userload) + G_InitNew(gameskill, gamemap); + else + G_DoLoadLevel(); + + leveltime = savedleveltime; + + // dearchive all the modifications + // [STRIFE] some portions of player_t are not overwritten when loading + // between hub levels + P_UnArchivePlayers (userload); + P_UnArchiveWorld (); + P_UnArchiveThinkers (); + P_UnArchiveSpecials (); + + if (!P_ReadSaveGameEOF()) + I_Error ("Bad savegame"); + + fclose(save_stream); + + if (setsizeneeded) + R_ExecuteSetViewSize (); + + // draw the pattern into the back screen + R_FillBackScreen (); +} + +// +// G_WriteSaveName +// +// haleyjd 20101003: [STRIFE] New function +// +// Writes the character name to the NAME file. +// +boolean G_WriteSaveName(int slot, const char *charname) +{ + //char savedir[16]; + char *tmpname; + boolean retval; + + savegameslot = slot; + + // haleyjd: removed special -cdrom treatment, as I believe it is taken + // care of automatically via using Choco's savegamedir setting. + + // haleyjd: free previous path, if any, and allocate new one using + // M_SafeFilePath routine, which isn't limited to 128 characters. + if(savepathtemp) + Z_Free(savepathtemp); + savepathtemp = M_SafeFilePath(savegamedir, "strfsav6.ssg"); + + // haleyjd: as above. + if(savepath) + Z_Free(savepath); + savepath = M_SafeFilePath(savegamedir, M_MakeStrifeSaveDir(savegameslot, "")); + + // haleyjd: memset full character_name for safety + memset(character_name, 0, CHARACTER_NAME_LEN); + strcpy(character_name, charname); + + // haleyjd: use M_SafeFilePath + tmpname = M_SafeFilePath(savepathtemp, "name"); + + // Write the "name" file under the directory + retval = M_WriteFile(tmpname, character_name, 32); + + Z_Free(tmpname); + + return retval; +} + +// +// G_SaveGame +// Called by the menu task. +// Description is a 24 byte text string +// +// [STRIFE] No such function, at least in v1.2 +// STRIFE-TODO: Does this make a comeback in v1.31? +/* +void +G_SaveGame +( int slot, + char* description ) +{ + savegameslot = slot; + strcpy (savedescription, description); + sendsave = true; +} +*/ + +void G_DoSaveGame (char *path) +{ + char *current_path; + char *savegame_file; + char *temp_savegame_file; + byte gamemapbytes[4]; + char gamemapstr[33]; + + temp_savegame_file = P_TempSaveGameFile(); + + // [STRIFE] custom save file path logic + memset(gamemapstr, 0, sizeof(gamemapstr)); + sprintf(gamemapstr, "%d", gamemap); + savegame_file = M_SafeFilePath(path, gamemapstr); + + // [STRIFE] write the "current" file, which tells which hub map + // the save slot is currently on. + current_path = M_SafeFilePath(path, "current"); + // haleyjd: endian-agnostic IO + gamemapbytes[0] = (byte)( gamemap & 0xff); + gamemapbytes[1] = (byte)((gamemap >> 8) & 0xff); + gamemapbytes[2] = (byte)((gamemap >> 16) & 0xff); + gamemapbytes[3] = (byte)((gamemap >> 24) & 0xff); + M_WriteFile(current_path, gamemapbytes, 4); + Z_Free(current_path); + + // Open the savegame file for writing. We write to a temporary file + // and then rename it at the end if it was successfully written. + // This prevents an existing savegame from being overwritten by + // a corrupted one, or if a savegame buffer overrun occurs. + + save_stream = fopen(temp_savegame_file, "wb"); + + if (save_stream == NULL) + { + return; + } + + savegame_error = false; + + P_WriteSaveGameHeader(savedescription); + + P_ArchivePlayers (); + P_ArchiveWorld (); + P_ArchiveThinkers (); + P_ArchiveSpecials (); + + P_WriteSaveGameEOF(); + + // Enforce the same savegame size limit as in Vanilla Doom, + // except if the vanilla_savegame_limit setting is turned off. + // [STRIFE]: Verified subject to same limit. + + if (vanilla_savegame_limit && ftell(save_stream) > SAVEGAMESIZE) + { + I_Error ("Savegame buffer overrun"); + } + + // Finish up, close the savegame file. + + fclose(save_stream); + + // Now rename the temporary savegame file to the actual savegame + // file, overwriting the old savegame if there was one there. + + remove(savegame_file); + rename(temp_savegame_file, savegame_file); + + // haleyjd: free the savegame_file path + Z_Free(savegame_file); + + gameaction = ga_nothing; + //strcpy(savedescription, ""); + + // [STRIFE]: custom message logic + if(!strcmp(path, savepath)) + { + sprintf(savename, "%s saved.", character_name); + players[consoleplayer].message = savename; + } + + // draw the pattern into the back screen + R_FillBackScreen (); +} + + +// +skill_t d_skill; +//int d_episode; [STRIFE] No such thing as episodes in Strife +int d_map; + +// +// G_DeferedInitNew +// +// Can be called by the startup code or the menu task, +// consoleplayer, displayplayer, playeringame[] should be set. +// +// haleyjd 20100922: [STRIFE] Removed episode parameter +// +void G_DeferedInitNew(skill_t skill, int map) +{ + d_skill = skill; + d_map = map; + gameaction = ga_newgame; +} + +// +// G_DoNewGame +// +// [STRIFE] Code added to turn off the stonecold effect. +// Someone also removed the nomonsters reset... +// +void G_DoNewGame (void) +{ + demoplayback = false; + netdemo = false; + netgame = false; + deathmatch = false; + playeringame[1] = playeringame[2] = playeringame[3] = 0; + respawnparm = false; + fastparm = false; + stonecold = false; // villsa [STRIFE] + //nomonsters = false; [STRIFE] not set here!?! + consoleplayer = 0; + G_InitNew (d_skill, d_map); + gameaction = ga_nothing; +} + +// +// G_InitNew +// +// haleyjd 20100824: [STRIFE]: +// * Added riftdest initialization +// * Removed episode parameter +// +void +G_InitNew +( skill_t skill, + int map ) +{ + char *skytexturename; + int i; + + if (paused) + { + paused = false; + S_ResumeSound (); + } + + if (skill > sk_nightmare) + skill = sk_nightmare; + + // [STRIFE] Removed episode nonsense and gamemap clipping + + M_ClearRandom (); + + if (skill == sk_nightmare || respawnparm ) + respawnmonsters = true; + else + respawnmonsters = false; + + // [STRIFE] Strife skill level mobjinfo/states tweaking + // BUG: None of this code runs properly when loading save games, so + // basically it's impossible to play any skill level properly unless + // you never quit and reload from the command line. + if(!skill && gameskill) + { + // Setting to Baby skill... make things easier. + + // Acolytes walk, attack, and feel pain slower + for(i = S_AGRD_13; i <= S_AGRD_23; i++) + states[i].tics *= 2; + + // Reavers attack slower + for(i = S_ROB1_10; i <= S_ROB1_15; i++) + states[i].tics *= 2; + + // Turrets attack slower + for(i = S_TURT_02; i <= S_TURT_03; i++) + states[i].tics *= 2; + + // Crusaders attack and feel pain slower + for(i = S_ROB2_09; i <= S_ROB2_19; i++) + states[i].tics *= 2; + + // Stalkers think, walk, and attack slower + for(i = S_SPID_03; i <= S_SPID_10; i++) + states[i].tics *= 2; + + // The Bishop's homing missiles are faster (what?? BUG?) + mobjinfo[MT_SEEKMISSILE].speed *= 2; + } + if(skill && !gameskill) + { + // Setting a higher skill when previously on baby... make things normal + + // Acolytes + for(i = S_AGRD_13; i <= S_AGRD_23; i++) + states[i].tics >>= 1; + + // Reavers + for(i = S_ROB1_10; i <= S_ROB1_15; i++) + states[i].tics >>= 1; + + // Turrets + for(i = S_TURT_02; i <= S_TURT_03; i++) + states[i].tics >>= 1; + + // Crusaders + for(i = S_ROB2_09; i <= S_ROB2_19; i++) + states[i].tics >>= 1; + + // Stalkers + for(i = S_SPID_03; i <= S_SPID_10; i++) + states[i].tics >>= 1; + + // The Bishop's homing missiles - again, seemingly backward. + mobjinfo[MT_SEEKMISSILE].speed >>= 1; + } + if(fastparm || (skill == sk_nightmare && skill != gameskill)) + { + // BLOODBATH! Make some things super-aggressive. + + // Acolytes walk, attack, and feel pain twice as fast + // (This makes just getting out of the first room almost impossible) + for(i = S_AGRD_13; i <= S_AGRD_23; i++) + states[i].tics >>= 1; + + // Bishop's homing missiles again get SLOWER and not faster o_O + mobjinfo[MT_SEEKMISSILE].speed >>= 1; + } + else if(skill != sk_nightmare && gameskill == sk_nightmare) + { + // Setting back to an ordinary skill after being on Bloodbath? + // Put stuff back to normal. + + // Acolytes + for(i = S_AGRD_13; i <= S_AGRD_23; i++) + states[i].tics *= 2; + + // Bishop's homing missiles + mobjinfo[MT_SEEKMISSILE].speed *= 2; + } + + // force players to be initialized upon first level load + for (i=0 ; i<MAXPLAYERS ; i++) + players[i].playerstate = PST_REBORN; + + usergame = true; // will be set false if a demo + paused = false; + demoplayback = false; + automapactive = false; + viewactive = true; + //gameepisode = episode; [STRIFE] no episodes + gamemap = map; + gameskill = skill; + riftdest = 0; // haleyjd 08/24/10: [STRIFE] init riftdest to zero on new game + + // Set the sky to use. + // + // Note: This IS broken, but it is how Vanilla Doom behaves. + // See http://doomwiki.org/wiki/Sky_never_changes_in_Doom_II. + // + // Because we set the sky here at the start of a game, not at the + // start of a level, the sky texture never changes unless we + // restore from a saved game. This was fixed before the Doom + // source release, but this IS the way Vanilla DOS Doom behaves. + + // [STRIFE] Strife skies (of which there are but two) + if(gamemap >= 9 && gamemap < 32) + skytexturename = "skymnt01"; + else + skytexturename = "skymnt02"; + + skytexturename = DEH_String(skytexturename); + + skytexture = R_TextureNumForName(skytexturename); + + // [STRIFE] HUBS + G_LoadPath(gamemap); + G_DoLoadLevel(); +} + + +// +// DEMO RECORDING +// +#define DEMOMARKER 0x80 + +// +// G_ReadDemoTiccmd +// +// [STRIFE] Modified for Strife ticcmd_t +// +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->buttons2 = (unsigned char)*demo_p++; // [STRIFE] + cmd->inventory = (int)*demo_p++; // [STRIFE] +} + +// Increase the size of the demo buffer to allow unlimited demos + +static void IncreaseDemoBuffer(void) +{ + int current_length; + byte *new_demobuffer; + byte *new_demop; + int new_length; + + // Find the current size + + current_length = demoend - demobuffer; + + // Generate a new buffer twice the size + new_length = current_length * 2; + + new_demobuffer = Z_Malloc(new_length, PU_STATIC, 0); + new_demop = new_demobuffer + (demo_p - demobuffer); + + // Copy over the old data + + memcpy(new_demobuffer, demobuffer, current_length); + + // Free the old buffer and point the demo pointers at the new buffer. + + Z_Free(demobuffer); + + demobuffer = new_demobuffer; + demo_p = new_demop; + demoend = demobuffer + new_length; +} + +// +// G_WriteDemoTiccmd +// +// [STRIFE] Modified for Strife ticcmd_t. +// +void G_WriteDemoTiccmd (ticcmd_t* cmd) +{ + byte *demo_start; + + if (gamekeydown[key_demo_quit]) // press q to end demo recording + G_CheckDemoStatus (); + + demo_start = demo_p; + + *demo_p++ = cmd->forwardmove; + *demo_p++ = cmd->sidemove; + *demo_p++ = cmd->angleturn >> 8; + *demo_p++ = cmd->buttons; + *demo_p++ = cmd->buttons2; // [STRIFE] + *demo_p++ = (byte)(cmd->inventory & 0xff); // [STRIFE] + + // reset demo pointer back + demo_p = demo_start; + + if (demo_p > demoend - 16) + { + if (vanilla_demo_limit) + { + // no more space + G_CheckDemoStatus (); + return; + } + else + { + // Vanilla demo limit disabled: unlimited + // demo lengths! + + IncreaseDemoBuffer(); + } + } + + G_ReadDemoTiccmd (cmd); // make SURE it is exactly the same +} + + + +// +// G_RecordDemo +// +// [STRIFE] Verified unmodified +// +void G_RecordDemo (char* name) +{ + int i; + int maxsize; + + usergame = false; + demoname = Z_Malloc(strlen(name) + 5, PU_STATIC, NULL); + sprintf(demoname, "%s.lmp", name); + maxsize = 0x20000; + + //! + // @arg <size> + // @category demo + // @vanilla + // + // Specify the demo buffer size (KiB) + // + + i = M_CheckParmWithArgs("-maxdemo", 1); + if (i) + maxsize = atoi(myargv[i+1])*1024; + demobuffer = Z_Malloc (maxsize,PU_STATIC,NULL); + demoend = demobuffer + maxsize; + + demorecording = true; +} + + +void G_BeginRecording (void) +{ + int i; + + // + // @category demo + // + // Record a high resolution "Doom 1.91" demo. + // + + // STRIFE-TODO: if somebody makes a "Strife Plus", we could add this. + /* + longtics = M_CheckParm("-longtics") != 0; + */ + longtics = false; + + // If not recording a longtics demo, record in low res + lowres_turn = !longtics; + + demo_p = demobuffer; + + // Save the right version code for this demo + *demo_p++ = STRIFE_VERSION; + + *demo_p++ = gameskill; + //*demo_p++ = gameepisode; [STRIFE] Doesn't have episodes. + *demo_p++ = gamemap; + *demo_p++ = deathmatch; + *demo_p++ = respawnparm; + *demo_p++ = fastparm; + *demo_p++ = nomonsters; + *demo_p++ = consoleplayer; + + for (i=0 ; i<MAXPLAYERS ; i++) + *demo_p++ = playeringame[i]; +} + + +// +// G_PlayDemo +// + +char* defdemoname; + +// +// G_DeferedPlayDemo +// +// [STRIFE] Verified unmodified +// +void G_DeferedPlayDemo (char* name) +{ + defdemoname = name; + gameaction = ga_playdemo; +} + +// Generate a string describing a demo version +// [STRIFE] Modified to handle the one and only Strife demo version. +static char *DemoVersionDescription(int version) +{ + static char resultbuf[16]; + + // [STRIFE] All versions of Strife 1.1 and later use 101 as their + // internal version number. Brilliant, huh? So we can't discern much + // here. + + switch (version) + { + case 100: + return "v1.0"; // v1.0 would be the ancient demo version + default: + break; + } + + // Unknown version. Who knows? + sprintf(resultbuf, "%i.%i (unknown)", version / 100, version % 100); + + return resultbuf; +} + +// +// G_DoPlayDemo +// +// [STRIFE] Modified for Strife demo format. +// +void G_DoPlayDemo (void) +{ + skill_t skill; + int i, map; + int demoversion; + + gameaction = ga_nothing; + demobuffer = demo_p = W_CacheLumpName (defdemoname, PU_STATIC); + + demoversion = *demo_p++; + + if (demoversion == STRIFE_VERSION) + { + longtics = false; + } + /* STRIFE-TODO: Not until/unless somebody makes a Strife-Plus :P + else if (demoversion == DOOM_191_VERSION) + { + // demo recorded with cph's modified "v1.91" doom exe + longtics = true; + } + */ + else + { + char *message = "Demo is from a different game version!\n" + "(read %i, should be %i)\n" + "\n" + "*** You may need to upgrade your version " + "of Strife to v1.1 or later. ***\n" + " See: http://doomworld.com/files/patches.shtml\n" + " This appears to be %s."; + + I_Error(message, demoversion, STRIFE_VERSION, + DemoVersionDescription(demoversion)); + } + + skill = *demo_p++; + //episode = *demo_p++; [STRIFE] No episodes + map = *demo_p++; + deathmatch = *demo_p++; + respawnparm = *demo_p++; + fastparm = *demo_p++; + nomonsters = *demo_p++; + consoleplayer = *demo_p++; + + for (i=0 ; i<MAXPLAYERS ; i++) + playeringame[i] = *demo_p++; + + //! + // @category demo + // + // Play back a demo recorded in a netgame with a single player. + // + + if (playeringame[1] || M_ParmExists("-solo-net")) + { + netgame = true; + netdemo = true; + } + + // don't spend a lot of time in loadlevel + precache = false; + G_InitNew(skill, map); + precache = true; + + // [STRIFE] not here... + //starttime = I_GetTime (); + + usergame = false; + demoplayback = true; +} + +// +// G_TimeDemo +// +// [STRIFE] Verified unmodified +// +void G_TimeDemo (char* name) +{ + //! + // @vanilla + // + // Disable rendering the screen entirely. + // + + nodrawers = M_ParmExists("-nodraw"); + + timingdemo = true; + singletics = true; + + defdemoname = name; + gameaction = ga_playdemo; +} + + +/* +=================== += += 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 +=================== +*/ +// +// [STRIFE] Verified unmodified +// +boolean G_CheckDemoStatus (void) +{ + int endtime; + + if (timingdemo) + { + float fps; + int realtics; + + endtime = I_GetTime (); + realtics = endtime - starttime; + fps = ((float) gametic * TICRATE) / realtics; + + // Prevent recursive calls + timingdemo = false; + demoplayback = false; + + I_Error ("timed %i gametics in %i realtics (%f fps)", + gametic, realtics, fps); + } + + if (demoplayback) + { + W_ReleaseLumpName(defdemoname); + demoplayback = false; + netdemo = false; + netgame = false; + deathmatch = false; + playeringame[1] = playeringame[2] = playeringame[3] = 0; + respawnparm = false; + fastparm = false; + nomonsters = false; + consoleplayer = 0; + + if (singledemo) + I_Quit (); + else + 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; +} |