// 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 #include #include #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_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" #include "m_misc.h" // STRIFE // 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 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 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); // 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]; // [STRIFE] unused //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[32]; 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_elecbow, wp_elecbow }, { wp_poisonbow, wp_elecbow }, { wp_rifle, wp_rifle }, { wp_missile, wp_missile }, { wp_hegrenade, wp_hegrenade }, { wp_wpgrenade, wp_hegrenade }, { wp_flame, wp_flame }, { wp_mauler, wp_mauler }, { wp_torpedo, 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; static char savedescription[32]; static int testcontrols_mousespeed; #define BODYQUESIZE 32 mobj_t* bodyque[BODYQUESIZE]; int bodyqueslot; int vanilla_savegame_limit = 1; int vanilla_demo_limit = 1; #define MOUSE_SPEED_BOX_WIDTH 16 #define COLOR_RED 0xb0 #define COLOR_BLACK 0x00 #define COLOR_WHITE 0x04 #define COLOR_YELLOW 0xe7 void G_DrawMouseSpeedBox(void) { extern int usemouse; int i; int box_x, box_y; int original_speed; int x, y; int redline_x; int linelen; char *lumpname; int color; // If the mouse is turned off or acceleration is turned off, don't // draw the box at all. if (!usemouse || fabs(mouse_acceleration - 1) < 0.01) { return; } // Calculate box position box_x = SCREENWIDTH - MOUSE_SPEED_BOX_WIDTH * 8; box_y = SCREENHEIGHT - 9; // Draw the box. x = box_x; for (i=0; iconsistancy = 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] TODO - 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; ibuttons |= BT_CHANGE; cmd->buttons |= i< 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<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 // extern gamestate_t wipegamestate; 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 ; itype == ev_keydown && ev->data1 == key_spy && (singledemo || !deathmatch) ) { // 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) ) { 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 data1] = true; } return true; // eat key down events case ev_keyup: if (ev->data1 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 ; iforwardmove > TURBOTHRESHOLD) { turbodetected[i] = true; } if ((gametic & 31) == 0 && ((gametic >> 5) % MAXPLAYERS) == i && turbodetected[i]) { static char turbomessage[80]; extern char *player_names[4]; 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>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; 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]; for(i = 0; i < 32; i++) p->inventory[i].type = NUMMOBJTYPES; // villsa [STRIFE] 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 ; ix == 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] Verified unmodified // 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_LoadPath // // haleyjd 10/03/10: [STRIFE] New function // Sets loadpath based on the map and "savepath2" // void G_LoadPath(int map) { // STRIFE-TODO: // sprintf(loadpath, "%s%d", savepath2, map) } // // G_DoReborn // void G_DoReborn (int playernum) { int i; if (!netgame) { // reload the level from scratch // STRIFE-TODO: HUB REBORN // G_LoadPath(gamemap); // gameaction = 3; gameaction = ga_loadlevel; // STRIFE-TODO: temporary } 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 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 10/03/10: [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 08/24/10: [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 08/23/2010: [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 09/21/10: [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 08/23/10: [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-TODO: not quite sure why it does this 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-TODO: needs call to G_DoSaveGame for hubs // if(!deathmatch) // G_DoSaveGame(savepath2); gameaction = ga_worlddone; } // haleyjd 08/24/10: [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 08/24/10: [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 08/24/10: [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 08/24/10: [STRIFE] Added destmap -> gamemap set. // STRIFE-TODO: Load hub save and other changes. // void G_DoWorldDone (void) { int temp_leveltime = leveltime; gamestate = GS_LEVEL; gamemap = destmap; // STRIFE-TODO: hubs bullshit // G_LoadPath(); // if (!deathmatch) // ebx0 = (*(_DWORD *)(players[0].mo + 104) & 0x8000000) > 0; // G_DoLoadGame(...); // temporary substitute: G_DoLoadLevel (); // [STRIFE] leveltime carries over between maps leveltime = temp_leveltime; if(!deathmatch) { // STRIFE-TODO: powerup transfers etc // *(_WORD *)(players[0].mo + 106) &= 0xF7FBu; // if (v1) // *(_BYTE *)(plaeyrs[0].mo + 106) |= 4u; // if (ebx0) // *(_BYTE *)(players[0].mo + 107) |= 8u; G_RiftPlayer(); // STRIFE-TODO: // G_DoSaveGame(savepath2); // M_SaveMisObj(savepath2, v6); } gameaction = ga_nothing; viewactive = true; } // // G_DoWorldDone2 // // haleyjd 10/03/10: [STRIFE] New function. No xrefs; unused. // void G_DoWorldDone2(void) { gamestate = GS_LEVEL; gameaction = ga_nothing; viewactive = true; } // // G_ReadCurrent // // haleyjd 10/03/10: [STRIFE] New function. // Reads the "CURRENT" file from the given path and then sets it to // gamemap. // void G_ReadCurrent(const char *path) { // STRIFE-TODO: Can't go live. Too much bullshit. // Without any kind of framework to work with file paths and directories // this stuff is nearly hopeless, and I am already getting fed up with the // idea of trying to make this work on 200 different platforms when there // is nothing I can find in this thing to facilitate portable programming! #if 0 char temppath[108]; // WARNING: not big enough for modern file paths! // STRIFE-TODO: SYSTEM SPECIFIC DIRECTORY SEPARATORS!!!!!!!!!!!!!!!! // What takes precedence? SeHackEd or Linux support? // Where is the godforsaken global that will tell me what character to // use in the first place????? DEH_snprintf(temppath, "%s\\current", path); // STRIFE-TODO: MOVE TO P_SAVEG.C ???? NO SAVEBUFFER OR SAVE_P HERE! // STRIFE-TODO: Read int from file with an appropriate routine if(M_ReadFile(temppath, &savebuffer) <= 0) gameaction = ga_newgame; else { save_p = savebuffer; gamemap = *(int *)savebuffer; gameaction = 3; Z_Free(savebuffer); } #endif 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]; void G_LoadGame (char* name) { strcpy (savename, name); gameaction = ga_loadgame; } // haleyjd 09/28/10: [STRIFE] VERSIONSIZE == 8 #define VERSIONSIZE 8 void G_DoLoadGame (void) { //int savedleveltime; gameaction = ga_nothing; save_stream = fopen(savename, "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: where did this come from? Was this in vanilla? o_O //savedleveltime = leveltime; // load a base level // STRIFE-TODO: ???? // if(v4) G_InitNew (gameskill, gamemap); // else // G_DoLoadLevel(); // haleyjd: As above, no clue. leveltime is supposed to be saved into // and restored from savegames, not carried over from whatever level // was being previously played. The fact this is here at all is // bizarre. //leveltime = savedleveltime; // dearchive all the modifications P_UnArchivePlayers (); 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 10/03/10: [STRIFE] New function // // Writes the character name to the NAME file. // boolean G_WriteSaveName() { // STRIFE-TODO: Yeah right. This is gonna happen really soon... #if 0 const char *dirstr; dword_86280 = eax0; // STRIFE-TODO: yeah good luck making THIS work on Linux. if(M_CheckParm(DEH_String("-cdrom"))) { sprintf(savepath2, "c:\\strife.cd\\strfsav%d.ssg\\", 6); v5 = dword_86280; dirstr = "c:\\strife.cd\\strfsav%d.ssg\\"; } else { sprintf(savepath2, "strfsav%d.ssg\\", 6); v5 = dword_86280; dirstr = "strfsav%d.ssg\\"; } sprintf(savepath, dirstr, v5); *character_name = 0; strcpy(character_name, edx); sprintf(hellifiknow, "%sname", savepath2); return M_WriteFile(hellifiknow, character_name, 32); #endif return false; } // // 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; } void G_DoSaveGame (void) { char *savegame_file; char *temp_savegame_file; temp_savegame_file = P_TempSaveGameFile(); savegame_file = P_SaveGameFile(savegameslot); // STRIFE-TODO: save the "current" file? // 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. 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); gameaction = ga_nothing; strcpy(savedescription, ""); players[consoleplayer].message = DEH_String(GGSAVED); // draw the pattern into the back screen R_FillBackScreen (); } // skill_t d_skill; int d_episode; int d_map; // // G_DeferedInitNew // // Can be called by the startup code or the menu task, // consoleplayer, displayplayer, playeringame[] should be set. // // haleyjd 09/22/10: [STRIFE] Removed episode parameter // void G_DeferedInitNew(skill_t skill, int map) { d_skill = skill; d_episode = 1; // STRIFE-TODO: no such thing as episodes. d_map = map; gameaction = ga_newgame; } // // G_DoNewGame // // [STRIFE] Code added to turn off the stonecold effect. // 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; consoleplayer = 0; G_InitNew (d_skill, d_map); gameaction = ga_nothing; } // The sky texture to be used instead of the F_SKY1 dummy. extern int skytexture; // // G_InitNew // // haleyjd 08/24/10: [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= 9 && gamemap < 32) skytexturename = "skymnt01"; else skytexturename = "skymnt02"; skytexturename = DEH_String(skytexturename); skytexture = R_TextureNumForName(skytexturename); // STRIFE-TODO: // 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; strcpy (demoname, name); strcat (demoname, ".lmp"); maxsize = 0x20000; //! // @arg // @category demo // @vanilla // // Specify the demo buffer size (KiB) // i = M_CheckParm ("-maxdemo"); if (i && i 0) { netgame = true; netdemo = true; } // don't spend a lot of time in loadlevel precache = false; G_InitNew(skill, map); precache = true; 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_CheckParm ("-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; }