2006-02-24 04:48:15 +00:00
|
|
|
// Emacs style mode select -*- C++ -*-
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// $Id:$
|
|
|
|
//
|
|
|
|
// Copyright (C) 1993-1996 by id Software, Inc.
|
|
|
|
//
|
|
|
|
// This source is available for distribution and/or modification
|
|
|
|
// only under the terms of the DOOM Source Code License as
|
|
|
|
// published by id Software. All rights reserved.
|
|
|
|
//
|
|
|
|
// The source is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
|
|
|
|
// for more details.
|
|
|
|
//
|
|
|
|
// $Log:$
|
|
|
|
//
|
|
|
|
// DESCRIPTION: none
|
|
|
|
//
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <time.h>
|
|
|
|
|
|
|
|
#include "templates.h"
|
|
|
|
#include "version.h"
|
|
|
|
#include "m_alloc.h"
|
|
|
|
#include "doomdef.h"
|
|
|
|
#include "doomstat.h"
|
|
|
|
#include "d_protocol.h"
|
|
|
|
#include "d_netinf.h"
|
|
|
|
#include "f_finale.h"
|
|
|
|
#include "m_argv.h"
|
|
|
|
#include "m_misc.h"
|
|
|
|
#include "m_menu.h"
|
|
|
|
#include "m_random.h"
|
|
|
|
#include "m_crc32.h"
|
|
|
|
#include "i_system.h"
|
|
|
|
#include "p_setup.h"
|
|
|
|
#include "p_saveg.h"
|
|
|
|
#include "p_effect.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 "c_console.h"
|
|
|
|
#include "c_cvars.h"
|
|
|
|
#include "c_bind.h"
|
|
|
|
#include "c_dispatch.h"
|
|
|
|
#include "v_video.h"
|
|
|
|
#include "w_wad.h"
|
|
|
|
#include "p_local.h"
|
|
|
|
#include "s_sound.h"
|
|
|
|
#include "gstrings.h"
|
|
|
|
#include "r_data.h"
|
|
|
|
#include "r_sky.h"
|
|
|
|
#include "r_draw.h"
|
|
|
|
#include "g_game.h"
|
|
|
|
#include "g_level.h"
|
|
|
|
#include "b_bot.h" //Added by MC:
|
|
|
|
#include "sbar.h"
|
|
|
|
#include "m_swap.h"
|
|
|
|
#include "m_png.h"
|
|
|
|
#include "gi.h"
|
|
|
|
#include "a_keys.h"
|
|
|
|
#include "a_artifacts.h"
|
2007-12-26 16:06:03 +00:00
|
|
|
#include "r_translate.h"
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
#include <zlib.h>
|
|
|
|
|
2006-04-11 16:27:41 +00:00
|
|
|
#include "g_hub.h"
|
|
|
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
static FRandom pr_dmspawn ("DMSpawn");
|
|
|
|
|
|
|
|
const int SAVEPICWIDTH = 216;
|
|
|
|
const int SAVEPICHEIGHT = 162;
|
|
|
|
|
2006-09-14 00:02:31 +00:00
|
|
|
bool G_CheckDemoStatus (void);
|
2006-02-24 04:48:15 +00:00
|
|
|
void G_ReadDemoTiccmd (ticcmd_t *cmd, int player);
|
|
|
|
void G_WriteDemoTiccmd (ticcmd_t *cmd, int player, int buf);
|
|
|
|
void G_PlayerReborn (int player);
|
|
|
|
|
|
|
|
void G_DoNewGame (void);
|
|
|
|
void G_DoLoadGame (void);
|
|
|
|
void G_DoPlayDemo (void);
|
|
|
|
void G_DoCompleted (void);
|
|
|
|
void G_DoVictory (void);
|
|
|
|
void G_DoWorldDone (void);
|
2008-02-05 23:32:49 +00:00
|
|
|
void G_DoSaveGame (bool okForQuicksave, FString filename, const char *description);
|
2006-10-19 20:20:56 +00:00
|
|
|
void G_DoAutoSave ();
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
FIntCVar gameskill ("skill", 2, CVAR_SERVERINFO|CVAR_LATCH);
|
|
|
|
CVAR (Int, deathmatch, 0, CVAR_SERVERINFO|CVAR_LATCH);
|
|
|
|
CVAR (Bool, chasedemo, false, 0);
|
|
|
|
CVAR (Bool, storesavepic, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
2008-02-16 10:23:12 +00:00
|
|
|
CVAR (Bool, longsavemessages, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
gameaction_t gameaction;
|
|
|
|
gamestate_t gamestate = GS_STARTUP;
|
|
|
|
|
|
|
|
int paused;
|
|
|
|
bool sendpause; // send a pause event next tic
|
|
|
|
bool sendsave; // send a save event next tic
|
|
|
|
bool sendturn180; // [RH] send a 180 degree turn next tic
|
|
|
|
bool usergame; // ok to save / end game
|
2006-05-03 14:54:48 +00:00
|
|
|
bool insave; // Game is saving - used to block exit commands
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2006-09-14 00:02:31 +00:00
|
|
|
bool timingdemo; // if true, exit with report on completion
|
|
|
|
bool nodrawers; // for comparative timing purposes
|
|
|
|
bool noblit; // for comparative timing purposes
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
bool viewactive;
|
|
|
|
|
2006-09-14 00:02:31 +00:00
|
|
|
bool netgame; // only true if packets are broadcast
|
|
|
|
bool multiplayer;
|
2006-02-24 04:48:15 +00:00
|
|
|
player_t players[MAXPLAYERS];
|
|
|
|
bool playeringame[MAXPLAYERS];
|
2007-02-15 00:01:21 +00:00
|
|
|
DWORD playerswiping;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
int consoleplayer; // player taking events
|
|
|
|
int gametic;
|
|
|
|
|
|
|
|
CVAR(Bool, demo_compress, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
|
|
|
|
char demoname[256];
|
2006-04-16 19:09:36 +00:00
|
|
|
bool demorecording;
|
|
|
|
bool demoplayback;
|
|
|
|
bool netdemo;
|
|
|
|
bool demonew; // [RH] Only used around G_InitNew for demos
|
2006-02-24 04:48:15 +00:00
|
|
|
int demover;
|
2006-09-14 00:02:31 +00:00
|
|
|
BYTE* demobuffer;
|
|
|
|
BYTE* demo_p;
|
|
|
|
BYTE* democompspot;
|
|
|
|
BYTE* demobodyspot;
|
2006-02-24 04:48:15 +00:00
|
|
|
size_t maxdemosize;
|
2006-09-14 00:02:31 +00:00
|
|
|
BYTE* zdemformend; // end of FORM ZDEM chunk
|
|
|
|
BYTE* zdembodyend; // end of ZDEM BODY chunk
|
|
|
|
bool singledemo; // quit after playing a demo from cmdline
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2006-09-14 00:02:31 +00:00
|
|
|
bool precache = true; // if true, load all graphics at start
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
wbstartstruct_t wminfo; // parms for world map / intermission
|
|
|
|
|
|
|
|
short consistancy[MAXPLAYERS][BACKUPTICS];
|
|
|
|
|
2006-09-14 00:02:31 +00:00
|
|
|
BYTE* savebuffer;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
|
|
#define MAXPLMOVE (forwardmove[1])
|
|
|
|
|
|
|
|
#define TURBOTHRESHOLD 12800
|
|
|
|
|
|
|
|
float normforwardmove[2] = {0x19, 0x32}; // [RH] For setting turbo from console
|
|
|
|
float normsidemove[2] = {0x18, 0x28}; // [RH] Ditto
|
|
|
|
|
|
|
|
fixed_t forwardmove[2], sidemove[2];
|
|
|
|
fixed_t angleturn[4] = {640, 1280, 320, 320}; // + slow turn
|
|
|
|
fixed_t flyspeed[2] = {1*256, 3*256};
|
|
|
|
int lookspeed[2] = {450, 512};
|
|
|
|
|
|
|
|
#define SLOWTURNTICS 6
|
|
|
|
|
|
|
|
CVAR (Bool, cl_run, false, CVAR_GLOBALCONFIG|CVAR_ARCHIVE) // Always run?
|
|
|
|
CVAR (Bool, invertmouse, false, CVAR_GLOBALCONFIG|CVAR_ARCHIVE) // Invert mouse look down/up?
|
|
|
|
CVAR (Bool, freelook, false, CVAR_GLOBALCONFIG|CVAR_ARCHIVE) // Always mlook?
|
|
|
|
CVAR (Bool, lookstrafe, false, CVAR_GLOBALCONFIG|CVAR_ARCHIVE) // Always strafe with mouse?
|
|
|
|
CVAR (Float, m_pitch, 1.f, CVAR_GLOBALCONFIG|CVAR_ARCHIVE) // Mouse speeds
|
|
|
|
CVAR (Float, m_yaw, 1.f, CVAR_GLOBALCONFIG|CVAR_ARCHIVE)
|
|
|
|
CVAR (Float, m_forward, 1.f, CVAR_GLOBALCONFIG|CVAR_ARCHIVE)
|
|
|
|
CVAR (Float, m_side, 2.f, CVAR_GLOBALCONFIG|CVAR_ARCHIVE)
|
|
|
|
|
|
|
|
int turnheld; // for accelerative turning
|
|
|
|
|
|
|
|
// mouse values are used once
|
|
|
|
int mousex;
|
|
|
|
int mousey;
|
|
|
|
|
2006-05-03 22:45:01 +00:00
|
|
|
FString savegamefile;
|
2006-02-24 04:48:15 +00:00
|
|
|
char savedescription[SAVESTRINGSIZE];
|
|
|
|
|
|
|
|
// [RH] Name of screenshot file to generate (usually NULL)
|
2006-08-15 04:34:35 +00:00
|
|
|
FString shotfile;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
AActor* bodyque[BODYQUESIZE];
|
|
|
|
int bodyqueslot;
|
|
|
|
|
|
|
|
void R_ExecuteSetViewSize (void);
|
|
|
|
|
2006-05-03 22:45:01 +00:00
|
|
|
FString savename;
|
|
|
|
FString BackupSaveName;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
bool SendLand;
|
|
|
|
const AInventory *SendItemUse, *SendItemDrop;
|
|
|
|
|
|
|
|
EXTERN_CVAR (Int, team)
|
|
|
|
|
|
|
|
CVAR (Bool, teamplay, false, CVAR_SERVERINFO)
|
|
|
|
|
|
|
|
// [RH] Allow turbo setting anytime during game
|
|
|
|
CUSTOM_CVAR (Float, turbo, 100.f, 0)
|
|
|
|
{
|
|
|
|
if (self < 10.f)
|
|
|
|
{
|
|
|
|
self = 10.f;
|
|
|
|
}
|
|
|
|
else if (self > 256.f)
|
|
|
|
{
|
|
|
|
self = 256.f;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
float scale = self * 0.01f;
|
|
|
|
|
|
|
|
forwardmove[0] = (int)(normforwardmove[0]*scale);
|
|
|
|
forwardmove[1] = (int)(normforwardmove[1]*scale);
|
|
|
|
sidemove[0] = (int)(normsidemove[0]*scale);
|
|
|
|
sidemove[1] = (int)(normsidemove[1]*scale);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (turnspeeds)
|
|
|
|
{
|
|
|
|
if (argv.argc() == 1)
|
|
|
|
{
|
2006-09-14 00:02:31 +00:00
|
|
|
Printf ("Current turn speeds: %d %d %d %d\n", angleturn[0],
|
2006-02-24 04:48:15 +00:00
|
|
|
angleturn[1], angleturn[2], angleturn[3]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 1; i <= 4 && i < argv.argc(); ++i)
|
|
|
|
{
|
|
|
|
angleturn[i-1] = atoi (argv[i]);
|
|
|
|
}
|
|
|
|
if (i <= 2)
|
|
|
|
{
|
|
|
|
angleturn[1] = angleturn[0] * 2;
|
|
|
|
}
|
|
|
|
if (i <= 3)
|
|
|
|
{
|
|
|
|
angleturn[2] = angleturn[0] / 2;
|
|
|
|
}
|
|
|
|
if (i <= 4)
|
|
|
|
{
|
|
|
|
angleturn[3] = angleturn[2];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (slot)
|
|
|
|
{
|
|
|
|
if (argv.argc() > 1)
|
|
|
|
{
|
|
|
|
int slot = atoi (argv[1]);
|
|
|
|
|
|
|
|
if (slot < NUM_WEAPON_SLOTS)
|
|
|
|
{
|
|
|
|
SendItemUse = LocalWeapons.Slots[slot].PickWeapon (&players[consoleplayer]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (centerview)
|
|
|
|
{
|
|
|
|
Net_WriteByte (DEM_CENTERVIEW);
|
|
|
|
}
|
|
|
|
|
2006-05-14 14:30:13 +00:00
|
|
|
CCMD(crouch)
|
|
|
|
{
|
|
|
|
Net_WriteByte(DEM_CROUCH);
|
|
|
|
}
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
CCMD (land)
|
|
|
|
{
|
|
|
|
SendLand = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (pause)
|
|
|
|
{
|
|
|
|
sendpause = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (turn180)
|
|
|
|
{
|
|
|
|
sendturn180 = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (weapnext)
|
|
|
|
{
|
|
|
|
SendItemUse = PickNextWeapon (&players[consoleplayer]);
|
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (weapprev)
|
|
|
|
{
|
|
|
|
SendItemUse = PickPrevWeapon (&players[consoleplayer]);
|
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (invnext)
|
|
|
|
{
|
|
|
|
AInventory *next;
|
|
|
|
|
- Added the ACS commands
ReplaceTextures (str old_texture, str new_texture, optional bool not_lower,
optional bool not_mid, optional bool not_upper, optional bool not_floor,
optional bool not_ceiling); and
SectorDamage (int tag, int amount, str type, bool players_only, bool in_air,
str protection_item, bool subclasses_okay);
- Added the vid_nowidescreen cvar to disable widescreen aspect ratio
correction. When this is enabled, the only display ratio available is 4:3
(and 5:4 if vid_tft is set).
- Added support for setting an actor's damage property to an expression
through decorate. Just enclose it within parentheses, and the expression
will be evaluated exactly as-is without the normal Doom damage calculation.
So if you want something that does exactly 6 damage, use a "Damage (6)"
property. To deal normal Doom missile damage, you can use
"Damage (random(1,8)*6)" instead of "Damage 6".
- Moved InvFirst and InvSel into APlayerPawn so that they can be consistantly
maintained by ObtainInventory.
SVN r288 (trunk)
2006-08-12 02:30:57 +00:00
|
|
|
if (who == NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
return;
|
|
|
|
|
- Added the ACS commands
ReplaceTextures (str old_texture, str new_texture, optional bool not_lower,
optional bool not_mid, optional bool not_upper, optional bool not_floor,
optional bool not_ceiling); and
SectorDamage (int tag, int amount, str type, bool players_only, bool in_air,
str protection_item, bool subclasses_okay);
- Added the vid_nowidescreen cvar to disable widescreen aspect ratio
correction. When this is enabled, the only display ratio available is 4:3
(and 5:4 if vid_tft is set).
- Added support for setting an actor's damage property to an expression
through decorate. Just enclose it within parentheses, and the expression
will be evaluated exactly as-is without the normal Doom damage calculation.
So if you want something that does exactly 6 damage, use a "Damage (6)"
property. To deal normal Doom missile damage, you can use
"Damage (random(1,8)*6)" instead of "Damage 6".
- Moved InvFirst and InvSel into APlayerPawn so that they can be consistantly
maintained by ObtainInventory.
SVN r288 (trunk)
2006-08-12 02:30:57 +00:00
|
|
|
if (who->InvSel != NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
- Added the ACS commands
ReplaceTextures (str old_texture, str new_texture, optional bool not_lower,
optional bool not_mid, optional bool not_upper, optional bool not_floor,
optional bool not_ceiling); and
SectorDamage (int tag, int amount, str type, bool players_only, bool in_air,
str protection_item, bool subclasses_okay);
- Added the vid_nowidescreen cvar to disable widescreen aspect ratio
correction. When this is enabled, the only display ratio available is 4:3
(and 5:4 if vid_tft is set).
- Added support for setting an actor's damage property to an expression
through decorate. Just enclose it within parentheses, and the expression
will be evaluated exactly as-is without the normal Doom damage calculation.
So if you want something that does exactly 6 damage, use a "Damage (6)"
property. To deal normal Doom missile damage, you can use
"Damage (random(1,8)*6)" instead of "Damage 6".
- Moved InvFirst and InvSel into APlayerPawn so that they can be consistantly
maintained by ObtainInventory.
SVN r288 (trunk)
2006-08-12 02:30:57 +00:00
|
|
|
if ((next = who->InvSel->NextInv()) != NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
- Added the ACS commands
ReplaceTextures (str old_texture, str new_texture, optional bool not_lower,
optional bool not_mid, optional bool not_upper, optional bool not_floor,
optional bool not_ceiling); and
SectorDamage (int tag, int amount, str type, bool players_only, bool in_air,
str protection_item, bool subclasses_okay);
- Added the vid_nowidescreen cvar to disable widescreen aspect ratio
correction. When this is enabled, the only display ratio available is 4:3
(and 5:4 if vid_tft is set).
- Added support for setting an actor's damage property to an expression
through decorate. Just enclose it within parentheses, and the expression
will be evaluated exactly as-is without the normal Doom damage calculation.
So if you want something that does exactly 6 damage, use a "Damage (6)"
property. To deal normal Doom missile damage, you can use
"Damage (random(1,8)*6)" instead of "Damage 6".
- Moved InvFirst and InvSel into APlayerPawn so that they can be consistantly
maintained by ObtainInventory.
SVN r288 (trunk)
2006-08-12 02:30:57 +00:00
|
|
|
who->InvSel = next;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Select the first item in the inventory
|
- Added the ACS commands
ReplaceTextures (str old_texture, str new_texture, optional bool not_lower,
optional bool not_mid, optional bool not_upper, optional bool not_floor,
optional bool not_ceiling); and
SectorDamage (int tag, int amount, str type, bool players_only, bool in_air,
str protection_item, bool subclasses_okay);
- Added the vid_nowidescreen cvar to disable widescreen aspect ratio
correction. When this is enabled, the only display ratio available is 4:3
(and 5:4 if vid_tft is set).
- Added support for setting an actor's damage property to an expression
through decorate. Just enclose it within parentheses, and the expression
will be evaluated exactly as-is without the normal Doom damage calculation.
So if you want something that does exactly 6 damage, use a "Damage (6)"
property. To deal normal Doom missile damage, you can use
"Damage (random(1,8)*6)" instead of "Damage 6".
- Moved InvFirst and InvSel into APlayerPawn so that they can be consistantly
maintained by ObtainInventory.
SVN r288 (trunk)
2006-08-12 02:30:57 +00:00
|
|
|
if (!(who->Inventory->ItemFlags & IF_INVBAR))
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
- Added the ACS commands
ReplaceTextures (str old_texture, str new_texture, optional bool not_lower,
optional bool not_mid, optional bool not_upper, optional bool not_floor,
optional bool not_ceiling); and
SectorDamage (int tag, int amount, str type, bool players_only, bool in_air,
str protection_item, bool subclasses_okay);
- Added the vid_nowidescreen cvar to disable widescreen aspect ratio
correction. When this is enabled, the only display ratio available is 4:3
(and 5:4 if vid_tft is set).
- Added support for setting an actor's damage property to an expression
through decorate. Just enclose it within parentheses, and the expression
will be evaluated exactly as-is without the normal Doom damage calculation.
So if you want something that does exactly 6 damage, use a "Damage (6)"
property. To deal normal Doom missile damage, you can use
"Damage (random(1,8)*6)" instead of "Damage 6".
- Moved InvFirst and InvSel into APlayerPawn so that they can be consistantly
maintained by ObtainInventory.
SVN r288 (trunk)
2006-08-12 02:30:57 +00:00
|
|
|
who->InvSel = who->Inventory->NextInv();
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
- Added the ACS commands
ReplaceTextures (str old_texture, str new_texture, optional bool not_lower,
optional bool not_mid, optional bool not_upper, optional bool not_floor,
optional bool not_ceiling); and
SectorDamage (int tag, int amount, str type, bool players_only, bool in_air,
str protection_item, bool subclasses_okay);
- Added the vid_nowidescreen cvar to disable widescreen aspect ratio
correction. When this is enabled, the only display ratio available is 4:3
(and 5:4 if vid_tft is set).
- Added support for setting an actor's damage property to an expression
through decorate. Just enclose it within parentheses, and the expression
will be evaluated exactly as-is without the normal Doom damage calculation.
So if you want something that does exactly 6 damage, use a "Damage (6)"
property. To deal normal Doom missile damage, you can use
"Damage (random(1,8)*6)" instead of "Damage 6".
- Moved InvFirst and InvSel into APlayerPawn so that they can be consistantly
maintained by ObtainInventory.
SVN r288 (trunk)
2006-08-12 02:30:57 +00:00
|
|
|
who->InvSel = who->Inventory;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
- Added the ACS commands
ReplaceTextures (str old_texture, str new_texture, optional bool not_lower,
optional bool not_mid, optional bool not_upper, optional bool not_floor,
optional bool not_ceiling); and
SectorDamage (int tag, int amount, str type, bool players_only, bool in_air,
str protection_item, bool subclasses_okay);
- Added the vid_nowidescreen cvar to disable widescreen aspect ratio
correction. When this is enabled, the only display ratio available is 4:3
(and 5:4 if vid_tft is set).
- Added support for setting an actor's damage property to an expression
through decorate. Just enclose it within parentheses, and the expression
will be evaluated exactly as-is without the normal Doom damage calculation.
So if you want something that does exactly 6 damage, use a "Damage (6)"
property. To deal normal Doom missile damage, you can use
"Damage (random(1,8)*6)" instead of "Damage 6".
- Moved InvFirst and InvSel into APlayerPawn so that they can be consistantly
maintained by ObtainInventory.
SVN r288 (trunk)
2006-08-12 02:30:57 +00:00
|
|
|
who->player->inventorytics = 5*TICRATE;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (invprev)
|
|
|
|
{
|
|
|
|
AInventory *item, *newitem;
|
|
|
|
|
- Added the ACS commands
ReplaceTextures (str old_texture, str new_texture, optional bool not_lower,
optional bool not_mid, optional bool not_upper, optional bool not_floor,
optional bool not_ceiling); and
SectorDamage (int tag, int amount, str type, bool players_only, bool in_air,
str protection_item, bool subclasses_okay);
- Added the vid_nowidescreen cvar to disable widescreen aspect ratio
correction. When this is enabled, the only display ratio available is 4:3
(and 5:4 if vid_tft is set).
- Added support for setting an actor's damage property to an expression
through decorate. Just enclose it within parentheses, and the expression
will be evaluated exactly as-is without the normal Doom damage calculation.
So if you want something that does exactly 6 damage, use a "Damage (6)"
property. To deal normal Doom missile damage, you can use
"Damage (random(1,8)*6)" instead of "Damage 6".
- Moved InvFirst and InvSel into APlayerPawn so that they can be consistantly
maintained by ObtainInventory.
SVN r288 (trunk)
2006-08-12 02:30:57 +00:00
|
|
|
if (who == NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
return;
|
|
|
|
|
- Added the ACS commands
ReplaceTextures (str old_texture, str new_texture, optional bool not_lower,
optional bool not_mid, optional bool not_upper, optional bool not_floor,
optional bool not_ceiling); and
SectorDamage (int tag, int amount, str type, bool players_only, bool in_air,
str protection_item, bool subclasses_okay);
- Added the vid_nowidescreen cvar to disable widescreen aspect ratio
correction. When this is enabled, the only display ratio available is 4:3
(and 5:4 if vid_tft is set).
- Added support for setting an actor's damage property to an expression
through decorate. Just enclose it within parentheses, and the expression
will be evaluated exactly as-is without the normal Doom damage calculation.
So if you want something that does exactly 6 damage, use a "Damage (6)"
property. To deal normal Doom missile damage, you can use
"Damage (random(1,8)*6)" instead of "Damage 6".
- Moved InvFirst and InvSel into APlayerPawn so that they can be consistantly
maintained by ObtainInventory.
SVN r288 (trunk)
2006-08-12 02:30:57 +00:00
|
|
|
if (who->InvSel != NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
- Added the ACS commands
ReplaceTextures (str old_texture, str new_texture, optional bool not_lower,
optional bool not_mid, optional bool not_upper, optional bool not_floor,
optional bool not_ceiling); and
SectorDamage (int tag, int amount, str type, bool players_only, bool in_air,
str protection_item, bool subclasses_okay);
- Added the vid_nowidescreen cvar to disable widescreen aspect ratio
correction. When this is enabled, the only display ratio available is 4:3
(and 5:4 if vid_tft is set).
- Added support for setting an actor's damage property to an expression
through decorate. Just enclose it within parentheses, and the expression
will be evaluated exactly as-is without the normal Doom damage calculation.
So if you want something that does exactly 6 damage, use a "Damage (6)"
property. To deal normal Doom missile damage, you can use
"Damage (random(1,8)*6)" instead of "Damage 6".
- Moved InvFirst and InvSel into APlayerPawn so that they can be consistantly
maintained by ObtainInventory.
SVN r288 (trunk)
2006-08-12 02:30:57 +00:00
|
|
|
if ((item = who->InvSel->PrevInv()) != NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
- Added the ACS commands
ReplaceTextures (str old_texture, str new_texture, optional bool not_lower,
optional bool not_mid, optional bool not_upper, optional bool not_floor,
optional bool not_ceiling); and
SectorDamage (int tag, int amount, str type, bool players_only, bool in_air,
str protection_item, bool subclasses_okay);
- Added the vid_nowidescreen cvar to disable widescreen aspect ratio
correction. When this is enabled, the only display ratio available is 4:3
(and 5:4 if vid_tft is set).
- Added support for setting an actor's damage property to an expression
through decorate. Just enclose it within parentheses, and the expression
will be evaluated exactly as-is without the normal Doom damage calculation.
So if you want something that does exactly 6 damage, use a "Damage (6)"
property. To deal normal Doom missile damage, you can use
"Damage (random(1,8)*6)" instead of "Damage 6".
- Moved InvFirst and InvSel into APlayerPawn so that they can be consistantly
maintained by ObtainInventory.
SVN r288 (trunk)
2006-08-12 02:30:57 +00:00
|
|
|
who->InvSel = item;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Select the last item in the inventory
|
- Added the ACS commands
ReplaceTextures (str old_texture, str new_texture, optional bool not_lower,
optional bool not_mid, optional bool not_upper, optional bool not_floor,
optional bool not_ceiling); and
SectorDamage (int tag, int amount, str type, bool players_only, bool in_air,
str protection_item, bool subclasses_okay);
- Added the vid_nowidescreen cvar to disable widescreen aspect ratio
correction. When this is enabled, the only display ratio available is 4:3
(and 5:4 if vid_tft is set).
- Added support for setting an actor's damage property to an expression
through decorate. Just enclose it within parentheses, and the expression
will be evaluated exactly as-is without the normal Doom damage calculation.
So if you want something that does exactly 6 damage, use a "Damage (6)"
property. To deal normal Doom missile damage, you can use
"Damage (random(1,8)*6)" instead of "Damage 6".
- Moved InvFirst and InvSel into APlayerPawn so that they can be consistantly
maintained by ObtainInventory.
SVN r288 (trunk)
2006-08-12 02:30:57 +00:00
|
|
|
item = who->InvSel;
|
2006-02-24 04:48:15 +00:00
|
|
|
while ((newitem = item->NextInv()) != NULL)
|
|
|
|
{
|
|
|
|
item = newitem;
|
|
|
|
}
|
- Added the ACS commands
ReplaceTextures (str old_texture, str new_texture, optional bool not_lower,
optional bool not_mid, optional bool not_upper, optional bool not_floor,
optional bool not_ceiling); and
SectorDamage (int tag, int amount, str type, bool players_only, bool in_air,
str protection_item, bool subclasses_okay);
- Added the vid_nowidescreen cvar to disable widescreen aspect ratio
correction. When this is enabled, the only display ratio available is 4:3
(and 5:4 if vid_tft is set).
- Added support for setting an actor's damage property to an expression
through decorate. Just enclose it within parentheses, and the expression
will be evaluated exactly as-is without the normal Doom damage calculation.
So if you want something that does exactly 6 damage, use a "Damage (6)"
property. To deal normal Doom missile damage, you can use
"Damage (random(1,8)*6)" instead of "Damage 6".
- Moved InvFirst and InvSel into APlayerPawn so that they can be consistantly
maintained by ObtainInventory.
SVN r288 (trunk)
2006-08-12 02:30:57 +00:00
|
|
|
who->InvSel = item;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
- Added the ACS commands
ReplaceTextures (str old_texture, str new_texture, optional bool not_lower,
optional bool not_mid, optional bool not_upper, optional bool not_floor,
optional bool not_ceiling); and
SectorDamage (int tag, int amount, str type, bool players_only, bool in_air,
str protection_item, bool subclasses_okay);
- Added the vid_nowidescreen cvar to disable widescreen aspect ratio
correction. When this is enabled, the only display ratio available is 4:3
(and 5:4 if vid_tft is set).
- Added support for setting an actor's damage property to an expression
through decorate. Just enclose it within parentheses, and the expression
will be evaluated exactly as-is without the normal Doom damage calculation.
So if you want something that does exactly 6 damage, use a "Damage (6)"
property. To deal normal Doom missile damage, you can use
"Damage (random(1,8)*6)" instead of "Damage 6".
- Moved InvFirst and InvSel into APlayerPawn so that they can be consistantly
maintained by ObtainInventory.
SVN r288 (trunk)
2006-08-12 02:30:57 +00:00
|
|
|
who->player->inventorytics = 5*TICRATE;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (invuseall)
|
|
|
|
{
|
|
|
|
SendItemUse = (const AInventory *)1;
|
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (invuse)
|
|
|
|
{
|
|
|
|
if (players[consoleplayer].inventorytics == 0 || gameinfo.gametype == GAME_Strife)
|
|
|
|
{
|
2007-10-29 20:27:40 +00:00
|
|
|
if (players[consoleplayer].mo) SendItemUse = players[consoleplayer].mo->InvSel;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
players[consoleplayer].inventorytics = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (use)
|
|
|
|
{
|
- Added the ACS commands
ReplaceTextures (str old_texture, str new_texture, optional bool not_lower,
optional bool not_mid, optional bool not_upper, optional bool not_floor,
optional bool not_ceiling); and
SectorDamage (int tag, int amount, str type, bool players_only, bool in_air,
str protection_item, bool subclasses_okay);
- Added the vid_nowidescreen cvar to disable widescreen aspect ratio
correction. When this is enabled, the only display ratio available is 4:3
(and 5:4 if vid_tft is set).
- Added support for setting an actor's damage property to an expression
through decorate. Just enclose it within parentheses, and the expression
will be evaluated exactly as-is without the normal Doom damage calculation.
So if you want something that does exactly 6 damage, use a "Damage (6)"
property. To deal normal Doom missile damage, you can use
"Damage (random(1,8)*6)" instead of "Damage 6".
- Moved InvFirst and InvSel into APlayerPawn so that they can be consistantly
maintained by ObtainInventory.
SVN r288 (trunk)
2006-08-12 02:30:57 +00:00
|
|
|
if (argv.argc() > 1 && who != NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
- Added the ACS commands
ReplaceTextures (str old_texture, str new_texture, optional bool not_lower,
optional bool not_mid, optional bool not_upper, optional bool not_floor,
optional bool not_ceiling); and
SectorDamage (int tag, int amount, str type, bool players_only, bool in_air,
str protection_item, bool subclasses_okay);
- Added the vid_nowidescreen cvar to disable widescreen aspect ratio
correction. When this is enabled, the only display ratio available is 4:3
(and 5:4 if vid_tft is set).
- Added support for setting an actor's damage property to an expression
through decorate. Just enclose it within parentheses, and the expression
will be evaluated exactly as-is without the normal Doom damage calculation.
So if you want something that does exactly 6 damage, use a "Damage (6)"
property. To deal normal Doom missile damage, you can use
"Damage (random(1,8)*6)" instead of "Damage 6".
- Moved InvFirst and InvSel into APlayerPawn so that they can be consistantly
maintained by ObtainInventory.
SVN r288 (trunk)
2006-08-12 02:30:57 +00:00
|
|
|
SendItemUse = who->FindInventory (PClass::FindClass (argv[1]));
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (invdrop)
|
|
|
|
{
|
2007-10-29 20:27:40 +00:00
|
|
|
if (players[consoleplayer].mo) SendItemDrop = players[consoleplayer].mo->InvSel;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (drop)
|
|
|
|
{
|
- Added the ACS commands
ReplaceTextures (str old_texture, str new_texture, optional bool not_lower,
optional bool not_mid, optional bool not_upper, optional bool not_floor,
optional bool not_ceiling); and
SectorDamage (int tag, int amount, str type, bool players_only, bool in_air,
str protection_item, bool subclasses_okay);
- Added the vid_nowidescreen cvar to disable widescreen aspect ratio
correction. When this is enabled, the only display ratio available is 4:3
(and 5:4 if vid_tft is set).
- Added support for setting an actor's damage property to an expression
through decorate. Just enclose it within parentheses, and the expression
will be evaluated exactly as-is without the normal Doom damage calculation.
So if you want something that does exactly 6 damage, use a "Damage (6)"
property. To deal normal Doom missile damage, you can use
"Damage (random(1,8)*6)" instead of "Damage 6".
- Moved InvFirst and InvSel into APlayerPawn so that they can be consistantly
maintained by ObtainInventory.
SVN r288 (trunk)
2006-08-12 02:30:57 +00:00
|
|
|
if (argv.argc() > 1 && who != NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
- Added the ACS commands
ReplaceTextures (str old_texture, str new_texture, optional bool not_lower,
optional bool not_mid, optional bool not_upper, optional bool not_floor,
optional bool not_ceiling); and
SectorDamage (int tag, int amount, str type, bool players_only, bool in_air,
str protection_item, bool subclasses_okay);
- Added the vid_nowidescreen cvar to disable widescreen aspect ratio
correction. When this is enabled, the only display ratio available is 4:3
(and 5:4 if vid_tft is set).
- Added support for setting an actor's damage property to an expression
through decorate. Just enclose it within parentheses, and the expression
will be evaluated exactly as-is without the normal Doom damage calculation.
So if you want something that does exactly 6 damage, use a "Damage (6)"
property. To deal normal Doom missile damage, you can use
"Damage (random(1,8)*6)" instead of "Damage 6".
- Moved InvFirst and InvSel into APlayerPawn so that they can be consistantly
maintained by ObtainInventory.
SVN r288 (trunk)
2006-08-12 02:30:57 +00:00
|
|
|
SendItemDrop = who->FindInventory (PClass::FindClass (argv[1]));
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (useflechette)
|
|
|
|
{ // Select from one of arti_poisonbag1-3, whichever the player has
|
2006-05-10 02:40:43 +00:00
|
|
|
static const ENamedName bagnames[3] =
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-05-10 02:40:43 +00:00
|
|
|
NAME_ArtiPoisonBag1,
|
|
|
|
NAME_ArtiPoisonBag2,
|
|
|
|
NAME_ArtiPoisonBag3
|
2006-02-24 04:48:15 +00:00
|
|
|
};
|
|
|
|
int i, j;
|
|
|
|
|
- Added the ACS commands
ReplaceTextures (str old_texture, str new_texture, optional bool not_lower,
optional bool not_mid, optional bool not_upper, optional bool not_floor,
optional bool not_ceiling); and
SectorDamage (int tag, int amount, str type, bool players_only, bool in_air,
str protection_item, bool subclasses_okay);
- Added the vid_nowidescreen cvar to disable widescreen aspect ratio
correction. When this is enabled, the only display ratio available is 4:3
(and 5:4 if vid_tft is set).
- Added support for setting an actor's damage property to an expression
through decorate. Just enclose it within parentheses, and the expression
will be evaluated exactly as-is without the normal Doom damage calculation.
So if you want something that does exactly 6 damage, use a "Damage (6)"
property. To deal normal Doom missile damage, you can use
"Damage (random(1,8)*6)" instead of "Damage 6".
- Moved InvFirst and InvSel into APlayerPawn so that they can be consistantly
maintained by ObtainInventory.
SVN r288 (trunk)
2006-08-12 02:30:57 +00:00
|
|
|
if (who == NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
return;
|
|
|
|
|
- Added the ACS commands
ReplaceTextures (str old_texture, str new_texture, optional bool not_lower,
optional bool not_mid, optional bool not_upper, optional bool not_floor,
optional bool not_ceiling); and
SectorDamage (int tag, int amount, str type, bool players_only, bool in_air,
str protection_item, bool subclasses_okay);
- Added the vid_nowidescreen cvar to disable widescreen aspect ratio
correction. When this is enabled, the only display ratio available is 4:3
(and 5:4 if vid_tft is set).
- Added support for setting an actor's damage property to an expression
through decorate. Just enclose it within parentheses, and the expression
will be evaluated exactly as-is without the normal Doom damage calculation.
So if you want something that does exactly 6 damage, use a "Damage (6)"
property. To deal normal Doom missile damage, you can use
"Damage (random(1,8)*6)" instead of "Damage 6".
- Moved InvFirst and InvSel into APlayerPawn so that they can be consistantly
maintained by ObtainInventory.
SVN r288 (trunk)
2006-08-12 02:30:57 +00:00
|
|
|
if (who->IsKindOf (PClass::FindClass (NAME_ClericPlayer)))
|
2006-07-13 10:17:56 +00:00
|
|
|
i = 0;
|
- Added the ACS commands
ReplaceTextures (str old_texture, str new_texture, optional bool not_lower,
optional bool not_mid, optional bool not_upper, optional bool not_floor,
optional bool not_ceiling); and
SectorDamage (int tag, int amount, str type, bool players_only, bool in_air,
str protection_item, bool subclasses_okay);
- Added the vid_nowidescreen cvar to disable widescreen aspect ratio
correction. When this is enabled, the only display ratio available is 4:3
(and 5:4 if vid_tft is set).
- Added support for setting an actor's damage property to an expression
through decorate. Just enclose it within parentheses, and the expression
will be evaluated exactly as-is without the normal Doom damage calculation.
So if you want something that does exactly 6 damage, use a "Damage (6)"
property. To deal normal Doom missile damage, you can use
"Damage (random(1,8)*6)" instead of "Damage 6".
- Moved InvFirst and InvSel into APlayerPawn so that they can be consistantly
maintained by ObtainInventory.
SVN r288 (trunk)
2006-08-12 02:30:57 +00:00
|
|
|
else if (who->IsKindOf (PClass::FindClass (NAME_MagePlayer)))
|
2006-07-13 10:17:56 +00:00
|
|
|
i = 1;
|
|
|
|
else
|
|
|
|
i = 2;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
for (j = 0; j < 3; ++j)
|
|
|
|
{
|
|
|
|
AInventory *item;
|
2006-12-29 03:38:37 +00:00
|
|
|
if ( (item = who->FindInventory (bagnames[(i+j)%3])) )
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
SendItemUse = item;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (select)
|
|
|
|
{
|
|
|
|
if (argv.argc() > 1)
|
|
|
|
{
|
- Added the ACS commands
ReplaceTextures (str old_texture, str new_texture, optional bool not_lower,
optional bool not_mid, optional bool not_upper, optional bool not_floor,
optional bool not_ceiling); and
SectorDamage (int tag, int amount, str type, bool players_only, bool in_air,
str protection_item, bool subclasses_okay);
- Added the vid_nowidescreen cvar to disable widescreen aspect ratio
correction. When this is enabled, the only display ratio available is 4:3
(and 5:4 if vid_tft is set).
- Added support for setting an actor's damage property to an expression
through decorate. Just enclose it within parentheses, and the expression
will be evaluated exactly as-is without the normal Doom damage calculation.
So if you want something that does exactly 6 damage, use a "Damage (6)"
property. To deal normal Doom missile damage, you can use
"Damage (random(1,8)*6)" instead of "Damage 6".
- Moved InvFirst and InvSel into APlayerPawn so that they can be consistantly
maintained by ObtainInventory.
SVN r288 (trunk)
2006-08-12 02:30:57 +00:00
|
|
|
AInventory *item = who->FindInventory (PClass::FindClass (argv[1]));
|
2006-02-24 04:48:15 +00:00
|
|
|
if (item != NULL)
|
|
|
|
{
|
- Added the ACS commands
ReplaceTextures (str old_texture, str new_texture, optional bool not_lower,
optional bool not_mid, optional bool not_upper, optional bool not_floor,
optional bool not_ceiling); and
SectorDamage (int tag, int amount, str type, bool players_only, bool in_air,
str protection_item, bool subclasses_okay);
- Added the vid_nowidescreen cvar to disable widescreen aspect ratio
correction. When this is enabled, the only display ratio available is 4:3
(and 5:4 if vid_tft is set).
- Added support for setting an actor's damage property to an expression
through decorate. Just enclose it within parentheses, and the expression
will be evaluated exactly as-is without the normal Doom damage calculation.
So if you want something that does exactly 6 damage, use a "Damage (6)"
property. To deal normal Doom missile damage, you can use
"Damage (random(1,8)*6)" instead of "Damage 6".
- Moved InvFirst and InvSel into APlayerPawn so that they can be consistantly
maintained by ObtainInventory.
SVN r288 (trunk)
2006-08-12 02:30:57 +00:00
|
|
|
who->InvSel = item;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
- Added the ACS commands
ReplaceTextures (str old_texture, str new_texture, optional bool not_lower,
optional bool not_mid, optional bool not_upper, optional bool not_floor,
optional bool not_ceiling); and
SectorDamage (int tag, int amount, str type, bool players_only, bool in_air,
str protection_item, bool subclasses_okay);
- Added the vid_nowidescreen cvar to disable widescreen aspect ratio
correction. When this is enabled, the only display ratio available is 4:3
(and 5:4 if vid_tft is set).
- Added support for setting an actor's damage property to an expression
through decorate. Just enclose it within parentheses, and the expression
will be evaluated exactly as-is without the normal Doom damage calculation.
So if you want something that does exactly 6 damage, use a "Damage (6)"
property. To deal normal Doom missile damage, you can use
"Damage (random(1,8)*6)" instead of "Damage 6".
- Moved InvFirst and InvSel into APlayerPawn so that they can be consistantly
maintained by ObtainInventory.
SVN r288 (trunk)
2006-08-12 02:30:57 +00:00
|
|
|
who->player->inventorytics = 5*TICRATE;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// 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 strafe;
|
|
|
|
int speed;
|
|
|
|
int forward;
|
|
|
|
int side;
|
|
|
|
int fly;
|
|
|
|
|
|
|
|
ticcmd_t *base;
|
|
|
|
|
|
|
|
base = I_BaseTiccmd (); // empty, or external driver
|
|
|
|
*cmd = *base;
|
|
|
|
|
|
|
|
cmd->consistancy = consistancy[consoleplayer][(maketic/ticdup)%BACKUPTICS];
|
|
|
|
|
|
|
|
strafe = Button_Strafe.bDown;
|
|
|
|
speed = Button_Speed.bDown ^ (int)cl_run;
|
|
|
|
|
|
|
|
forward = side = fly = 0;
|
|
|
|
|
|
|
|
// [RH] only use two stage accelerative turning on the keyboard
|
|
|
|
// and not the joystick, since we treat the joystick as
|
|
|
|
// the analog device it is.
|
|
|
|
if (Button_Left.bDown || Button_Right.bDown)
|
|
|
|
turnheld += ticdup;
|
|
|
|
else
|
|
|
|
turnheld = 0;
|
|
|
|
|
|
|
|
// let movement keys cancel each other out
|
|
|
|
if (strafe)
|
|
|
|
{
|
|
|
|
if (Button_Right.bDown)
|
|
|
|
side += sidemove[speed];
|
|
|
|
if (Button_Left.bDown)
|
|
|
|
side -= sidemove[speed];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int tspeed = speed;
|
|
|
|
|
|
|
|
if (turnheld < SLOWTURNTICS)
|
|
|
|
tspeed *= 2; // slow turn
|
|
|
|
|
|
|
|
if (Button_Right.bDown)
|
|
|
|
{
|
|
|
|
G_AddViewAngle (angleturn[tspeed]);
|
|
|
|
LocalKeyboardTurner = true;
|
|
|
|
}
|
|
|
|
if (Button_Left.bDown)
|
|
|
|
{
|
|
|
|
G_AddViewAngle (-angleturn[tspeed]);
|
|
|
|
LocalKeyboardTurner = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Button_LookUp.bDown)
|
|
|
|
G_AddViewPitch (lookspeed[speed]);
|
|
|
|
if (Button_LookDown.bDown)
|
|
|
|
G_AddViewPitch (-lookspeed[speed]);
|
|
|
|
|
|
|
|
if (Button_MoveUp.bDown)
|
|
|
|
fly += flyspeed[speed];
|
|
|
|
if (Button_MoveDown.bDown)
|
|
|
|
fly -= flyspeed[speed];
|
|
|
|
|
|
|
|
if (Button_Klook.bDown)
|
|
|
|
{
|
|
|
|
if (Button_Forward.bDown)
|
|
|
|
G_AddViewPitch (lookspeed[speed]);
|
|
|
|
if (Button_Back.bDown)
|
|
|
|
G_AddViewPitch (-lookspeed[speed]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (Button_Forward.bDown)
|
|
|
|
forward += forwardmove[speed];
|
|
|
|
if (Button_Back.bDown)
|
|
|
|
forward -= forwardmove[speed];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Button_MoveRight.bDown)
|
|
|
|
side += sidemove[speed];
|
|
|
|
if (Button_MoveLeft.bDown)
|
|
|
|
side -= sidemove[speed];
|
|
|
|
|
|
|
|
// buttons
|
|
|
|
if (Button_Attack.bDown)
|
|
|
|
cmd->ucmd.buttons |= BT_ATTACK;
|
|
|
|
|
|
|
|
if (Button_AltAttack.bDown)
|
|
|
|
cmd->ucmd.buttons |= BT_ALTATTACK;
|
|
|
|
|
|
|
|
if (Button_Use.bDown)
|
|
|
|
cmd->ucmd.buttons |= BT_USE;
|
|
|
|
|
|
|
|
if (Button_Jump.bDown)
|
|
|
|
cmd->ucmd.buttons |= BT_JUMP;
|
|
|
|
|
2006-05-14 14:30:13 +00:00
|
|
|
if (Button_Crouch.bDown)
|
|
|
|
cmd->ucmd.buttons |= BT_DUCK;
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
// [RH] Scale joystick moves to full range of allowed speeds
|
|
|
|
if (JoyAxes[JOYAXIS_PITCH] != 0)
|
|
|
|
{
|
|
|
|
G_AddViewPitch (int((JoyAxes[JOYAXIS_PITCH] * 2048) / 256));
|
|
|
|
LocalKeyboardTurner = true;
|
|
|
|
}
|
|
|
|
if (JoyAxes[JOYAXIS_YAW] != 0)
|
|
|
|
{
|
|
|
|
G_AddViewAngle (int((-1280 * JoyAxes[JOYAXIS_YAW]) / 256));
|
|
|
|
LocalKeyboardTurner = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
side += int((MAXPLMOVE * JoyAxes[JOYAXIS_SIDE]) / 256);
|
|
|
|
forward += int((JoyAxes[JOYAXIS_FORWARD] * MAXPLMOVE) / 256);
|
|
|
|
fly += int(JoyAxes[JOYAXIS_UP] * 8);
|
|
|
|
|
|
|
|
if (!Button_Mlook.bDown && !freelook)
|
|
|
|
{
|
|
|
|
forward += (int)((float)mousey * m_forward);
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd->ucmd.pitch = LocalViewPitch >> 16;
|
|
|
|
|
|
|
|
if (SendLand)
|
|
|
|
{
|
|
|
|
SendLand = false;
|
|
|
|
fly = -32768;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strafe || lookstrafe)
|
|
|
|
side += (int)((float)mousex * m_side);
|
|
|
|
|
|
|
|
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->ucmd.forwardmove += forward;
|
|
|
|
cmd->ucmd.sidemove += side;
|
|
|
|
cmd->ucmd.yaw = LocalViewAngle >> 16;
|
|
|
|
cmd->ucmd.upmove = fly;
|
|
|
|
LocalViewAngle = 0;
|
|
|
|
LocalViewPitch = 0;
|
|
|
|
|
|
|
|
// special buttons
|
|
|
|
if (sendturn180)
|
|
|
|
{
|
|
|
|
sendturn180 = false;
|
|
|
|
cmd->ucmd.buttons |= BT_TURN180;
|
|
|
|
}
|
|
|
|
if (sendpause)
|
|
|
|
{
|
|
|
|
sendpause = false;
|
|
|
|
Net_WriteByte (DEM_PAUSE);
|
|
|
|
}
|
|
|
|
if (sendsave)
|
|
|
|
{
|
|
|
|
sendsave = false;
|
|
|
|
Net_WriteByte (DEM_SAVEGAME);
|
2006-05-03 22:45:01 +00:00
|
|
|
Net_WriteString (savegamefile);
|
2006-02-24 04:48:15 +00:00
|
|
|
Net_WriteString (savedescription);
|
|
|
|
savegamefile = "";
|
|
|
|
}
|
|
|
|
if (SendItemUse == (const AInventory *)1)
|
|
|
|
{
|
|
|
|
Net_WriteByte (DEM_INVUSEALL);
|
|
|
|
SendItemUse = NULL;
|
|
|
|
}
|
|
|
|
else if (SendItemUse != NULL)
|
|
|
|
{
|
|
|
|
Net_WriteByte (DEM_INVUSE);
|
|
|
|
Net_WriteLong (SendItemUse->InventoryID);
|
|
|
|
SendItemUse = NULL;
|
|
|
|
}
|
|
|
|
if (SendItemDrop != NULL)
|
|
|
|
{
|
|
|
|
Net_WriteByte (DEM_INVDROP);
|
|
|
|
Net_WriteLong (SendItemDrop->InventoryID);
|
|
|
|
SendItemDrop = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd->ucmd.forwardmove <<= 8;
|
|
|
|
cmd->ucmd.sidemove <<= 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
//[Graf Zahl] This really helps if the mouse update rate can't be increased!
|
|
|
|
CVAR (Bool, smooth_mouse, false, CVAR_GLOBALCONFIG|CVAR_ARCHIVE)
|
|
|
|
|
|
|
|
void G_AddViewPitch (int look)
|
|
|
|
{
|
|
|
|
if (gamestate == GS_TITLELEVEL)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2007-01-22 23:50:09 +00:00
|
|
|
look <<= 16;
|
2007-12-11 04:03:40 +00:00
|
|
|
if (!level.IsFreelookAllowed())
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
LocalViewPitch = 0;
|
|
|
|
}
|
2007-01-22 23:50:09 +00:00
|
|
|
else if (look > 0)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2007-01-22 23:50:09 +00:00
|
|
|
// Avoid overflowing
|
|
|
|
if (LocalViewPitch + look <= LocalViewPitch)
|
|
|
|
{
|
|
|
|
LocalViewPitch = 0x78000000;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LocalViewPitch = MIN(LocalViewPitch + look, 0x78000000);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (look < 0)
|
|
|
|
{
|
|
|
|
// Avoid overflowing
|
|
|
|
if (LocalViewPitch + look >= LocalViewPitch)
|
|
|
|
{
|
|
|
|
LocalViewPitch = -0x78000000;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LocalViewPitch = MAX(LocalViewPitch + look, -0x78000000);
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
if (look != 0)
|
|
|
|
{
|
|
|
|
LocalKeyboardTurner = smooth_mouse;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void G_AddViewAngle (int yaw)
|
|
|
|
{
|
|
|
|
if (gamestate == GS_TITLELEVEL)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
LocalViewAngle -= yaw << 16;
|
|
|
|
if (yaw != 0)
|
|
|
|
{
|
|
|
|
LocalKeyboardTurner = smooth_mouse;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CVAR (Bool, bot_allowspy, false, 0)
|
|
|
|
|
|
|
|
// [RH] Spy mode has been separated into two console commands.
|
|
|
|
// One goes forward; the other goes backward.
|
|
|
|
static void ChangeSpy (bool forward)
|
|
|
|
{
|
|
|
|
// If you're not in a level, then you can't spy.
|
|
|
|
if (gamestate != GS_LEVEL)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If not viewing through a player, return your eyes to your own head.
|
|
|
|
if (players[consoleplayer].camera->player == NULL)
|
|
|
|
{
|
|
|
|
players[consoleplayer].camera = players[consoleplayer].mo;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, cycle to the next player.
|
|
|
|
bool checkTeam = !demoplayback && deathmatch;
|
|
|
|
int pnum = players[consoleplayer].camera->player - players;
|
|
|
|
int step = forward ? 1 : -1;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
pnum += step;
|
|
|
|
pnum &= MAXPLAYERS-1;
|
|
|
|
if (playeringame[pnum] &&
|
|
|
|
(!checkTeam || players[pnum].mo->IsTeammate (players[consoleplayer].mo) ||
|
|
|
|
(bot_allowspy && players[pnum].isbot)))
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} while (pnum != consoleplayer);
|
|
|
|
|
|
|
|
players[consoleplayer].camera = players[pnum].mo;
|
|
|
|
S_UpdateSounds(players[consoleplayer].camera);
|
|
|
|
StatusBar->AttachToPlayer (&players[pnum]);
|
|
|
|
if (demoplayback || multiplayer)
|
|
|
|
{
|
|
|
|
StatusBar->ShowPlayerName ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (spynext)
|
|
|
|
{
|
|
|
|
// allow spy mode changes even during the demo
|
|
|
|
ChangeSpy (true);
|
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (spyprev)
|
|
|
|
{
|
|
|
|
// allow spy mode changes even during the demo
|
|
|
|
ChangeSpy (false);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// G_Responder
|
|
|
|
// Get info needed to make ticcmd_ts for the players.
|
|
|
|
//
|
2006-09-14 00:02:31 +00:00
|
|
|
bool G_Responder (event_t *ev)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
// any other key pops up menu if in demos
|
|
|
|
// [RH] But only if the key isn't bound to a "special" command
|
|
|
|
if (gameaction == ga_nothing &&
|
|
|
|
(demoplayback || gamestate == GS_DEMOSCREEN || gamestate == GS_TITLELEVEL))
|
|
|
|
{
|
2006-05-16 02:50:18 +00:00
|
|
|
const char *cmd = C_GetBinding (ev->data1);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
if (ev->type == EV_KeyDown)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (!cmd || (
|
|
|
|
strnicmp (cmd, "menu_", 5) &&
|
|
|
|
stricmp (cmd, "toggleconsole") &&
|
|
|
|
stricmp (cmd, "sizeup") &&
|
|
|
|
stricmp (cmd, "sizedown") &&
|
|
|
|
stricmp (cmd, "togglemap") &&
|
|
|
|
stricmp (cmd, "spynext") &&
|
|
|
|
stricmp (cmd, "spyprev") &&
|
|
|
|
stricmp (cmd, "chase") &&
|
|
|
|
stricmp (cmd, "+showscores") &&
|
|
|
|
stricmp (cmd, "bumpgamma") &&
|
|
|
|
stricmp (cmd, "screenshot")))
|
|
|
|
{
|
|
|
|
M_StartControlPanel (true);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return C_DoKey (ev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (cmd && cmd[0] == '+')
|
|
|
|
return C_DoKey (ev);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (CT_Responder (ev))
|
|
|
|
return true; // chat ate the event
|
|
|
|
|
|
|
|
if (gamestate == GS_LEVEL)
|
|
|
|
{
|
|
|
|
if (ST_Responder (ev))
|
|
|
|
return true; // status window ate it
|
|
|
|
if (!viewactive && AM_Responder (ev))
|
|
|
|
return true; // automap ate it
|
|
|
|
}
|
|
|
|
else if (gamestate == GS_FINALE)
|
|
|
|
{
|
|
|
|
if (F_Responder (ev))
|
|
|
|
return true; // finale ate the event
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (ev->type)
|
|
|
|
{
|
|
|
|
case EV_KeyDown:
|
|
|
|
if (C_DoKey (ev))
|
|
|
|
return true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EV_KeyUp:
|
|
|
|
C_DoKey (ev);
|
|
|
|
break;
|
|
|
|
|
|
|
|
// [RH] mouse buttons are sent as key up/down events
|
|
|
|
case EV_Mouse:
|
|
|
|
mousex = (int)(ev->x * mouse_sensitivity);
|
|
|
|
mousey = (int)(ev->y * mouse_sensitivity);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// [RH] If the view is active, give the automap a chance at
|
|
|
|
// the events *last* so that any bound keys get precedence.
|
|
|
|
|
|
|
|
if (gamestate == GS_LEVEL && viewactive)
|
|
|
|
return AM_Responder (ev);
|
|
|
|
|
|
|
|
return (ev->type == EV_KeyDown ||
|
|
|
|
ev->type == EV_Mouse);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// G_Ticker
|
|
|
|
// Make ticcmd_ts for the players.
|
|
|
|
//
|
|
|
|
extern FTexture *Page;
|
|
|
|
|
|
|
|
|
|
|
|
void G_Ticker ()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
gamestate_t oldgamestate;
|
|
|
|
|
|
|
|
// do player reborns if needed
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
|
|
{
|
|
|
|
if (playeringame[i] &&
|
|
|
|
(players[i].playerstate == PST_REBORN || players[i].playerstate == PST_ENTER))
|
|
|
|
{
|
|
|
|
G_DoReborn (i, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ToggleFullscreen)
|
2006-12-29 20:28:23 +00:00
|
|
|
{
|
2006-12-29 03:38:37 +00:00
|
|
|
static char toggle_fullscreen[] = "toggle fullscreen";
|
2006-02-24 04:48:15 +00:00
|
|
|
ToggleFullscreen = false;
|
2006-12-29 03:38:37 +00:00
|
|
|
AddCommandString (toggle_fullscreen);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// do things to change the game state
|
|
|
|
oldgamestate = gamestate;
|
|
|
|
while (gameaction != ga_nothing)
|
|
|
|
{
|
|
|
|
if (gameaction == ga_newgame2)
|
|
|
|
{
|
|
|
|
gameaction = ga_newgame;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
switch (gameaction)
|
|
|
|
{
|
|
|
|
case ga_loadlevel:
|
|
|
|
G_DoLoadLevel (-1, false);
|
|
|
|
break;
|
|
|
|
case ga_newgame2: // Silence GCC (see above)
|
|
|
|
case ga_newgame:
|
|
|
|
G_DoNewGame ();
|
|
|
|
break;
|
|
|
|
case ga_loadgame:
|
|
|
|
G_DoLoadGame ();
|
|
|
|
break;
|
|
|
|
case ga_savegame:
|
2008-02-05 23:32:49 +00:00
|
|
|
G_DoSaveGame (true, savegamefile, savedescription);
|
|
|
|
gameaction = ga_nothing;
|
|
|
|
savegamefile = "";
|
|
|
|
savedescription[0] = '\0';
|
2006-02-24 04:48:15 +00:00
|
|
|
break;
|
|
|
|
case ga_autosave:
|
|
|
|
G_DoAutoSave ();
|
2008-02-05 23:32:49 +00:00
|
|
|
gameaction = ga_nothing;
|
2006-02-24 04:48:15 +00:00
|
|
|
break;
|
|
|
|
case ga_playdemo:
|
|
|
|
G_DoPlayDemo ();
|
|
|
|
break;
|
|
|
|
case ga_completed:
|
|
|
|
G_DoCompleted ();
|
|
|
|
break;
|
|
|
|
case ga_slideshow:
|
|
|
|
F_StartSlideshow ();
|
|
|
|
break;
|
|
|
|
case ga_worlddone:
|
|
|
|
G_DoWorldDone ();
|
|
|
|
break;
|
|
|
|
case ga_screenshot:
|
|
|
|
M_ScreenShot (shotfile);
|
2006-08-15 04:34:35 +00:00
|
|
|
shotfile = "";
|
2006-02-24 04:48:15 +00:00
|
|
|
gameaction = ga_nothing;
|
|
|
|
break;
|
|
|
|
case ga_fullconsole:
|
|
|
|
C_FullConsole ();
|
|
|
|
gameaction = ga_nothing;
|
|
|
|
break;
|
|
|
|
case ga_togglemap:
|
|
|
|
AM_ToggleMap ();
|
|
|
|
gameaction = ga_nothing;
|
|
|
|
break;
|
|
|
|
case ga_nothing:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
C_AdjustBottom ();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (oldgamestate != gamestate)
|
|
|
|
{
|
|
|
|
if (oldgamestate == GS_DEMOSCREEN && Page != NULL)
|
|
|
|
{
|
|
|
|
Page->Unload();
|
|
|
|
Page = NULL;
|
|
|
|
}
|
|
|
|
else if (oldgamestate == GS_FINALE)
|
|
|
|
{
|
|
|
|
F_EndFinale ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// get commands, check consistancy, and build new consistancy check
|
|
|
|
int buf = (gametic/ticdup)%BACKUPTICS;
|
|
|
|
|
|
|
|
// [RH] Include some random seeds and player stuff in the consistancy
|
|
|
|
// check, not just the player's x position like BOOM.
|
|
|
|
DWORD rngsum = FRandom::StaticSumSeeds ();
|
|
|
|
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
|
|
{
|
|
|
|
if (playeringame[i])
|
|
|
|
{
|
|
|
|
ticcmd_t *cmd = &players[i].cmd;
|
|
|
|
ticcmd_t *newcmd = &netcmds[i][buf];
|
|
|
|
|
|
|
|
if ((gametic % ticdup) == 0)
|
|
|
|
{
|
|
|
|
RunNetSpecs (i, buf);
|
|
|
|
}
|
|
|
|
if (demorecording)
|
|
|
|
{
|
|
|
|
G_WriteDemoTiccmd (newcmd, i, buf);
|
|
|
|
}
|
|
|
|
// If the user alt-tabbed away, paused gets set to -1. In this case,
|
|
|
|
// we do not want to read more demo commands until paused is no
|
|
|
|
// longer negative.
|
|
|
|
if (demoplayback && paused >= 0)
|
|
|
|
{
|
|
|
|
G_ReadDemoTiccmd (cmd, i);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
memcpy (cmd, newcmd, sizeof(ticcmd_t));
|
|
|
|
}
|
|
|
|
|
|
|
|
// check for turbo cheats
|
|
|
|
if (cmd->ucmd.forwardmove > TURBOTHRESHOLD &&
|
|
|
|
!(gametic&31) && ((gametic>>5)&(MAXPLAYERS-1)) == i )
|
|
|
|
{
|
|
|
|
Printf ("%s is turbo!\n", players[i].userinfo.netname);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (netgame && !players[i].isbot && !netdemo && (gametic%ticdup) == 0)
|
|
|
|
{
|
|
|
|
//players[i].inconsistant = 0;
|
|
|
|
if (gametic > BACKUPTICS*ticdup && consistancy[i][buf] != cmd->consistancy)
|
|
|
|
{
|
|
|
|
players[i].inconsistant = gametic - BACKUPTICS*ticdup;
|
|
|
|
}
|
|
|
|
if (players[i].mo)
|
|
|
|
{
|
|
|
|
DWORD sum = rngsum + players[i].mo->x + players[i].mo->y + players[i].mo->z
|
|
|
|
+ players[i].mo->angle + players[i].mo->pitch;
|
|
|
|
sum ^= players[i].health;
|
|
|
|
consistancy[i][buf] = sum;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
consistancy[i][buf] = rngsum;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// do main actions
|
|
|
|
switch (gamestate)
|
|
|
|
{
|
|
|
|
case GS_LEVEL:
|
|
|
|
P_Ticker ();
|
|
|
|
AM_Ticker ();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GS_TITLELEVEL:
|
|
|
|
P_Ticker ();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GS_INTERMISSION:
|
|
|
|
WI_Ticker ();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GS_FINALE:
|
|
|
|
F_Ticker ();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GS_DEMOSCREEN:
|
|
|
|
D_PageTicker ();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GS_STARTUP:
|
|
|
|
if (gameaction == ga_nothing)
|
|
|
|
{
|
|
|
|
gamestate = GS_FULLCONSOLE;
|
|
|
|
gameaction = ga_fullconsole;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// PLAYER STRUCTURE FUNCTIONS
|
|
|
|
// also see P_SpawnPlayer in P_Mobj
|
|
|
|
//
|
|
|
|
|
|
|
|
//
|
|
|
|
// G_PlayerFinishLevel
|
|
|
|
// Called when a player completes a level.
|
|
|
|
//
|
2006-08-10 15:28:12 +00:00
|
|
|
void G_PlayerFinishLevel (int player, EFinishLevelType mode, bool resetinventory)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
AInventory *item, *next;
|
|
|
|
player_t *p;
|
|
|
|
|
|
|
|
p = &players[player];
|
|
|
|
|
|
|
|
// Strip all current powers, unless moving in a hub and the power is okay to keep.
|
|
|
|
item = p->mo->Inventory;
|
|
|
|
while (item != NULL)
|
|
|
|
{
|
|
|
|
next = item->Inventory;
|
|
|
|
if (item->IsKindOf (RUNTIME_CLASS(APowerup)))
|
|
|
|
{
|
|
|
|
if (deathmatch || mode != FINISH_SameHub || !(item->ItemFlags & IF_HUBPOWER))
|
|
|
|
{
|
|
|
|
item->Destroy ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
item = next;
|
|
|
|
}
|
2006-05-07 23:55:17 +00:00
|
|
|
if (p->ReadyWeapon != NULL &&
|
|
|
|
p->ReadyWeapon->WeaponFlags&WIF_POWERED_UP &&
|
|
|
|
p->PendingWeapon == p->ReadyWeapon->SisterWeapon)
|
|
|
|
{
|
|
|
|
// Unselect powered up weapons if the unpowered counterpart is pending
|
|
|
|
p->ReadyWeapon=p->PendingWeapon;
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
p->mo->flags &= ~MF_SHADOW; // cancel invisibility
|
|
|
|
p->mo->RenderStyle = STYLE_Normal;
|
|
|
|
p->mo->alpha = FRACUNIT;
|
|
|
|
p->extralight = 0; // cancel gun flashes
|
|
|
|
p->fixedcolormap = 0; // cancel ir goggles
|
|
|
|
p->damagecount = 0; // no palette changes
|
|
|
|
p->bonuscount = 0;
|
|
|
|
p->poisoncount = 0;
|
|
|
|
p->inventorytics = 0;
|
|
|
|
|
|
|
|
if (mode != FINISH_SameHub)
|
|
|
|
{
|
|
|
|
// Take away flight and keys (and anything else with IF_INTERHUBSTRIP set)
|
|
|
|
item = p->mo->Inventory;
|
|
|
|
while (item != NULL)
|
|
|
|
{
|
|
|
|
next = item->Inventory;
|
|
|
|
if (item->ItemFlags & IF_INTERHUBSTRIP)
|
|
|
|
{
|
|
|
|
item->Destroy ();
|
|
|
|
}
|
|
|
|
item = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mode == FINISH_NoHub && !(level.flags & LEVEL_KEEPFULLINVENTORY))
|
|
|
|
{ // Reduce all owned (visible) inventory to 1 item each
|
|
|
|
for (item = p->mo->Inventory; item != NULL; item = item->Inventory)
|
|
|
|
{
|
2007-12-09 02:27:02 +00:00
|
|
|
// There may be depletable items with an amount of 0.
|
|
|
|
// Those need to stay at 0; the rest get dropped to 1.
|
|
|
|
if (item->ItemFlags & IF_INVBAR && item->Amount > 1)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
item->Amount = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p->morphTics)
|
|
|
|
{ // Undo morph
|
|
|
|
P_UndoPlayerMorph (p, true);
|
|
|
|
}
|
2006-08-10 15:28:12 +00:00
|
|
|
|
|
|
|
// Clears the entire inventory and gives back the defaults for starting a game
|
|
|
|
if (resetinventory)
|
|
|
|
{
|
|
|
|
AInventory *inv = p->mo->Inventory;
|
|
|
|
|
|
|
|
while (inv != NULL)
|
|
|
|
{
|
|
|
|
AInventory *next = inv->Inventory;
|
|
|
|
if (!(inv->ItemFlags & IF_UNDROPPABLE))
|
|
|
|
{
|
|
|
|
inv->Destroy ();
|
|
|
|
}
|
|
|
|
else if (inv->GetClass() == RUNTIME_CLASS(AHexenArmor))
|
|
|
|
{
|
|
|
|
AHexenArmor *harmor = static_cast<AHexenArmor *> (inv);
|
|
|
|
harmor->Slots[3] = harmor->Slots[2] = harmor->Slots[1] = harmor->Slots[0] = 0;
|
|
|
|
}
|
|
|
|
inv = next;
|
|
|
|
}
|
|
|
|
p->ReadyWeapon = NULL;
|
|
|
|
p->PendingWeapon = WP_NOCHANGE;
|
|
|
|
p->psprites[ps_weapon].state = NULL;
|
|
|
|
p->psprites[ps_flash].state = NULL;
|
|
|
|
p->mo->GiveDefaultInventory();
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// G_PlayerReborn
|
|
|
|
// Called after a player dies
|
|
|
|
// almost everything is cleared and initialized
|
|
|
|
//
|
|
|
|
void G_PlayerReborn (int player)
|
|
|
|
{
|
|
|
|
player_t* p;
|
|
|
|
int frags[MAXPLAYERS];
|
|
|
|
int fragcount; // [RH] Cumulative frags
|
|
|
|
int killcount;
|
|
|
|
int itemcount;
|
|
|
|
int secretcount;
|
2006-09-01 01:52:50 +00:00
|
|
|
int chasecam;
|
2006-02-24 04:48:15 +00:00
|
|
|
BYTE currclass;
|
|
|
|
userinfo_t userinfo; // [RH] Save userinfo
|
|
|
|
botskill_t b_skill;//Added by MC:
|
|
|
|
APlayerPawn *actor;
|
2006-05-10 02:40:43 +00:00
|
|
|
const PClass *cls;
|
2006-05-16 02:50:18 +00:00
|
|
|
FString log;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
p = &players[player];
|
|
|
|
|
|
|
|
memcpy (frags, p->frags, sizeof(frags));
|
|
|
|
fragcount = p->fragcount;
|
|
|
|
killcount = p->killcount;
|
|
|
|
itemcount = p->itemcount;
|
|
|
|
secretcount = p->secretcount;
|
|
|
|
currclass = p->CurrentPlayerClass;
|
|
|
|
b_skill = p->skill; //Added by MC:
|
|
|
|
memcpy (&userinfo, &p->userinfo, sizeof(userinfo));
|
|
|
|
actor = p->mo;
|
|
|
|
cls = p->cls;
|
|
|
|
log = p->LogText;
|
2006-09-01 01:52:50 +00:00
|
|
|
chasecam = p->cheats & CF_CHASECAM;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2006-05-16 02:50:18 +00:00
|
|
|
// Reset player structure to its defaults
|
|
|
|
p->~player_t();
|
|
|
|
::new(p) player_t;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
memcpy (p->frags, frags, sizeof(p->frags));
|
|
|
|
p->fragcount = fragcount;
|
|
|
|
p->killcount = killcount;
|
|
|
|
p->itemcount = itemcount;
|
|
|
|
p->secretcount = secretcount;
|
|
|
|
p->CurrentPlayerClass = currclass;
|
|
|
|
memcpy (&p->userinfo, &userinfo, sizeof(userinfo));
|
|
|
|
p->mo = actor;
|
|
|
|
p->cls = cls;
|
|
|
|
p->LogText = log;
|
2006-09-01 01:52:50 +00:00
|
|
|
p->cheats |= chasecam;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
p->skill = b_skill; //Added by MC:
|
|
|
|
|
|
|
|
p->oldbuttons = 255, p->attackdown = true; // don't do anything immediately
|
|
|
|
p->playerstate = PST_LIVE;
|
|
|
|
|
|
|
|
if (gamestate != GS_TITLELEVEL)
|
|
|
|
{
|
|
|
|
actor->GiveDefaultInventory ();
|
|
|
|
p->ReadyWeapon = p->PendingWeapon;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Added by MC: Init bot structure.
|
|
|
|
if (bglobal.botingame[player])
|
|
|
|
bglobal.CleanBotstuff (p);
|
|
|
|
else
|
|
|
|
p->isbot = false;
|
|
|
|
|
|
|
|
// [BC] Handle temporary invulnerability when respawned
|
2008-01-09 02:53:38 +00:00
|
|
|
if ((dmflags2 & DF2_YES_RESPAWN_INVUL) && (deathmatch || alwaysapplydmflags))
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
APowerup *invul = static_cast<APowerup*>(actor->GiveInventoryType (RUNTIME_CLASS(APowerInvulnerable)));
|
|
|
|
invul->EffectTics = 2*TICRATE;
|
2006-05-03 14:54:48 +00:00
|
|
|
invul->BlendColor = 0; // don't mess with the view
|
2006-02-24 04:48:15 +00:00
|
|
|
actor->effects |= FX_RESPAWNINVUL; // [RH] special effect
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// G_CheckSpot
|
|
|
|
// Returns false if the player cannot be respawned
|
|
|
|
// at the given mapthing2_t spot
|
|
|
|
// because something is occupying it
|
|
|
|
//
|
|
|
|
|
2006-09-14 00:02:31 +00:00
|
|
|
bool G_CheckSpot (int playernum, mapthing2_t *mthing)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
fixed_t x;
|
|
|
|
fixed_t y;
|
|
|
|
fixed_t z, oldz;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
x = mthing->x << FRACBITS;
|
|
|
|
y = mthing->y << FRACBITS;
|
|
|
|
z = mthing->z << FRACBITS;
|
|
|
|
|
2007-12-25 10:07:58 +00:00
|
|
|
z += P_PointInSector (x, y)->floorplane.ZatPoint (x, y);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
if (!players[playernum].mo)
|
|
|
|
{ // first spawn of level, before corpses
|
|
|
|
for (i = 0; i < playernum; i++)
|
|
|
|
if (players[i].mo && players[i].mo->x == x && players[i].mo->y == y)
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
oldz = players[playernum].mo->z; // [RH] Need to save corpse's z-height
|
|
|
|
players[playernum].mo->z = z; // [RH] Checks are now full 3-D
|
|
|
|
|
|
|
|
// killough 4/2/98: fix bug where P_CheckPosition() uses a non-solid
|
|
|
|
// corpse to detect collisions with other players in DM starts
|
|
|
|
//
|
|
|
|
// Old code:
|
|
|
|
// if (!P_CheckPosition (players[playernum].mo, x, y))
|
|
|
|
// return false;
|
|
|
|
|
|
|
|
players[playernum].mo->flags |= MF_SOLID;
|
|
|
|
i = P_CheckPosition(players[playernum].mo, x, y);
|
|
|
|
players[playernum].mo->flags &= ~MF_SOLID;
|
|
|
|
players[playernum].mo->z = oldz; // [RH] Restore corpse's height
|
|
|
|
if (!i)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// G_DeathMatchSpawnPlayer
|
|
|
|
// Spawns a player at one of the random death match spots
|
|
|
|
// called at level load and each death
|
|
|
|
//
|
|
|
|
|
|
|
|
// [RH] Returns the distance of the closest player to the given mapthing2_t.
|
|
|
|
static fixed_t PlayersRangeFromSpot (mapthing2_t *spot)
|
|
|
|
{
|
|
|
|
fixed_t closest = INT_MAX;
|
|
|
|
fixed_t distance;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
|
|
{
|
|
|
|
if (!playeringame[i] || !players[i].mo || players[i].health <= 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
distance = P_AproxDistance (players[i].mo->x - spot->x * FRACUNIT,
|
|
|
|
players[i].mo->y - spot->y * FRACUNIT);
|
|
|
|
|
|
|
|
if (distance < closest)
|
|
|
|
closest = distance;
|
|
|
|
}
|
|
|
|
|
|
|
|
return closest;
|
|
|
|
}
|
|
|
|
|
|
|
|
// [RH] Select the deathmatch spawn spot farthest from everyone.
|
|
|
|
static mapthing2_t *SelectFarthestDeathmatchSpot (size_t selections)
|
|
|
|
{
|
|
|
|
fixed_t bestdistance = 0;
|
|
|
|
mapthing2_t *bestspot = NULL;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
for (i = 0; i < selections; i++)
|
|
|
|
{
|
|
|
|
fixed_t distance = PlayersRangeFromSpot (&deathmatchstarts[i]);
|
|
|
|
|
|
|
|
if (distance > bestdistance)
|
|
|
|
{
|
|
|
|
bestdistance = distance;
|
|
|
|
bestspot = &deathmatchstarts[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return bestspot;
|
|
|
|
}
|
|
|
|
|
|
|
|
// [RH] Select a deathmatch spawn spot at random (original mechanism)
|
|
|
|
static mapthing2_t *SelectRandomDeathmatchSpot (int playernum, unsigned int selections)
|
|
|
|
{
|
|
|
|
unsigned int i, j;
|
|
|
|
|
|
|
|
for (j = 0; j < 20; j++)
|
|
|
|
{
|
|
|
|
i = pr_dmspawn() % selections;
|
|
|
|
if (G_CheckSpot (playernum, &deathmatchstarts[i]) )
|
|
|
|
{
|
|
|
|
return &deathmatchstarts[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// [RH] return a spot anyway, since we allow telefragging when a player spawns
|
|
|
|
return &deathmatchstarts[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
void G_DeathMatchSpawnPlayer (int playernum)
|
|
|
|
{
|
|
|
|
unsigned int selections;
|
|
|
|
mapthing2_t *spot;
|
|
|
|
|
|
|
|
selections = deathmatchstarts.Size ();
|
|
|
|
// [RH] We can get by with just 1 deathmatch start
|
|
|
|
if (selections < 1)
|
|
|
|
I_Error ("No deathmatch starts");
|
|
|
|
|
|
|
|
// At level start, none of the players have mobjs attached to them,
|
|
|
|
// so we always use the random deathmatch spawn. During the game,
|
|
|
|
// though, we use whatever dmflags specifies.
|
|
|
|
if ((dmflags & DF_SPAWN_FARTHEST) && players[playernum].mo)
|
|
|
|
spot = SelectFarthestDeathmatchSpot (selections);
|
|
|
|
else
|
|
|
|
spot = SelectRandomDeathmatchSpot (playernum, selections);
|
|
|
|
|
|
|
|
if (!spot)
|
|
|
|
{ // no good spot, so the player will probably get stuck
|
|
|
|
spot = &playerstarts[playernum];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (playernum < 4)
|
|
|
|
spot->type = playernum+1;
|
|
|
|
else if (gameinfo.gametype != GAME_Hexen)
|
|
|
|
spot->type = playernum+4001-4; // [RH] > 4 players
|
|
|
|
else
|
|
|
|
spot->type = playernum+9100-4;
|
|
|
|
}
|
|
|
|
|
|
|
|
P_SpawnPlayer (spot);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// G_QueueBody
|
|
|
|
//
|
|
|
|
static void G_QueueBody (AActor *body)
|
|
|
|
{
|
|
|
|
// flush an old corpse if needed
|
|
|
|
int modslot = bodyqueslot%BODYQUESIZE;
|
|
|
|
|
|
|
|
if (bodyqueslot >= BODYQUESIZE && bodyque[modslot] != NULL)
|
|
|
|
{
|
|
|
|
bodyque[modslot]->Destroy ();
|
|
|
|
}
|
|
|
|
bodyque[modslot] = body;
|
|
|
|
|
2007-12-15 03:51:17 +00:00
|
|
|
// Copy the player's translation, so that if they change their color later, only
|
|
|
|
// their current body will change and not all their old corpses.
|
|
|
|
if (GetTranslationType(body->Translation) == TRANSLATION_Players ||
|
|
|
|
GetTranslationType(body->Translation) == TRANSLATION_PlayersExtra)
|
|
|
|
{
|
2007-12-26 16:37:27 +00:00
|
|
|
*translationtables[TRANSLATION_PlayerCorpses][modslot] = *TranslationToTable(body->Translation);
|
2007-12-15 03:51:17 +00:00
|
|
|
body->Translation = TRANSLATION(TRANSLATION_PlayerCorpses,modslot);
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
bodyqueslot++;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// G_DoReborn
|
|
|
|
//
|
|
|
|
void G_DoReborn (int playernum, bool freshbot)
|
|
|
|
{
|
2007-12-06 08:55:17 +00:00
|
|
|
if (!multiplayer && !(level.flags & LEVEL_ALLOWRESPAWN))
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
if (BackupSaveName.Len() > 0 && FileExists (BackupSaveName.GetChars()))
|
|
|
|
{ // Load game from the last point it was saved
|
|
|
|
savename = BackupSaveName;
|
|
|
|
gameaction = ga_loadgame;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // Reload the level from scratch
|
2006-04-16 19:09:36 +00:00
|
|
|
bool indemo = demoplayback;
|
2006-02-24 04:48:15 +00:00
|
|
|
BackupSaveName = "";
|
|
|
|
G_InitNew (level.mapname, false);
|
2006-04-16 19:09:36 +00:00
|
|
|
demoplayback = indemo;
|
2006-02-24 04:48:15 +00:00
|
|
|
// gameaction = ga_loadlevel;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// respawn at the start
|
|
|
|
int i;
|
|
|
|
|
|
|
|
// first disassociate the corpse
|
|
|
|
if (players[playernum].mo)
|
|
|
|
{
|
|
|
|
G_QueueBody (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]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// try to spawn at one of the other players' spots
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
|
|
{
|
|
|
|
if (G_CheckSpot (playernum, &playerstarts[i]) )
|
|
|
|
{
|
|
|
|
int oldtype = playerstarts[i].type;
|
|
|
|
|
|
|
|
// fake as other player
|
|
|
|
// [RH] These numbers should be common across all games. Or better yet, not
|
|
|
|
// used at all outside P_SpawnMapThing().
|
|
|
|
if (playernum < 4 || gameinfo.gametype == GAME_Strife)
|
|
|
|
{
|
|
|
|
playerstarts[i].type = playernum + 1;
|
|
|
|
}
|
|
|
|
else if (gameinfo.gametype == GAME_Hexen)
|
|
|
|
{
|
|
|
|
playerstarts[i].type = playernum + 9100 - 4;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
playerstarts[i].type = playernum + 4001 - 4;
|
|
|
|
}
|
|
|
|
P_SpawnPlayer (&playerstarts[i]);
|
|
|
|
playerstarts[i].type = oldtype; // restore
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// he's going to be inside something. Too bad.
|
|
|
|
}
|
|
|
|
P_SpawnPlayer (&playerstarts[playernum]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void G_ScreenShot (char *filename)
|
|
|
|
{
|
|
|
|
shotfile = filename;
|
|
|
|
gameaction = ga_screenshot;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// G_InitFromSavegame
|
|
|
|
// Can be called by the startup code or the menu task.
|
|
|
|
//
|
2006-05-16 02:50:18 +00:00
|
|
|
void G_LoadGame (const char* name)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
if (name != NULL)
|
|
|
|
{
|
|
|
|
savename = name;
|
|
|
|
gameaction = ga_loadgame;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool CheckSingleWad (char *name, bool &printRequires, bool printwarn)
|
|
|
|
{
|
|
|
|
if (name == NULL)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
2006-05-03 22:45:01 +00:00
|
|
|
if (Wads.CheckIfWadLoaded (name) < 0)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
if (printwarn)
|
|
|
|
{
|
|
|
|
if (!printRequires)
|
|
|
|
{
|
|
|
|
Printf ("This savegame needs these wads:\n%s", name);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Printf (", %s", name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
printRequires = true;
|
|
|
|
delete[] name;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
delete[] name;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return false if not all the needed wads have been loaded.
|
|
|
|
bool G_CheckSaveGameWads (PNGHandle *png, bool printwarn)
|
|
|
|
{
|
|
|
|
char *text;
|
|
|
|
bool printRequires = false;
|
|
|
|
|
|
|
|
text = M_GetPNGText (png, "Game WAD");
|
|
|
|
CheckSingleWad (text, printRequires, printwarn);
|
|
|
|
text = M_GetPNGText (png, "Map WAD");
|
|
|
|
CheckSingleWad (text, printRequires, printwarn);
|
|
|
|
|
|
|
|
if (printRequires)
|
|
|
|
{
|
|
|
|
if (printwarn)
|
|
|
|
{
|
|
|
|
Printf ("\n");
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2007-04-12 23:00:52 +00:00
|
|
|
static void WriteVars (FILE *file, SDWORD *vars, size_t count, DWORD id)
|
|
|
|
{
|
|
|
|
size_t i, j;
|
|
|
|
|
|
|
|
for (i = 0; i < count; ++i)
|
|
|
|
{
|
|
|
|
if (vars[i] != 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i < count)
|
|
|
|
{
|
|
|
|
// Find last non-zero var. Anything beyond the last stored variable
|
|
|
|
// will be zeroed at load time.
|
|
|
|
for (j = count-1; j > i; --j)
|
|
|
|
{
|
|
|
|
if (vars[j] != 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
FPNGChunkArchive arc (file, id);
|
|
|
|
for (i = 0; i <= j; ++i)
|
|
|
|
{
|
|
|
|
DWORD var = vars[i];
|
|
|
|
arc << var;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
static void ReadVars (PNGHandle *png, SDWORD *vars, size_t count, DWORD id)
|
|
|
|
{
|
|
|
|
size_t len = M_FindPNGChunk (png, id);
|
|
|
|
size_t used = 0;
|
|
|
|
|
|
|
|
if (len != 0)
|
|
|
|
{
|
|
|
|
DWORD var;
|
|
|
|
size_t i;
|
|
|
|
FPNGChunkArchive arc (png->File->GetFile(), id, len);
|
|
|
|
used = len / 4;
|
|
|
|
|
|
|
|
for (i = 0; i < used; ++i)
|
|
|
|
{
|
|
|
|
arc << var;
|
|
|
|
vars[i] = var;
|
|
|
|
}
|
|
|
|
png->File->ResetFilePtr();
|
|
|
|
}
|
|
|
|
if (used < count)
|
|
|
|
{
|
|
|
|
memset (&vars[used], 0, (count-used)*4);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-04-12 23:00:52 +00:00
|
|
|
static void WriteArrayVars (FILE *file, FWorldGlobalArray *vars, unsigned int count, DWORD id)
|
|
|
|
{
|
|
|
|
unsigned int i, j;
|
|
|
|
|
|
|
|
// Find the first non-empty array.
|
|
|
|
for (i = 0; i < count; ++i)
|
|
|
|
{
|
|
|
|
if (vars[i].CountUsed() != 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i < count)
|
|
|
|
{
|
|
|
|
// Find last non-empty array. Anything beyond the last stored array
|
|
|
|
// will be emptied at load time.
|
|
|
|
for (j = count-1; j > i; --j)
|
|
|
|
{
|
|
|
|
if (vars[j].CountUsed() != 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
FPNGChunkArchive arc (file, id);
|
|
|
|
arc.WriteCount (i);
|
|
|
|
arc.WriteCount (j);
|
|
|
|
for (; i <= j; ++i)
|
|
|
|
{
|
|
|
|
arc.WriteCount (vars[i].CountUsed());
|
2007-12-26 22:33:26 +00:00
|
|
|
|
|
|
|
FWorldGlobalArray::ConstIterator it(vars[i]);
|
|
|
|
const FWorldGlobalArray::Pair *pair;
|
|
|
|
|
|
|
|
while (it.NextPair (pair))
|
2007-04-12 23:00:52 +00:00
|
|
|
{
|
2007-12-26 22:33:26 +00:00
|
|
|
arc.WriteCount (pair->Key);
|
|
|
|
arc.WriteCount (pair->Value);
|
2007-04-12 23:00:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ReadArrayVars (PNGHandle *png, FWorldGlobalArray *vars, size_t count, DWORD id)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
size_t len = M_FindPNGChunk (png, id);
|
|
|
|
unsigned int i, k;
|
|
|
|
|
|
|
|
for (i = 0; i < count; ++i)
|
|
|
|
{
|
|
|
|
vars[i].Clear ();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len != 0)
|
|
|
|
{
|
|
|
|
DWORD max, size;
|
|
|
|
FPNGChunkArchive arc (png->File->GetFile(), id, len);
|
|
|
|
|
|
|
|
i = arc.ReadCount ();
|
|
|
|
max = arc.ReadCount ();
|
|
|
|
|
|
|
|
for (; i <= max; ++i)
|
|
|
|
{
|
|
|
|
size = arc.ReadCount ();
|
|
|
|
for (k = 0; k < size; ++k)
|
|
|
|
{
|
2007-04-12 23:00:52 +00:00
|
|
|
SDWORD key, val;
|
|
|
|
key = arc.ReadCount();
|
|
|
|
val = arc.ReadCount();
|
|
|
|
vars[i].Insert (key, val);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
png->File->ResetFilePtr();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void G_DoLoadGame ()
|
|
|
|
{
|
2008-01-05 09:12:05 +00:00
|
|
|
char sigcheck[20];
|
2006-02-24 04:48:15 +00:00
|
|
|
char *text = NULL;
|
|
|
|
char *map;
|
|
|
|
|
|
|
|
gameaction = ga_nothing;
|
2008-01-06 19:10:52 +00:00
|
|
|
demoplayback = false;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
FILE *stdfile = fopen (savename.GetChars(), "rb");
|
|
|
|
if (stdfile == NULL)
|
|
|
|
{
|
|
|
|
Printf ("Could not read savegame '%s'\n", savename.GetChars());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
PNGHandle *png = M_VerifyPNG (stdfile);
|
|
|
|
if (png == NULL)
|
|
|
|
{
|
|
|
|
fclose (stdfile);
|
|
|
|
Printf ("'%s' is not a valid (PNG) savegame\n", savename.GetChars());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
SaveVersion = 0;
|
|
|
|
|
2006-04-11 16:27:41 +00:00
|
|
|
// Check whether this savegame actually has been created by a compatible engine.
|
|
|
|
// Since there are ZDoom derivates using the exact same savegame format but
|
|
|
|
// with mutual incompatibilities this check simplifies things significantly.
|
2006-05-12 03:14:40 +00:00
|
|
|
char *engine = M_GetPNGText (png, "Engine");
|
2006-04-11 16:27:41 +00:00
|
|
|
if (engine == NULL || 0 != strcmp (engine, GAMESIG))
|
|
|
|
{
|
|
|
|
// Make a special case for the message printed for old savegames that don't
|
|
|
|
// have this information.
|
- Fixed compilation with mingw again.
- Added multiple-choice sound sequences. These overcome one of the major
deficiences of the Hexen-inherited SNDSEQ system while still being Hexen
compatible: Custom door sounds can now use different opening and closing
sequences, for both normal and blazing speeds.
- Added a serializer for TArray.
- Added a countof macro to doomtype.h. See the1's blog to find out why
it's implemented the way it is.
<http://blogs.msdn.com/the1/articles/210011.aspx>
- Added a new method to FRandom for getting random numbers larger than 255,
which lets me:
- Fixed: SNDSEQ delayrand commands could delay for no more than 255 tics.
- Fixed: If you're going to have sector_t.SoundTarget, then they need to
be included in the pointer cleanup scans.
- Ported back newer name code from 2.1.
- Fixed: Using -warp with only one parameter in Doom and Heretic to
select a map on episode 1 no longer worked.
- New: Loading a multiplayer save now restores the players based on
their names rather than on their connection order. Using connection
order was sensible when -net was the only way to start a network game,
but with -host/-join, it's not so nice. Also, if there aren't enough
players in the save, then the extra players will be spawned normally,
so you can continue a saved game with more players than you started it
with.
- Added some new SNDSEQ commands to make it possible to define Heretic's
ambient sounds in SNDSEQ: volumerel, volumerand, slot, randomsequence,
delayonce, and restart. With these, it is basically possible to obsolete
all of the $ambient SNDINFO commands.
- Fixed: Sound sequences would only execute one command each time they were
ticked.
- Fixed: No bounds checking was done on the volume sound sequences played at.
- Fixed: The tic parameter to playloop was useless and caused it to
act like a redundant playrepeat. I have removed all the logic that
caused playloop to play repeating sounds, and now it acts like an
infinite sequence of play/delay commands until the sequence is
stopped.
- Fixed: Sound sequences were ticked every frame, not every tic, so all
the delay commands were timed incorrectly and varied depending on your
framerate. Since this is useful for restarting looping sounds that got
cut off, I have not changed this. Instead, the delay commands now
record the tic when execution should resume, not the number of tics
left to delay.
SVN r57 (trunk)
2006-04-21 01:22:55 +00:00
|
|
|
if (engine == NULL)
|
|
|
|
{
|
|
|
|
Printf ("Savegame is from an incompatible version\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-05-12 03:14:40 +00:00
|
|
|
Printf ("Savegame is from another ZDoom-based engine: %s\n", engine);
|
|
|
|
delete[] engine;
|
- Fixed compilation with mingw again.
- Added multiple-choice sound sequences. These overcome one of the major
deficiences of the Hexen-inherited SNDSEQ system while still being Hexen
compatible: Custom door sounds can now use different opening and closing
sequences, for both normal and blazing speeds.
- Added a serializer for TArray.
- Added a countof macro to doomtype.h. See the1's blog to find out why
it's implemented the way it is.
<http://blogs.msdn.com/the1/articles/210011.aspx>
- Added a new method to FRandom for getting random numbers larger than 255,
which lets me:
- Fixed: SNDSEQ delayrand commands could delay for no more than 255 tics.
- Fixed: If you're going to have sector_t.SoundTarget, then they need to
be included in the pointer cleanup scans.
- Ported back newer name code from 2.1.
- Fixed: Using -warp with only one parameter in Doom and Heretic to
select a map on episode 1 no longer worked.
- New: Loading a multiplayer save now restores the players based on
their names rather than on their connection order. Using connection
order was sensible when -net was the only way to start a network game,
but with -host/-join, it's not so nice. Also, if there aren't enough
players in the save, then the extra players will be spawned normally,
so you can continue a saved game with more players than you started it
with.
- Added some new SNDSEQ commands to make it possible to define Heretic's
ambient sounds in SNDSEQ: volumerel, volumerand, slot, randomsequence,
delayonce, and restart. With these, it is basically possible to obsolete
all of the $ambient SNDINFO commands.
- Fixed: Sound sequences would only execute one command each time they were
ticked.
- Fixed: No bounds checking was done on the volume sound sequences played at.
- Fixed: The tic parameter to playloop was useless and caused it to
act like a redundant playrepeat. I have removed all the logic that
caused playloop to play repeating sounds, and now it acts like an
infinite sequence of play/delay commands until the sequence is
stopped.
- Fixed: Sound sequences were ticked every frame, not every tic, so all
the delay commands were timed incorrectly and varied depending on your
framerate. Since this is useful for restarting looping sounds that got
cut off, I have not changed this. Instead, the delay commands now
record the tic when execution should resume, not the number of tics
left to delay.
SVN r57 (trunk)
2006-04-21 01:22:55 +00:00
|
|
|
}
|
2006-04-11 16:27:41 +00:00
|
|
|
delete png;
|
|
|
|
fclose (stdfile);
|
|
|
|
return;
|
|
|
|
}
|
2006-05-12 03:14:40 +00:00
|
|
|
if (engine != NULL)
|
|
|
|
{
|
|
|
|
delete[] engine;
|
|
|
|
}
|
2006-04-11 16:27:41 +00:00
|
|
|
|
2008-01-05 09:12:05 +00:00
|
|
|
if (!M_GetPNGText (png, "ZDoom Save Version", sigcheck, 20) ||
|
2006-02-24 04:48:15 +00:00
|
|
|
0 != strncmp (sigcheck, SAVESIG, 9) || // ZDOOMSAVE is the first 9 chars
|
|
|
|
(SaveVersion = atoi (sigcheck+9)) < MINSAVEVER)
|
|
|
|
{
|
|
|
|
Printf ("Savegame is from an incompatible version\n");
|
|
|
|
delete png;
|
|
|
|
fclose (stdfile);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!G_CheckSaveGameWads (png, true))
|
|
|
|
{
|
|
|
|
fclose (stdfile);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
map = M_GetPNGText (png, "Current Map");
|
|
|
|
if (map == NULL)
|
|
|
|
{
|
|
|
|
Printf ("Savegame is missing the current map\n");
|
|
|
|
fclose (stdfile);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2006-04-11 16:27:41 +00:00
|
|
|
// Read intermission data for hubs
|
|
|
|
G_ReadHubInfo(png);
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
bglobal.RemoveAllBots (true);
|
|
|
|
|
|
|
|
text = M_GetPNGText (png, "Important CVARs");
|
|
|
|
if (text != NULL)
|
|
|
|
{
|
2006-09-14 00:02:31 +00:00
|
|
|
BYTE *vars_p = (BYTE *)text;
|
2006-02-24 04:48:15 +00:00
|
|
|
C_ReadCVars (&vars_p);
|
|
|
|
delete[] text;
|
|
|
|
}
|
|
|
|
|
|
|
|
// dearchive all the modifications
|
2006-04-11 16:27:41 +00:00
|
|
|
if (M_FindPNGChunk (png, MAKE_ID('p','t','I','c')) == 8)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
DWORD time[2];
|
|
|
|
fread (&time, 8, 1, stdfile);
|
|
|
|
time[0] = BigLong((unsigned int)time[0]);
|
|
|
|
time[1] = BigLong((unsigned int)time[1]);
|
|
|
|
level.time = Scale (time[1], TICRATE, time[0]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // No ptIc chunk so we don't know how long the user was playing
|
|
|
|
level.time = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
G_ReadSnapshots (png);
|
2006-05-26 04:38:22 +00:00
|
|
|
FRandom::StaticReadRNGState (png);
|
2006-02-24 04:48:15 +00:00
|
|
|
P_ReadACSDefereds (png);
|
|
|
|
|
|
|
|
// load a base level
|
|
|
|
savegamerestore = true; // Use the player actors in the savegame
|
2006-10-19 20:20:56 +00:00
|
|
|
bool demoplaybacksave = demoplayback;
|
2006-02-24 04:48:15 +00:00
|
|
|
G_InitNew (map, false);
|
2006-10-19 20:20:56 +00:00
|
|
|
demoplayback = demoplaybacksave;
|
2006-02-24 04:48:15 +00:00
|
|
|
delete[] map;
|
|
|
|
savegamerestore = false;
|
|
|
|
|
|
|
|
ReadVars (png, ACS_WorldVars, NUM_WORLDVARS, MAKE_ID('w','v','A','r'));
|
|
|
|
ReadVars (png, ACS_GlobalVars, NUM_GLOBALVARS, MAKE_ID('g','v','A','r'));
|
|
|
|
ReadArrayVars (png, ACS_WorldArrays, NUM_WORLDVARS, MAKE_ID('w','a','R','r'));
|
|
|
|
ReadArrayVars (png, ACS_GlobalArrays, NUM_GLOBALVARS, MAKE_ID('g','a','R','r'));
|
|
|
|
|
|
|
|
NextSkill = -1;
|
|
|
|
if (M_FindPNGChunk (png, MAKE_ID('s','n','X','t')) == 1)
|
|
|
|
{
|
|
|
|
BYTE next;
|
|
|
|
fread (&next, 1, 1, stdfile);
|
|
|
|
NextSkill = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (level.info->snapshot != NULL)
|
|
|
|
{
|
|
|
|
delete level.info->snapshot;
|
|
|
|
level.info->snapshot = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
BackupSaveName = savename;
|
|
|
|
|
|
|
|
delete png;
|
|
|
|
fclose (stdfile);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// G_SaveGame
|
|
|
|
// Called by the menu task.
|
|
|
|
// Description is a 24 byte text string
|
|
|
|
//
|
|
|
|
void G_SaveGame (const char *filename, const char *description)
|
|
|
|
{
|
2008-02-05 23:32:49 +00:00
|
|
|
if (sendsave || gameaction == ga_savegame)
|
|
|
|
{
|
|
|
|
Printf ("A game save is still pending.\n");
|
|
|
|
return;
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
savegamefile = filename;
|
2008-02-05 23:32:49 +00:00
|
|
|
strncpy (savedescription, description, sizeof(savedescription)-1);
|
|
|
|
savedescription[sizeof(savedescription)-1] = '\0';
|
2006-02-24 04:48:15 +00:00
|
|
|
sendsave = true;
|
|
|
|
}
|
|
|
|
|
2006-05-03 22:45:01 +00:00
|
|
|
FString G_BuildSaveName (const char *prefix, int slot)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-05-03 22:45:01 +00:00
|
|
|
FString name;
|
2006-02-24 04:48:15 +00:00
|
|
|
const char *leader;
|
|
|
|
const char *slash = "";
|
|
|
|
|
2008-03-12 02:56:11 +00:00
|
|
|
if (NULL != (leader = Args->CheckValue ("-savedir")))
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
size_t len = strlen (leader);
|
|
|
|
if (leader[len-1] != '\\' && leader[len-1] != '/')
|
|
|
|
{
|
|
|
|
slash = "/";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#ifndef unix
|
2008-03-12 02:56:11 +00:00
|
|
|
else if (Args->CheckParm ("-cdrom"))
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2007-12-26 09:56:09 +00:00
|
|
|
leader = CDROM_DIR "/";
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
leader = progdir;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
else
|
|
|
|
{
|
|
|
|
leader = "";
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (slot < 0)
|
|
|
|
{
|
|
|
|
name.Format ("%s%s%s", leader, slash, prefix);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
name.Format ("%s%s%s%d.zds", leader, slash, prefix, slot);
|
|
|
|
}
|
|
|
|
#ifdef unix
|
|
|
|
if (leader[0] == 0)
|
|
|
|
{
|
|
|
|
name = GetUserFile (name);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
CVAR (Int, autosavenum, 0, CVAR_NOSET|CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
|
|
|
CVAR (Int, disableautosave, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
|
|
|
CUSTOM_CVAR (Int, autosavecount, 4, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
|
|
|
{
|
|
|
|
if (self < 0)
|
|
|
|
self = 0;
|
|
|
|
if (self > 20)
|
|
|
|
self = 20;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern void P_CalcHeight (player_t *);
|
|
|
|
|
|
|
|
void G_DoAutoSave ()
|
|
|
|
{
|
2008-02-05 23:32:49 +00:00
|
|
|
char description[SAVESTRINGSIZE];
|
|
|
|
FString file;
|
2006-02-24 04:48:15 +00:00
|
|
|
// Keep up to four autosaves at a time
|
|
|
|
UCVarValue num;
|
2006-12-29 03:38:37 +00:00
|
|
|
const char *readableTime;
|
2006-10-19 20:20:56 +00:00
|
|
|
int count = autosavecount != 0 ? autosavecount : 1;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2006-10-19 20:20:56 +00:00
|
|
|
num.Int = (autosavenum + 1) % count;
|
2006-02-24 04:48:15 +00:00
|
|
|
autosavenum.ForceSet (num, CVAR_Int);
|
|
|
|
|
2008-02-05 23:32:49 +00:00
|
|
|
file = G_BuildSaveName ("auto", num.Int);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
readableTime = myasctime ();
|
2008-02-05 23:32:49 +00:00
|
|
|
strcpy (description, "Autosave ");
|
|
|
|
strncpy (description+9, readableTime+4, 12);
|
|
|
|
description[9+12] = 0;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2008-02-05 23:32:49 +00:00
|
|
|
G_DoSaveGame (false, file, description);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void PutSaveWads (FILE *file)
|
|
|
|
{
|
|
|
|
const char *name;
|
|
|
|
|
|
|
|
// Name of IWAD
|
|
|
|
name = Wads.GetWadName (FWadCollection::IWAD_FILENUM);
|
|
|
|
M_AppendPNGText (file, "Game WAD", name);
|
|
|
|
|
|
|
|
// Name of wad the map resides in
|
2006-06-14 15:56:56 +00:00
|
|
|
if (Wads.GetLumpFile (level.lumpnum) > 1)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
name = Wads.GetWadName (Wads.GetLumpFile (level.lumpnum));
|
|
|
|
M_AppendPNGText (file, "Map WAD", name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void PutSaveComment (FILE *file)
|
|
|
|
{
|
|
|
|
char comment[256];
|
2006-12-29 03:38:37 +00:00
|
|
|
const char *readableTime;
|
2006-02-24 04:48:15 +00:00
|
|
|
WORD len;
|
|
|
|
int levelTime;
|
|
|
|
|
|
|
|
// Get the current date and time
|
|
|
|
readableTime = myasctime ();
|
|
|
|
|
|
|
|
strncpy (comment, readableTime, 10);
|
|
|
|
strncpy (comment+10, readableTime+19, 5);
|
|
|
|
strncpy (comment+15, readableTime+10, 9);
|
|
|
|
comment[24] = 0;
|
|
|
|
|
|
|
|
M_AppendPNGText (file, "Creation Time", comment);
|
|
|
|
|
|
|
|
// Get level name
|
2006-04-11 16:27:41 +00:00
|
|
|
//strcpy (comment, level.level_name);
|
|
|
|
sprintf(comment, "%s - %s", level.mapname, level.level_name);
|
2006-02-24 04:48:15 +00:00
|
|
|
len = (WORD)strlen (comment);
|
|
|
|
comment[len] = '\n';
|
|
|
|
|
|
|
|
// Append elapsed time
|
|
|
|
levelTime = level.time / TICRATE;
|
|
|
|
sprintf (comment+len+1, "time: %02d:%02d:%02d",
|
|
|
|
levelTime/3600, (levelTime%3600)/60, levelTime%60);
|
|
|
|
comment[len+16] = 0;
|
|
|
|
|
|
|
|
// Write out the comment
|
|
|
|
M_AppendPNGText (file, "Comment", comment);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void PutSavePic (FILE *file, int width, int height)
|
|
|
|
{
|
|
|
|
if (width <= 0 || height <= 0 || !storesavepic)
|
|
|
|
{
|
|
|
|
M_CreateDummyPNG (file);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-06-11 11:28:48 +00:00
|
|
|
P_CheckPlayerSprites();
|
2008-01-12 12:49:05 +00:00
|
|
|
screen->WriteSavePic(&players[consoleplayer], file, width, height);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-02-05 23:32:49 +00:00
|
|
|
void G_DoSaveGame (bool okForQuicksave, FString filename, const char *description)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
if (demoplayback)
|
|
|
|
{
|
2008-02-05 23:32:49 +00:00
|
|
|
filename = G_BuildSaveName ("demosave.zds", -1);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
2006-10-19 20:20:56 +00:00
|
|
|
insave = true;
|
2006-02-24 04:48:15 +00:00
|
|
|
G_SnapshotLevel ();
|
|
|
|
|
2008-02-05 23:32:49 +00:00
|
|
|
FILE *stdfile = fopen (filename.GetChars(), "wb");
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
if (stdfile == NULL)
|
|
|
|
{
|
2008-02-05 23:32:49 +00:00
|
|
|
Printf ("Could not create savegame '%s'\n", filename.GetChars());
|
2006-05-03 14:54:48 +00:00
|
|
|
insave = false;
|
2006-02-24 04:48:15 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
SaveVersion = SAVEVER;
|
|
|
|
PutSavePic (stdfile, SAVEPICWIDTH, SAVEPICHEIGHT);
|
|
|
|
M_AppendPNGText (stdfile, "Software", "ZDoom " DOTVERSIONSTR);
|
2006-04-11 16:27:41 +00:00
|
|
|
M_AppendPNGText (stdfile, "Engine", GAMESIG);
|
2006-02-24 04:48:15 +00:00
|
|
|
M_AppendPNGText (stdfile, "ZDoom Save Version", SAVESIG);
|
2008-02-05 23:32:49 +00:00
|
|
|
M_AppendPNGText (stdfile, "Title", description);
|
2006-02-24 04:48:15 +00:00
|
|
|
M_AppendPNGText (stdfile, "Current Map", level.mapname);
|
|
|
|
PutSaveWads (stdfile);
|
|
|
|
PutSaveComment (stdfile);
|
|
|
|
|
2006-04-11 16:27:41 +00:00
|
|
|
// Intermission stats for hubs
|
|
|
|
G_WriteHubInfo(stdfile);
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-09-14 00:02:31 +00:00
|
|
|
BYTE vars[4096], *vars_p;
|
2006-02-24 04:48:15 +00:00
|
|
|
vars_p = vars;
|
|
|
|
C_WriteCVars (&vars_p, CVAR_SERVERINFO);
|
|
|
|
*vars_p = 0;
|
|
|
|
M_AppendPNGText (stdfile, "Important CVARs", (char *)vars);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (level.time != 0 || level.maptime != 0)
|
|
|
|
{
|
|
|
|
DWORD time[2] = { BigLong(TICRATE), BigLong(level.time) };
|
|
|
|
M_AppendPNGChunk (stdfile, MAKE_ID('p','t','I','c'), (BYTE *)&time, 8);
|
|
|
|
}
|
|
|
|
|
|
|
|
G_WriteSnapshots (stdfile);
|
2006-05-26 04:38:22 +00:00
|
|
|
FRandom::StaticWriteRNGState (stdfile);
|
2006-02-24 04:48:15 +00:00
|
|
|
P_WriteACSDefereds (stdfile);
|
|
|
|
|
|
|
|
WriteVars (stdfile, ACS_WorldVars, NUM_WORLDVARS, MAKE_ID('w','v','A','r'));
|
|
|
|
WriteVars (stdfile, ACS_GlobalVars, NUM_GLOBALVARS, MAKE_ID('g','v','A','r'));
|
|
|
|
WriteArrayVars (stdfile, ACS_WorldArrays, NUM_WORLDVARS, MAKE_ID('w','a','R','r'));
|
|
|
|
WriteArrayVars (stdfile, ACS_GlobalArrays, NUM_GLOBALVARS, MAKE_ID('g','a','R','r'));
|
|
|
|
|
|
|
|
if (NextSkill != -1)
|
|
|
|
{
|
|
|
|
BYTE next = NextSkill;
|
|
|
|
M_AppendPNGChunk (stdfile, MAKE_ID('s','n','X','t'), &next, 1);
|
|
|
|
}
|
|
|
|
|
2008-02-05 23:32:49 +00:00
|
|
|
M_NotifyNewSave (filename.GetChars(), description, okForQuicksave);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
M_FinishPNG (stdfile);
|
|
|
|
fclose (stdfile);
|
|
|
|
|
2006-04-11 16:27:41 +00:00
|
|
|
// Check whether the file is ok.
|
|
|
|
bool success = false;
|
2008-02-05 23:32:49 +00:00
|
|
|
stdfile = fopen (filename.GetChars(), "rb");
|
2006-04-11 16:27:41 +00:00
|
|
|
if (stdfile != NULL)
|
|
|
|
{
|
2008-02-05 23:32:49 +00:00
|
|
|
PNGHandle *pngh = M_VerifyPNG(stdfile);
|
2006-04-11 16:27:41 +00:00
|
|
|
if (pngh != NULL)
|
|
|
|
{
|
2008-02-05 23:32:49 +00:00
|
|
|
success = true;
|
2006-04-11 16:27:41 +00:00
|
|
|
delete pngh;
|
|
|
|
}
|
|
|
|
fclose(stdfile);
|
|
|
|
}
|
2008-02-16 10:23:12 +00:00
|
|
|
if (success)
|
|
|
|
{
|
|
|
|
if (longsavemessages) Printf ("%s (%s)\n", GStrings("GGSAVED"), filename.GetChars());
|
|
|
|
else Printf ("%s\n", GStrings("GGSAVED"));
|
|
|
|
}
|
2006-04-11 16:27:41 +00:00
|
|
|
else Printf(PRINT_HIGH, "Save failed\n");
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2008-02-05 23:32:49 +00:00
|
|
|
BackupSaveName = filename;
|
2006-06-14 15:56:56 +00:00
|
|
|
|
|
|
|
// We don't need the snapshot any longer.
|
|
|
|
if (level.info->snapshot != NULL)
|
|
|
|
{
|
|
|
|
delete level.info->snapshot;
|
|
|
|
level.info->snapshot = NULL;
|
|
|
|
}
|
|
|
|
|
2006-05-03 14:54:48 +00:00
|
|
|
insave = false;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// DEMO RECORDING
|
|
|
|
//
|
|
|
|
|
|
|
|
void G_ReadDemoTiccmd (ticcmd_t *cmd, int player)
|
|
|
|
{
|
|
|
|
int id = DEM_BAD;
|
|
|
|
|
|
|
|
while (id != DEM_USERCMD && id != DEM_EMPTYUSERCMD)
|
|
|
|
{
|
|
|
|
if (!demorecording && demo_p >= zdembodyend)
|
|
|
|
{
|
|
|
|
// nothing left in the BODY chunk, so end playback.
|
|
|
|
G_CheckDemoStatus ();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
id = ReadByte (&demo_p);
|
|
|
|
|
|
|
|
switch (id)
|
|
|
|
{
|
|
|
|
case DEM_STOP:
|
|
|
|
// end of demo stream
|
|
|
|
G_CheckDemoStatus ();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DEM_USERCMD:
|
|
|
|
UnpackUserCmd (&cmd->ucmd, &cmd->ucmd, &demo_p);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DEM_EMPTYUSERCMD:
|
|
|
|
// leave cmd->ucmd unchanged
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DEM_DROPPLAYER:
|
|
|
|
{
|
2006-09-14 00:02:31 +00:00
|
|
|
BYTE i = ReadByte (&demo_p);
|
2006-02-24 04:48:15 +00:00
|
|
|
if (i < MAXPLAYERS)
|
2007-02-15 00:01:21 +00:00
|
|
|
{
|
2006-02-24 04:48:15 +00:00
|
|
|
playeringame[i] = false;
|
2007-02-15 00:01:21 +00:00
|
|
|
playerswiping &= ~(1 << i);
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
Net_DoCommand (id, &demo_p, player);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-09-14 00:02:31 +00:00
|
|
|
bool stoprecording;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
CCMD (stop)
|
|
|
|
{
|
|
|
|
stoprecording = true;
|
|
|
|
}
|
|
|
|
|
2006-09-14 00:02:31 +00:00
|
|
|
extern BYTE *lenspot;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
void G_WriteDemoTiccmd (ticcmd_t *cmd, int player, int buf)
|
|
|
|
{
|
2006-09-14 00:02:31 +00:00
|
|
|
BYTE *specdata;
|
2006-02-24 04:48:15 +00:00
|
|
|
int speclen;
|
|
|
|
|
|
|
|
if (stoprecording)
|
|
|
|
{ // use "stop" console command to end demo recording
|
|
|
|
G_CheckDemoStatus ();
|
- Added some hackery at the start of MouseRead_Win32() that prevents it from
yanking the mouse around if they keys haven't been read yet to combat the
same situation that causes the keyboard to return DIERR_NOTACQUIRED in
KeyRead(): The window is sort of in focus and sort of not. User.dll
considers it to be focused and it's drawn as such, but another focused
window is on top of it, and DirectInput doesn't see it as focused.
- Fixed: KeyRead() should handle DIERR_NOTACQUIRED errors the same way it
handles DIERR_INPUTLOST errors. This can happen if our window had the
focus stolen away from it before we tried to acquire the keyboard in
DI_Init2(). Strangely, MouseRead_DI() already did this.
- When a stack overflow occurs, report.txt now only includes the first and
last 16KB of the stack to make it more manageable.
- Limited StreamEditBinary() to the first 64KB of the file to keep it from
taking too long on large dumps.
- And now I know why gathering crash information in the same process that
crashed can be bad: Stack overflows. You get one spare page to play with
when the stack overflows. MiniDumpWriteDump() needs more than that and
causes an access violation when it runs out of leftover stack, silently
terminating the application. Windows XP x64 offers SetThreadStackGuarantee()
to increase this, but that isn't available on anything older, including
32-bit XP. To get around this, a new thread is created to write the mini
dump when the stack overflows.
- Changed A_Burnination() to be closer to Strife's.
- Fixed: When playing back demos, DoAddBot() can be called without an
associated call to SpawnBot(). So if the bot can't spawn, botnum can
go negative, which will cause problems later in DCajunMaster::Main()
when it sees that wanted_botnum (0) is higher than botnum (-1).
- Fixed: Stopping demo recording in multiplayer games should not abruptly
drop the recorder out of the game without notifying the other players.
In fact, there's no reason why it should drop them out of multiplayer at
all.
- Fixed: Earthquakes were unreliable in multiplayer games because
P_PredictPlayer() did not preserve the player's xviewshift.
- Fixed: PlayerIsGone() needs to stop any scripts that belong to the player
who left, in addition to executing disconnect scripts.
- Fixed: APlayerPawn::AddInventory() should also check for a NULL player->mo
in case the player left but somebody still has a reference to their actor.
- Fixed: DDrawFB::PaintToWindow() should simulate proper unlocking behavior
and set Buffer to NULL.
- Improved feedback for network game initialization with the console ticker.
- Moved i_net.cpp and i_net.h out of sdl/ and win32/ and into the main source
directory. They are identical, so keeping two copies of them is bad.
- Fixed: (At least with Creative's driver's,) EAX settings are global and not
per-application. So if you play a multiplayer ZDoom game on one computer
(or even another EAX-using application), ZDoom needs to restore the
environment when it regains focus.
- Maybe fixed: (See http://forum.zdoom.org/potato.php?t=10689) Apparently,
PacketGet can receive ECONNRESET from nodes that aren't in the game. It
should be safe to just ignore these packets.
- Fixed: PlayerIsGone() should set the gone player's camera to NULL in case
the player who left was player 0. This is because if a remaining player
receives a "recoverable" error, they will become player 0. Once that happens,
they game will try to update sounds through their camera and crash in
FMODSoundRenderer::UpdateListener() because the zones array is now NULL.
G_NewInit() should also clear all the player structures.
SVN r233 (trunk)
2006-06-30 02:13:26 +00:00
|
|
|
if (!netgame)
|
|
|
|
{
|
|
|
|
gameaction = ga_fullconsole;
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// [RH] Write any special "ticcmds" for this player to the demo
|
|
|
|
if ((specdata = NetSpecs[player][buf].GetData (&speclen)) && gametic % ticdup == 0)
|
|
|
|
{
|
|
|
|
memcpy (demo_p, specdata, speclen);
|
|
|
|
demo_p += speclen;
|
|
|
|
NetSpecs[player][buf].SetData (NULL, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// [RH] Now write out a "normal" ticcmd.
|
|
|
|
WriteUserCmdMessage (&cmd->ucmd, &players[player].cmd.ucmd, &demo_p);
|
|
|
|
|
|
|
|
// [RH] Bigger safety margin
|
|
|
|
if (demo_p > demobuffer + maxdemosize - 64)
|
|
|
|
{
|
|
|
|
ptrdiff_t pos = demo_p - demobuffer;
|
|
|
|
ptrdiff_t spot = lenspot - demobuffer;
|
|
|
|
ptrdiff_t comp = democompspot - demobuffer;
|
|
|
|
ptrdiff_t body = demobodyspot - demobuffer;
|
|
|
|
// [RH] Allocate more space for the demo
|
|
|
|
maxdemosize += 0x20000;
|
2006-09-14 00:02:31 +00:00
|
|
|
demobuffer = (BYTE *)M_Realloc (demobuffer, maxdemosize);
|
2006-02-24 04:48:15 +00:00
|
|
|
demo_p = demobuffer + pos;
|
|
|
|
lenspot = demobuffer + spot;
|
|
|
|
democompspot = demobuffer + comp;
|
|
|
|
demobodyspot = demobuffer + body;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// G_RecordDemo
|
|
|
|
//
|
|
|
|
void G_RecordDemo (char* name)
|
|
|
|
{
|
|
|
|
char *v;
|
|
|
|
|
|
|
|
usergame = false;
|
|
|
|
strcpy (demoname, name);
|
|
|
|
FixPathSeperator (demoname);
|
|
|
|
DefaultExtension (demoname, ".lmp");
|
2008-03-12 02:56:11 +00:00
|
|
|
v = Args->CheckValue ("-maxdemo");
|
2006-07-08 02:17:35 +00:00
|
|
|
maxdemosize = 0x20000;
|
2006-09-14 00:02:31 +00:00
|
|
|
demobuffer = (BYTE *)M_Malloc (maxdemosize);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
demorecording = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// [RH] Demos are now saved as IFF FORMs. I've also removed support
|
|
|
|
// for earlier ZDEMs since I didn't want to bother supporting
|
|
|
|
// something that probably wasn't used much (if at all).
|
|
|
|
|
2006-04-16 19:09:36 +00:00
|
|
|
void G_BeginRecording (const char *startmap)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2006-04-16 19:09:36 +00:00
|
|
|
if (startmap == NULL)
|
|
|
|
{
|
|
|
|
startmap = level.mapname;
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
demo_p = demobuffer;
|
|
|
|
|
|
|
|
WriteLong (FORM_ID, &demo_p); // Write FORM ID
|
|
|
|
demo_p += 4; // Leave space for len
|
|
|
|
WriteLong (ZDEM_ID, &demo_p); // Write ZDEM ID
|
|
|
|
|
|
|
|
// Write header chunk
|
|
|
|
StartChunk (ZDHD_ID, &demo_p);
|
2006-04-21 05:44:21 +00:00
|
|
|
WriteWord (DEMOGAMEVERSION, &demo_p); // Write ZDoom version
|
2006-02-24 04:48:15 +00:00
|
|
|
*demo_p++ = 2; // Write minimum version needed to use this demo.
|
|
|
|
*demo_p++ = 3; // (Useful?)
|
|
|
|
for (i = 0; i < 8; i++) // Write name of map demo was recorded on.
|
|
|
|
{
|
2006-04-16 19:09:36 +00:00
|
|
|
*demo_p++ = startmap[i];
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
WriteLong (rngseed, &demo_p); // Write RNG seed
|
|
|
|
*demo_p++ = consoleplayer;
|
|
|
|
FinishChunk (&demo_p);
|
|
|
|
|
|
|
|
// Write player info chunks
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
|
|
{
|
|
|
|
if (playeringame[i])
|
|
|
|
{
|
|
|
|
StartChunk (UINF_ID, &demo_p);
|
2006-09-14 00:02:31 +00:00
|
|
|
WriteByte ((BYTE)i, &demo_p);
|
2006-02-24 04:48:15 +00:00
|
|
|
D_WriteUserInfoStrings (i, &demo_p);
|
|
|
|
FinishChunk (&demo_p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-09-20 02:00:19 +00:00
|
|
|
// It is possible to start a "multiplayer" game with only one player,
|
|
|
|
// so checking the number of players when playing back the demo is not
|
|
|
|
// enough.
|
|
|
|
if (multiplayer)
|
|
|
|
{
|
|
|
|
StartChunk (NETD_ID, &demo_p);
|
|
|
|
FinishChunk (&demo_p);
|
|
|
|
}
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
// Write cvars chunk
|
|
|
|
StartChunk (VARS_ID, &demo_p);
|
|
|
|
C_WriteCVars (&demo_p, CVAR_SERVERINFO|CVAR_DEMOSAVE);
|
|
|
|
FinishChunk (&demo_p);
|
|
|
|
|
|
|
|
// Indicate body is compressed
|
|
|
|
StartChunk (COMP_ID, &demo_p);
|
|
|
|
democompspot = demo_p;
|
|
|
|
WriteLong (0, &demo_p);
|
|
|
|
FinishChunk (&demo_p);
|
|
|
|
|
|
|
|
// Begin BODY chunk
|
|
|
|
StartChunk (BODY_ID, &demo_p);
|
|
|
|
demobodyspot = demo_p;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// G_PlayDemo
|
|
|
|
//
|
|
|
|
|
|
|
|
char defdemoname[128];
|
|
|
|
|
|
|
|
void G_DeferedPlayDemo (char *name)
|
|
|
|
{
|
|
|
|
strncpy (defdemoname, name, 127);
|
|
|
|
gameaction = ga_playdemo;
|
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (playdemo)
|
|
|
|
{
|
|
|
|
if (argv.argc() > 1)
|
|
|
|
{
|
|
|
|
G_DeferedPlayDemo (argv[1]);
|
|
|
|
singledemo = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (timedemo)
|
|
|
|
{
|
|
|
|
if (argv.argc() > 1)
|
|
|
|
{
|
|
|
|
G_TimeDemo (argv[1]);
|
|
|
|
singledemo = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// [RH] Process all the information in a FORM ZDEM
|
|
|
|
// until a BODY chunk is entered.
|
2006-09-14 00:02:31 +00:00
|
|
|
bool G_ProcessIFFDemo (char *mapname)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-09-14 00:02:31 +00:00
|
|
|
bool headerHit = false;
|
|
|
|
bool bodyHit = false;
|
2006-02-24 04:48:15 +00:00
|
|
|
int numPlayers = 0;
|
|
|
|
int id, len, i;
|
|
|
|
uLong uncompSize = 0;
|
2006-09-14 00:02:31 +00:00
|
|
|
BYTE *nextchunk;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
demoplayback = true;
|
|
|
|
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
|
|
playeringame[i] = 0;
|
|
|
|
|
|
|
|
len = ReadLong (&demo_p);
|
|
|
|
zdemformend = demo_p + len + (len & 1);
|
|
|
|
|
|
|
|
// Check to make sure this is a ZDEM chunk file.
|
|
|
|
// TODO: Support multiple FORM ZDEMs in a CAT. Might be useful.
|
|
|
|
|
|
|
|
id = ReadLong (&demo_p);
|
|
|
|
if (id != ZDEM_ID)
|
|
|
|
{
|
|
|
|
Printf ("Not a ZDoom demo file!\n");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Process all chunks until a BODY chunk is encountered.
|
|
|
|
|
|
|
|
while (demo_p < zdemformend && !bodyHit)
|
|
|
|
{
|
|
|
|
id = ReadLong (&demo_p);
|
|
|
|
len = ReadLong (&demo_p);
|
|
|
|
nextchunk = demo_p + len + (len & 1);
|
|
|
|
if (nextchunk > zdemformend)
|
|
|
|
{
|
|
|
|
Printf ("Demo is mangled!\n");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (id)
|
|
|
|
{
|
|
|
|
case ZDHD_ID:
|
|
|
|
headerHit = true;
|
|
|
|
|
|
|
|
demover = ReadWord (&demo_p); // ZDoom version demo was created with
|
2006-04-21 05:44:21 +00:00
|
|
|
if (demover < MINDEMOVERSION)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
Printf ("Demo requires an older version of ZDoom!\n");
|
|
|
|
//return true;
|
|
|
|
}
|
2006-04-21 05:44:21 +00:00
|
|
|
if (ReadWord (&demo_p) > DEMOGAMEVERSION) // Minimum ZDoom version
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
Printf ("Demo requires a newer version of ZDoom!\n");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
memcpy (mapname, demo_p, 8); // Read map name
|
|
|
|
mapname[8] = 0;
|
|
|
|
demo_p += 8;
|
|
|
|
rngseed = ReadLong (&demo_p);
|
|
|
|
FRandom::StaticClearRandom ();
|
|
|
|
consoleplayer = *demo_p++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VARS_ID:
|
|
|
|
C_ReadCVars (&demo_p);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case UINF_ID:
|
|
|
|
i = ReadByte (&demo_p);
|
|
|
|
if (!playeringame[i])
|
|
|
|
{
|
|
|
|
playeringame[i] = 1;
|
|
|
|
numPlayers++;
|
|
|
|
}
|
|
|
|
D_ReadUserInfoStrings (i, &demo_p, false);
|
|
|
|
break;
|
|
|
|
|
2006-09-20 02:00:19 +00:00
|
|
|
case NETD_ID:
|
2006-10-19 20:20:56 +00:00
|
|
|
multiplayer = true;
|
2006-09-20 02:00:19 +00:00
|
|
|
break;
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
case BODY_ID:
|
|
|
|
bodyHit = true;
|
|
|
|
zdembodyend = demo_p + len;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case COMP_ID:
|
|
|
|
uncompSize = ReadLong (&demo_p);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!bodyHit)
|
|
|
|
demo_p = nextchunk;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!numPlayers)
|
|
|
|
{
|
|
|
|
Printf ("Demo has no players!\n");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!bodyHit)
|
|
|
|
{
|
|
|
|
zdembodyend = NULL;
|
|
|
|
Printf ("Demo has no BODY chunk!\n");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (numPlayers > 1)
|
|
|
|
multiplayer = netgame = netdemo = true;
|
|
|
|
|
|
|
|
if (uncompSize > 0)
|
|
|
|
{
|
|
|
|
BYTE *uncompressed = new BYTE[uncompSize];
|
|
|
|
int r = uncompress (uncompressed, &uncompSize, demo_p, zdembodyend - demo_p);
|
|
|
|
if (r != Z_OK)
|
|
|
|
{
|
|
|
|
Printf ("Could not decompress demo!\n");
|
|
|
|
delete[] uncompressed;
|
|
|
|
return true;
|
|
|
|
}
|
2008-02-17 02:40:03 +00:00
|
|
|
M_Free (demobuffer);
|
2006-02-24 04:48:15 +00:00
|
|
|
zdembodyend = uncompressed + uncompSize;
|
|
|
|
demobuffer = demo_p = uncompressed;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void G_DoPlayDemo (void)
|
|
|
|
{
|
|
|
|
char mapname[9];
|
|
|
|
int demolump;
|
|
|
|
|
|
|
|
gameaction = ga_nothing;
|
|
|
|
|
|
|
|
// [RH] Allow for demos not loaded as lumps
|
|
|
|
demolump = Wads.CheckNumForName (defdemoname);
|
|
|
|
if (demolump >= 0)
|
|
|
|
{
|
|
|
|
int demolen = Wads.LumpLength (demolump);
|
2008-02-17 02:40:03 +00:00
|
|
|
demobuffer = (BYTE *)M_Malloc(demolen);
|
2006-02-24 04:48:15 +00:00
|
|
|
Wads.ReadLump (demolump, demobuffer);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
FixPathSeperator (defdemoname);
|
|
|
|
DefaultExtension (defdemoname, ".lmp");
|
|
|
|
M_ReadFile (defdemoname, &demobuffer);
|
|
|
|
}
|
|
|
|
demo_p = demobuffer;
|
|
|
|
|
|
|
|
Printf ("Playing demo %s\n", defdemoname);
|
|
|
|
|
|
|
|
C_BackupCVars (); // [RH] Save cvars that might be affected by demo
|
|
|
|
|
|
|
|
if (ReadLong (&demo_p) != FORM_ID)
|
|
|
|
{
|
|
|
|
const char *eek = "Cannot play non-ZDoom demos.\n(They would go out of sync badly.)\n";
|
|
|
|
|
|
|
|
if (singledemo)
|
|
|
|
{
|
|
|
|
I_Error (eek);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Printf (PRINT_BOLD, eek);
|
|
|
|
gameaction = ga_nothing;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (G_ProcessIFFDemo (mapname))
|
|
|
|
{
|
|
|
|
gameaction = ga_nothing;
|
|
|
|
demoplayback = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// don't spend a lot of time in loadlevel
|
|
|
|
precache = false;
|
|
|
|
demonew = true;
|
|
|
|
G_InitNew (mapname, false);
|
|
|
|
C_HideConsole ();
|
|
|
|
demonew = false;
|
|
|
|
precache = true;
|
|
|
|
|
|
|
|
usergame = false;
|
|
|
|
demoplayback = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// G_TimeDemo
|
|
|
|
//
|
|
|
|
void G_TimeDemo (char* name)
|
|
|
|
{
|
2008-03-12 02:56:11 +00:00
|
|
|
nodrawers = !!Args->CheckParm ("-nodraw");
|
|
|
|
noblit = !!Args->CheckParm ("-noblit");
|
2006-02-24 04:48:15 +00:00
|
|
|
timingdemo = true;
|
|
|
|
singletics = true;
|
|
|
|
|
|
|
|
strncpy (defdemoname, name, 128);
|
|
|
|
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
|
|
|
|
===================
|
|
|
|
*/
|
|
|
|
|
2006-09-14 00:02:31 +00:00
|
|
|
bool G_CheckDemoStatus (void)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
if (!demorecording)
|
|
|
|
{ // [RH] Restore the player's userinfo settings.
|
2006-04-21 05:44:21 +00:00
|
|
|
D_SetupUserInfo();
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (demoplayback)
|
|
|
|
{
|
|
|
|
extern int starttime;
|
|
|
|
int endtime = 0;
|
|
|
|
|
|
|
|
if (timingdemo)
|
|
|
|
endtime = I_GetTime (false) - starttime;
|
|
|
|
|
|
|
|
C_RestoreCVars (); // [RH] Restore cvars demo might have changed
|
|
|
|
|
2008-02-17 02:40:03 +00:00
|
|
|
M_Free (demobuffer);
|
2006-02-24 04:48:15 +00:00
|
|
|
demoplayback = false;
|
|
|
|
netdemo = false;
|
|
|
|
netgame = false;
|
|
|
|
multiplayer = false;
|
|
|
|
singletics = false;
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 1; i < MAXPLAYERS; i++)
|
|
|
|
playeringame[i] = 0;
|
|
|
|
}
|
|
|
|
consoleplayer = 0;
|
2006-10-20 01:58:26 +00:00
|
|
|
players[0].camera = NULL;
|
|
|
|
StatusBar->AttachToPlayer (&players[0]);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
if (singledemo || timingdemo)
|
|
|
|
{
|
|
|
|
if (timingdemo)
|
|
|
|
{
|
|
|
|
// Trying to get back to a stable state after timing a demo
|
|
|
|
// seems to cause problems. I don't feel like fixing that
|
|
|
|
// right now.
|
|
|
|
I_FatalError ("timed %i gametics in %i realtics (%.1f fps)\n"
|
|
|
|
"(This is not really an error.)", gametic,
|
|
|
|
endtime, (float)gametic/(float)endtime*(float)TICRATE);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Printf ("Demo ended.\n");
|
|
|
|
}
|
|
|
|
gameaction = ga_fullconsole;
|
|
|
|
timingdemo = false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
D_AdvanceDemo ();
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (demorecording)
|
|
|
|
{
|
2006-09-14 00:02:31 +00:00
|
|
|
BYTE *formlen;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
WriteByte (DEM_STOP, &demo_p);
|
|
|
|
|
|
|
|
if (demo_compress)
|
|
|
|
{
|
|
|
|
// Now that the entire BODY chunk has been created, replace it with
|
|
|
|
// a compressed version. If the BODY successfully compresses, the
|
|
|
|
// contents of the COMP chunk will be changed to indicate the
|
|
|
|
// uncompressed size of the BODY.
|
|
|
|
uLong len = demo_p - demobodyspot;
|
|
|
|
uLong outlen = (len + len/100 + 12);
|
|
|
|
Byte *compressed = new Byte[outlen];
|
|
|
|
int r = compress2 (compressed, &outlen, demobodyspot, len, 9);
|
|
|
|
if (r == Z_OK && outlen < len)
|
|
|
|
{
|
|
|
|
formlen = democompspot;
|
|
|
|
WriteLong (len, &democompspot);
|
|
|
|
memcpy (demobodyspot, compressed, outlen);
|
|
|
|
demo_p = demobodyspot + outlen;
|
|
|
|
}
|
|
|
|
delete[] compressed;
|
|
|
|
}
|
|
|
|
FinishChunk (&demo_p);
|
|
|
|
formlen = demobuffer + 4;
|
|
|
|
WriteLong (demo_p - demobuffer - 8, &formlen);
|
|
|
|
|
|
|
|
M_WriteFile (demoname, demobuffer, demo_p - demobuffer);
|
2008-02-17 02:40:03 +00:00
|
|
|
M_Free (demobuffer);
|
2006-02-24 04:48:15 +00:00
|
|
|
demorecording = false;
|
|
|
|
stoprecording = false;
|
|
|
|
Printf ("Demo %s recorded\n", demoname);
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|