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:
|
|
|
|
// Player related stuff.
|
|
|
|
// Bobbing POV/weapon, movement.
|
|
|
|
// Pending weapon.
|
|
|
|
//
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
#include "templates.h"
|
|
|
|
#include "doomdef.h"
|
|
|
|
#include "d_event.h"
|
|
|
|
#include "p_local.h"
|
|
|
|
#include "doomstat.h"
|
|
|
|
#include "s_sound.h"
|
|
|
|
#include "i_system.h"
|
|
|
|
#include "gi.h"
|
|
|
|
#include "m_random.h"
|
|
|
|
#include "p_pspr.h"
|
|
|
|
#include "p_enemy.h"
|
|
|
|
#include "s_sound.h"
|
|
|
|
#include "a_sharedglobal.h"
|
2006-06-18 04:10:47 +00:00
|
|
|
#include "a_keys.h"
|
2006-02-24 04:48:15 +00:00
|
|
|
#include "statnums.h"
|
|
|
|
#include "v_palette.h"
|
|
|
|
#include "v_video.h"
|
|
|
|
#include "w_wad.h"
|
|
|
|
#include "cmdlib.h"
|
|
|
|
#include "sbar.h"
|
2010-10-06 10:44:03 +00:00
|
|
|
#include "intermission/intermission.h"
|
2006-02-24 04:48:15 +00:00
|
|
|
#include "c_console.h"
|
2006-06-18 04:10:47 +00:00
|
|
|
#include "doomdef.h"
|
2006-07-13 10:17:56 +00:00
|
|
|
#include "c_dispatch.h"
|
|
|
|
#include "tarray.h"
|
2007-05-28 14:46:49 +00:00
|
|
|
#include "thingdef/thingdef.h"
|
2008-09-14 23:54:38 +00:00
|
|
|
#include "g_level.h"
|
|
|
|
#include "d_net.h"
|
2010-09-21 09:43:54 +00:00
|
|
|
#include "gstrings.h"
|
2011-07-06 14:20:54 +00:00
|
|
|
#include "farchive.h"
|
2011-07-07 15:37:47 +00:00
|
|
|
#include "r_renderer.h"
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2006-11-07 10:20:09 +00:00
|
|
|
static FRandom pr_skullpop ("SkullPop");
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
// [RH] # of ticks to complete a turn180
|
|
|
|
#define TURN180_TICKS ((TICRATE / 4) + 1)
|
|
|
|
|
|
|
|
// Variables for prediction
|
|
|
|
CVAR (Bool, cl_noprediction, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
2014-08-23 03:17:11 +00:00
|
|
|
CVAR(Bool, cl_predict_specials, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
2014-10-06 02:27:13 +00:00
|
|
|
|
2014-10-13 03:32:49 +00:00
|
|
|
CUSTOM_CVAR(Float, cl_predict_lerpscale, 0.05f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
2014-10-12 11:29:15 +00:00
|
|
|
{
|
|
|
|
P_PredictionLerpReset();
|
|
|
|
}
|
2014-10-13 03:32:49 +00:00
|
|
|
CUSTOM_CVAR(Float, cl_predict_lerpthreshold, 2.00f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
2014-10-12 11:29:15 +00:00
|
|
|
{
|
2014-10-13 03:32:49 +00:00
|
|
|
if (self < 0.1f)
|
|
|
|
self = 0.1f;
|
2014-10-12 11:29:15 +00:00
|
|
|
P_PredictionLerpReset();
|
|
|
|
}
|
2014-10-06 02:27:13 +00:00
|
|
|
|
2014-10-12 11:29:15 +00:00
|
|
|
struct PredictPos
|
2014-10-06 02:27:13 +00:00
|
|
|
{
|
|
|
|
int gametic;
|
2014-10-12 11:29:15 +00:00
|
|
|
FVector3 point;
|
2014-10-06 02:27:13 +00:00
|
|
|
fixed_t pitch;
|
|
|
|
fixed_t yaw;
|
2014-10-12 11:29:15 +00:00
|
|
|
} static PredictionLerpFrom, PredictionLerpResult, PredictionLast;
|
2014-10-06 02:27:13 +00:00
|
|
|
static int PredictionLerptics;
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
static player_t PredictionPlayerBackup;
|
|
|
|
static BYTE PredictionActorBackup[sizeof(AActor)];
|
|
|
|
static TArray<sector_t *> PredictionTouchingSectorsBackup;
|
2014-04-01 10:05:08 +00:00
|
|
|
static TArray<AActor *> PredictionSectorListBackup;
|
|
|
|
static TArray<msecnode_t *> PredictionSector_sprev_Backup;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2006-07-13 10:17:56 +00:00
|
|
|
// [GRB] Custom player classes
|
|
|
|
TArray<FPlayerClass> PlayerClasses;
|
|
|
|
|
|
|
|
FPlayerClass::FPlayerClass ()
|
|
|
|
{
|
|
|
|
Type = NULL;
|
|
|
|
Flags = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
FPlayerClass::FPlayerClass (const FPlayerClass &other)
|
|
|
|
{
|
|
|
|
Type = other.Type;
|
|
|
|
Flags = other.Flags;
|
|
|
|
Skins = other.Skins;
|
|
|
|
}
|
|
|
|
|
|
|
|
FPlayerClass::~FPlayerClass ()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FPlayerClass::CheckSkin (int skin)
|
|
|
|
{
|
2006-07-16 01:25:29 +00:00
|
|
|
for (unsigned int i = 0; i < Skins.Size (); i++)
|
2006-07-13 10:17:56 +00:00
|
|
|
{
|
|
|
|
if (Skins[i] == skin)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-09-21 09:43:54 +00:00
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// GetDisplayName
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
const char *GetPrintableDisplayName(const PClass *cls)
|
|
|
|
{
|
|
|
|
// Fixme; This needs a decent way to access the string table without creating a mess.
|
|
|
|
const char *name = cls->Meta.GetMetaString(APMETA_DisplayName);
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
2010-09-19 08:27:20 +00:00
|
|
|
bool ValidatePlayerClass(const PClass *ti, const char *name)
|
2006-07-13 10:17:56 +00:00
|
|
|
{
|
2010-09-19 08:27:20 +00:00
|
|
|
if (!ti)
|
2006-07-13 10:17:56 +00:00
|
|
|
{
|
2010-09-19 08:27:20 +00:00
|
|
|
Printf ("Unknown player class '%s'\n", name);
|
|
|
|
return false;
|
2006-07-13 10:17:56 +00:00
|
|
|
}
|
2010-09-19 08:27:20 +00:00
|
|
|
else if (!ti->IsDescendantOf (RUNTIME_CLASS (APlayerPawn)))
|
2006-07-13 10:17:56 +00:00
|
|
|
{
|
2010-09-19 08:27:20 +00:00
|
|
|
Printf ("Invalid player class '%s'\n", name);
|
|
|
|
return false;
|
2006-07-13 10:17:56 +00:00
|
|
|
}
|
2010-09-19 08:27:20 +00:00
|
|
|
else if (ti->Meta.GetMetaString (APMETA_DisplayName) == NULL)
|
2006-07-13 10:17:56 +00:00
|
|
|
{
|
2010-09-19 08:27:20 +00:00
|
|
|
Printf ("Missing displayname for player class '%s'\n", name);
|
|
|
|
return false;
|
2006-07-13 10:17:56 +00:00
|
|
|
}
|
2010-09-19 08:27:20 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetupPlayerClasses ()
|
|
|
|
{
|
|
|
|
FPlayerClass newclass;
|
|
|
|
|
2010-12-15 00:09:31 +00:00
|
|
|
PlayerClasses.Clear();
|
2010-09-19 08:27:20 +00:00
|
|
|
for (unsigned i=0; i<gameinfo.PlayerClasses.Size(); i++)
|
2008-08-30 19:44:19 +00:00
|
|
|
{
|
2010-09-19 08:27:20 +00:00
|
|
|
newclass.Flags = 0;
|
|
|
|
newclass.Type = PClass::FindClass(gameinfo.PlayerClasses[i]);
|
|
|
|
|
|
|
|
if (ValidatePlayerClass(newclass.Type, gameinfo.PlayerClasses[i]))
|
|
|
|
{
|
|
|
|
if ((GetDefaultByType(newclass.Type)->flags6 & MF6_NOMENU))
|
|
|
|
{
|
|
|
|
newclass.Flags |= PCF_NOMENU;
|
|
|
|
}
|
|
|
|
PlayerClasses.Push (newclass);
|
|
|
|
}
|
2008-08-30 19:44:19 +00:00
|
|
|
}
|
2006-07-13 10:17:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (clearplayerclasses)
|
|
|
|
{
|
|
|
|
if (ParsingKeyConf)
|
|
|
|
{
|
|
|
|
PlayerClasses.Clear ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (addplayerclass)
|
|
|
|
{
|
|
|
|
if (ParsingKeyConf && argv.argc () > 1)
|
|
|
|
{
|
|
|
|
const PClass *ti = PClass::FindClass (argv[1]);
|
|
|
|
|
2010-09-19 08:27:20 +00:00
|
|
|
if (ValidatePlayerClass(ti, argv[1]))
|
2006-07-13 10:17:56 +00:00
|
|
|
{
|
|
|
|
FPlayerClass newclass;
|
|
|
|
|
|
|
|
newclass.Type = ti;
|
|
|
|
newclass.Flags = 0;
|
|
|
|
|
|
|
|
int arg = 2;
|
|
|
|
while (arg < argv.argc ())
|
|
|
|
{
|
|
|
|
if (!stricmp (argv[arg], "nomenu"))
|
|
|
|
{
|
|
|
|
newclass.Flags |= PCF_NOMENU;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-02-16 15:02:52 +00:00
|
|
|
Printf ("Unknown flag '%s' for player class '%s'\n", argv[arg], argv[1]);
|
2006-07-13 10:17:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
arg++;
|
|
|
|
}
|
|
|
|
|
|
|
|
PlayerClasses.Push (newclass);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (playerclasses)
|
|
|
|
{
|
2006-07-16 01:25:29 +00:00
|
|
|
for (unsigned int i = 0; i < PlayerClasses.Size (); i++)
|
2006-07-13 10:17:56 +00:00
|
|
|
{
|
2010-09-19 08:27:20 +00:00
|
|
|
Printf ("%3d: Class = %s, Name = %s\n", i,
|
|
|
|
PlayerClasses[i].Type->TypeName.GetChars(),
|
2006-07-13 10:17:56 +00:00
|
|
|
PlayerClasses[i].Type->Meta.GetMetaString (APMETA_DisplayName));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
//
|
|
|
|
// Movement.
|
|
|
|
//
|
|
|
|
|
|
|
|
// 16 pixels of bob
|
|
|
|
#define MAXBOB 0x100000
|
|
|
|
|
2011-07-06 14:20:54 +00:00
|
|
|
FArchive &operator<< (FArchive &arc, player_t *&p)
|
|
|
|
{
|
|
|
|
return arc.SerializePointer (players, (BYTE **)&p, sizeof(*players));
|
|
|
|
}
|
|
|
|
|
2008-06-01 07:52:33 +00:00
|
|
|
// The player_t constructor. Since LogText is not a POD, we cannot just
|
2006-05-16 02:50:18 +00:00
|
|
|
// memset it all to 0.
|
2008-06-01 07:52:33 +00:00
|
|
|
player_t::player_t()
|
2006-05-16 02:50:18 +00:00
|
|
|
: mo(0),
|
|
|
|
playerstate(0),
|
|
|
|
cls(0),
|
|
|
|
DesiredFOV(0),
|
|
|
|
FOV(0),
|
|
|
|
viewz(0),
|
|
|
|
viewheight(0),
|
|
|
|
deltaviewheight(0),
|
|
|
|
bob(0),
|
2009-06-30 20:57:51 +00:00
|
|
|
velx(0),
|
|
|
|
vely(0),
|
2006-05-16 02:50:18 +00:00
|
|
|
centering(0),
|
|
|
|
turnticks(0),
|
|
|
|
attackdown(0),
|
2009-12-25 11:09:56 +00:00
|
|
|
usedown(0),
|
2008-09-17 20:24:08 +00:00
|
|
|
oldbuttons(0),
|
2006-05-16 02:50:18 +00:00
|
|
|
health(0),
|
|
|
|
inventorytics(0),
|
|
|
|
CurrentPlayerClass(0),
|
|
|
|
fragcount(0),
|
|
|
|
lastkilltime(0),
|
|
|
|
multicount(0),
|
|
|
|
spreecount(0),
|
2013-02-22 18:16:23 +00:00
|
|
|
WeaponState(0),
|
2006-05-16 02:50:18 +00:00
|
|
|
ReadyWeapon(0),
|
|
|
|
PendingWeapon(0),
|
|
|
|
cheats(0),
|
2012-04-27 01:40:50 +00:00
|
|
|
timefreezer(0),
|
2006-05-16 02:50:18 +00:00
|
|
|
refire(0),
|
|
|
|
inconsistant(0),
|
|
|
|
killcount(0),
|
|
|
|
itemcount(0),
|
|
|
|
secretcount(0),
|
|
|
|
damagecount(0),
|
|
|
|
bonuscount(0),
|
|
|
|
hazardcount(0),
|
|
|
|
poisoncount(0),
|
|
|
|
poisoner(0),
|
|
|
|
attacker(0),
|
|
|
|
extralight(0),
|
|
|
|
morphTics(0),
|
2008-04-06 17:33:43 +00:00
|
|
|
MorphedPlayerClass(0),
|
2008-04-08 08:53:42 +00:00
|
|
|
MorphStyle(0),
|
|
|
|
MorphExitFlash(0),
|
2006-05-16 02:50:18 +00:00
|
|
|
PremorphWeapon(0),
|
|
|
|
chickenPeck(0),
|
|
|
|
jumpTics(0),
|
2014-03-02 12:00:47 +00:00
|
|
|
onground(0),
|
2006-05-16 02:50:18 +00:00
|
|
|
respawn_time(0),
|
|
|
|
camera(0),
|
|
|
|
air_finished(0),
|
|
|
|
savedyaw(0),
|
|
|
|
savedpitch(0),
|
|
|
|
angle(0),
|
|
|
|
dest(0),
|
|
|
|
prev(0),
|
|
|
|
enemy(0),
|
|
|
|
missile(0),
|
|
|
|
mate(0),
|
|
|
|
last_mate(0),
|
|
|
|
t_active(0),
|
|
|
|
t_respawn(0),
|
|
|
|
t_strafe(0),
|
|
|
|
t_react(0),
|
|
|
|
t_fight(0),
|
|
|
|
t_roam(0),
|
|
|
|
t_rocket(0),
|
|
|
|
isbot(0),
|
|
|
|
first_shot(0),
|
|
|
|
sleft(0),
|
|
|
|
allround(0),
|
|
|
|
oldx(0),
|
|
|
|
oldy(0),
|
|
|
|
BlendR(0),
|
|
|
|
BlendG(0),
|
|
|
|
BlendB(0),
|
|
|
|
BlendA(0),
|
|
|
|
LogText(),
|
|
|
|
crouching(0),
|
|
|
|
crouchdir(0),
|
|
|
|
crouchfactor(0),
|
|
|
|
crouchoffset(0),
|
2008-03-26 08:29:02 +00:00
|
|
|
crouchviewdelta(0),
|
|
|
|
ConversationNPC(0),
|
|
|
|
ConversationPC(0),
|
|
|
|
ConversationNPCAngle(0),
|
|
|
|
ConversationFaceTalker(0)
|
2006-05-16 02:50:18 +00:00
|
|
|
{
|
|
|
|
memset (&cmd, 0, sizeof(cmd));
|
|
|
|
memset (frags, 0, sizeof(frags));
|
|
|
|
memset (psprites, 0, sizeof(psprites));
|
|
|
|
memset (&skill, 0, sizeof(skill));
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2013-09-19 02:14:44 +00:00
|
|
|
player_t &player_t::operator=(const player_t &p)
|
|
|
|
{
|
|
|
|
mo = p.mo;
|
|
|
|
playerstate = p.playerstate;
|
|
|
|
cmd = p.cmd;
|
|
|
|
original_cmd = p.original_cmd;
|
|
|
|
original_oldbuttons = p.original_oldbuttons;
|
|
|
|
// Intentionally not copying userinfo!
|
|
|
|
cls = p.cls;
|
|
|
|
DesiredFOV = p.DesiredFOV;
|
|
|
|
FOV = p.FOV;
|
|
|
|
viewz = p.viewz;
|
|
|
|
viewheight = p.viewheight;
|
|
|
|
deltaviewheight = p.deltaviewheight;
|
|
|
|
bob = p.bob;
|
|
|
|
velx = p.velx;
|
|
|
|
vely = p.vely;
|
|
|
|
centering = p.centering;
|
|
|
|
turnticks = p.turnticks;
|
|
|
|
attackdown = p.attackdown;
|
|
|
|
usedown = p.usedown;
|
|
|
|
oldbuttons = p.oldbuttons;
|
|
|
|
health = p.health;
|
|
|
|
inventorytics = p.inventorytics;
|
|
|
|
CurrentPlayerClass = p.CurrentPlayerClass;
|
|
|
|
memcpy(frags, &p.frags, sizeof(frags));
|
|
|
|
fragcount = p.fragcount;
|
|
|
|
lastkilltime = p.lastkilltime;
|
|
|
|
multicount = p.multicount;
|
|
|
|
spreecount = p.spreecount;
|
|
|
|
WeaponState = p.WeaponState;
|
|
|
|
ReadyWeapon = p.ReadyWeapon;
|
|
|
|
PendingWeapon = p.PendingWeapon;
|
|
|
|
cheats = p.cheats;
|
|
|
|
timefreezer = p.timefreezer;
|
|
|
|
refire = p.refire;
|
|
|
|
inconsistant = p.inconsistant;
|
|
|
|
waiting = p.waiting;
|
|
|
|
killcount = p.killcount;
|
|
|
|
itemcount = p.itemcount;
|
|
|
|
secretcount = p.secretcount;
|
|
|
|
damagecount = p.damagecount;
|
|
|
|
bonuscount = p.bonuscount;
|
|
|
|
hazardcount = p.hazardcount;
|
|
|
|
poisoncount = p.poisoncount;
|
|
|
|
poisontype = p.poisontype;
|
|
|
|
poisonpaintype = p.poisonpaintype;
|
|
|
|
poisoner = p.poisoner;
|
|
|
|
attacker = p.attacker;
|
|
|
|
extralight = p.extralight;
|
|
|
|
fixedcolormap = p.fixedcolormap;
|
|
|
|
fixedlightlevel = p.fixedlightlevel;
|
|
|
|
memcpy(psprites, &p.psprites, sizeof(psprites));
|
|
|
|
morphTics = p.morphTics;
|
|
|
|
MorphedPlayerClass = p.MorphedPlayerClass;
|
|
|
|
MorphStyle = p.MorphStyle;
|
|
|
|
MorphExitFlash = p.MorphExitFlash;
|
|
|
|
PremorphWeapon = p.PremorphWeapon;
|
|
|
|
chickenPeck = p.chickenPeck;
|
|
|
|
jumpTics = p.jumpTics;
|
2014-03-02 12:00:47 +00:00
|
|
|
onground = p.onground;
|
2013-09-19 02:14:44 +00:00
|
|
|
respawn_time = p.respawn_time;
|
|
|
|
camera = p.camera;
|
|
|
|
air_finished = p.air_finished;
|
|
|
|
LastDamageType = p.LastDamageType;
|
|
|
|
savedyaw = p.savedyaw;
|
|
|
|
savedpitch = p.savedpitch;
|
|
|
|
angle = p.angle;
|
|
|
|
dest = p.dest;
|
|
|
|
prev = p.prev;
|
|
|
|
enemy = p.enemy;
|
|
|
|
missile = p.missile;
|
|
|
|
mate = p.mate;
|
|
|
|
last_mate = p.last_mate;
|
|
|
|
settings_controller = p.settings_controller;
|
|
|
|
skill = p.skill;
|
|
|
|
t_active = p.t_active;
|
|
|
|
t_respawn = p.t_respawn;
|
|
|
|
t_strafe = p.t_strafe;
|
|
|
|
t_react = p.t_react;
|
|
|
|
t_fight = p.t_fight;
|
|
|
|
t_roam = p.t_roam;
|
|
|
|
t_rocket = p.t_rocket;
|
|
|
|
isbot = p.isbot;
|
|
|
|
first_shot = p.first_shot;
|
|
|
|
sleft = p.sleft;
|
|
|
|
allround = p.allround;
|
|
|
|
oldx = p.oldx;
|
|
|
|
oldy = p.oldy;
|
|
|
|
BlendR = p.BlendR;
|
|
|
|
BlendG = p.BlendG;
|
|
|
|
BlendB = p.BlendB;
|
|
|
|
BlendA = p.BlendA;
|
|
|
|
LogText = p.LogText;
|
|
|
|
MinPitch = p.MinPitch;
|
|
|
|
MaxPitch = p.MaxPitch;
|
|
|
|
crouching = p.crouching;
|
|
|
|
crouchdir = p.crouchdir;
|
|
|
|
crouchfactor = p.crouchfactor;
|
|
|
|
crouchoffset = p.crouchoffset;
|
|
|
|
crouchviewdelta = p.crouchviewdelta;
|
|
|
|
weapons = p.weapons;
|
|
|
|
ConversationNPC = p.ConversationNPC;
|
|
|
|
ConversationPC = p.ConversationPC;
|
|
|
|
ConversationNPCAngle = p.ConversationNPCAngle;
|
|
|
|
ConversationFaceTalker = p.ConversationFaceTalker;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
// This function supplements the pointer cleanup in dobject.cpp, because
|
2008-06-01 07:52:33 +00:00
|
|
|
// player_t is not derived from DObject. (I tried it, and DestroyScan was
|
2006-02-24 04:48:15 +00:00
|
|
|
// unable to properly determine the player object's type--possibly
|
|
|
|
// because it gets staticly allocated in an array.)
|
|
|
|
//
|
2008-06-01 07:52:33 +00:00
|
|
|
// This function checks all the DObject pointers in a player_t and NULLs any
|
2006-02-24 04:48:15 +00:00
|
|
|
// that match the pointer passed in. If you add any pointers that point to
|
|
|
|
// DObject (or a subclass), add them here too.
|
|
|
|
|
2008-06-01 07:52:33 +00:00
|
|
|
size_t player_t::FixPointers (const DObject *old, DObject *rep)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
APlayerPawn *replacement = static_cast<APlayerPawn *>(rep);
|
2008-03-13 23:00:33 +00:00
|
|
|
size_t changed = 0;
|
2012-03-17 00:52:33 +00:00
|
|
|
|
|
|
|
// The construct *& is used in several of these to avoid the read barriers
|
|
|
|
// that would turn the pointer we want to check to NULL if the old object
|
|
|
|
// is pending deletion.
|
|
|
|
if (mo == old) mo = replacement, changed++;
|
|
|
|
if (*&poisoner == old) poisoner = replacement, changed++;
|
|
|
|
if (*&attacker == old) attacker = replacement, changed++;
|
|
|
|
if (*&camera == old) camera = replacement, changed++;
|
|
|
|
if (*&dest == old) dest = replacement, changed++;
|
|
|
|
if (*&prev == old) prev = replacement, changed++;
|
|
|
|
if (*&enemy == old) enemy = replacement, changed++;
|
|
|
|
if (*&missile == old) missile = replacement, changed++;
|
|
|
|
if (*&mate == old) mate = replacement, changed++;
|
|
|
|
if (*&last_mate == old) last_mate = replacement, changed++;
|
|
|
|
if (ReadyWeapon == old) ReadyWeapon = static_cast<AWeapon *>(rep), changed++;
|
|
|
|
if (PendingWeapon == old) PendingWeapon = static_cast<AWeapon *>(rep), changed++;
|
|
|
|
if (*&PremorphWeapon == old) PremorphWeapon = static_cast<AWeapon *>(rep), changed++;
|
|
|
|
if (*&ConversationNPC == old) ConversationNPC = replacement, changed++;
|
|
|
|
if (*&ConversationPC == old) ConversationPC = replacement, changed++;
|
2008-03-13 23:00:33 +00:00
|
|
|
return changed;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
2008-06-01 07:52:33 +00:00
|
|
|
size_t player_t::PropagateMark()
|
2008-03-12 02:56:11 +00:00
|
|
|
{
|
|
|
|
GC::Mark(mo);
|
|
|
|
GC::Mark(poisoner);
|
|
|
|
GC::Mark(attacker);
|
|
|
|
GC::Mark(camera);
|
|
|
|
GC::Mark(dest);
|
|
|
|
GC::Mark(prev);
|
|
|
|
GC::Mark(enemy);
|
|
|
|
GC::Mark(missile);
|
|
|
|
GC::Mark(mate);
|
|
|
|
GC::Mark(last_mate);
|
|
|
|
GC::Mark(ReadyWeapon);
|
2008-03-26 08:29:02 +00:00
|
|
|
GC::Mark(ConversationNPC);
|
|
|
|
GC::Mark(ConversationPC);
|
2008-06-15 23:09:01 +00:00
|
|
|
GC::Mark(PremorphWeapon);
|
2008-03-12 02:56:11 +00:00
|
|
|
if (PendingWeapon != WP_NOCHANGE)
|
|
|
|
{
|
|
|
|
GC::Mark(PendingWeapon);
|
|
|
|
}
|
|
|
|
return sizeof(*this);
|
|
|
|
}
|
|
|
|
|
2008-06-01 07:52:33 +00:00
|
|
|
void player_t::SetLogNumber (int num)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
char lumpname[16];
|
|
|
|
int lumpnum;
|
|
|
|
|
About a week's worth of changes here. As a heads-up, I wouldn't be
surprised if this doesn't build in Linux right now. The CMakeLists.txt
were checked with MinGW and NMake, but how they fair under Linux is an
unknown to me at this time.
- Converted most sprintf (and all wsprintf) calls to either mysnprintf or
FStrings, depending on the situation.
- Changed the strings in the wbstartstruct to be FStrings.
- Changed myvsnprintf() to output nothing if count is greater than INT_MAX.
This is so that I can use a series of mysnprintf() calls and advance the
pointer for each one. Once the pointer goes beyond the end of the buffer,
the count will go negative, but since it's an unsigned type it will be
seen as excessively huge instead. This should not be a problem, as there's
no reason for ZDoom to be using text buffers larger than 2 GB anywhere.
- Ripped out the disabled bit from FGameConfigFile::MigrateOldConfig().
- Changed CalcMapName() to return an FString instead of a pointer to a static
buffer.
- Changed startmap in d_main.cpp into an FString.
- Changed CheckWarpTransMap() to take an FString& as the first argument.
- Changed d_mapname in g_level.cpp into an FString.
- Changed DoSubstitution() in ct_chat.cpp to place the substitutions in an
FString.
- Fixed: The MAPINFO parser wrote into the string buffer to construct a map
name when given a Hexen map number. This was fine with the old scanner
code, but only a happy coincidence prevents it from crashing with the new
code
- Added the 'B' conversion specifier to StringFormat::VWorker() for printing
binary numbers.
- Added CMake support for building with MinGW, MSYS, and NMake. Linux support
is probably broken until I get around to booting into Linux again. Niceties
provided over the existing Makefiles they're replacing:
* All command-line builds can use the same build system, rather than having
a separate one for MinGW and another for Linux.
* Microsoft's NMake tool is supported as a target.
* Progress meters.
* Parallel makes work from a fresh checkout without needing to be primed
first with a single-threaded make.
* Porting to other architectures should be simplified, whenever that day
comes.
- Replaced the makewad tool with zipdir. This handles the dependency tracking
itself instead of generating an external makefile to do it, since I couldn't
figure out how to generate a makefile with an external tool and include it
with a CMake-generated makefile. Where makewad used a master list of files
to generate the package file, zipdir just zips the entire contents of one or
more directories.
- Added the gdtoa package from netlib's fp library so that ZDoom's printf-style
formatting can be entirely independant of the CRT.
SVN r1082 (trunk)
2008-07-23 04:57:26 +00:00
|
|
|
mysnprintf (lumpname, countof(lumpname), "LOG%d", num);
|
2006-02-24 04:48:15 +00:00
|
|
|
lumpnum = Wads.CheckNumForName (lumpname);
|
|
|
|
if (lumpnum == -1)
|
|
|
|
{
|
|
|
|
// Leave the log message alone if this one doesn't exist.
|
|
|
|
//SetLogText (lumpname);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-04-11 16:27:41 +00:00
|
|
|
int length=Wads.LumpLength(lumpnum);
|
|
|
|
char *data= new char[length+1];
|
|
|
|
Wads.ReadLump (lumpnum, data);
|
|
|
|
data[length]=0;
|
|
|
|
SetLogText (data);
|
|
|
|
delete[] data;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-06-01 07:52:33 +00:00
|
|
|
void player_t::SetLogText (const char *text)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-05-11 04:00:58 +00:00
|
|
|
LogText = text;
|
2010-08-20 12:20:51 +00:00
|
|
|
|
|
|
|
// Print log text to console
|
|
|
|
AddToConsole(-1, TEXTCOLOR_GOLD);
|
|
|
|
AddToConsole(-1, LogText);
|
|
|
|
AddToConsole(-1, "\n");
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
2006-07-13 10:17:56 +00:00
|
|
|
int player_t::GetSpawnClass()
|
|
|
|
{
|
2006-10-01 01:38:37 +00:00
|
|
|
const PClass * type = PlayerClasses[CurrentPlayerClass].Type;
|
2006-07-13 10:17:56 +00:00
|
|
|
return static_cast<APlayerPawn*>(GetDefaultByType(type))->SpawnMask;
|
|
|
|
}
|
|
|
|
|
2011-12-14 00:16:19 +00:00
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// player_t :: SendPitchLimits
|
|
|
|
//
|
|
|
|
// Ask the local player's renderer what pitch restrictions should be imposed
|
|
|
|
// and let everybody know. Only sends data for the consoleplayer, since the
|
|
|
|
// local player is the only one our data is valid for.
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
void player_t::SendPitchLimits() const
|
|
|
|
{
|
|
|
|
if (this - players == consoleplayer)
|
|
|
|
{
|
|
|
|
Net_WriteByte(DEM_SETPITCHLIMIT);
|
|
|
|
Net_WriteByte(Renderer->GetMaxViewPitch(false)); // up
|
|
|
|
Net_WriteByte(Renderer->GetMaxViewPitch(true)); // down
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-05-14 14:30:13 +00:00
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// APlayerPawn
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
- 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
|
|
|
IMPLEMENT_POINTY_CLASS (APlayerPawn)
|
|
|
|
DECLARE_POINTER(InvFirst)
|
|
|
|
DECLARE_POINTER(InvSel)
|
|
|
|
END_POINTERS
|
|
|
|
|
2008-08-09 11:35:42 +00:00
|
|
|
IMPLEMENT_CLASS (APlayerChunk)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
void APlayerPawn::Serialize (FArchive &arc)
|
|
|
|
{
|
|
|
|
Super::Serialize (arc);
|
|
|
|
|
2006-07-13 10:17:56 +00:00
|
|
|
arc << JumpZ
|
|
|
|
<< MaxHealth
|
2007-01-12 15:24:10 +00:00
|
|
|
<< RunHealth
|
2006-07-13 10:17:56 +00:00
|
|
|
<< SpawnMask
|
|
|
|
<< ForwardMove1
|
|
|
|
<< ForwardMove2
|
|
|
|
<< SideMove1
|
|
|
|
<< SideMove2
|
- 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
|
|
|
<< ScoreIcon
|
|
|
|
<< InvFirst
|
2006-08-31 00:16:12 +00:00
|
|
|
<< InvSel
|
2008-09-01 18:16:53 +00:00
|
|
|
<< MorphWeapon
|
2009-09-14 22:12:31 +00:00
|
|
|
<< DamageFade
|
2011-01-01 11:16:46 +00:00
|
|
|
<< PlayerFlags
|
|
|
|
<< FlechetteType;
|
2012-08-14 03:24:59 +00:00
|
|
|
if (SaveVersion < 3829)
|
|
|
|
{
|
|
|
|
GruntSpeed = 12*FRACUNIT;
|
|
|
|
FallingScreamMinSpeed = 35*FRACUNIT;
|
|
|
|
FallingScreamMaxSpeed = 40*FRACUNIT;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
arc << GruntSpeed << FallingScreamMinSpeed << FallingScreamMaxSpeed;
|
|
|
|
}
|
2013-07-22 20:37:50 +00:00
|
|
|
if (SaveVersion >= 4502)
|
|
|
|
{
|
|
|
|
arc << UseRange;
|
|
|
|
}
|
2013-08-09 09:57:14 +00:00
|
|
|
if (SaveVersion >= 4503)
|
|
|
|
{
|
|
|
|
arc << AirCapacity;
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
2012-08-22 23:46:47 +00:00
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// APlayerPawn :: MarkPrecacheSounds
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
void APlayerPawn::MarkPrecacheSounds() const
|
|
|
|
{
|
|
|
|
Super::MarkPrecacheSounds();
|
|
|
|
S_MarkPlayerSounds(GetSoundClass());
|
|
|
|
}
|
|
|
|
|
2006-07-13 10:17:56 +00:00
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// APlayerPawn :: BeginPlay
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
void APlayerPawn::BeginPlay ()
|
|
|
|
{
|
|
|
|
Super::BeginPlay ();
|
|
|
|
ChangeStatNum (STAT_PLAYER);
|
|
|
|
|
2006-07-13 10:17:56 +00:00
|
|
|
// Check whether a PWADs normal sprite is to be combined with the base WADs
|
|
|
|
// crouch sprite. In such a case the sprites normally don't match and it is
|
|
|
|
// best to disable the crouch sprite.
|
|
|
|
if (crouchsprite > 0)
|
|
|
|
{
|
|
|
|
// This assumes that player sprites always exist in rotated form and
|
|
|
|
// that the front view is always a separate sprite. So far this is
|
|
|
|
// true for anything that exists.
|
2008-08-10 14:19:47 +00:00
|
|
|
FString normspritename = sprites[SpawnState->sprite].name;
|
2006-07-13 10:17:56 +00:00
|
|
|
FString crouchspritename = sprites[crouchsprite].name;
|
|
|
|
|
|
|
|
int spritenorm = Wads.CheckNumForName(normspritename + "A1", ns_sprites);
|
2014-09-13 08:08:22 +00:00
|
|
|
if (spritenorm==-1)
|
|
|
|
{
|
|
|
|
spritenorm = Wads.CheckNumForName(normspritename + "A0", ns_sprites);
|
|
|
|
}
|
|
|
|
|
2006-07-13 10:17:56 +00:00
|
|
|
int spritecrouch = Wads.CheckNumForName(crouchspritename + "A1", ns_sprites);
|
2014-09-13 08:08:22 +00:00
|
|
|
if (spritecrouch==-1)
|
|
|
|
{
|
|
|
|
spritecrouch = Wads.CheckNumForName(crouchspritename + "A0", ns_sprites);
|
|
|
|
}
|
2006-07-13 10:17:56 +00:00
|
|
|
|
|
|
|
if (spritenorm==-1 || spritecrouch ==-1)
|
|
|
|
{
|
|
|
|
// Sprites do not exist so it is best to disable the crouch sprite.
|
|
|
|
crouchsprite = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int wadnorm = Wads.GetLumpFile(spritenorm);
|
|
|
|
int wadcrouch = Wads.GetLumpFile(spritenorm);
|
|
|
|
|
|
|
|
if (wadnorm > FWadCollection::IWAD_FILENUM && wadcrouch <= FWadCollection::IWAD_FILENUM)
|
|
|
|
{
|
|
|
|
// Question: Add an option / disable crouching or do what?
|
|
|
|
crouchsprite = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2006-05-14 14:30:13 +00:00
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// APlayerPawn :: Tick
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
void APlayerPawn::Tick()
|
|
|
|
{
|
2013-04-28 02:31:34 +00:00
|
|
|
if (player != NULL && player->mo == this && player->CanCrouch() && player->playerstate != PST_DEAD)
|
2006-05-14 14:30:13 +00:00
|
|
|
{
|
|
|
|
height = FixedMul(GetDefault()->height, player->crouchfactor);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (health > 0) height = GetDefault()->height;
|
|
|
|
}
|
|
|
|
Super::Tick();
|
|
|
|
}
|
|
|
|
|
2009-03-12 03:54:23 +00:00
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// APlayerPawn :: PostBeginPlay
|
|
|
|
//
|
|
|
|
//===========================================================================
|
2006-06-11 11:28:48 +00:00
|
|
|
|
2009-03-12 03:54:23 +00:00
|
|
|
void APlayerPawn::PostBeginPlay()
|
|
|
|
{
|
2012-03-07 01:03:56 +00:00
|
|
|
Super::PostBeginPlay();
|
2009-03-13 03:18:06 +00:00
|
|
|
SetupWeaponSlots();
|
2010-08-29 17:57:10 +00:00
|
|
|
|
|
|
|
// Voodoo dolls: restore original floorz/ceilingz logic
|
2010-09-05 20:51:13 +00:00
|
|
|
if (player == NULL || player->mo != this)
|
2010-08-29 17:57:10 +00:00
|
|
|
{
|
|
|
|
dropoffz = floorz = Sector->floorplane.ZatPoint(x, y);
|
|
|
|
ceilingz = Sector->ceilingplane.ZatPoint(x, y);
|
2012-04-08 05:39:46 +00:00
|
|
|
P_FindFloorCeiling(this, FFCF_ONLYSPAWNPOS);
|
2010-08-29 17:57:10 +00:00
|
|
|
z = floorz;
|
|
|
|
}
|
2011-12-14 00:16:19 +00:00
|
|
|
else
|
2011-12-06 01:25:37 +00:00
|
|
|
{
|
2011-12-14 00:16:19 +00:00
|
|
|
player->SendPitchLimits();
|
2011-12-06 01:25:37 +00:00
|
|
|
}
|
2009-03-13 03:18:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// APlayerPawn :: SetupWeaponSlots
|
|
|
|
//
|
|
|
|
// Sets up the default weapon slots for this player. If this is also the
|
|
|
|
// local player, determines local modifications and sends those across the
|
|
|
|
// network. Ignores voodoo dolls.
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
void APlayerPawn::SetupWeaponSlots()
|
|
|
|
{
|
2009-03-12 03:54:23 +00:00
|
|
|
if (player != NULL && player->mo == this)
|
|
|
|
{
|
|
|
|
player->weapons.StandardSetup(GetClass());
|
2012-03-22 22:29:25 +00:00
|
|
|
// If we're the local player, then there's a bit more work to do.
|
|
|
|
// This also applies if we're a bot and this is the net arbitrator.
|
|
|
|
if (player - players == consoleplayer ||
|
|
|
|
(player->isbot && consoleplayer == Net_Arbitrator))
|
|
|
|
{
|
2009-03-12 03:54:23 +00:00
|
|
|
FWeaponSlots local_slots(player->weapons);
|
2012-03-22 22:29:25 +00:00
|
|
|
if (player->isbot)
|
|
|
|
{ // Bots only need weapons from KEYCONF, not INI modifications.
|
|
|
|
P_PlaybackKeyConfWeapons(&local_slots);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
local_slots.LocalSetup(GetClass());
|
|
|
|
}
|
|
|
|
local_slots.SendDifferences(int(player - players), player->weapons);
|
2009-03-12 03:54:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2006-06-11 11:28:48 +00:00
|
|
|
|
2006-05-14 14:30:13 +00:00
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// APlayerPawn :: AddInventory
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
void APlayerPawn::AddInventory (AInventory *item)
|
|
|
|
{
|
|
|
|
// Adding inventory to a voodoo doll should add it to the real player instead.
|
- 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 (player != NULL && player->mo != this && player->mo != NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
player->mo->AddInventory (item);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Super::AddInventory (item);
|
|
|
|
|
|
|
|
// If nothing is selected, select this item.
|
- 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 (InvSel == NULL && (item->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
|
|
|
InvSel = item;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-05-14 14:30:13 +00:00
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// APlayerPawn :: RemoveInventory
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
void APlayerPawn::RemoveInventory (AInventory *item)
|
|
|
|
{
|
|
|
|
bool pickWeap = false;
|
|
|
|
|
|
|
|
// Since voodoo dolls aren't supposed to have an inventory, there should be
|
|
|
|
// no need to redirect them to the real player here as there is with AddInventory.
|
|
|
|
|
|
|
|
// If the item removed is the selected one, select something else, either the next
|
|
|
|
// item, if there is one, or the previous item.
|
|
|
|
if (player != 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
|
|
|
if (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
|
|
|
InvSel = item->NextInv ();
|
|
|
|
if (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
|
|
|
InvSel = item->PrevInv ();
|
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 (InvFirst == 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
|
|
|
InvFirst = item->NextInv ();
|
|
|
|
if (InvFirst == 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
|
|
|
InvFirst = item->PrevInv ();
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (item == player->PendingWeapon)
|
|
|
|
{
|
|
|
|
player->PendingWeapon = WP_NOCHANGE;
|
|
|
|
}
|
|
|
|
if (item == player->ReadyWeapon)
|
|
|
|
{
|
2006-07-13 02:08:39 +00:00
|
|
|
// If the current weapon is removed, clear the refire counter and pick a new one.
|
2006-02-24 04:48:15 +00:00
|
|
|
pickWeap = true;
|
|
|
|
player->ReadyWeapon = NULL;
|
2006-07-13 02:08:39 +00:00
|
|
|
player->refire = 0;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Super::RemoveInventory (item);
|
|
|
|
if (pickWeap && player->mo == this && player->PendingWeapon == WP_NOCHANGE)
|
|
|
|
{
|
|
|
|
PickNewWeapon (NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-05-14 14:30:13 +00:00
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// APlayerPawn :: UseInventory
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
bool APlayerPawn::UseInventory (AInventory *item)
|
|
|
|
{
|
2006-05-10 02:40:43 +00:00
|
|
|
const PClass *itemtype = item->GetClass();
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
if (player->cheats & CF_TOTALLYFROZEN)
|
|
|
|
{ // You can't use items if you're totally frozen
|
|
|
|
return false;
|
|
|
|
}
|
2012-04-27 01:40:50 +00:00
|
|
|
if ((level.flags2 & LEVEL2_FROZEN) && (player == NULL || player->timefreezer == 0))
|
2009-12-16 16:09:48 +00:00
|
|
|
{
|
|
|
|
// Time frozen
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
if (!Super::UseInventory (item))
|
|
|
|
{
|
|
|
|
// Heretic and Hexen advance the inventory cursor if the use failed.
|
|
|
|
// Should this behavior be retained?
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (player == &players[consoleplayer])
|
|
|
|
{
|
2008-06-15 02:25:09 +00:00
|
|
|
S_Sound (this, CHAN_ITEM, item->UseSound, 1, ATTN_NORM);
|
2006-02-24 04:48:15 +00:00
|
|
|
StatusBar->FlashItem (itemtype);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// APlayerPawn :: BestWeapon
|
|
|
|
//
|
|
|
|
// Returns the best weapon a player has, possibly restricted to a single
|
|
|
|
// type of ammo.
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2006-05-10 02:40:43 +00:00
|
|
|
AWeapon *APlayerPawn::BestWeapon (const PClass *ammotype)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
AWeapon *bestMatch = NULL;
|
|
|
|
int bestOrder = INT_MAX;
|
|
|
|
AInventory *item;
|
|
|
|
AWeapon *weap;
|
2011-11-07 00:43:41 +00:00
|
|
|
bool tomed = NULL != FindInventory (RUNTIME_CLASS(APowerWeaponLevel2), true);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
// Find the best weapon the player has.
|
|
|
|
for (item = Inventory; item != NULL; item = item->Inventory)
|
|
|
|
{
|
|
|
|
if (!item->IsKindOf (RUNTIME_CLASS(AWeapon)))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
weap = static_cast<AWeapon *> (item);
|
|
|
|
|
|
|
|
// Don't select it if it's worse than what was already found.
|
|
|
|
if (weap->SelectionOrder > bestOrder)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Don't select it if its primary fire doesn't use the desired ammo.
|
|
|
|
if (ammotype != NULL &&
|
|
|
|
(weap->Ammo1 == NULL ||
|
|
|
|
weap->Ammo1->GetClass() != ammotype))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Don't select it if the Tome is active and this isn't the powered-up version.
|
|
|
|
if (tomed && weap->SisterWeapon != NULL && weap->SisterWeapon->WeaponFlags & WIF_POWERED_UP)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Don't select it if it's powered-up and the Tome is not active.
|
|
|
|
if (!tomed && weap->WeaponFlags & WIF_POWERED_UP)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Don't select it if there isn't enough ammo to use its primary fire.
|
|
|
|
if (!(weap->WeaponFlags & WIF_AMMO_OPTIONAL) &&
|
|
|
|
!weap->CheckAmmo (AWeapon::PrimaryFire, false))
|
|
|
|
continue;
|
|
|
|
|
2013-03-24 02:25:12 +00:00
|
|
|
// Don't select if if there isn't enough ammo as determined by the weapon's author.
|
|
|
|
if (weap->MinSelAmmo1 > 0 && (weap->Ammo1 == NULL || weap->Ammo1->Amount < weap->MinSelAmmo1))
|
|
|
|
continue;
|
|
|
|
if (weap->MinSelAmmo2 > 0 && (weap->Ammo2 == NULL || weap->Ammo2->Amount < weap->MinSelAmmo2))
|
|
|
|
continue;
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
// This weapon is usable!
|
|
|
|
bestOrder = weap->SelectionOrder;
|
|
|
|
bestMatch = weap;
|
|
|
|
}
|
|
|
|
return bestMatch;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// APlayerPawn :: PickNewWeapon
|
|
|
|
//
|
|
|
|
// Picks a new weapon for this player. Used mostly for running out of ammo,
|
|
|
|
// but it also works when an ACS script explicitly takes the ready weapon
|
|
|
|
// away or the player picks up some ammo they had previously run out of.
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2006-05-10 02:40:43 +00:00
|
|
|
AWeapon *APlayerPawn::PickNewWeapon (const PClass *ammotype)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
AWeapon *best = BestWeapon (ammotype);
|
|
|
|
|
|
|
|
if (best != NULL)
|
|
|
|
{
|
|
|
|
player->PendingWeapon = best;
|
|
|
|
if (player->ReadyWeapon != NULL)
|
|
|
|
{
|
2013-01-25 03:32:49 +00:00
|
|
|
P_DropWeapon(player);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
else if (player->PendingWeapon != WP_NOCHANGE)
|
|
|
|
{
|
|
|
|
P_BringUpWeapon (player);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return best;
|
|
|
|
}
|
|
|
|
|
2008-12-27 20:18:31 +00:00
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// APlayerPawn :: CheckWeaponSwitch
|
|
|
|
//
|
|
|
|
// Checks if weapons should be changed after picking up ammo
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
void APlayerPawn::CheckWeaponSwitch(const PClass *ammotype)
|
|
|
|
{
|
2013-05-12 18:27:03 +00:00
|
|
|
if (!player->userinfo.GetNeverSwitch() &&
|
2008-12-27 20:18:31 +00:00
|
|
|
player->PendingWeapon == WP_NOCHANGE &&
|
|
|
|
(player->ReadyWeapon == NULL ||
|
|
|
|
(player->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)))
|
|
|
|
{
|
|
|
|
AWeapon *best = BestWeapon (ammotype);
|
|
|
|
if (best != NULL && (player->ReadyWeapon == NULL ||
|
|
|
|
best->SelectionOrder < player->ReadyWeapon->SelectionOrder))
|
|
|
|
{
|
|
|
|
player->PendingWeapon = best;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-06-18 04:10:47 +00:00
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// APlayerPawn :: GiveDeathmatchInventory
|
|
|
|
//
|
|
|
|
// Gives players items they should have in addition to their default
|
|
|
|
// inventory when playing deathmatch. (i.e. all keys)
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
void APlayerPawn::GiveDeathmatchInventory()
|
|
|
|
{
|
|
|
|
for (unsigned int i = 0; i < PClass::m_Types.Size(); ++i)
|
|
|
|
{
|
|
|
|
if (PClass::m_Types[i]->IsDescendantOf (RUNTIME_CLASS(AKey)))
|
|
|
|
{
|
|
|
|
AKey *key = (AKey *)GetDefaultByType (PClass::m_Types[i]);
|
|
|
|
if (key->KeyNumber != 0)
|
|
|
|
{
|
2006-07-16 09:10:45 +00:00
|
|
|
key = static_cast<AKey *>(Spawn (PClass::m_Types[i], 0,0,0, NO_REPLACE));
|
2008-09-13 22:08:41 +00:00
|
|
|
if (!key->CallTryPickup (this))
|
2006-06-18 04:10:47 +00:00
|
|
|
{
|
|
|
|
key->Destroy ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// APlayerPawn :: FilterCoopRespawnInventory
|
|
|
|
//
|
|
|
|
// When respawning in coop, this function is called to walk through the dead
|
|
|
|
// player's inventory and modify it according to the current game flags so
|
|
|
|
// that it can be transferred to the new live player. This player currently
|
|
|
|
// has the default inventory, and the oldplayer has the inventory at the time
|
|
|
|
// of death.
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
void APlayerPawn::FilterCoopRespawnInventory (APlayerPawn *oldplayer)
|
|
|
|
{
|
|
|
|
AInventory *item, *next, *defitem;
|
|
|
|
|
|
|
|
// If we're losing everything, this is really simple.
|
|
|
|
if (dmflags & DF_COOP_LOSE_INVENTORY)
|
|
|
|
{
|
|
|
|
oldplayer->DestroyAllInventory();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dmflags & (DF_COOP_LOSE_KEYS |
|
|
|
|
DF_COOP_LOSE_WEAPONS |
|
|
|
|
DF_COOP_LOSE_AMMO |
|
|
|
|
DF_COOP_HALVE_AMMO |
|
|
|
|
DF_COOP_LOSE_ARMOR |
|
|
|
|
DF_COOP_LOSE_POWERUPS))
|
|
|
|
{
|
|
|
|
// Walk through the old player's inventory and destroy or modify
|
|
|
|
// according to dmflags.
|
|
|
|
for (item = oldplayer->Inventory; item != NULL; item = next)
|
|
|
|
{
|
|
|
|
next = item->Inventory;
|
|
|
|
|
|
|
|
// If this item is part of the default inventory, we never want
|
|
|
|
// to destroy it, although we might want to copy the default
|
|
|
|
// inventory amount.
|
|
|
|
defitem = FindInventory (item->GetClass());
|
|
|
|
|
|
|
|
if ((dmflags & DF_COOP_LOSE_KEYS) &&
|
|
|
|
defitem == NULL &&
|
|
|
|
item->IsKindOf(RUNTIME_CLASS(AKey)))
|
|
|
|
{
|
|
|
|
item->Destroy();
|
|
|
|
}
|
|
|
|
else if ((dmflags & DF_COOP_LOSE_WEAPONS) &&
|
|
|
|
defitem == NULL &&
|
|
|
|
item->IsKindOf(RUNTIME_CLASS(AWeapon)))
|
|
|
|
{
|
|
|
|
item->Destroy();
|
|
|
|
}
|
|
|
|
else if ((dmflags & DF_COOP_LOSE_ARMOR) &&
|
|
|
|
item->IsKindOf(RUNTIME_CLASS(AArmor)))
|
|
|
|
{
|
2008-06-15 03:46:04 +00:00
|
|
|
if (defitem != NULL)
|
|
|
|
{
|
|
|
|
item->Destroy();
|
|
|
|
}
|
|
|
|
else if (item->IsKindOf(RUNTIME_CLASS(ABasicArmor)))
|
|
|
|
{
|
|
|
|
static_cast<ABasicArmor*>(item)->SavePercent = static_cast<ABasicArmor*>(defitem)->SavePercent;
|
|
|
|
item->Amount = defitem->Amount;
|
|
|
|
}
|
|
|
|
else if (item->IsKindOf(RUNTIME_CLASS(AHexenArmor)))
|
|
|
|
{
|
|
|
|
static_cast<AHexenArmor*>(item)->Slots[0] = static_cast<AHexenArmor*>(defitem)->Slots[0];
|
|
|
|
static_cast<AHexenArmor*>(item)->Slots[1] = static_cast<AHexenArmor*>(defitem)->Slots[1];
|
|
|
|
static_cast<AHexenArmor*>(item)->Slots[2] = static_cast<AHexenArmor*>(defitem)->Slots[2];
|
|
|
|
static_cast<AHexenArmor*>(item)->Slots[3] = static_cast<AHexenArmor*>(defitem)->Slots[3];
|
|
|
|
}
|
2006-06-18 04:10:47 +00:00
|
|
|
}
|
|
|
|
else if ((dmflags & DF_COOP_LOSE_POWERUPS) &&
|
|
|
|
defitem == NULL &&
|
|
|
|
item->IsKindOf(RUNTIME_CLASS(APowerupGiver)))
|
|
|
|
{
|
|
|
|
item->Destroy();
|
|
|
|
}
|
|
|
|
else if ((dmflags & (DF_COOP_LOSE_AMMO | DF_COOP_HALVE_AMMO)) &&
|
|
|
|
item->IsKindOf(RUNTIME_CLASS(AAmmo)))
|
|
|
|
{
|
|
|
|
if (defitem == NULL)
|
|
|
|
{
|
|
|
|
if (dmflags & DF_COOP_LOSE_AMMO)
|
|
|
|
{
|
|
|
|
// Do NOT destroy the ammo, because a weapon might reference it.
|
|
|
|
item->Amount = 0;
|
|
|
|
}
|
|
|
|
else if (item->Amount > 1)
|
|
|
|
{
|
|
|
|
item->Amount /= 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// When set to lose ammo, you get to keep all your starting ammo.
|
|
|
|
// When set to halve ammo, you won't be left with less than your starting amount.
|
|
|
|
if (dmflags & DF_COOP_LOSE_AMMO)
|
|
|
|
{
|
|
|
|
item->Amount = defitem->Amount;
|
|
|
|
}
|
|
|
|
else if (item->Amount > 1)
|
|
|
|
{
|
|
|
|
item->Amount = MAX(item->Amount / 2, defitem->Amount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now destroy the default inventory this player is holding and move
|
|
|
|
// over the old player's remaining inventory.
|
|
|
|
DestroyAllInventory();
|
|
|
|
ObtainInventory (oldplayer);
|
|
|
|
|
|
|
|
player->ReadyWeapon = NULL;
|
|
|
|
PickNewWeapon (NULL);
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2006-07-13 10:17:56 +00:00
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// APlayerPawn :: GetSoundClass
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2012-08-22 23:46:47 +00:00
|
|
|
const char *APlayerPawn::GetSoundClass() const
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
if (player != NULL &&
|
2012-04-14 03:10:07 +00:00
|
|
|
(player->mo == NULL || !(player->mo->flags4 &MF4_NOSKIN)) &&
|
2013-05-12 18:27:03 +00:00
|
|
|
(unsigned int)player->userinfo.GetSkin() >= PlayerClasses.Size () &&
|
|
|
|
(size_t)player->userinfo.GetSkin() < numskins)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2013-05-12 18:27:03 +00:00
|
|
|
return skins[player->userinfo.GetSkin()].name;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
2006-07-13 10:17:56 +00:00
|
|
|
|
|
|
|
// [GRB]
|
|
|
|
const char *sclass = GetClass ()->Meta.GetMetaString (APMETA_SoundClass);
|
|
|
|
return sclass != NULL ? sclass : "player";
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// APlayerPawn :: GetMaxHealth
|
|
|
|
//
|
|
|
|
// only needed because Boom screwed up Dehacked.
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
int APlayerPawn::GetMaxHealth() const
|
|
|
|
{
|
|
|
|
return MaxHealth > 0? MaxHealth : ((i_compatflags&COMPATF_DEHHEALTH)? 100 : deh.MaxHealth);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
2007-02-04 00:22:56 +00:00
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// APlayerPawn :: UpdateWaterLevel
|
|
|
|
//
|
|
|
|
// Plays surfacing and diving sounds, as appropriate.
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
bool APlayerPawn::UpdateWaterLevel (fixed_t oldz, bool splash)
|
|
|
|
{
|
|
|
|
int oldlevel = waterlevel;
|
|
|
|
bool retval = Super::UpdateWaterLevel (oldz, splash);
|
2007-10-29 20:27:40 +00:00
|
|
|
if (player != NULL)
|
|
|
|
{
|
|
|
|
if (oldlevel < 3 && waterlevel == 3)
|
|
|
|
{ // Our head just went under.
|
|
|
|
S_Sound (this, CHAN_VOICE, "*dive", 1, ATTN_NORM);
|
|
|
|
}
|
|
|
|
else if (oldlevel == 3 && waterlevel < 3)
|
|
|
|
{ // Our head just came up.
|
|
|
|
if (player->air_finished > level.time)
|
|
|
|
{ // We hadn't run out of air yet.
|
|
|
|
S_Sound (this, CHAN_VOICE, "*surface", 1, ATTN_NORM);
|
|
|
|
}
|
|
|
|
// If we were running out of air, then ResetAirSupply() will play *gasp.
|
2007-02-04 00:22:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// APlayerPawn :: ResetAirSupply
|
|
|
|
//
|
|
|
|
// Gives the player a full "tank" of air. If they had previously completely
|
|
|
|
// run out of air, also plays the *gasp sound. Returns true if the player
|
|
|
|
// was drowning.
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2008-07-19 12:40:10 +00:00
|
|
|
bool APlayerPawn::ResetAirSupply (bool playgasp)
|
2007-02-04 00:22:56 +00:00
|
|
|
{
|
|
|
|
bool wasdrowning = (player->air_finished < level.time);
|
|
|
|
|
2008-07-19 12:40:10 +00:00
|
|
|
if (playgasp && wasdrowning)
|
2007-02-04 00:22:56 +00:00
|
|
|
{
|
|
|
|
S_Sound (this, CHAN_VOICE, "*gasp", 1, ATTN_NORM);
|
|
|
|
}
|
2013-08-09 09:57:14 +00:00
|
|
|
if (level.airsupply> 0 && player->mo->AirCapacity > 0) player->air_finished = level.time + FixedMul(level.airsupply, player->mo->AirCapacity);
|
2009-05-30 08:56:40 +00:00
|
|
|
else player->air_finished = INT_MAX;
|
2007-02-04 00:22:56 +00:00
|
|
|
return wasdrowning;
|
|
|
|
}
|
|
|
|
|
2006-07-13 10:17:56 +00:00
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Animations
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
void APlayerPawn::PlayIdle ()
|
|
|
|
{
|
2006-07-13 10:17:56 +00:00
|
|
|
if (InStateSequence(state, SeeState))
|
2006-02-24 04:48:15 +00:00
|
|
|
SetState (SpawnState);
|
|
|
|
}
|
|
|
|
|
|
|
|
void APlayerPawn::PlayRunning ()
|
|
|
|
{
|
2006-09-09 08:55:47 +00:00
|
|
|
if (InStateSequence(state, SpawnState) && SeeState != NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
SetState (SeeState);
|
|
|
|
}
|
|
|
|
|
|
|
|
void APlayerPawn::PlayAttacking ()
|
|
|
|
{
|
2006-09-09 08:55:47 +00:00
|
|
|
if (MissileState != NULL) SetState (MissileState);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void APlayerPawn::PlayAttacking2 ()
|
|
|
|
{
|
2006-09-09 08:55:47 +00:00
|
|
|
if (MeleeState != NULL) SetState (MeleeState);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void APlayerPawn::ThrowPoisonBag ()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2006-07-13 10:17:56 +00:00
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// APlayerPawn :: GiveDefaultInventory
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
void APlayerPawn::GiveDefaultInventory ()
|
|
|
|
{
|
2009-02-06 00:16:57 +00:00
|
|
|
if (player == NULL) return;
|
|
|
|
|
2006-07-13 10:17:56 +00:00
|
|
|
// [GRB] Give inventory specified in DECORATE
|
|
|
|
player->health = GetDefault ()->health;
|
|
|
|
|
2007-01-12 15:24:10 +00:00
|
|
|
// HexenArmor must always be the first item in the inventory because
|
|
|
|
// it provides player class based protection that should not affect
|
|
|
|
// any other protection item.
|
|
|
|
fixed_t hx[5];
|
|
|
|
for(int i=0;i<5;i++)
|
|
|
|
{
|
|
|
|
hx[i] = GetClass()->Meta.GetMetaFixed(APMETA_Hexenarmor0+i);
|
|
|
|
}
|
|
|
|
GiveInventoryType (RUNTIME_CLASS(AHexenArmor));
|
|
|
|
AHexenArmor *harmor = FindInventory<AHexenArmor>();
|
|
|
|
harmor->Slots[4] = hx[0];
|
|
|
|
harmor->SlotsIncrement[0] = hx[1];
|
|
|
|
harmor->SlotsIncrement[1] = hx[2];
|
|
|
|
harmor->SlotsIncrement[2] = hx[3];
|
|
|
|
harmor->SlotsIncrement[3] = hx[4];
|
|
|
|
|
|
|
|
// BasicArmor must come right after that. It should not affect any
|
|
|
|
// other protection item as well but needs to process the damage
|
|
|
|
// before the HexenArmor does.
|
|
|
|
ABasicArmor *barmor = Spawn<ABasicArmor> (0,0,0, NO_REPLACE);
|
|
|
|
barmor->BecomeItem ();
|
|
|
|
barmor->SavePercent = 0;
|
|
|
|
barmor->Amount = 0;
|
|
|
|
AddInventory (barmor);
|
|
|
|
|
|
|
|
// Now add the items from the DECORATE definition
|
2008-09-21 18:02:38 +00:00
|
|
|
FDropItem *di = GetDropItems();
|
2006-07-13 10:17:56 +00:00
|
|
|
|
|
|
|
while (di)
|
|
|
|
{
|
|
|
|
const PClass *ti = PClass::FindClass (di->Name);
|
|
|
|
if (ti)
|
|
|
|
{
|
|
|
|
AInventory *item = FindInventory (ti);
|
|
|
|
if (item != NULL)
|
|
|
|
{
|
|
|
|
item->Amount = clamp<int>(
|
|
|
|
item->Amount + (di->amount ? di->amount : ((AInventory *)item->GetDefault ())->Amount),
|
|
|
|
0, item->MaxAmount);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-12-29 20:28:23 +00:00
|
|
|
item = static_cast<AInventory *>(Spawn (ti, 0,0,0, NO_REPLACE));
|
2007-07-28 12:38:10 +00:00
|
|
|
item->ItemFlags|=IF_IGNORESKILL; // no skill multiplicators here
|
2006-07-13 10:17:56 +00:00
|
|
|
item->Amount = di->amount;
|
|
|
|
if (item->IsKindOf (RUNTIME_CLASS (AWeapon)))
|
|
|
|
{
|
|
|
|
// To allow better control any weapon is emptied of
|
|
|
|
// ammo before being given to the player.
|
|
|
|
static_cast<AWeapon*>(item)->AmmoGive1 =
|
|
|
|
static_cast<AWeapon*>(item)->AmmoGive2 = 0;
|
|
|
|
}
|
2009-02-06 00:16:57 +00:00
|
|
|
AActor *check;
|
|
|
|
if (!item->CallTryPickup(this, &check))
|
2006-07-13 10:17:56 +00:00
|
|
|
{
|
2009-02-06 00:16:57 +00:00
|
|
|
if (check != this)
|
|
|
|
{
|
|
|
|
// Player was morphed. This is illegal at game start.
|
|
|
|
// This problem is only detectable when it's too late to do something about it...
|
|
|
|
I_Error("Cannot give morph items when starting a game");
|
|
|
|
}
|
2006-07-13 10:17:56 +00:00
|
|
|
item->Destroy ();
|
|
|
|
item = NULL;
|
|
|
|
}
|
|
|
|
}
|
2009-02-06 00:16:57 +00:00
|
|
|
if (item != NULL && item->IsKindOf (RUNTIME_CLASS (AWeapon)) &&
|
2006-11-05 21:46:28 +00:00
|
|
|
static_cast<AWeapon*>(item)->CheckAmmo(AWeapon::EitherFire, false))
|
2006-07-13 10:17:56 +00:00
|
|
|
{
|
2010-09-16 06:51:42 +00:00
|
|
|
player->ReadyWeapon = player->PendingWeapon = static_cast<AWeapon *> (item);
|
2006-07-13 10:17:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
di = di->Next;
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void APlayerPawn::MorphPlayerThink ()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void APlayerPawn::ActivateMorphWeapon ()
|
|
|
|
{
|
2006-08-31 00:16:12 +00:00
|
|
|
const PClass *morphweapon = PClass::FindClass (MorphWeapon);
|
2006-08-17 00:19:26 +00:00
|
|
|
player->PendingWeapon = WP_NOCHANGE;
|
|
|
|
player->psprites[ps_weapon].sy = WEAPONTOP;
|
|
|
|
|
|
|
|
if (morphweapon == NULL || !morphweapon->IsDescendantOf (RUNTIME_CLASS(AWeapon)))
|
|
|
|
{ // No weapon at all while morphed!
|
|
|
|
player->ReadyWeapon = NULL;
|
|
|
|
P_SetPsprite (player, ps_weapon, NULL);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
player->ReadyWeapon = static_cast<AWeapon *>(player->mo->FindInventory (morphweapon));
|
|
|
|
if (player->ReadyWeapon == NULL)
|
|
|
|
{
|
|
|
|
player->ReadyWeapon = static_cast<AWeapon *>(player->mo->GiveInventoryType (morphweapon));
|
2008-04-15 22:17:30 +00:00
|
|
|
if (player->ReadyWeapon != NULL)
|
|
|
|
{
|
|
|
|
player->ReadyWeapon->GivenAsMorphWeapon = true; // flag is used only by new beastweap semantics in P_UndoPlayerMorph
|
|
|
|
}
|
2006-08-17 00:19:26 +00:00
|
|
|
}
|
|
|
|
if (player->ReadyWeapon != NULL)
|
|
|
|
{
|
|
|
|
P_SetPsprite (player, ps_weapon, player->ReadyWeapon->GetReadyState());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
P_SetPsprite (player, ps_weapon, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
P_SetPsprite (player, ps_flash, NULL);
|
2008-04-12 15:31:18 +00:00
|
|
|
|
|
|
|
player->PendingWeapon = WP_NOCHANGE;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
2006-07-13 10:17:56 +00:00
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// APlayerPawn :: Die
|
|
|
|
//
|
|
|
|
//===========================================================================
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2012-05-13 07:54:44 +00:00
|
|
|
void APlayerPawn::Die (AActor *source, AActor *inflictor, int dmgflags)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2012-05-13 07:54:44 +00:00
|
|
|
Super::Die (source, inflictor, dmgflags);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2007-07-12 15:15:57 +00:00
|
|
|
if (player != NULL && player->mo == this) player->bonuscount = 0;
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
if (player != NULL && player->mo != this)
|
|
|
|
{ // Make the real player die, too
|
2012-05-13 07:54:44 +00:00
|
|
|
player->mo->Die (source, inflictor, dmgflags);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (player != NULL && (dmflags2 & DF2_YES_WEAPONDROP))
|
|
|
|
{ // Voodoo dolls don't drop weapons
|
|
|
|
AWeapon *weap = player->ReadyWeapon;
|
|
|
|
if (weap != NULL)
|
|
|
|
{
|
|
|
|
AInventory *item;
|
|
|
|
|
2011-09-08 00:55:47 +00:00
|
|
|
// kgDROP - start - modified copy from a_action.cpp
|
|
|
|
FDropItem *di = weap->GetDropItems();
|
|
|
|
|
|
|
|
if (di != NULL)
|
|
|
|
{
|
|
|
|
while (di != NULL)
|
|
|
|
{
|
|
|
|
if (di->Name != NAME_None)
|
|
|
|
{
|
|
|
|
const PClass *ti = PClass::FindClass(di->Name);
|
|
|
|
if (ti) P_DropItem (player->mo, ti, di->amount, di->probability);
|
|
|
|
}
|
|
|
|
di = di->Next;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
// kgDROP - end
|
2006-02-24 04:48:15 +00:00
|
|
|
if (weap->SpawnState != NULL &&
|
2008-08-10 14:19:47 +00:00
|
|
|
weap->SpawnState != ::GetDefault<AActor>()->SpawnState)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
item = P_DropItem (this, weap->GetClass(), -1, 256);
|
|
|
|
if (item != NULL)
|
|
|
|
{
|
|
|
|
if (weap->AmmoGive1 && weap->Ammo1)
|
|
|
|
{
|
|
|
|
static_cast<AWeapon *>(item)->AmmoGive1 = weap->Ammo1->Amount;
|
|
|
|
}
|
|
|
|
if (weap->AmmoGive2 && weap->Ammo2)
|
|
|
|
{
|
|
|
|
static_cast<AWeapon *>(item)->AmmoGive2 = weap->Ammo2->Amount;
|
|
|
|
}
|
2008-04-24 17:17:46 +00:00
|
|
|
item->ItemFlags |= IF_IGNORESKILL;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
item = P_DropItem (this, weap->AmmoType1, -1, 256);
|
|
|
|
if (item != NULL)
|
|
|
|
{
|
|
|
|
item->Amount = weap->Ammo1->Amount;
|
2008-04-24 17:17:46 +00:00
|
|
|
item->ItemFlags |= IF_IGNORESKILL;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
item = P_DropItem (this, weap->AmmoType2, -1, 256);
|
|
|
|
if (item != NULL)
|
|
|
|
{
|
|
|
|
item->Amount = weap->Ammo2->Amount;
|
2008-04-24 17:17:46 +00:00
|
|
|
item->ItemFlags |= IF_IGNORESKILL;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-10-06 10:44:03 +00:00
|
|
|
if (!multiplayer && level.info->deathsequence != NAME_None)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2010-10-06 10:44:03 +00:00
|
|
|
F_StartIntermission(level.info->deathsequence, FSTATE_EndingGame);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-07-13 10:17:56 +00:00
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// APlayerPawn :: TweakSpeeds
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
void APlayerPawn::TweakSpeeds (int &forward, int &side)
|
|
|
|
{
|
2007-01-12 15:24:10 +00:00
|
|
|
// Strife's player can't run when its healh is below 10
|
|
|
|
if (health <= RunHealth)
|
|
|
|
{
|
|
|
|
forward = clamp(forward, -0x1900, 0x1900);
|
|
|
|
side = clamp(side, -0x1800, 0x1800);
|
|
|
|
}
|
|
|
|
|
2006-07-13 10:17:56 +00:00
|
|
|
// [GRB]
|
|
|
|
if ((unsigned int)(forward + 0x31ff) < 0x63ff)
|
|
|
|
{
|
|
|
|
forward = FixedMul (forward, ForwardMove1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
forward = FixedMul (forward, ForwardMove2);
|
|
|
|
}
|
|
|
|
if ((unsigned int)(side + 0x27ff) < 0x4fff)
|
|
|
|
{
|
|
|
|
side = FixedMul (side, SideMove1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
side = FixedMul (side, SideMove2);
|
|
|
|
}
|
|
|
|
|
2007-05-12 11:14:09 +00:00
|
|
|
if (!player->morphTics && Inventory != NULL)
|
|
|
|
{
|
|
|
|
fixed_t factor = Inventory->GetSpeedFactor ();
|
|
|
|
forward = FixedMul(forward, factor);
|
|
|
|
side = FixedMul(side, factor);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-11-05 21:46:28 +00:00
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// A_PlayerScream
|
|
|
|
//
|
|
|
|
// try to find the appropriate death sound and use suitable
|
|
|
|
// replacements if necessary
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2008-08-10 20:48:55 +00:00
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_PlayerScream)
|
2006-11-05 21:46:28 +00:00
|
|
|
{
|
|
|
|
int sound = 0;
|
|
|
|
int chan = CHAN_VOICE;
|
|
|
|
|
|
|
|
if (self->player == NULL || self->DeathSound != 0)
|
|
|
|
{
|
2009-02-19 14:36:37 +00:00
|
|
|
if (self->DeathSound != 0)
|
|
|
|
{
|
|
|
|
S_Sound (self, CHAN_VOICE, self->DeathSound, 1, ATTN_NORM);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
S_Sound (self, CHAN_VOICE, "*death", 1, ATTN_NORM);
|
|
|
|
}
|
2006-11-05 21:46:28 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle the different player death screams
|
|
|
|
if ((((level.flags >> 15) | (dmflags)) &
|
|
|
|
(DF_FORCE_FALLINGZD | DF_FORCE_FALLINGHX)) &&
|
2009-06-30 20:57:51 +00:00
|
|
|
self->velz <= -39*FRACUNIT)
|
2006-11-05 21:46:28 +00:00
|
|
|
{
|
|
|
|
sound = S_FindSkinnedSound (self, "*splat");
|
|
|
|
chan = CHAN_BODY;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!sound && self->special1<10)
|
|
|
|
{ // Wimpy death sound
|
2007-02-04 00:22:56 +00:00
|
|
|
sound = S_FindSkinnedSoundEx (self, "*wimpydeath", self->player->LastDamageType);
|
2006-11-05 21:46:28 +00:00
|
|
|
}
|
|
|
|
if (!sound && self->health <= -50)
|
|
|
|
{
|
|
|
|
if (self->health > -100)
|
|
|
|
{ // Crazy death sound
|
2007-02-04 00:22:56 +00:00
|
|
|
sound = S_FindSkinnedSoundEx (self, "*crazydeath", self->player->LastDamageType);
|
2006-11-05 21:46:28 +00:00
|
|
|
}
|
|
|
|
if (!sound)
|
|
|
|
{ // Extreme death sound
|
2007-02-04 00:22:56 +00:00
|
|
|
sound = S_FindSkinnedSoundEx (self, "*xdeath", self->player->LastDamageType);
|
2006-11-05 21:46:28 +00:00
|
|
|
if (!sound)
|
|
|
|
{
|
2007-02-04 00:22:56 +00:00
|
|
|
sound = S_FindSkinnedSoundEx (self, "*gibbed", self->player->LastDamageType);
|
2006-11-05 21:46:28 +00:00
|
|
|
chan = CHAN_BODY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!sound)
|
|
|
|
{ // Normal death sound
|
2007-02-04 00:22:56 +00:00
|
|
|
sound = S_FindSkinnedSoundEx (self, "*death", self->player->LastDamageType);
|
2006-11-05 21:46:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (chan != CHAN_VOICE)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < 8; ++i)
|
|
|
|
{ // Stop most playing sounds from this player.
|
|
|
|
// This is mainly to stop *land from messing up *splat.
|
|
|
|
if (i != CHAN_WEAPON && i != CHAN_VOICE)
|
|
|
|
{
|
|
|
|
S_StopSound (self, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-06-15 02:25:09 +00:00
|
|
|
S_Sound (self, chan, sound, 1, ATTN_NORM);
|
2006-11-05 21:46:28 +00:00
|
|
|
}
|
|
|
|
|
2006-06-11 11:28:48 +00:00
|
|
|
|
2006-11-07 10:20:09 +00:00
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// PROC A_SkullPop
|
|
|
|
//
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
2008-08-12 14:30:07 +00:00
|
|
|
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SkullPop)
|
2006-11-07 10:20:09 +00:00
|
|
|
{
|
2008-08-13 22:54:24 +00:00
|
|
|
ACTION_PARAM_START(1);
|
|
|
|
ACTION_PARAM_CLASS(spawntype, 0);
|
|
|
|
|
2006-11-07 10:20:09 +00:00
|
|
|
APlayerPawn *mo;
|
|
|
|
player_t *player;
|
|
|
|
|
|
|
|
// [GRB] Parameterized version
|
|
|
|
if (!spawntype || !spawntype->IsDescendantOf (RUNTIME_CLASS (APlayerChunk)))
|
|
|
|
{
|
|
|
|
spawntype = PClass::FindClass("BloodySkull");
|
|
|
|
if (spawntype == NULL) return;
|
|
|
|
}
|
|
|
|
|
2008-08-10 20:48:55 +00:00
|
|
|
self->flags &= ~MF_SOLID;
|
|
|
|
mo = (APlayerPawn *)Spawn (spawntype, self->x, self->y, self->z + 48*FRACUNIT, NO_REPLACE);
|
|
|
|
//mo->target = self;
|
2009-06-30 20:57:51 +00:00
|
|
|
mo->velx = pr_skullpop.Random2() << 9;
|
|
|
|
mo->vely = pr_skullpop.Random2() << 9;
|
|
|
|
mo->velz = 2*FRACUNIT + (pr_skullpop() << 6);
|
2006-11-07 10:20:09 +00:00
|
|
|
// Attach player mobj to bloody skull
|
2008-08-10 20:48:55 +00:00
|
|
|
player = self->player;
|
|
|
|
self->player = NULL;
|
|
|
|
mo->ObtainInventory (self);
|
2006-11-07 10:20:09 +00:00
|
|
|
mo->player = player;
|
2008-08-10 20:48:55 +00:00
|
|
|
mo->health = self->health;
|
|
|
|
mo->angle = self->angle;
|
2006-11-07 10:20:09 +00:00
|
|
|
if (player != NULL)
|
|
|
|
{
|
|
|
|
player->mo = mo;
|
2008-08-10 20:48:55 +00:00
|
|
|
if (player->camera == self)
|
2006-11-07 10:20:09 +00:00
|
|
|
{
|
|
|
|
player->camera = mo;
|
|
|
|
}
|
|
|
|
player->damagecount = 32;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// PROC A_CheckSkullDone
|
|
|
|
//
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
2008-08-10 20:48:55 +00:00
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_CheckPlayerDone)
|
2006-11-07 10:20:09 +00:00
|
|
|
{
|
2008-08-10 20:48:55 +00:00
|
|
|
if (self->player == NULL)
|
2006-11-07 10:20:09 +00:00
|
|
|
{
|
2008-08-10 20:48:55 +00:00
|
|
|
self->Destroy ();
|
2006-11-07 10:20:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-06-11 11:28:48 +00:00
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// P_CheckPlayerSprites
|
|
|
|
//
|
2013-01-02 02:10:26 +00:00
|
|
|
// Here's the place where crouching sprites are handled.
|
|
|
|
// R_ProjectSprite() calls this for any players.
|
2006-06-11 11:28:48 +00:00
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2013-01-21 22:30:30 +00:00
|
|
|
void P_CheckPlayerSprite(AActor *actor, int &spritenum, fixed_t &scalex, fixed_t &scaley)
|
2006-06-11 11:28:48 +00:00
|
|
|
{
|
2013-01-02 02:10:26 +00:00
|
|
|
player_t *player = actor->player;
|
|
|
|
int crouchspriteno;
|
|
|
|
|
2013-05-12 18:27:03 +00:00
|
|
|
if (player->userinfo.GetSkin() != 0 && !(actor->flags4 & MF4_NOSKIN))
|
2006-06-11 11:28:48 +00:00
|
|
|
{
|
2013-01-02 03:52:58 +00:00
|
|
|
// Convert from default scale to skin scale.
|
|
|
|
fixed_t defscaleY = actor->GetDefault()->scaleY;
|
|
|
|
fixed_t defscaleX = actor->GetDefault()->scaleX;
|
2013-05-12 18:27:03 +00:00
|
|
|
scaley = Scale(scaley, skins[player->userinfo.GetSkin()].ScaleY, defscaleY);
|
|
|
|
scalex = Scale(scalex, skins[player->userinfo.GetSkin()].ScaleX, defscaleX);
|
2013-01-02 02:10:26 +00:00
|
|
|
}
|
2006-06-11 11:28:48 +00:00
|
|
|
|
2013-01-02 02:10:26 +00:00
|
|
|
// Set the crouch sprite?
|
|
|
|
if (player->crouchfactor < FRACUNIT*3/4)
|
|
|
|
{
|
|
|
|
if (spritenum == actor->SpawnState->sprite || spritenum == player->mo->crouchsprite)
|
2006-06-11 11:28:48 +00:00
|
|
|
{
|
2013-01-02 02:10:26 +00:00
|
|
|
crouchspriteno = player->mo->crouchsprite;
|
|
|
|
}
|
|
|
|
else if (!(actor->flags4 & MF4_NOSKIN) &&
|
2013-05-12 18:27:03 +00:00
|
|
|
(spritenum == skins[player->userinfo.GetSkin()].sprite ||
|
|
|
|
spritenum == skins[player->userinfo.GetSkin()].crouchsprite))
|
2013-01-02 02:10:26 +00:00
|
|
|
{
|
2013-05-12 18:27:03 +00:00
|
|
|
crouchspriteno = skins[player->userinfo.GetSkin()].crouchsprite;
|
2013-01-02 02:10:26 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // no sprite -> squash the existing one
|
|
|
|
crouchspriteno = -1;
|
|
|
|
}
|
2006-06-11 11:28:48 +00:00
|
|
|
|
2013-01-02 02:10:26 +00:00
|
|
|
if (crouchspriteno > 0)
|
|
|
|
{
|
|
|
|
spritenum = crouchspriteno;
|
|
|
|
}
|
2013-01-02 03:52:58 +00:00
|
|
|
else if (player->playerstate != PST_DEAD && player->crouchfactor < FRACUNIT*3/4)
|
2013-01-02 02:10:26 +00:00
|
|
|
{
|
2013-01-02 03:52:58 +00:00
|
|
|
scaley /= 2;
|
2006-06-11 11:28:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
/*
|
|
|
|
==================
|
|
|
|
=
|
|
|
|
= P_Thrust
|
|
|
|
=
|
|
|
|
= moves the given origin along a given angle
|
|
|
|
=
|
|
|
|
==================
|
|
|
|
*/
|
|
|
|
|
|
|
|
void P_SideThrust (player_t *player, angle_t angle, fixed_t move)
|
|
|
|
{
|
|
|
|
angle = (angle - ANGLE_90) >> ANGLETOFINESHIFT;
|
|
|
|
|
2009-06-30 20:57:51 +00:00
|
|
|
player->mo->velx += FixedMul (move, finecosine[angle]);
|
|
|
|
player->mo->vely += FixedMul (move, finesine[angle]);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void P_ForwardThrust (player_t *player, angle_t angle, fixed_t move)
|
|
|
|
{
|
|
|
|
angle >>= ANGLETOFINESHIFT;
|
|
|
|
|
|
|
|
if ((player->mo->waterlevel || (player->mo->flags & MF_NOGRAVITY))
|
|
|
|
&& player->mo->pitch != 0)
|
|
|
|
{
|
|
|
|
angle_t pitch = (angle_t)player->mo->pitch >> ANGLETOFINESHIFT;
|
|
|
|
fixed_t zpush = FixedMul (move, finesine[pitch]);
|
|
|
|
if (player->mo->waterlevel && player->mo->waterlevel < 2 && zpush < 0)
|
|
|
|
zpush = 0;
|
2009-06-30 20:57:51 +00:00
|
|
|
player->mo->velz -= zpush;
|
2006-02-24 04:48:15 +00:00
|
|
|
move = FixedMul (move, finecosine[pitch]);
|
|
|
|
}
|
2009-06-30 20:57:51 +00:00
|
|
|
player->mo->velx += FixedMul (move, finecosine[angle]);
|
|
|
|
player->mo->vely += FixedMul (move, finesine[angle]);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// P_Bob
|
|
|
|
// Same as P_Thrust, but only affects bobbing.
|
|
|
|
//
|
|
|
|
// killough 10/98: We apply thrust separately between the real physical player
|
|
|
|
// and the part which affects bobbing. This way, bobbing only comes from player
|
|
|
|
// motion, nothing external, avoiding many problems, e.g. bobbing should not
|
|
|
|
// occur on conveyors, unless the player walks on one, and bobbing should be
|
|
|
|
// reduced at a regular rate, even on ice (where the player coasts).
|
|
|
|
//
|
|
|
|
|
2013-02-14 01:29:53 +00:00
|
|
|
void P_Bob (player_t *player, angle_t angle, fixed_t move, bool forward)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2013-02-14 01:29:53 +00:00
|
|
|
if (forward
|
|
|
|
&& (player->mo->waterlevel || (player->mo->flags & MF_NOGRAVITY))
|
|
|
|
&& player->mo->pitch != 0)
|
|
|
|
{
|
|
|
|
angle_t pitch = (angle_t)player->mo->pitch >> ANGLETOFINESHIFT;
|
|
|
|
move = FixedMul (move, finecosine[pitch]);
|
|
|
|
}
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
angle >>= ANGLETOFINESHIFT;
|
|
|
|
|
2009-06-30 20:57:51 +00:00
|
|
|
player->velx += FixedMul(move, finecosine[angle]);
|
|
|
|
player->vely += FixedMul(move, finesine[angle]);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
=
|
|
|
|
= P_CalcHeight
|
|
|
|
=
|
|
|
|
=
|
|
|
|
Calculate the walking / running height adjustment
|
|
|
|
=
|
|
|
|
==================
|
|
|
|
*/
|
|
|
|
|
|
|
|
void P_CalcHeight (player_t *player)
|
|
|
|
{
|
|
|
|
int angle;
|
|
|
|
fixed_t bob;
|
|
|
|
bool still = false;
|
|
|
|
|
|
|
|
// Regular movement bobbing
|
|
|
|
// (needs to be calculated for gun swing even if not on ground)
|
|
|
|
|
|
|
|
// killough 10/98: Make bobbing depend only on player-applied motion.
|
|
|
|
//
|
|
|
|
// Note: don't reduce bobbing here if on ice: if you reduce bobbing here,
|
|
|
|
// it causes bobbing jerkiness when the player moves from ice to non-ice,
|
|
|
|
// and vice-versa.
|
|
|
|
|
2012-08-22 21:31:48 +00:00
|
|
|
if (player->cheats & CF_NOCLIP2)
|
|
|
|
{
|
|
|
|
player->bob = 0;
|
|
|
|
}
|
2014-03-02 12:00:47 +00:00
|
|
|
else if ((player->mo->flags & MF_NOGRAVITY) && !player->onground)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
player->bob = FRACUNIT / 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-06-30 20:57:51 +00:00
|
|
|
player->bob = DMulScale16 (player->velx, player->velx, player->vely, player->vely);
|
2006-02-24 04:48:15 +00:00
|
|
|
if (player->bob == 0)
|
|
|
|
{
|
|
|
|
still = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-05-12 18:27:03 +00:00
|
|
|
player->bob = FixedMul (player->bob, player->userinfo.GetMoveBob());
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
if (player->bob > MAXBOB)
|
|
|
|
player->bob = MAXBOB;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-07-13 10:17:56 +00:00
|
|
|
fixed_t defaultviewheight = player->mo->ViewHeight + player->crouchviewdelta;
|
2006-04-11 16:27:41 +00:00
|
|
|
|
2009-06-30 20:57:51 +00:00
|
|
|
if (player->cheats & CF_NOVELOCITY)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-04-11 16:27:41 +00:00
|
|
|
player->viewz = player->mo->z + defaultviewheight;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
if (player->viewz > player->mo->ceilingz-4*FRACUNIT)
|
|
|
|
player->viewz = player->mo->ceilingz-4*FRACUNIT;
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (still)
|
|
|
|
{
|
|
|
|
if (player->health > 0)
|
|
|
|
{
|
|
|
|
angle = DivScale13 (level.time, 120*TICRATE/35) & FINEMASK;
|
2013-05-12 18:27:03 +00:00
|
|
|
bob = FixedMul (player->userinfo.GetStillBob(), finesine[angle]);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
bob = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// DivScale 13 because FINEANGLES == (1<<13)
|
|
|
|
angle = DivScale13 (level.time, 20*TICRATE/35) & FINEMASK;
|
|
|
|
bob = FixedMul (player->bob>>(player->mo->waterlevel > 1 ? 2 : 1), finesine[angle]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// move viewheight
|
|
|
|
if (player->playerstate == PST_LIVE)
|
|
|
|
{
|
|
|
|
player->viewheight += player->deltaviewheight;
|
|
|
|
|
2006-04-11 16:27:41 +00:00
|
|
|
if (player->viewheight > defaultviewheight)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-04-11 16:27:41 +00:00
|
|
|
player->viewheight = defaultviewheight;
|
2006-02-24 04:48:15 +00:00
|
|
|
player->deltaviewheight = 0;
|
|
|
|
}
|
2006-04-11 16:27:41 +00:00
|
|
|
else if (player->viewheight < (defaultviewheight>>1))
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-04-11 16:27:41 +00:00
|
|
|
player->viewheight = defaultviewheight>>1;
|
2006-02-24 04:48:15 +00:00
|
|
|
if (player->deltaviewheight <= 0)
|
|
|
|
player->deltaviewheight = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (player->deltaviewheight)
|
|
|
|
{
|
|
|
|
player->deltaviewheight += FRACUNIT/4;
|
|
|
|
if (!player->deltaviewheight)
|
|
|
|
player->deltaviewheight = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (player->morphTics)
|
|
|
|
{
|
2006-07-13 10:17:56 +00:00
|
|
|
bob = 0;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
2006-07-13 10:17:56 +00:00
|
|
|
player->viewz = player->mo->z + player->viewheight + bob;
|
2006-02-24 04:48:15 +00:00
|
|
|
if (player->mo->floorclip && player->playerstate != PST_DEAD
|
|
|
|
&& player->mo->z <= player->mo->floorz)
|
|
|
|
{
|
|
|
|
player->viewz -= player->mo->floorclip;
|
|
|
|
}
|
|
|
|
if (player->viewz > player->mo->ceilingz - 4*FRACUNIT)
|
|
|
|
{
|
|
|
|
player->viewz = player->mo->ceilingz - 4*FRACUNIT;
|
|
|
|
}
|
|
|
|
if (player->viewz < player->mo->floorz + 4*FRACUNIT)
|
|
|
|
{
|
|
|
|
player->viewz = player->mo->floorz + 4*FRACUNIT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=================
|
|
|
|
=
|
|
|
|
= P_MovePlayer
|
|
|
|
=
|
|
|
|
=================
|
|
|
|
*/
|
|
|
|
CUSTOM_CVAR (Float, sv_aircontrol, 0.00390625f, CVAR_SERVERINFO|CVAR_NOSAVE)
|
|
|
|
{
|
|
|
|
level.aircontrol = (fixed_t)(self * 65536.f);
|
|
|
|
G_AirControlChanged ();
|
|
|
|
}
|
|
|
|
|
|
|
|
void P_MovePlayer (player_t *player)
|
|
|
|
{
|
|
|
|
ticcmd_t *cmd = &player->cmd;
|
|
|
|
APlayerPawn *mo = player->mo;
|
|
|
|
|
|
|
|
// [RH] 180-degree turn overrides all other yaws
|
|
|
|
if (player->turnticks)
|
|
|
|
{
|
|
|
|
player->turnticks--;
|
|
|
|
mo->angle += (ANGLE_180 / TURN180_TICKS);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mo->angle += cmd->ucmd.yaw << 16;
|
|
|
|
}
|
|
|
|
|
2014-03-02 12:00:47 +00:00
|
|
|
player->onground = (mo->z <= mo->floorz) || (mo->flags2 & MF2_ONMOBJ) || (mo->BounceFlags & BOUNCE_MBF) || (player->cheats & CF_NOCLIP2);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
// killough 10/98:
|
|
|
|
//
|
|
|
|
// We must apply thrust to the player and bobbing separately, to avoid
|
|
|
|
// anomalies. The thrust applied to bobbing is always the same strength on
|
|
|
|
// ice, because the player still "works just as hard" to move, while the
|
|
|
|
// thrust applied to the movement varies with 'movefactor'.
|
|
|
|
|
|
|
|
if (cmd->ucmd.forwardmove | cmd->ucmd.sidemove)
|
|
|
|
{
|
|
|
|
fixed_t forwardmove, sidemove;
|
|
|
|
int bobfactor;
|
|
|
|
int friction, movefactor;
|
|
|
|
int fm, sm;
|
|
|
|
|
|
|
|
movefactor = P_GetMoveFactor (mo, &friction);
|
|
|
|
bobfactor = friction < ORIG_FRICTION ? movefactor : ORIG_FRICTION_FACTOR;
|
2014-03-02 12:00:47 +00:00
|
|
|
if (!player->onground && !(player->mo->flags & MF_NOGRAVITY) && !player->mo->waterlevel)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
// [RH] allow very limited movement if not on ground.
|
|
|
|
movefactor = FixedMul (movefactor, level.aircontrol);
|
|
|
|
bobfactor = FixedMul (bobfactor, level.aircontrol);
|
|
|
|
}
|
|
|
|
|
|
|
|
fm = cmd->ucmd.forwardmove;
|
|
|
|
sm = cmd->ucmd.sidemove;
|
|
|
|
mo->TweakSpeeds (fm, sm);
|
|
|
|
fm = FixedMul (fm, player->mo->Speed);
|
|
|
|
sm = FixedMul (sm, player->mo->Speed);
|
|
|
|
|
2013-02-14 01:29:53 +00:00
|
|
|
// When crouching, speed and bobbing have to be reduced
|
2013-04-28 02:31:34 +00:00
|
|
|
if (player->CanCrouch() && player->crouchfactor != FRACUNIT)
|
2006-05-14 14:30:13 +00:00
|
|
|
{
|
|
|
|
fm = FixedMul(fm, player->crouchfactor);
|
|
|
|
sm = FixedMul(sm, player->crouchfactor);
|
|
|
|
bobfactor = FixedMul(bobfactor, player->crouchfactor);
|
|
|
|
}
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
forwardmove = Scale (fm, movefactor * 35, TICRATE << 8);
|
|
|
|
sidemove = Scale (sm, movefactor * 35, TICRATE << 8);
|
|
|
|
|
|
|
|
if (forwardmove)
|
|
|
|
{
|
2013-02-14 01:29:53 +00:00
|
|
|
P_Bob (player, mo->angle, (cmd->ucmd.forwardmove * bobfactor) >> 8, true);
|
2006-02-24 04:48:15 +00:00
|
|
|
P_ForwardThrust (player, mo->angle, forwardmove);
|
|
|
|
}
|
|
|
|
if (sidemove)
|
|
|
|
{
|
2013-02-14 01:29:53 +00:00
|
|
|
P_Bob (player, mo->angle-ANG90, (cmd->ucmd.sidemove * bobfactor) >> 8, false);
|
2006-02-24 04:48:15 +00:00
|
|
|
P_SideThrust (player, mo->angle, sidemove);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (debugfile)
|
|
|
|
{
|
2008-05-14 03:39:30 +00:00
|
|
|
fprintf (debugfile, "move player for pl %d%c: (%d,%d,%d) (%d,%d) %d %d w%d [", int(player-players),
|
2006-02-24 04:48:15 +00:00
|
|
|
player->cheats&CF_PREDICTING?'p':' ',
|
|
|
|
player->mo->x, player->mo->y, player->mo->z,forwardmove, sidemove, movefactor, friction, player->mo->waterlevel);
|
|
|
|
msecnode_t *n = player->mo->touching_sectorlist;
|
|
|
|
while (n != NULL)
|
|
|
|
{
|
2008-05-14 03:39:30 +00:00
|
|
|
fprintf (debugfile, "%td ", n->m_sector-sectors);
|
2006-02-24 04:48:15 +00:00
|
|
|
n = n->m_tnext;
|
|
|
|
}
|
|
|
|
fprintf (debugfile, "]\n");
|
|
|
|
}
|
|
|
|
|
2007-11-02 22:03:03 +00:00
|
|
|
if (!(player->cheats & CF_PREDICTING) && (forwardmove|sidemove))
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
player->mo->PlayRunning ();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (player->cheats & CF_REVERTPLEASE)
|
|
|
|
{
|
|
|
|
player->cheats &= ~CF_REVERTPLEASE;
|
|
|
|
player->camera = player->mo;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// P_FallingDamage
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void P_FallingDamage (AActor *actor)
|
|
|
|
{
|
|
|
|
int damagestyle;
|
|
|
|
int damage;
|
2009-06-30 20:57:51 +00:00
|
|
|
fixed_t vel;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
damagestyle = ((level.flags >> 15) | (dmflags)) &
|
|
|
|
(DF_FORCE_FALLINGZD | DF_FORCE_FALLINGHX);
|
|
|
|
|
|
|
|
if (damagestyle == 0)
|
|
|
|
return;
|
2008-05-11 21:16:32 +00:00
|
|
|
|
|
|
|
if (actor->floorsector->Flags & SECF_NOFALLINGDAMAGE)
|
|
|
|
return;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2009-06-30 20:57:51 +00:00
|
|
|
vel = abs(actor->velz);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
// Since Hexen falling damage is stronger than ZDoom's, it takes
|
|
|
|
// precedence. ZDoom falling damage may not be as strong, but it
|
|
|
|
// gets felt sooner.
|
|
|
|
|
|
|
|
switch (damagestyle)
|
|
|
|
{
|
|
|
|
case DF_FORCE_FALLINGHX: // Hexen falling damage
|
2009-06-30 20:57:51 +00:00
|
|
|
if (vel <= 23*FRACUNIT)
|
2006-02-24 04:48:15 +00:00
|
|
|
{ // Not fast enough to hurt
|
|
|
|
return;
|
|
|
|
}
|
2009-06-30 20:57:51 +00:00
|
|
|
if (vel >= 63*FRACUNIT)
|
2006-02-24 04:48:15 +00:00
|
|
|
{ // automatic death
|
|
|
|
damage = 1000000;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-06-30 20:57:51 +00:00
|
|
|
vel = FixedMul (vel, 16*FRACUNIT/23);
|
|
|
|
damage = ((FixedMul (vel, vel) / 10) >> FRACBITS) - 24;
|
|
|
|
if (actor->velz > -39*FRACUNIT && damage > actor->health
|
2006-02-24 04:48:15 +00:00
|
|
|
&& actor->health != 1)
|
|
|
|
{ // No-death threshold
|
|
|
|
damage = actor->health-1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DF_FORCE_FALLINGZD: // ZDoom falling damage
|
2009-06-30 20:57:51 +00:00
|
|
|
if (vel <= 19*FRACUNIT)
|
2006-02-24 04:48:15 +00:00
|
|
|
{ // Not fast enough to hurt
|
|
|
|
return;
|
|
|
|
}
|
2009-06-30 20:57:51 +00:00
|
|
|
if (vel >= 84*FRACUNIT)
|
2006-02-24 04:48:15 +00:00
|
|
|
{ // automatic death
|
|
|
|
damage = 1000000;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-06-30 20:57:51 +00:00
|
|
|
damage = ((MulScale23 (vel, vel*11) >> FRACBITS) - 30) / 2;
|
2006-02-24 04:48:15 +00:00
|
|
|
if (damage < 1)
|
|
|
|
{
|
|
|
|
damage = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DF_FORCE_FALLINGST: // Strife falling damage
|
2009-06-30 20:57:51 +00:00
|
|
|
if (vel <= 20*FRACUNIT)
|
2006-02-24 04:48:15 +00:00
|
|
|
{ // Not fast enough to hurt
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// The minimum amount of damage you take from falling in Strife
|
|
|
|
// is 52. Ouch!
|
2009-06-30 20:57:51 +00:00
|
|
|
damage = vel / 25000;
|
2006-02-24 04:48:15 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (actor->player)
|
|
|
|
{
|
|
|
|
S_Sound (actor, CHAN_AUTO, "*land", 1, ATTN_NORM);
|
|
|
|
P_NoiseAlert (actor, actor, true);
|
2009-08-07 04:08:38 +00:00
|
|
|
if (damage == 1000000 && (actor->player->cheats & (CF_GODMODE | CF_BUDDHA)))
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
damage = 999;
|
|
|
|
}
|
|
|
|
}
|
2006-10-31 14:53:21 +00:00
|
|
|
P_DamageMobj (actor, NULL, NULL, damage, NAME_Falling);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// P_DeathThink
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void P_DeathThink (player_t *player)
|
|
|
|
{
|
|
|
|
int dir;
|
|
|
|
angle_t delta;
|
|
|
|
int lookDelta;
|
|
|
|
|
|
|
|
P_MovePsprites (player);
|
|
|
|
|
2014-03-02 12:00:47 +00:00
|
|
|
player->onground = (player->mo->z <= player->mo->floorz);
|
2006-02-24 04:48:15 +00:00
|
|
|
if (player->mo->IsKindOf (RUNTIME_CLASS(APlayerChunk)))
|
|
|
|
{ // Flying bloody skull or flying ice chunk
|
|
|
|
player->viewheight = 6 * FRACUNIT;
|
|
|
|
player->deltaviewheight = 0;
|
2014-03-02 12:00:47 +00:00
|
|
|
if (player->onground)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
if (player->mo->pitch > -(int)ANGLE_1*19)
|
|
|
|
{
|
|
|
|
lookDelta = (-(int)ANGLE_1*19 - player->mo->pitch) / 8;
|
|
|
|
player->mo->pitch += lookDelta;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2006-10-31 14:53:21 +00:00
|
|
|
else if (!(player->mo->flags & MF_ICECORPSE))
|
2006-02-24 04:48:15 +00:00
|
|
|
{ // Fall to ground (if not frozen)
|
|
|
|
player->deltaviewheight = 0;
|
|
|
|
if (player->viewheight > 6*FRACUNIT)
|
|
|
|
{
|
|
|
|
player->viewheight -= FRACUNIT;
|
|
|
|
}
|
|
|
|
if (player->viewheight < 6*FRACUNIT)
|
|
|
|
{
|
|
|
|
player->viewheight = 6*FRACUNIT;
|
|
|
|
}
|
|
|
|
if (player->mo->pitch < 0)
|
|
|
|
{
|
|
|
|
player->mo->pitch += ANGLE_1*3;
|
|
|
|
}
|
|
|
|
else if (player->mo->pitch > 0)
|
|
|
|
{
|
|
|
|
player->mo->pitch -= ANGLE_1*3;
|
|
|
|
}
|
|
|
|
if (abs(player->mo->pitch) < ANGLE_1*3)
|
|
|
|
{
|
|
|
|
player->mo->pitch = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
P_CalcHeight (player);
|
|
|
|
|
|
|
|
if (player->attacker && player->attacker != player->mo)
|
|
|
|
{ // Watch killer
|
|
|
|
dir = P_FaceMobj (player->mo, player->attacker, &delta);
|
|
|
|
if (delta < ANGLE_1*10)
|
|
|
|
{ // Looking at killer, so fade damage and poison counters
|
|
|
|
if (player->damagecount)
|
|
|
|
{
|
|
|
|
player->damagecount--;
|
|
|
|
}
|
|
|
|
if (player->poisoncount)
|
|
|
|
{
|
|
|
|
player->poisoncount--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
delta /= 8;
|
|
|
|
if (delta > ANGLE_1*5)
|
|
|
|
{
|
|
|
|
delta = ANGLE_1*5;
|
|
|
|
}
|
|
|
|
if (dir)
|
|
|
|
{ // Turn clockwise
|
|
|
|
player->mo->angle += delta;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // Turn counter clockwise
|
|
|
|
player->mo->angle -= delta;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (player->damagecount)
|
|
|
|
{
|
|
|
|
player->damagecount--;
|
|
|
|
}
|
|
|
|
if (player->poisoncount)
|
|
|
|
{
|
|
|
|
player->poisoncount--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-12-24 14:24:24 +00:00
|
|
|
if ((player->cmd.ucmd.buttons & BT_USE ||
|
2008-06-15 03:46:04 +00:00
|
|
|
((multiplayer || alwaysapplydmflags) && (dmflags & DF_FORCE_RESPAWN))) && !(dmflags2 & DF2_NO_RESPAWN))
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
if (level.time >= player->respawn_time || ((player->cmd.ucmd.buttons & BT_USE) && !player->isbot))
|
|
|
|
{
|
|
|
|
player->cls = NULL; // Force a new class if the player is using a random class
|
2009-02-03 19:11:43 +00:00
|
|
|
player->playerstate = (multiplayer || (level.flags2 & LEVEL2_ALLOWRESPAWN)) ? PST_REBORN : PST_ENTER;
|
2006-02-24 04:48:15 +00:00
|
|
|
if (player->mo->special1 > 2)
|
|
|
|
{
|
|
|
|
player->mo->special1 = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-05-14 14:30:13 +00:00
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// PROC P_CrouchMove
|
|
|
|
//
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void P_CrouchMove(player_t * player, int direction)
|
|
|
|
{
|
|
|
|
fixed_t defaultheight = player->mo->GetDefault()->height;
|
|
|
|
fixed_t savedheight = player->mo->height;
|
|
|
|
fixed_t crouchspeed = direction * CROUCHSPEED;
|
2007-12-09 03:40:02 +00:00
|
|
|
fixed_t oldheight = player->viewheight;
|
2006-05-14 14:30:13 +00:00
|
|
|
|
|
|
|
player->crouchdir = (signed char) direction;
|
|
|
|
player->crouchfactor += crouchspeed;
|
|
|
|
|
|
|
|
// check whether the move is ok
|
|
|
|
player->mo->height = FixedMul(defaultheight, player->crouchfactor);
|
2010-09-01 03:30:18 +00:00
|
|
|
if (!P_TryMove(player->mo, player->mo->x, player->mo->y, false, NULL))
|
2006-05-14 14:30:13 +00:00
|
|
|
{
|
|
|
|
player->mo->height = savedheight;
|
|
|
|
if (direction > 0)
|
|
|
|
{
|
|
|
|
// doesn't fit
|
|
|
|
player->crouchfactor -= crouchspeed;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
player->mo->height = savedheight;
|
|
|
|
|
|
|
|
player->crouchfactor = clamp<fixed_t>(player->crouchfactor, FRACUNIT/2, FRACUNIT);
|
2006-07-13 10:17:56 +00:00
|
|
|
player->viewheight = FixedMul(player->mo->ViewHeight, player->crouchfactor);
|
|
|
|
player->crouchviewdelta = player->viewheight - player->mo->ViewHeight;
|
2007-12-09 03:40:02 +00:00
|
|
|
|
|
|
|
// Check for eyes going above/below fake floor due to crouching motion.
|
|
|
|
P_CheckFakeFloorTriggers(player->mo, player->mo->z + oldheight, true);
|
2006-05-14 14:30:13 +00:00
|
|
|
}
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// PROC P_PlayerThink
|
|
|
|
//
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void P_PlayerThink (player_t *player)
|
|
|
|
{
|
|
|
|
ticcmd_t *cmd;
|
|
|
|
|
|
|
|
if (player->mo == NULL)
|
|
|
|
{
|
2008-05-14 03:39:30 +00:00
|
|
|
I_Error ("No player %td start\n", player - players + 1);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (debugfile && !(player->cheats & CF_PREDICTING))
|
|
|
|
{
|
2008-05-14 03:39:30 +00:00
|
|
|
fprintf (debugfile, "tic %d for pl %td: (%d, %d, %d, %u) b:%02x p:%d y:%d f:%d s:%d u:%d\n",
|
2006-02-24 04:48:15 +00:00
|
|
|
gametic, player-players, player->mo->x, player->mo->y, player->mo->z,
|
|
|
|
player->mo->angle>>ANGLETOFINESHIFT, player->cmd.ucmd.buttons,
|
|
|
|
player->cmd.ucmd.pitch, player->cmd.ucmd.yaw, player->cmd.ucmd.forwardmove,
|
|
|
|
player->cmd.ucmd.sidemove, player->cmd.ucmd.upmove);
|
|
|
|
}
|
|
|
|
|
|
|
|
// [RH] Zoom the player's FOV
|
2009-06-30 19:20:39 +00:00
|
|
|
float desired = player->DesiredFOV;
|
|
|
|
// Adjust FOV using on the currently held weapon.
|
|
|
|
if (player->playerstate != PST_DEAD && // No adjustment while dead.
|
|
|
|
player->ReadyWeapon != NULL && // No adjustment if no weapon.
|
|
|
|
player->ReadyWeapon->FOVScale != 0) // No adjustment if the adjustment is zero.
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2013-03-21 03:47:39 +00:00
|
|
|
// A negative scale is used to prevent G_AddViewAngle/G_AddViewPitch
|
2009-06-30 22:37:40 +00:00
|
|
|
// from scaling with the FOV scale.
|
|
|
|
desired *= fabs(player->ReadyWeapon->FOVScale);
|
2009-06-30 19:20:39 +00:00
|
|
|
}
|
|
|
|
if (player->FOV != desired)
|
|
|
|
{
|
|
|
|
if (fabsf (player->FOV - desired) < 7.f)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2009-06-30 19:20:39 +00:00
|
|
|
player->FOV = desired;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-06-30 19:20:39 +00:00
|
|
|
float zoom = MAX(7.f, fabsf(player->FOV - desired) * 0.025f);
|
|
|
|
if (player->FOV > desired)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
player->FOV = player->FOV - zoom;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
player->FOV = player->FOV + zoom;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (player->inventorytics)
|
|
|
|
{
|
|
|
|
player->inventorytics--;
|
|
|
|
}
|
2013-10-10 02:50:24 +00:00
|
|
|
// Don't interpolate the view for more than one tic
|
|
|
|
player->cheats &= ~CF_INTERPVIEW;
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
// No-clip cheat
|
2012-08-22 21:31:48 +00:00
|
|
|
if ((player->cheats & (CF_NOCLIP | CF_NOCLIP2)) == CF_NOCLIP2)
|
|
|
|
{ // No noclip2 without noclip
|
|
|
|
player->cheats &= ~CF_NOCLIP2;
|
|
|
|
}
|
|
|
|
if (player->cheats & (CF_NOCLIP | CF_NOCLIP2) || (player->mo->GetDefault()->flags & MF_NOCLIP))
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
player->mo->flags |= MF_NOCLIP;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
player->mo->flags &= ~MF_NOCLIP;
|
|
|
|
}
|
2012-08-22 21:31:48 +00:00
|
|
|
if (player->cheats & CF_NOCLIP2)
|
|
|
|
{
|
|
|
|
player->mo->flags |= MF_NOGRAVITY;
|
|
|
|
}
|
|
|
|
else if (!(player->mo->flags2 & MF2_FLY) && !(player->mo->GetDefault()->flags & MF_NOGRAVITY))
|
|
|
|
{
|
|
|
|
player->mo->flags &= ~MF_NOGRAVITY;
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
cmd = &player->cmd;
|
2008-09-13 02:55:45 +00:00
|
|
|
|
|
|
|
// Make unmodified copies for ACS's GetPlayerInput.
|
|
|
|
player->original_oldbuttons = player->original_cmd.buttons;
|
|
|
|
player->original_cmd = cmd->ucmd;
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
if (player->mo->flags & MF_JUSTATTACKED)
|
|
|
|
{ // Chainsaw/Gauntlets attack auto forward motion
|
|
|
|
cmd->ucmd.yaw = 0;
|
|
|
|
cmd->ucmd.forwardmove = 0xc800/2;
|
|
|
|
cmd->ucmd.sidemove = 0;
|
|
|
|
player->mo->flags &= ~MF_JUSTATTACKED;
|
|
|
|
}
|
|
|
|
|
2012-01-22 00:31:42 +00:00
|
|
|
bool totallyfrozen = P_IsPlayerTotallyFrozen(player);
|
2009-12-16 16:09:48 +00:00
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
// [RH] Being totally frozen zeros out most input parameters.
|
2009-12-16 16:09:48 +00:00
|
|
|
if (totallyfrozen)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
if (gamestate == GS_TITLELEVEL)
|
|
|
|
{
|
|
|
|
cmd->ucmd.buttons = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cmd->ucmd.buttons &= BT_USE;
|
|
|
|
}
|
|
|
|
cmd->ucmd.pitch = 0;
|
|
|
|
cmd->ucmd.yaw = 0;
|
|
|
|
cmd->ucmd.roll = 0;
|
|
|
|
cmd->ucmd.forwardmove = 0;
|
|
|
|
cmd->ucmd.sidemove = 0;
|
|
|
|
cmd->ucmd.upmove = 0;
|
|
|
|
player->turnticks = 0;
|
|
|
|
}
|
|
|
|
else if (player->cheats & CF_FROZEN)
|
|
|
|
{
|
|
|
|
cmd->ucmd.forwardmove = 0;
|
|
|
|
cmd->ucmd.sidemove = 0;
|
|
|
|
cmd->ucmd.upmove = 0;
|
|
|
|
}
|
|
|
|
|
2006-05-14 14:30:13 +00:00
|
|
|
// Handle crouching
|
2008-09-13 07:22:13 +00:00
|
|
|
if (player->cmd.ucmd.buttons & BT_JUMP)
|
2008-09-13 02:55:45 +00:00
|
|
|
{
|
|
|
|
player->cmd.ucmd.buttons &= ~BT_CROUCH;
|
|
|
|
}
|
2013-04-28 02:31:34 +00:00
|
|
|
if (player->CanCrouch() && player->health > 0 && level.IsCrouchingAllowed())
|
2006-05-14 14:30:13 +00:00
|
|
|
{
|
2009-12-16 16:09:48 +00:00
|
|
|
if (!totallyfrozen)
|
2006-05-14 14:30:13 +00:00
|
|
|
{
|
|
|
|
int crouchdir = player->crouching;
|
|
|
|
|
2009-06-30 22:37:40 +00:00
|
|
|
if (crouchdir == 0)
|
2006-05-14 14:30:13 +00:00
|
|
|
{
|
2013-04-19 02:24:20 +00:00
|
|
|
crouchdir = (player->cmd.ucmd.buttons & BT_CROUCH) ? -1 : 1;
|
2006-05-14 14:30:13 +00:00
|
|
|
}
|
2008-09-13 02:55:45 +00:00
|
|
|
else if (player->cmd.ucmd.buttons & BT_CROUCH)
|
2006-05-14 14:30:13 +00:00
|
|
|
{
|
2013-04-19 02:24:20 +00:00
|
|
|
player->crouching = 0;
|
2006-05-14 14:30:13 +00:00
|
|
|
}
|
|
|
|
if (crouchdir == 1 && player->crouchfactor < FRACUNIT &&
|
|
|
|
player->mo->z + player->mo->height < player->mo->ceilingz)
|
|
|
|
{
|
|
|
|
P_CrouchMove(player, 1);
|
|
|
|
}
|
|
|
|
else if (crouchdir == -1 && player->crouchfactor > FRACUNIT/2)
|
|
|
|
{
|
|
|
|
P_CrouchMove(player, -1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
player->Uncrouch();
|
|
|
|
}
|
|
|
|
|
2006-07-13 10:17:56 +00:00
|
|
|
player->crouchoffset = -FixedMul(player->mo->ViewHeight, (FRACUNIT - player->crouchfactor));
|
2006-05-14 14:30:13 +00:00
|
|
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
if (player->playerstate == PST_DEAD)
|
|
|
|
{
|
2006-05-14 14:30:13 +00:00
|
|
|
player->Uncrouch();
|
2006-02-24 04:48:15 +00:00
|
|
|
P_DeathThink (player);
|
|
|
|
return;
|
|
|
|
}
|
2010-11-04 02:19:34 +00:00
|
|
|
if (player->jumpTics != 0)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
player->jumpTics--;
|
2014-03-02 12:00:47 +00:00
|
|
|
if (player->onground && player->jumpTics < -18)
|
2010-11-04 02:19:34 +00:00
|
|
|
{
|
|
|
|
player->jumpTics = 0;
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
if (player->morphTics && !(player->cheats & CF_PREDICTING))
|
|
|
|
{
|
|
|
|
player->mo->MorphPlayerThink ();
|
|
|
|
}
|
|
|
|
|
|
|
|
// [RH] Look up/down stuff
|
2007-12-11 04:03:40 +00:00
|
|
|
if (!level.IsFreelookAllowed())
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
player->mo->pitch = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int look = cmd->ucmd.pitch << 16;
|
|
|
|
|
|
|
|
// The player's view pitch is clamped between -32 and +56 degrees,
|
|
|
|
// which translates to about half a screen height up and (more than)
|
|
|
|
// one full screen height down from straight ahead when view panning
|
|
|
|
// is used.
|
|
|
|
if (look)
|
|
|
|
{
|
|
|
|
if (look == -32768 << 16)
|
|
|
|
{ // center view
|
2012-04-03 04:09:30 +00:00
|
|
|
player->centering = true;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
2012-04-03 04:09:30 +00:00
|
|
|
else if (!player->centering)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2012-05-01 11:27:54 +00:00
|
|
|
fixed_t oldpitch = player->mo->pitch;
|
2006-02-24 04:48:15 +00:00
|
|
|
player->mo->pitch -= look;
|
|
|
|
if (look > 0)
|
|
|
|
{ // look up
|
2011-12-06 01:25:37 +00:00
|
|
|
player->mo->pitch = MAX(player->mo->pitch, player->MinPitch);
|
2012-05-01 11:27:54 +00:00
|
|
|
if (player->mo->pitch > oldpitch)
|
|
|
|
{
|
|
|
|
player->mo->pitch = player->MinPitch;
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // look down
|
2011-12-06 01:25:37 +00:00
|
|
|
player->mo->pitch = MIN(player->mo->pitch, player->MaxPitch);
|
2012-05-01 11:27:54 +00:00
|
|
|
if (player->mo->pitch < oldpitch)
|
|
|
|
{
|
|
|
|
player->mo->pitch = player->MaxPitch;
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-04-03 04:09:30 +00:00
|
|
|
if (player->centering)
|
|
|
|
{
|
|
|
|
if (abs(player->mo->pitch) > 2*ANGLE_1)
|
|
|
|
{
|
|
|
|
player->mo->pitch = FixedMul(player->mo->pitch, FRACUNIT*2/3);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
player->mo->pitch = 0;
|
|
|
|
player->centering = false;
|
|
|
|
if (player - players == consoleplayer)
|
|
|
|
{
|
|
|
|
LocalViewPitch = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
// [RH] Check for fast turn around
|
|
|
|
if (cmd->ucmd.buttons & BT_TURN180 && !(player->oldbuttons & BT_TURN180))
|
|
|
|
{
|
|
|
|
player->turnticks = TURN180_TICKS;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle movement
|
|
|
|
if (player->mo->reactiontime)
|
|
|
|
{ // Player is frozen
|
|
|
|
player->mo->reactiontime--;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
P_MovePlayer (player);
|
|
|
|
|
|
|
|
// [RH] check for jump
|
|
|
|
if (cmd->ucmd.buttons & BT_JUMP)
|
|
|
|
{
|
- Revised usage of jumpTics. In Hexen, it went like this:
* When you jump, it gets set to 18.
* When you land, it gets set to 7.
* As long as it is non-zero, it counts down, and you cannot jump.
Of note here, is that setting it to 18 upon jumping seems useless, since you can't jump unless
you're on the ground, and when you reach the ground, it will always be set to 7. With that in
mind, the new behavior is:
* When you jump, it gets set to -1.
* When you land, if it is less than zero or you fall far enough to squat, jumpTics will
be set to 7. Otherwise, jumpTics is left alone.
* If jumpTics is positive, it will count down each tic.
* As long as JumpTics is non-zero, you cannot jump.
SVN r2970 (trunk)
2010-11-03 02:07:56 +00:00
|
|
|
if (player->crouchoffset != 0)
|
2006-05-14 14:30:13 +00:00
|
|
|
{
|
|
|
|
// Jumping while crouching will force an un-crouch but not jump
|
|
|
|
player->crouching = 1;
|
|
|
|
}
|
2013-04-09 21:57:48 +00:00
|
|
|
else if (player->mo->waterlevel >= 2)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2013-04-09 21:57:48 +00:00
|
|
|
player->mo->velz = FixedMul(4*FRACUNIT, player->mo->Speed);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
else if (player->mo->flags & MF_NOGRAVITY)
|
|
|
|
{
|
2009-06-30 20:57:51 +00:00
|
|
|
player->mo->velz = 3*FRACUNIT;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
2014-03-02 12:00:47 +00:00
|
|
|
else if (level.IsJumpingAllowed() && player->onground && player->jumpTics == 0)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2009-06-30 20:57:51 +00:00
|
|
|
fixed_t jumpvelz = player->mo->JumpZ * 35 / TICRATE;
|
2007-05-10 22:22:38 +00:00
|
|
|
|
|
|
|
// [BC] If the player has the high jump power, double his jump velocity.
|
2009-06-30 20:57:51 +00:00
|
|
|
if ( player->cheats & CF_HIGHJUMP ) jumpvelz *= 2;
|
2007-05-10 22:22:38 +00:00
|
|
|
|
2009-06-30 20:57:51 +00:00
|
|
|
player->mo->velz += jumpvelz;
|
2006-02-24 04:48:15 +00:00
|
|
|
player->mo->flags2 &= ~MF2_ONMOBJ;
|
- Revised usage of jumpTics. In Hexen, it went like this:
* When you jump, it gets set to 18.
* When you land, it gets set to 7.
* As long as it is non-zero, it counts down, and you cannot jump.
Of note here, is that setting it to 18 upon jumping seems useless, since you can't jump unless
you're on the ground, and when you reach the ground, it will always be set to 7. With that in
mind, the new behavior is:
* When you jump, it gets set to -1.
* When you land, if it is less than zero or you fall far enough to squat, jumpTics will
be set to 7. Otherwise, jumpTics is left alone.
* If jumpTics is positive, it will count down each tic.
* As long as JumpTics is non-zero, you cannot jump.
SVN r2970 (trunk)
2010-11-03 02:07:56 +00:00
|
|
|
player->jumpTics = -1;
|
2013-12-16 10:02:27 +00:00
|
|
|
if (!(player->cheats & CF_PREDICTING))
|
|
|
|
S_Sound(player->mo, CHAN_BODY, "*jump", 1, ATTN_NORM);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cmd->ucmd.upmove == -32768)
|
|
|
|
{ // Only land if in the air
|
|
|
|
if ((player->mo->flags & MF_NOGRAVITY) && player->mo->waterlevel < 2)
|
|
|
|
{
|
|
|
|
//player->mo->flags2 &= ~MF2_FLY;
|
|
|
|
player->mo->flags &= ~MF_NOGRAVITY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (cmd->ucmd.upmove != 0)
|
|
|
|
{
|
|
|
|
// Clamp the speed to some reasonable maximum.
|
|
|
|
int magnitude = abs (cmd->ucmd.upmove);
|
|
|
|
if (magnitude > 0x300)
|
|
|
|
{
|
|
|
|
cmd->ucmd.upmove = ksgn (cmd->ucmd.upmove) * 0x300;
|
|
|
|
}
|
2012-08-22 21:31:48 +00:00
|
|
|
if (player->mo->waterlevel >= 2 || (player->mo->flags2 & MF2_FLY) || (player->cheats & CF_NOCLIP2))
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2013-03-21 03:47:39 +00:00
|
|
|
player->mo->velz = FixedMul(player->mo->Speed, cmd->ucmd.upmove << 9);
|
2006-02-24 04:48:15 +00:00
|
|
|
if (player->mo->waterlevel < 2 && !(player->mo->flags & MF_NOGRAVITY))
|
|
|
|
{
|
|
|
|
player->mo->flags2 |= MF2_FLY;
|
|
|
|
player->mo->flags |= MF_NOGRAVITY;
|
2013-12-16 10:02:27 +00:00
|
|
|
if ((player->mo->velz <= -39 * FRACUNIT) && !(player->cheats & CF_PREDICTING))
|
2006-02-24 04:48:15 +00:00
|
|
|
{ // Stop falling scream
|
|
|
|
S_StopSound (player->mo, CHAN_VOICE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (cmd->ucmd.upmove > 0 && !(player->cheats & CF_PREDICTING))
|
|
|
|
{
|
2006-12-25 13:43:11 +00:00
|
|
|
AInventory *fly = player->mo->FindInventory (NAME_ArtiFly);
|
2006-02-24 04:48:15 +00:00
|
|
|
if (fly != NULL)
|
|
|
|
{
|
|
|
|
player->mo->UseInventory (fly);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
P_CalcHeight (player);
|
|
|
|
|
|
|
|
if (!(player->cheats & CF_PREDICTING))
|
|
|
|
{
|
2009-01-04 15:00:29 +00:00
|
|
|
P_PlayerOnSpecial3DFloor (player);
|
2006-02-24 04:48:15 +00:00
|
|
|
if (player->mo->Sector->special || player->mo->Sector->damage)
|
|
|
|
{
|
|
|
|
P_PlayerInSpecialSector (player);
|
|
|
|
}
|
|
|
|
P_PlayerOnSpecialFlat (player, P_GetThingFloorType (player->mo));
|
2012-08-14 03:24:59 +00:00
|
|
|
if (player->mo->velz <= -player->mo->FallingScreamMinSpeed &&
|
|
|
|
player->mo->velz >= -player->mo->FallingScreamMaxSpeed && !player->morphTics &&
|
2006-05-16 17:53:15 +00:00
|
|
|
player->mo->waterlevel == 0)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
int id = S_FindSkinnedSound (player->mo, "*falling");
|
2006-12-24 23:08:49 +00:00
|
|
|
if (id != 0 && !S_IsActorPlayingSomething (player->mo, CHAN_VOICE, id))
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2008-06-15 02:25:09 +00:00
|
|
|
S_Sound (player->mo, CHAN_VOICE, id, 1, ATTN_NORM);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// check for use
|
2009-12-25 16:22:57 +00:00
|
|
|
if (cmd->ucmd.buttons & BT_USE)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2009-12-25 16:22:57 +00:00
|
|
|
if (!player->usedown)
|
|
|
|
{
|
|
|
|
player->usedown = true;
|
2010-01-06 04:36:38 +00:00
|
|
|
if (!P_TalkFacing(player->mo))
|
|
|
|
{
|
|
|
|
P_UseLines(player);
|
|
|
|
}
|
2009-12-25 16:22:57 +00:00
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
2009-12-25 11:09:56 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
player->usedown = false;
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
// Morph counter
|
|
|
|
if (player->morphTics)
|
|
|
|
{
|
|
|
|
if (player->chickenPeck)
|
|
|
|
{ // Chicken attack counter
|
|
|
|
player->chickenPeck -= 3;
|
|
|
|
}
|
|
|
|
if (!--player->morphTics)
|
|
|
|
{ // Attempt to undo the chicken/pig
|
2009-12-28 22:41:14 +00:00
|
|
|
P_UndoPlayerMorph (player, player, MORPH_UNDOBYTIMEOUT);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// Cycle psprites
|
|
|
|
P_MovePsprites (player);
|
|
|
|
|
|
|
|
// Other Counters
|
|
|
|
if (player->damagecount)
|
|
|
|
player->damagecount--;
|
|
|
|
|
|
|
|
if (player->bonuscount)
|
|
|
|
player->bonuscount--;
|
|
|
|
|
|
|
|
if (player->hazardcount)
|
|
|
|
{
|
|
|
|
player->hazardcount--;
|
|
|
|
if (!(level.time & 31) && player->hazardcount > 16*TICRATE)
|
2006-10-31 14:53:21 +00:00
|
|
|
P_DamageMobj (player->mo, NULL, NULL, 5, NAME_Slime);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (player->poisoncount && !(level.time & 15))
|
|
|
|
{
|
|
|
|
player->poisoncount -= 5;
|
|
|
|
if (player->poisoncount < 0)
|
|
|
|
{
|
|
|
|
player->poisoncount = 0;
|
|
|
|
}
|
|
|
|
P_PoisonDamage (player, player->poisoner, 1, true);
|
|
|
|
}
|
|
|
|
|
2007-12-23 17:03:52 +00:00
|
|
|
// Apply degeneration.
|
|
|
|
if (dmflags2 & DF2_YES_DEGENERATION)
|
|
|
|
{
|
2008-01-09 02:53:38 +00:00
|
|
|
if ((level.time % TICRATE) == 0 && player->health > deh.MaxHealth)
|
2007-12-23 17:03:52 +00:00
|
|
|
{
|
|
|
|
if (player->health - 5 < deh.MaxHealth)
|
|
|
|
player->health = deh.MaxHealth;
|
|
|
|
else
|
|
|
|
player->health--;
|
|
|
|
|
|
|
|
player->mo->health = player->health;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
// Handle air supply
|
2009-05-30 08:56:40 +00:00
|
|
|
//if (level.airsupply > 0)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
if (player->mo->waterlevel < 3 ||
|
|
|
|
(player->mo->flags2 & MF2_INVULNERABLE) ||
|
2012-08-22 21:31:48 +00:00
|
|
|
(player->cheats & (CF_GODMODE | CF_NOCLIP2)))
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2007-02-04 00:22:56 +00:00
|
|
|
player->mo->ResetAirSupply ();
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
else if (player->air_finished <= level.time && !(level.time & 31))
|
|
|
|
{
|
2007-02-04 00:22:56 +00:00
|
|
|
P_DamageMobj (player->mo, NULL, NULL, 2 + ((level.time-player->air_finished)/TICRATE), NAME_Drowning);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-12 11:29:15 +00:00
|
|
|
void P_PredictionLerpReset()
|
|
|
|
{
|
|
|
|
PredictionLerptics = PredictionLast.gametic = PredictionLerpFrom.gametic = PredictionLerpResult.gametic = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool P_LerpCalculate(FVector3 from, FVector3 to, FVector3 &result, float scale)
|
|
|
|
{
|
|
|
|
result = to - from;
|
|
|
|
result *= scale;
|
|
|
|
result = result + from;
|
|
|
|
FVector3 delta = result - to;
|
|
|
|
|
2014-10-13 03:32:49 +00:00
|
|
|
// As a fail safe, assume extrapolation is the threshold.
|
|
|
|
return (delta.LengthSquared() > cl_predict_lerpthreshold && scale <= 1.00f);
|
2014-10-12 11:29:15 +00:00
|
|
|
}
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
void P_PredictPlayer (player_t *player)
|
|
|
|
{
|
|
|
|
int maxtic;
|
|
|
|
|
|
|
|
if (cl_noprediction ||
|
|
|
|
singletics ||
|
|
|
|
demoplayback ||
|
|
|
|
player->mo == NULL ||
|
|
|
|
player != &players[consoleplayer] ||
|
|
|
|
player->playerstate != PST_LIVE ||
|
|
|
|
!netgame ||
|
|
|
|
/*player->morphTics ||*/
|
|
|
|
(player->cheats & CF_PREDICTING))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
maxtic = maketic;
|
|
|
|
|
|
|
|
if (gametic == maxtic)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save original values for restoration later
|
|
|
|
PredictionPlayerBackup = *player;
|
|
|
|
|
2014-10-06 02:27:13 +00:00
|
|
|
APlayerPawn *act = player->mo;
|
|
|
|
memcpy(PredictionActorBackup, &act->x, sizeof(APlayerPawn) - ((BYTE *)&act->x - (BYTE *)act));
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
act->flags &= ~MF_PICKUP;
|
|
|
|
act->flags2 &= ~MF2_PUSHWALL;
|
|
|
|
player->cheats |= CF_PREDICTING;
|
|
|
|
|
|
|
|
// The ordering of the touching_sectorlist needs to remain unchanged
|
2014-04-01 10:05:08 +00:00
|
|
|
// Also store a copy of all previous sector_thinglist nodes
|
2006-02-24 04:48:15 +00:00
|
|
|
msecnode_t *mnode = act->touching_sectorlist;
|
2014-04-01 10:05:08 +00:00
|
|
|
msecnode_t *snode;
|
|
|
|
PredictionSector_sprev_Backup.Clear();
|
2006-02-24 04:48:15 +00:00
|
|
|
PredictionTouchingSectorsBackup.Clear ();
|
|
|
|
|
|
|
|
while (mnode != NULL)
|
|
|
|
{
|
|
|
|
PredictionTouchingSectorsBackup.Push (mnode->m_sector);
|
2014-04-01 10:05:08 +00:00
|
|
|
|
|
|
|
for (snode = mnode->m_sector->touching_thinglist; snode; snode = snode->m_snext)
|
|
|
|
{
|
|
|
|
if (snode->m_thing == act)
|
|
|
|
{
|
|
|
|
PredictionSector_sprev_Backup.Push(snode->m_sprev);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
mnode = mnode->m_tnext;
|
|
|
|
}
|
|
|
|
|
2014-04-01 10:05:08 +00:00
|
|
|
// Keep an ordered list off all actors in the linked sector.
|
|
|
|
PredictionSectorListBackup.Clear();
|
|
|
|
if (!(act->flags & MF_NOSECTOR))
|
|
|
|
{
|
|
|
|
AActor *link = act->Sector->thinglist;
|
|
|
|
|
|
|
|
while (link != NULL)
|
|
|
|
{
|
|
|
|
PredictionSectorListBackup.Push(link);
|
|
|
|
link = link->snext;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
// Blockmap ordering also needs to stay the same, so unlink the block nodes
|
|
|
|
// without releasing them. (They will be used again in P_UnpredictPlayer).
|
|
|
|
FBlockNode *block = act->BlockNode;
|
|
|
|
|
|
|
|
while (block != NULL)
|
|
|
|
{
|
|
|
|
if (block->NextActor != NULL)
|
|
|
|
{
|
|
|
|
block->NextActor->PrevActor = block->PrevActor;
|
|
|
|
}
|
|
|
|
*(block->PrevActor) = block->NextActor;
|
|
|
|
block = block->NextBlock;
|
|
|
|
}
|
|
|
|
act->BlockNode = NULL;
|
|
|
|
|
2014-10-13 03:32:49 +00:00
|
|
|
// Values too small to be usable for lerping can be considered "off".
|
2014-10-13 04:09:29 +00:00
|
|
|
bool CanLerp = (!(cl_predict_lerpscale < 0.01f) && (ticdup == 1)), DoLerp = false, NoInterpolateOld = R_GetViewInterpolationStatus();
|
2006-02-24 04:48:15 +00:00
|
|
|
for (int i = gametic; i < maxtic; ++i)
|
|
|
|
{
|
2014-08-23 03:17:11 +00:00
|
|
|
if (!NoInterpolateOld)
|
|
|
|
R_RebuildViewInterpolation(player);
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
player->cmd = localcmds[i % LOCALCMDTICS];
|
|
|
|
P_PlayerThink (player);
|
|
|
|
player->mo->Tick ();
|
2014-10-06 02:27:13 +00:00
|
|
|
|
2014-10-12 11:29:15 +00:00
|
|
|
if (CanLerp && PredictionLast.gametic > 0 && i == PredictionLast.gametic && !NoInterpolateOld)
|
2014-10-06 02:27:13 +00:00
|
|
|
{
|
2014-10-12 11:29:15 +00:00
|
|
|
// Z is not compared as lifts will alter this with no apparent change
|
|
|
|
DoLerp = (PredictionLast.point.X != FIXED2FLOAT(player->mo->x) ||
|
|
|
|
PredictionLast.point.Y != FIXED2FLOAT(player->mo->y));
|
2014-10-06 02:27:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-12 11:29:15 +00:00
|
|
|
if (CanLerp)
|
2014-10-06 02:27:13 +00:00
|
|
|
{
|
2014-10-13 03:32:49 +00:00
|
|
|
if (NoInterpolateOld)
|
|
|
|
P_PredictionLerpReset();
|
|
|
|
|
|
|
|
else if (DoLerp)
|
2014-10-12 11:29:15 +00:00
|
|
|
{
|
|
|
|
// If lerping is already in effect, use the previous camera postion so the view doesn't suddenly snap
|
|
|
|
PredictionLerpFrom = (PredictionLerptics == 0) ? PredictionLast : PredictionLerpResult;
|
|
|
|
PredictionLerptics = 1;
|
|
|
|
}
|
2014-10-06 02:27:13 +00:00
|
|
|
|
2014-10-12 11:29:15 +00:00
|
|
|
PredictionLast.gametic = maxtic - 1;
|
|
|
|
PredictionLast.point.X = FIXED2FLOAT(player->mo->x);
|
|
|
|
PredictionLast.point.Y = FIXED2FLOAT(player->mo->y);
|
|
|
|
PredictionLast.point.Z = FIXED2FLOAT(player->mo->z);
|
2014-10-06 02:27:13 +00:00
|
|
|
|
2014-10-12 11:29:15 +00:00
|
|
|
if (PredictionLerptics > 0)
|
|
|
|
{
|
|
|
|
if (PredictionLerpFrom.gametic > 0 &&
|
|
|
|
P_LerpCalculate(PredictionLerpFrom.point, PredictionLast.point, PredictionLerpResult.point, (float)PredictionLerptics * cl_predict_lerpscale))
|
|
|
|
{
|
|
|
|
PredictionLerptics++;
|
|
|
|
player->mo->x = FLOAT2FIXED(PredictionLerpResult.point.X);
|
|
|
|
player->mo->y = FLOAT2FIXED(PredictionLerpResult.point.Y);
|
|
|
|
player->mo->z = FLOAT2FIXED(PredictionLerpResult.point.Z);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
PredictionLerptics = 0;
|
|
|
|
}
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
extern msecnode_t *P_AddSecnode (sector_t *s, AActor *thing, msecnode_t *nextnode);
|
|
|
|
|
|
|
|
void P_UnPredictPlayer ()
|
|
|
|
{
|
|
|
|
player_t *player = &players[consoleplayer];
|
|
|
|
|
|
|
|
if (player->cheats & CF_PREDICTING)
|
|
|
|
{
|
2014-04-01 10:05:08 +00:00
|
|
|
unsigned int i;
|
2014-10-05 03:57:31 +00:00
|
|
|
APlayerPawn *act = player->mo;
|
2013-02-12 23:23:33 +00:00
|
|
|
AActor *savedcamera = player->camera;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2014-10-05 03:57:31 +00:00
|
|
|
TObjPtr<AInventory> InvSel = act->InvSel;
|
|
|
|
int inventorytics = player->inventorytics;
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
*player = PredictionPlayerBackup;
|
|
|
|
|
2013-02-12 23:23:33 +00:00
|
|
|
// Restore the camera instead of using the backup's copy, because spynext/prev
|
|
|
|
// could cause it to change during prediction.
|
|
|
|
player->camera = savedcamera;
|
|
|
|
|
2014-04-01 10:05:08 +00:00
|
|
|
act->UnlinkFromWorld();
|
2014-10-06 02:27:13 +00:00
|
|
|
memcpy(&act->x, PredictionActorBackup, sizeof(APlayerPawn) - ((BYTE *)&act->x - (BYTE *)act));
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2014-06-15 15:19:05 +00:00
|
|
|
// The blockmap ordering needs to remain unchanged, too.
|
|
|
|
// Restore sector links and refrences.
|
|
|
|
// [ED850] This is somewhat of a duplicate of LinkToWorld(), but we need to keep every thing the same,
|
|
|
|
// otherwise we end up fixing bugs in blockmap logic (i.e undefined behaviour with polyobject collisions),
|
|
|
|
// which we really don't want to do here.
|
2014-04-01 10:05:08 +00:00
|
|
|
if (!(act->flags & MF_NOSECTOR))
|
|
|
|
{
|
|
|
|
sector_t *sec = act->Sector;
|
|
|
|
AActor *me, *next;
|
|
|
|
AActor **link;// , **prev;
|
|
|
|
|
|
|
|
// The thinglist is just a pointer chain. We are restoring the exact same things, so we can NULL the head safely
|
|
|
|
sec->thinglist = NULL;
|
|
|
|
|
|
|
|
for (i = PredictionSectorListBackup.Size(); i-- > 0;)
|
|
|
|
{
|
|
|
|
me = PredictionSectorListBackup[i];
|
|
|
|
link = &sec->thinglist;
|
|
|
|
next = *link;
|
|
|
|
if ((me->snext = next))
|
|
|
|
next->sprev = &me->snext;
|
|
|
|
me->sprev = link;
|
|
|
|
*link = me;
|
|
|
|
}
|
|
|
|
|
2014-06-15 15:19:05 +00:00
|
|
|
// Destroy old refrences
|
|
|
|
msecnode_t *node = sector_list;
|
|
|
|
while (node)
|
|
|
|
{
|
|
|
|
node->m_thing = NULL;
|
|
|
|
node = node->m_tnext;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make the sector_list match the player's touching_sectorlist before it got predicted.
|
|
|
|
P_DelSeclist(sector_list);
|
|
|
|
sector_list = NULL;
|
|
|
|
for (i = PredictionTouchingSectorsBackup.Size(); i-- > 0;)
|
|
|
|
{
|
|
|
|
sector_list = P_AddSecnode(PredictionTouchingSectorsBackup[i], act, sector_list);
|
|
|
|
}
|
|
|
|
act->touching_sectorlist = sector_list; // Attach to thing
|
|
|
|
sector_list = NULL; // clear for next time
|
|
|
|
|
|
|
|
node = sector_list;
|
|
|
|
while (node)
|
|
|
|
{
|
|
|
|
if (node->m_thing == NULL)
|
|
|
|
{
|
|
|
|
if (node == sector_list)
|
|
|
|
sector_list = node->m_tnext;
|
|
|
|
node = P_DelSecnode(node);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
node = node->m_tnext;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-02 08:12:10 +00:00
|
|
|
msecnode_t *snode;
|
|
|
|
|
2014-04-01 10:05:08 +00:00
|
|
|
// Restore sector thinglist order
|
|
|
|
for (i = PredictionTouchingSectorsBackup.Size(); i-- > 0;)
|
|
|
|
{
|
2014-04-02 08:12:10 +00:00
|
|
|
// If we were already the head node, then nothing needs to change
|
|
|
|
if (PredictionSector_sprev_Backup[i] == NULL)
|
|
|
|
continue;
|
2014-04-01 10:05:08 +00:00
|
|
|
|
|
|
|
for (snode = PredictionTouchingSectorsBackup[i]->touching_thinglist; snode; snode = snode->m_snext)
|
|
|
|
{
|
2014-04-02 08:12:10 +00:00
|
|
|
if (snode->m_thing == act)
|
2014-04-01 10:05:08 +00:00
|
|
|
{
|
|
|
|
if (snode->m_sprev)
|
|
|
|
snode->m_sprev->m_snext = snode->m_snext;
|
|
|
|
else
|
|
|
|
snode->m_sector->touching_thinglist = snode->m_snext;
|
|
|
|
if (snode->m_snext)
|
|
|
|
snode->m_snext->m_sprev = snode->m_sprev;
|
|
|
|
|
|
|
|
snode->m_sprev = PredictionSector_sprev_Backup[i];
|
|
|
|
|
|
|
|
// At the moment, we don't exist in the list anymore, but we do know what our previous node is, so we set its current m_snext->m_sprev to us.
|
|
|
|
if (snode->m_sprev->m_snext)
|
|
|
|
snode->m_sprev->m_snext->m_sprev = snode;
|
|
|
|
snode->m_snext = snode->m_sprev->m_snext;
|
|
|
|
snode->m_sprev->m_snext = snode;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
// Now fix the pointers in the blocknode chain
|
|
|
|
FBlockNode *block = act->BlockNode;
|
|
|
|
|
|
|
|
while (block != NULL)
|
|
|
|
{
|
|
|
|
*(block->PrevActor) = block;
|
|
|
|
if (block->NextActor != NULL)
|
|
|
|
{
|
|
|
|
block->NextActor->PrevActor = &block->NextActor;
|
|
|
|
}
|
|
|
|
block = block->NextBlock;
|
|
|
|
}
|
2014-10-05 03:57:31 +00:00
|
|
|
|
|
|
|
act->InvSel = InvSel;
|
|
|
|
player->inventorytics = inventorytics;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-06-01 07:52:33 +00:00
|
|
|
void player_t::Serialize (FArchive &arc)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
int i;
|
2014-02-24 22:27:57 +00:00
|
|
|
FString skinname;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
- 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
|
|
|
arc << cls
|
|
|
|
<< mo
|
2006-02-24 04:48:15 +00:00
|
|
|
<< camera
|
|
|
|
<< playerstate
|
2014-02-24 22:27:57 +00:00
|
|
|
<< cmd;
|
|
|
|
if (arc.IsLoading())
|
|
|
|
{
|
|
|
|
ReadUserInfo(arc, userinfo, skinname);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
WriteUserInfo(arc, userinfo);
|
|
|
|
}
|
|
|
|
arc << DesiredFOV << FOV
|
2006-02-24 04:48:15 +00:00
|
|
|
<< viewz
|
|
|
|
<< viewheight
|
|
|
|
<< deltaviewheight
|
|
|
|
<< bob
|
2009-06-30 20:57:51 +00:00
|
|
|
<< velx
|
|
|
|
<< vely
|
2006-02-24 04:48:15 +00:00
|
|
|
<< centering
|
|
|
|
<< health
|
2014-10-13 07:45:36 +00:00
|
|
|
<< inventorytics;
|
|
|
|
if (SaveVersion < 4513)
|
|
|
|
{
|
|
|
|
bool backpack;
|
|
|
|
arc << backpack;
|
|
|
|
}
|
|
|
|
arc << fragcount
|
2006-02-24 04:48:15 +00:00
|
|
|
<< spreecount
|
|
|
|
<< multicount
|
|
|
|
<< lastkilltime
|
|
|
|
<< ReadyWeapon << PendingWeapon
|
|
|
|
<< cheats
|
|
|
|
<< refire
|
|
|
|
<< inconsistant
|
|
|
|
<< killcount
|
|
|
|
<< itemcount
|
|
|
|
<< secretcount
|
|
|
|
<< damagecount
|
|
|
|
<< bonuscount
|
|
|
|
<< hazardcount
|
|
|
|
<< poisoncount
|
|
|
|
<< poisoner
|
|
|
|
<< attacker
|
2010-12-12 15:43:35 +00:00
|
|
|
<< extralight
|
|
|
|
<< fixedcolormap << fixedlightlevel
|
|
|
|
<< morphTics
|
2008-04-06 17:33:43 +00:00
|
|
|
<< MorphedPlayerClass
|
2008-04-08 08:53:42 +00:00
|
|
|
<< MorphStyle
|
|
|
|
<< MorphExitFlash
|
2006-02-24 04:48:15 +00:00
|
|
|
<< PremorphWeapon
|
|
|
|
<< chickenPeck
|
|
|
|
<< jumpTics
|
|
|
|
<< respawn_time
|
|
|
|
<< air_finished
|
|
|
|
<< turnticks
|
|
|
|
<< oldbuttons
|
|
|
|
<< isbot
|
|
|
|
<< BlendR
|
|
|
|
<< BlendG
|
|
|
|
<< BlendB
|
2012-03-13 02:43:24 +00:00
|
|
|
<< BlendA;
|
|
|
|
if (SaveVersion < 3427)
|
|
|
|
{
|
|
|
|
WORD oldaccuracy, oldstamina;
|
|
|
|
arc << oldaccuracy << oldstamina;
|
|
|
|
if (mo != NULL)
|
|
|
|
{
|
|
|
|
mo->accuracy = oldaccuracy;
|
|
|
|
mo->stamina = oldstamina;
|
|
|
|
}
|
|
|
|
}
|
2013-01-25 03:09:17 +00:00
|
|
|
if (SaveVersion < 4041)
|
|
|
|
{
|
|
|
|
// Move weapon state flags from cheats and into WeaponState.
|
|
|
|
WeaponState = ((cheats >> 14) & 1) | ((cheats & (0x37 << 24)) >> (24 - 1));
|
|
|
|
cheats &= ~((1 << 14) | (0x37 << 24));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
arc << WeaponState;
|
|
|
|
}
|
2012-03-13 02:43:24 +00:00
|
|
|
arc << LogText
|
2008-03-26 08:29:02 +00:00
|
|
|
<< ConversationNPC
|
|
|
|
<< ConversationPC
|
|
|
|
<< ConversationNPCAngle
|
|
|
|
<< ConversationFaceTalker;
|
2009-09-20 03:50:05 +00:00
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
|
|
arc << frags[i];
|
|
|
|
for (i = 0; i < NUMPSPRITES; i++)
|
|
|
|
arc << psprites[i];
|
|
|
|
|
|
|
|
arc << CurrentPlayerClass;
|
|
|
|
|
2006-05-14 14:30:13 +00:00
|
|
|
arc << crouchfactor
|
|
|
|
<< crouching
|
|
|
|
<< crouchdir
|
2008-09-13 02:55:45 +00:00
|
|
|
<< crouchviewdelta
|
|
|
|
<< original_cmd
|
|
|
|
<< original_oldbuttons;
|
2006-05-14 14:30:13 +00:00
|
|
|
|
2012-03-23 20:57:10 +00:00
|
|
|
if (SaveVersion >= 3475)
|
|
|
|
{
|
|
|
|
arc << poisontype << poisonpaintype;
|
|
|
|
}
|
|
|
|
else if (poisoner != NULL)
|
|
|
|
{
|
|
|
|
poisontype = poisoner->DamageType;
|
|
|
|
poisonpaintype = poisoner->PainType != NAME_None ? poisoner->PainType : poisoner->DamageType;
|
|
|
|
}
|
|
|
|
|
2012-04-27 01:40:50 +00:00
|
|
|
if (SaveVersion >= 3599)
|
|
|
|
{
|
|
|
|
arc << timefreezer;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cheats &= ~(1 << 15); // make sure old CF_TIMEFREEZE bit is cleared
|
|
|
|
}
|
2012-05-11 04:05:55 +00:00
|
|
|
if (SaveVersion < 3640)
|
|
|
|
{
|
|
|
|
cheats &= ~(1 << 17); // make sure old CF_REGENERATION bit is cleared
|
|
|
|
}
|
2012-07-21 03:04:59 +00:00
|
|
|
if (SaveVersion >= 3780)
|
|
|
|
{
|
|
|
|
arc << settings_controller;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
settings_controller = (this - players == Net_Arbitrator);
|
|
|
|
}
|
2014-03-02 12:00:47 +00:00
|
|
|
if (SaveVersion >= 4505)
|
|
|
|
{
|
|
|
|
arc << onground;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
onground = (mo->z <= mo->floorz) || (mo->flags2 & MF2_ONMOBJ) || (mo->BounceFlags & BOUNCE_MBF) || (cheats & CF_NOCLIP2);
|
|
|
|
}
|
2012-04-27 01:40:50 +00:00
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
if (isbot)
|
|
|
|
{
|
|
|
|
arc << angle
|
|
|
|
<< dest
|
|
|
|
<< prev
|
|
|
|
<< enemy
|
|
|
|
<< missile
|
|
|
|
<< mate
|
|
|
|
<< last_mate
|
|
|
|
<< skill
|
|
|
|
<< t_active
|
|
|
|
<< t_respawn
|
|
|
|
<< t_strafe
|
|
|
|
<< t_react
|
|
|
|
<< t_fight
|
|
|
|
<< t_roam
|
|
|
|
<< t_rocket
|
|
|
|
<< first_shot
|
|
|
|
<< sleft
|
|
|
|
<< allround
|
|
|
|
<< oldx
|
|
|
|
<< oldy;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dest = prev = enemy = missile = mate = last_mate = NULL;
|
|
|
|
}
|
|
|
|
if (arc.IsLoading ())
|
|
|
|
{
|
|
|
|
// If the player reloaded because they pressed +use after dying, we
|
|
|
|
// don't want +use to still be down after the game is loaded.
|
|
|
|
oldbuttons = ~0;
|
2008-09-13 02:55:45 +00:00
|
|
|
original_oldbuttons = ~0;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
2014-02-24 22:27:57 +00:00
|
|
|
if (skinname.IsNotEmpty())
|
|
|
|
{
|
|
|
|
userinfo.SkinChanged(skinname, CurrentPlayerClass);
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
2006-04-11 16:27:41 +00:00
|
|
|
|
2010-03-06 02:51:23 +00:00
|
|
|
|
2010-03-07 09:13:41 +00:00
|
|
|
static FPlayerColorSetMap *GetPlayerColors(FName classname)
|
2010-03-06 02:51:23 +00:00
|
|
|
{
|
2010-03-07 09:13:41 +00:00
|
|
|
const PClass *cls = PClass::FindClass(classname);
|
2010-03-06 02:51:23 +00:00
|
|
|
|
2010-03-07 09:13:41 +00:00
|
|
|
if (cls != NULL)
|
2010-03-06 02:51:23 +00:00
|
|
|
{
|
2010-03-07 09:13:41 +00:00
|
|
|
FActorInfo *inf = cls->ActorInfo;
|
|
|
|
|
|
|
|
if (inf != NULL)
|
2010-03-06 02:51:23 +00:00
|
|
|
{
|
2010-03-07 09:13:41 +00:00
|
|
|
return inf->ColorSets;
|
2010-03-06 02:51:23 +00:00
|
|
|
}
|
|
|
|
}
|
2010-03-07 09:13:41 +00:00
|
|
|
return NULL;
|
2010-03-06 02:51:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
FPlayerColorSet *P_GetPlayerColorSet(FName classname, int setnum)
|
|
|
|
{
|
2010-03-07 09:13:41 +00:00
|
|
|
FPlayerColorSetMap *map = GetPlayerColors(classname);
|
2010-03-06 02:51:23 +00:00
|
|
|
if (map == NULL)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return map->CheckKey(setnum);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int STACK_ARGS intcmp(const void *a, const void *b)
|
|
|
|
{
|
|
|
|
return *(const int *)a - *(const int *)b;
|
|
|
|
}
|
|
|
|
|
|
|
|
void P_EnumPlayerColorSets(FName classname, TArray<int> *out)
|
|
|
|
{
|
|
|
|
out->Clear();
|
2010-03-07 09:13:41 +00:00
|
|
|
FPlayerColorSetMap *map = GetPlayerColors(classname);
|
2010-03-06 02:51:23 +00:00
|
|
|
if (map != NULL)
|
|
|
|
{
|
|
|
|
FPlayerColorSetMap::Iterator it(*map);
|
|
|
|
FPlayerColorSetMap::Pair *pair;
|
|
|
|
|
|
|
|
while (it.NextPair(pair))
|
|
|
|
{
|
|
|
|
out->Push(pair->Key);
|
|
|
|
}
|
|
|
|
qsort(&(*out)[0], out->Size(), sizeof(int), intcmp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-22 00:31:42 +00:00
|
|
|
bool P_IsPlayerTotallyFrozen(const player_t *player)
|
|
|
|
{
|
|
|
|
return
|
|
|
|
gamestate == GS_TITLELEVEL ||
|
|
|
|
player->cheats & CF_TOTALLYFROZEN ||
|
2012-04-27 01:40:50 +00:00
|
|
|
((level.flags2 & LEVEL2_FROZEN) && player->timefreezer == 0);
|
2012-01-22 00:31:42 +00:00
|
|
|
}
|