929 lines
23 KiB
C++
929 lines
23 KiB
C++
/***
|
|
*
|
|
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
|
*
|
|
* This product contains software technology licensed from Id
|
|
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
|
* All Rights Reserved.
|
|
*
|
|
* Use, distribution, and modification of this source code and/or resulting
|
|
* object code is restricted to non-commercial enhancements to products from
|
|
* Valve LLC. All other use, distribution, or modification is prohibited
|
|
* without written permission from Valve LLC.
|
|
*
|
|
****/
|
|
#include "extdll.h"
|
|
#include "util.h"
|
|
#include "cbase.h"
|
|
#include "monsters.h"
|
|
#include "weapons.h"
|
|
#include "player.h"
|
|
|
|
#include "usercmd.h"
|
|
#include "entity_state.h"
|
|
#include "demo_api.h"
|
|
#include "pm_defs.h"
|
|
#include "event_api.h"
|
|
#include "r_efx.h"
|
|
|
|
#include "cl_dll.h"
|
|
#include "../com_weapons.h"
|
|
#include "../demo.h"
|
|
|
|
extern int g_iUser1;
|
|
|
|
// Pool of client side entities/entvars_t
|
|
static entvars_t ev[MAX_WEAPONS + 1];
|
|
static int num_ents = 0;
|
|
|
|
// The entity we'll use to represent the local client
|
|
static CBasePlayer player;
|
|
|
|
// Local version of game .dll global variables ( time, etc. )
|
|
static globalvars_t Globals;
|
|
|
|
static CBasePlayerWeapon* g_pWpns[MAX_WEAPONS];
|
|
|
|
float g_flApplyVel = 0.0;
|
|
bool g_irunninggausspred = false;
|
|
|
|
Vector previousorigin;
|
|
|
|
// HLDM Weapon placeholder entities.
|
|
CGlock g_Glock;
|
|
CCrowbar g_Crowbar;
|
|
CPython g_Python;
|
|
CMP5 g_Mp5;
|
|
CCrossbow g_Crossbow;
|
|
CShotgun g_Shotgun;
|
|
CRpg g_Rpg;
|
|
CGauss g_Gauss;
|
|
CEgon g_Egon;
|
|
CHgun g_HGun;
|
|
CHandGrenade g_HandGren;
|
|
CSatchel g_Satchel;
|
|
CTripmine g_Tripmine;
|
|
CSqueak g_Snark;
|
|
|
|
|
|
/*
|
|
======================
|
|
AlertMessage
|
|
|
|
Print debug messages to console
|
|
======================
|
|
*/
|
|
void AlertMessage(ALERT_TYPE atype, const char* szFmt, ...)
|
|
{
|
|
va_list argptr;
|
|
static char string[1024];
|
|
|
|
va_start(argptr, szFmt);
|
|
vsprintf(string, szFmt, argptr);
|
|
va_end(argptr);
|
|
|
|
gEngfuncs.Con_Printf("cl: ");
|
|
gEngfuncs.Con_Printf(string);
|
|
}
|
|
|
|
//Returns if it's multiplayer.
|
|
//Mostly used by the client side weapons.
|
|
bool bIsMultiplayer()
|
|
{
|
|
return gEngfuncs.GetMaxClients() != 1;
|
|
}
|
|
//Just loads a v_ model.
|
|
void LoadVModel(const char* szViewModel, CBasePlayer* m_pPlayer)
|
|
{
|
|
gEngfuncs.CL_LoadModel(szViewModel, &m_pPlayer->pev->viewmodel);
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
HUD_PrepEntity
|
|
|
|
Links the raw entity to an entvars_s holder. If a player is passed in as the owner, then
|
|
we set up the m_pPlayer field.
|
|
=====================
|
|
*/
|
|
void HUD_PrepEntity(CBaseEntity* pEntity, CBasePlayer* pWeaponOwner)
|
|
{
|
|
memset(&ev[num_ents], 0, sizeof(entvars_t));
|
|
pEntity->pev = &ev[num_ents++];
|
|
|
|
pEntity->Precache();
|
|
pEntity->Spawn();
|
|
|
|
if (pWeaponOwner)
|
|
{
|
|
ItemInfo info;
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
|
|
((CBasePlayerWeapon*)pEntity)->m_pPlayer = pWeaponOwner;
|
|
|
|
((CBasePlayerWeapon*)pEntity)->GetItemInfo(&info);
|
|
|
|
CBasePlayerItem::ItemInfoArray[info.iId] = info;
|
|
|
|
const char* weaponName = ((info.iFlags & ITEM_FLAG_EXHAUSTIBLE) != 0) ? STRING(pEntity->pev->classname) : nullptr;
|
|
|
|
if (info.pszAmmo1 && '\0' != *info.pszAmmo1)
|
|
{
|
|
AddAmmoNameToAmmoRegistry(info.pszAmmo1, weaponName);
|
|
}
|
|
|
|
if (info.pszAmmo2 && '\0' != *info.pszAmmo2)
|
|
{
|
|
AddAmmoNameToAmmoRegistry(info.pszAmmo2, weaponName);
|
|
}
|
|
|
|
g_pWpns[info.iId] = (CBasePlayerWeapon*)pEntity;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
CBaseEntity:: Killed
|
|
|
|
If weapons code "kills" an entity, just set its effects to EF_NODRAW
|
|
=====================
|
|
*/
|
|
void CBaseEntity::Killed(entvars_t* pevAttacker, int iGib)
|
|
{
|
|
pev->effects |= EF_NODRAW;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
CBasePlayerWeapon:: DefaultDeploy
|
|
|
|
=====================
|
|
*/
|
|
bool CBasePlayerWeapon::DefaultDeploy(const char* szViewModel, const char* szWeaponModel, int iAnim, const char* szAnimExt, int body)
|
|
{
|
|
if (!CanDeploy())
|
|
return false;
|
|
|
|
gEngfuncs.CL_LoadModel(szViewModel, &m_pPlayer->pev->viewmodel);
|
|
|
|
SendWeaponAnim(iAnim, body);
|
|
|
|
g_irunninggausspred = false;
|
|
m_pPlayer->m_flNextAttack = 0.5;
|
|
m_flTimeWeaponIdle = 1.0;
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
CBasePlayerWeapon:: PlayEmptySound
|
|
|
|
=====================
|
|
*/
|
|
bool CBasePlayerWeapon::PlayEmptySound()
|
|
{
|
|
if (m_iPlayEmptySound)
|
|
{
|
|
HUD_PlaySound("weapons/357_cock1.wav", 0.8);
|
|
m_iPlayEmptySound = false;
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
CBasePlayerWeapon::Holster
|
|
|
|
Put away weapon
|
|
=====================
|
|
*/
|
|
void CBasePlayerWeapon::Holster()
|
|
{
|
|
m_fInReload = false; // cancel any reload in progress.
|
|
g_irunninggausspred = false;
|
|
m_pPlayer->pev->viewmodel = 0;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
CBasePlayerWeapon::SendWeaponAnim
|
|
|
|
Animate weapon model
|
|
=====================
|
|
*/
|
|
void CBasePlayerWeapon::SendWeaponAnim(int iAnim, int body)
|
|
{
|
|
m_pPlayer->pev->weaponanim = iAnim;
|
|
|
|
HUD_SendWeaponAnim(iAnim, body, false);
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
CBaseEntity::FireBulletsPlayer
|
|
|
|
Only produces random numbers to match the server ones.
|
|
=====================
|
|
*/
|
|
Vector CBaseEntity::FireBulletsPlayer(unsigned int cShots, Vector vecSrc, Vector vecDirShooting, Vector vecSpread, float flDistance, int iBulletType, int iTracerFreq, int iDamage, entvars_t* pevAttacker, int shared_rand)
|
|
{
|
|
float x = 0, y = 0, z;
|
|
|
|
for (unsigned int iShot = 1; iShot <= cShots; iShot++)
|
|
{
|
|
if (pevAttacker == NULL)
|
|
{
|
|
// get circular gaussian spread
|
|
do
|
|
{
|
|
x = RANDOM_FLOAT(-0.5, 0.5) + RANDOM_FLOAT(-0.5, 0.5);
|
|
y = RANDOM_FLOAT(-0.5, 0.5) + RANDOM_FLOAT(-0.5, 0.5);
|
|
z = x * x + y * y;
|
|
} while (z > 1);
|
|
}
|
|
else
|
|
{
|
|
//Use player's random seed.
|
|
// get circular gaussian spread
|
|
x = UTIL_SharedRandomFloat(shared_rand + iShot, -0.5, 0.5) + UTIL_SharedRandomFloat(shared_rand + (1 + iShot), -0.5, 0.5);
|
|
y = UTIL_SharedRandomFloat(shared_rand + (2 + iShot), -0.5, 0.5) + UTIL_SharedRandomFloat(shared_rand + (3 + iShot), -0.5, 0.5);
|
|
z = x * x + y * y;
|
|
}
|
|
}
|
|
|
|
return Vector(x * vecSpread.x, y * vecSpread.y, 0.0);
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
CBasePlayer::SelectItem
|
|
|
|
Switch weapons
|
|
=====================
|
|
*/
|
|
void CBasePlayer::SelectItem(const char* pstr)
|
|
{
|
|
if (!pstr)
|
|
return;
|
|
|
|
CBasePlayerItem* pItem = NULL;
|
|
|
|
if (!pItem)
|
|
return;
|
|
|
|
|
|
if (pItem == m_pActiveItem)
|
|
return;
|
|
|
|
if (m_pActiveItem)
|
|
m_pActiveItem->Holster();
|
|
|
|
m_pLastItem = m_pActiveItem;
|
|
m_pActiveItem = pItem;
|
|
|
|
if (m_pActiveItem)
|
|
{
|
|
m_pActiveItem->Deploy();
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
CBasePlayer::Killed
|
|
|
|
=====================
|
|
*/
|
|
void CBasePlayer::Killed(entvars_t* pevAttacker, int iGib)
|
|
{
|
|
// Holster weapon immediately, to allow it to cleanup
|
|
if (m_pActiveItem)
|
|
m_pActiveItem->Holster();
|
|
|
|
g_irunninggausspred = false;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
CBasePlayer::Spawn
|
|
|
|
=====================
|
|
*/
|
|
void CBasePlayer::Spawn()
|
|
{
|
|
if (m_pActiveItem)
|
|
m_pActiveItem->Deploy();
|
|
|
|
g_irunninggausspred = false;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
UTIL_TraceLine
|
|
|
|
Don't actually trace, but act like the trace didn't hit anything.
|
|
=====================
|
|
*/
|
|
void UTIL_TraceLine(const Vector& vecStart, const Vector& vecEnd, IGNORE_MONSTERS igmon, edict_t* pentIgnore, TraceResult* ptr)
|
|
{
|
|
memset(ptr, 0, sizeof(*ptr));
|
|
ptr->flFraction = 1.0;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
UTIL_ParticleBox
|
|
|
|
For debugging, draw a box around a player made out of particles
|
|
=====================
|
|
*/
|
|
void UTIL_ParticleBox(CBasePlayer* player, float* mins, float* maxs, float life, unsigned char r, unsigned char g, unsigned char b)
|
|
{
|
|
int i;
|
|
Vector mmin, mmax;
|
|
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
mmin[i] = player->pev->origin[i] + mins[i];
|
|
mmax[i] = player->pev->origin[i] + maxs[i];
|
|
}
|
|
|
|
gEngfuncs.pEfxAPI->R_ParticleBox((float*)&mmin, (float*)&mmax, 5.0, 0, 255, 0);
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
UTIL_ParticleBoxes
|
|
|
|
For debugging, draw boxes for other collidable players
|
|
=====================
|
|
*/
|
|
void UTIL_ParticleBoxes()
|
|
{
|
|
int idx;
|
|
physent_t* pe;
|
|
cl_entity_t* player;
|
|
Vector mins, maxs;
|
|
|
|
gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction(0, 1);
|
|
|
|
// Store off the old count
|
|
gEngfuncs.pEventAPI->EV_PushPMStates();
|
|
|
|
player = gEngfuncs.GetLocalPlayer();
|
|
// Now add in all of the players.
|
|
gEngfuncs.pEventAPI->EV_SetSolidPlayers(player->index - 1);
|
|
|
|
for (idx = 1; idx < 100; idx++)
|
|
{
|
|
pe = gEngfuncs.pEventAPI->EV_GetPhysent(idx);
|
|
if (!pe)
|
|
break;
|
|
|
|
if (pe->info >= 1 && pe->info <= gEngfuncs.GetMaxClients())
|
|
{
|
|
mins = pe->origin + pe->mins;
|
|
maxs = pe->origin + pe->maxs;
|
|
|
|
gEngfuncs.pEfxAPI->R_ParticleBox((float*)&mins, (float*)&maxs, 0, 0, 255, 2.0);
|
|
}
|
|
}
|
|
|
|
gEngfuncs.pEventAPI->EV_PopPMStates();
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
UTIL_ParticleLine
|
|
|
|
For debugging, draw a line made out of particles
|
|
=====================
|
|
*/
|
|
void UTIL_ParticleLine(CBasePlayer* player, float* start, float* end, float life, unsigned char r, unsigned char g, unsigned char b)
|
|
{
|
|
gEngfuncs.pEfxAPI->R_ParticleLine(start, end, r, g, b, life);
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
HUD_InitClientWeapons
|
|
|
|
Set up weapons, player and functions needed to run weapons code client-side.
|
|
=====================
|
|
*/
|
|
void HUD_InitClientWeapons()
|
|
{
|
|
static bool initialized = false;
|
|
if (initialized)
|
|
return;
|
|
|
|
initialized = true;
|
|
|
|
// Set up pointer ( dummy object )
|
|
gpGlobals = &Globals;
|
|
|
|
// Fill in current time ( probably not needed )
|
|
gpGlobals->time = gEngfuncs.GetClientTime();
|
|
|
|
// Fake functions
|
|
g_engfuncs.pfnPrecacheModel = stub_PrecacheModel;
|
|
g_engfuncs.pfnPrecacheSound = stub_PrecacheSound;
|
|
g_engfuncs.pfnPrecacheEvent = stub_PrecacheEvent;
|
|
g_engfuncs.pfnNameForFunction = stub_NameForFunction;
|
|
g_engfuncs.pfnSetModel = stub_SetModel;
|
|
g_engfuncs.pfnSetClientMaxspeed = HUD_SetMaxSpeed;
|
|
|
|
// Handled locally
|
|
g_engfuncs.pfnPlaybackEvent = HUD_PlaybackEvent;
|
|
g_engfuncs.pfnAlertMessage = AlertMessage;
|
|
|
|
// Pass through to engine
|
|
g_engfuncs.pfnPrecacheEvent = gEngfuncs.pfnPrecacheEvent;
|
|
g_engfuncs.pfnRandomFloat = gEngfuncs.pfnRandomFloat;
|
|
g_engfuncs.pfnRandomLong = gEngfuncs.pfnRandomLong;
|
|
g_engfuncs.pfnCVarGetPointer = gEngfuncs.pfnGetCvarPointer;
|
|
g_engfuncs.pfnCVarGetString = gEngfuncs.pfnGetCvarString;
|
|
g_engfuncs.pfnCVarGetFloat = gEngfuncs.pfnGetCvarFloat;
|
|
|
|
// Allocate a slot for the local player
|
|
HUD_PrepEntity(&player, NULL);
|
|
|
|
// Allocate slot(s) for each weapon that we are going to be predicting
|
|
HUD_PrepEntity(&g_Glock, &player);
|
|
HUD_PrepEntity(&g_Crowbar, &player);
|
|
HUD_PrepEntity(&g_Python, &player);
|
|
HUD_PrepEntity(&g_Mp5, &player);
|
|
HUD_PrepEntity(&g_Crossbow, &player);
|
|
HUD_PrepEntity(&g_Shotgun, &player);
|
|
HUD_PrepEntity(&g_Rpg, &player);
|
|
HUD_PrepEntity(&g_Gauss, &player);
|
|
HUD_PrepEntity(&g_Egon, &player);
|
|
HUD_PrepEntity(&g_HGun, &player);
|
|
HUD_PrepEntity(&g_HandGren, &player);
|
|
HUD_PrepEntity(&g_Satchel, &player);
|
|
HUD_PrepEntity(&g_Tripmine, &player);
|
|
HUD_PrepEntity(&g_Snark, &player);
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
HUD_GetLastOrg
|
|
|
|
Retruns the last position that we stored for egon beam endpoint.
|
|
=====================
|
|
*/
|
|
void HUD_GetLastOrg(float* org)
|
|
{
|
|
int i;
|
|
|
|
// Return last origin
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
org[i] = previousorigin[i];
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
HUD_SetLastOrg
|
|
|
|
Remember our exact predicted origin so we can draw the egon to the right position.
|
|
=====================
|
|
*/
|
|
void HUD_SetLastOrg()
|
|
{
|
|
int i;
|
|
|
|
// Offset final origin by view_offset
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
previousorigin[i] = g_finalstate->playerstate.origin[i] + g_finalstate->client.view_ofs[i];
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
HUD_WeaponsPostThink
|
|
|
|
Run Weapon firing code on client
|
|
=====================
|
|
*/
|
|
void HUD_WeaponsPostThink(local_state_s* from, local_state_s* to, usercmd_t* cmd, double time, unsigned int random_seed)
|
|
{
|
|
int i;
|
|
int buttonsChanged;
|
|
CBasePlayerWeapon* pWeapon = NULL;
|
|
CBasePlayerWeapon* pCurrent;
|
|
weapon_data_t nulldata, *pfrom, *pto;
|
|
static int lasthealth;
|
|
|
|
memset(&nulldata, 0, sizeof(nulldata));
|
|
|
|
// Get current clock
|
|
//Use actual time instead of prediction frame time because that time value breaks anything that uses absolute time values.
|
|
gpGlobals->time = gEngfuncs.GetClientTime(); //time;
|
|
|
|
//Lets weapons code use frametime to decrement timers and stuff.
|
|
gpGlobals->frametime = cmd->msec / 1000.0f;
|
|
|
|
// Fill in data based on selected weapon
|
|
// FIXME, make this a method in each weapon? where you pass in an entity_state_t *?
|
|
switch (from->client.m_iId)
|
|
{
|
|
case WEAPON_CROWBAR:
|
|
pWeapon = &g_Crowbar;
|
|
break;
|
|
|
|
case WEAPON_GLOCK:
|
|
pWeapon = &g_Glock;
|
|
break;
|
|
|
|
case WEAPON_PYTHON:
|
|
pWeapon = &g_Python;
|
|
break;
|
|
|
|
case WEAPON_MP5:
|
|
pWeapon = &g_Mp5;
|
|
break;
|
|
|
|
case WEAPON_CROSSBOW:
|
|
pWeapon = &g_Crossbow;
|
|
break;
|
|
|
|
case WEAPON_SHOTGUN:
|
|
pWeapon = &g_Shotgun;
|
|
break;
|
|
|
|
case WEAPON_RPG:
|
|
pWeapon = &g_Rpg;
|
|
break;
|
|
|
|
case WEAPON_GAUSS:
|
|
pWeapon = &g_Gauss;
|
|
break;
|
|
|
|
case WEAPON_EGON:
|
|
pWeapon = &g_Egon;
|
|
break;
|
|
|
|
case WEAPON_HORNETGUN:
|
|
pWeapon = &g_HGun;
|
|
break;
|
|
|
|
case WEAPON_HANDGRENADE:
|
|
pWeapon = &g_HandGren;
|
|
break;
|
|
|
|
case WEAPON_SATCHEL:
|
|
pWeapon = &g_Satchel;
|
|
break;
|
|
|
|
case WEAPON_TRIPMINE:
|
|
pWeapon = &g_Tripmine;
|
|
break;
|
|
|
|
case WEAPON_SNARK:
|
|
pWeapon = &g_Snark;
|
|
break;
|
|
}
|
|
|
|
// Store pointer to our destination entity_state_t so we can get our origin, etc. from it
|
|
// for setting up events on the client
|
|
g_finalstate = to;
|
|
|
|
// If we are running events/etc. go ahead and see if we
|
|
// managed to die between last frame and this one
|
|
// If so, run the appropriate player killed or spawn function
|
|
if (g_runfuncs)
|
|
{
|
|
if (to->client.health <= 0 && lasthealth > 0)
|
|
{
|
|
player.Killed(NULL, 0);
|
|
}
|
|
else if (to->client.health > 0 && lasthealth <= 0)
|
|
{
|
|
player.Spawn();
|
|
}
|
|
|
|
lasthealth = to->client.health;
|
|
}
|
|
|
|
// We are not predicting the current weapon, just bow out here.
|
|
if (!pWeapon)
|
|
return;
|
|
|
|
for (i = 0; i < MAX_WEAPONS; i++)
|
|
{
|
|
pCurrent = g_pWpns[i];
|
|
if (!pCurrent)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
pfrom = &from->weapondata[i];
|
|
|
|
pCurrent->m_fInReload = 0 != pfrom->m_fInReload;
|
|
pCurrent->m_fInSpecialReload = pfrom->m_fInSpecialReload;
|
|
// pCurrent->m_flPumpTime = pfrom->m_flPumpTime;
|
|
pCurrent->m_iClip = pfrom->m_iClip;
|
|
pCurrent->m_flNextPrimaryAttack = pfrom->m_flNextPrimaryAttack;
|
|
pCurrent->m_flNextSecondaryAttack = pfrom->m_flNextSecondaryAttack;
|
|
pCurrent->m_flTimeWeaponIdle = pfrom->m_flTimeWeaponIdle;
|
|
pCurrent->pev->fuser1 = pfrom->fuser1;
|
|
pCurrent->m_flStartThrow = pfrom->fuser2;
|
|
pCurrent->m_flReleaseThrow = pfrom->fuser3;
|
|
pCurrent->m_chargeReady = pfrom->iuser1;
|
|
pCurrent->m_fInAttack = pfrom->iuser2;
|
|
pCurrent->m_fireState = pfrom->iuser3;
|
|
|
|
pCurrent->m_iSecondaryAmmoType = (int)from->client.vuser3[2];
|
|
pCurrent->m_iPrimaryAmmoType = (int)from->client.vuser4[0];
|
|
player.m_rgAmmo[pCurrent->m_iPrimaryAmmoType] = (int)from->client.vuser4[1];
|
|
player.m_rgAmmo[pCurrent->m_iSecondaryAmmoType] = (int)from->client.vuser4[2];
|
|
|
|
pCurrent->SetWeaponData(*pfrom);
|
|
}
|
|
|
|
// For random weapon events, use this seed to seed random # generator
|
|
player.random_seed = random_seed;
|
|
|
|
// Get old buttons from previous state.
|
|
player.m_afButtonLast = from->playerstate.oldbuttons;
|
|
|
|
// Which buttsons chave changed
|
|
buttonsChanged = (player.m_afButtonLast ^ cmd->buttons); // These buttons have changed this frame
|
|
|
|
// Debounced button codes for pressed/released
|
|
// The changed ones still down are "pressed"
|
|
player.m_afButtonPressed = buttonsChanged & cmd->buttons;
|
|
// The ones not down are "released"
|
|
player.m_afButtonReleased = buttonsChanged & (~cmd->buttons);
|
|
|
|
// Set player variables that weapons code might check/alter
|
|
player.pev->button = cmd->buttons;
|
|
|
|
player.pev->velocity = from->client.velocity;
|
|
player.pev->flags = from->client.flags;
|
|
|
|
player.pev->deadflag = from->client.deadflag;
|
|
player.pev->waterlevel = from->client.waterlevel;
|
|
player.pev->maxspeed = from->client.maxspeed;
|
|
player.m_iFOV = from->client.fov;
|
|
player.pev->weaponanim = from->client.weaponanim;
|
|
player.pev->viewmodel = from->client.viewmodel;
|
|
player.m_flNextAttack = from->client.m_flNextAttack;
|
|
player.m_flNextAmmoBurn = from->client.fuser2;
|
|
player.m_flAmmoStartCharge = from->client.fuser3;
|
|
|
|
//Stores all our ammo info, so the client side weapons can use them.
|
|
player.ammo_9mm = (int)from->client.vuser1[0];
|
|
player.ammo_357 = (int)from->client.vuser1[1];
|
|
player.ammo_argrens = (int)from->client.vuser1[2];
|
|
player.ammo_bolts = (int)from->client.ammo_nails; //is an int anyways...
|
|
player.ammo_buckshot = (int)from->client.ammo_shells;
|
|
player.ammo_uranium = (int)from->client.ammo_cells;
|
|
player.ammo_hornets = (int)from->client.vuser2[0];
|
|
player.ammo_rockets = (int)from->client.ammo_rockets;
|
|
|
|
|
|
// Point to current weapon object
|
|
if (WEAPON_NONE != from->client.m_iId)
|
|
{
|
|
player.m_pActiveItem = g_pWpns[from->client.m_iId];
|
|
}
|
|
|
|
if (player.m_pActiveItem->m_iId == WEAPON_RPG)
|
|
{
|
|
((CRpg*)player.m_pActiveItem)->m_fSpotActive = static_cast<bool>(from->client.vuser2[1]);
|
|
((CRpg*)player.m_pActiveItem)->m_cActiveRockets = (int)from->client.vuser2[2];
|
|
}
|
|
|
|
// Don't go firing anything if we have died or are spectating
|
|
// Or if we don't have a weapon model deployed
|
|
if ((player.pev->deadflag != (DEAD_DISCARDBODY + 1)) &&
|
|
!CL_IsDead() && 0 != player.pev->viewmodel && 0 == g_iUser1)
|
|
{
|
|
if (player.m_flNextAttack <= 0)
|
|
{
|
|
pWeapon->ItemPostFrame();
|
|
}
|
|
}
|
|
|
|
// Assume that we are not going to switch weapons
|
|
to->client.m_iId = from->client.m_iId;
|
|
|
|
// Now see if we issued a changeweapon command ( and we're not dead )
|
|
if (0 != cmd->weaponselect && (player.pev->deadflag != (DEAD_DISCARDBODY + 1)))
|
|
{
|
|
// Switched to a different weapon?
|
|
if (from->weapondata[cmd->weaponselect].m_iId == cmd->weaponselect)
|
|
{
|
|
CBasePlayerWeapon* pNew = g_pWpns[cmd->weaponselect];
|
|
if (pNew && (pNew != pWeapon))
|
|
{
|
|
// Put away old weapon
|
|
if (player.m_pActiveItem)
|
|
player.m_pActiveItem->Holster();
|
|
|
|
player.m_pLastItem = player.m_pActiveItem;
|
|
player.m_pActiveItem = pNew;
|
|
|
|
// Deploy new weapon
|
|
if (player.m_pActiveItem)
|
|
{
|
|
player.m_pActiveItem->Deploy();
|
|
}
|
|
|
|
// Update weapon id so we can predict things correctly.
|
|
to->client.m_iId = cmd->weaponselect;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy in results of prediction code
|
|
to->client.viewmodel = player.pev->viewmodel;
|
|
to->client.fov = player.m_iFOV;
|
|
to->client.weaponanim = player.pev->weaponanim;
|
|
to->client.m_flNextAttack = player.m_flNextAttack;
|
|
to->client.fuser2 = player.m_flNextAmmoBurn;
|
|
to->client.fuser3 = player.m_flAmmoStartCharge;
|
|
to->client.maxspeed = player.pev->maxspeed;
|
|
|
|
//HL Weapons
|
|
to->client.vuser1[0] = player.ammo_9mm;
|
|
to->client.vuser1[1] = player.ammo_357;
|
|
to->client.vuser1[2] = player.ammo_argrens;
|
|
|
|
to->client.ammo_nails = player.ammo_bolts;
|
|
to->client.ammo_shells = player.ammo_buckshot;
|
|
to->client.ammo_cells = player.ammo_uranium;
|
|
to->client.vuser2[0] = player.ammo_hornets;
|
|
to->client.ammo_rockets = player.ammo_rockets;
|
|
|
|
if (player.m_pActiveItem->m_iId == WEAPON_RPG)
|
|
{
|
|
to->client.vuser2[1] = static_cast<float>(((CRpg*)player.m_pActiveItem)->m_fSpotActive);
|
|
to->client.vuser2[2] = ((CRpg*)player.m_pActiveItem)->m_cActiveRockets;
|
|
}
|
|
|
|
// Make sure that weapon animation matches what the game .dll is telling us
|
|
// over the wire ( fixes some animation glitches )
|
|
if (g_runfuncs && (HUD_GetWeaponAnim() != to->client.weaponanim))
|
|
{
|
|
//Make sure the 357 has the right body
|
|
g_Python.pev->body = bIsMultiplayer() ? 1 : 0;
|
|
|
|
// Force a fixed anim down to viewmodel
|
|
HUD_SendWeaponAnim(to->client.weaponanim, pWeapon->pev->body, true);
|
|
}
|
|
|
|
for (i = 0; i < MAX_WEAPONS; i++)
|
|
{
|
|
pCurrent = g_pWpns[i];
|
|
|
|
pto = &to->weapondata[i];
|
|
|
|
if (!pCurrent)
|
|
{
|
|
memset(pto, 0, sizeof(weapon_data_t));
|
|
continue;
|
|
}
|
|
|
|
pto->m_fInReload = static_cast<int>(pCurrent->m_fInReload);
|
|
pto->m_fInSpecialReload = pCurrent->m_fInSpecialReload;
|
|
// pto->m_flPumpTime = pCurrent->m_flPumpTime;
|
|
pto->m_iClip = pCurrent->m_iClip;
|
|
pto->m_flNextPrimaryAttack = pCurrent->m_flNextPrimaryAttack;
|
|
pto->m_flNextSecondaryAttack = pCurrent->m_flNextSecondaryAttack;
|
|
pto->m_flTimeWeaponIdle = pCurrent->m_flTimeWeaponIdle;
|
|
pto->fuser1 = pCurrent->pev->fuser1;
|
|
pto->fuser2 = pCurrent->m_flStartThrow;
|
|
pto->fuser3 = pCurrent->m_flReleaseThrow;
|
|
pto->iuser1 = pCurrent->m_chargeReady;
|
|
pto->iuser2 = pCurrent->m_fInAttack;
|
|
pto->iuser3 = pCurrent->m_fireState;
|
|
|
|
// Decrement weapon counters, server does this at same time ( during post think, after doing everything else )
|
|
pto->m_flNextReload -= cmd->msec / 1000.0;
|
|
pto->m_fNextAimBonus -= cmd->msec / 1000.0;
|
|
pto->m_flNextPrimaryAttack -= cmd->msec / 1000.0;
|
|
pto->m_flNextSecondaryAttack -= cmd->msec / 1000.0;
|
|
pto->m_flTimeWeaponIdle -= cmd->msec / 1000.0;
|
|
pto->fuser1 -= cmd->msec / 1000.0;
|
|
|
|
to->client.vuser3[2] = pCurrent->m_iSecondaryAmmoType;
|
|
to->client.vuser4[0] = pCurrent->m_iPrimaryAmmoType;
|
|
to->client.vuser4[1] = player.m_rgAmmo[pCurrent->m_iPrimaryAmmoType];
|
|
to->client.vuser4[2] = player.m_rgAmmo[pCurrent->m_iSecondaryAmmoType];
|
|
|
|
pCurrent->DecrementTimers();
|
|
|
|
pCurrent->GetWeaponData(*pto);
|
|
|
|
/* if ( pto->m_flPumpTime != -9999 )
|
|
{
|
|
pto->m_flPumpTime -= cmd->msec / 1000.0;
|
|
if ( pto->m_flPumpTime < -0.001 )
|
|
pto->m_flPumpTime = -0.001;
|
|
}*/
|
|
|
|
if (pto->m_fNextAimBonus < -1.0)
|
|
{
|
|
pto->m_fNextAimBonus = -1.0;
|
|
}
|
|
|
|
if (pto->m_flNextPrimaryAttack < -1.1)
|
|
{
|
|
pto->m_flNextPrimaryAttack = -1.1;
|
|
}
|
|
|
|
if (pto->m_flNextSecondaryAttack < -0.001)
|
|
{
|
|
pto->m_flNextSecondaryAttack = -0.001;
|
|
}
|
|
|
|
if (pto->m_flTimeWeaponIdle < -0.001)
|
|
{
|
|
pto->m_flTimeWeaponIdle = -0.001;
|
|
}
|
|
|
|
if (pto->m_flNextReload < -0.001)
|
|
{
|
|
pto->m_flNextReload = -0.001;
|
|
}
|
|
|
|
if (pto->fuser1 < -0.001)
|
|
{
|
|
pto->fuser1 = -0.001;
|
|
}
|
|
}
|
|
|
|
// m_flNextAttack is now part of the weapons, but is part of the player instead
|
|
to->client.m_flNextAttack -= cmd->msec / 1000.0;
|
|
if (to->client.m_flNextAttack < -0.001)
|
|
{
|
|
to->client.m_flNextAttack = -0.001;
|
|
}
|
|
|
|
to->client.fuser2 -= cmd->msec / 1000.0;
|
|
if (to->client.fuser2 < -0.001)
|
|
{
|
|
to->client.fuser2 = -0.001;
|
|
}
|
|
|
|
to->client.fuser3 -= cmd->msec / 1000.0;
|
|
if (to->client.fuser3 < -0.001)
|
|
{
|
|
to->client.fuser3 = -0.001;
|
|
}
|
|
|
|
// Store off the last position from the predicted state.
|
|
HUD_SetLastOrg();
|
|
|
|
// Wipe it so we can't use it after this frame
|
|
g_finalstate = NULL;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
HUD_PostRunCmd
|
|
|
|
Client calls this during prediction, after it has moved the player and updated any info changed into to->
|
|
time is the current client clock based on prediction
|
|
cmd is the command that caused the movement, etc
|
|
runfuncs is 1 if this is the first time we've predicted this command. If so, sounds and effects should play, otherwise, they should
|
|
be ignored
|
|
=====================
|
|
*/
|
|
void DLLEXPORT HUD_PostRunCmd(struct local_state_s* from, struct local_state_s* to, struct usercmd_s* cmd, int runfuncs, double time, unsigned int random_seed)
|
|
{
|
|
// RecClPostRunCmd(from, to, cmd, runfuncs, time, random_seed);
|
|
|
|
g_runfuncs = runfuncs != 0;
|
|
|
|
//Event code depends on this stuff, so always initialize it.
|
|
HUD_InitClientWeapons();
|
|
|
|
#if defined(CLIENT_WEAPONS)
|
|
if (cl_lw && 0 != cl_lw->value)
|
|
{
|
|
HUD_WeaponsPostThink(from, to, cmd, time, random_seed);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
to->client.fov = g_lastFOV;
|
|
}
|
|
|
|
if (g_irunninggausspred)
|
|
{
|
|
Vector forward;
|
|
gEngfuncs.pfnAngleVectors(v_angles, forward, NULL, NULL);
|
|
to->client.velocity = to->client.velocity - forward * g_flApplyVel * 5;
|
|
g_irunninggausspred = false;
|
|
}
|
|
|
|
// All games can use FOV state
|
|
g_lastFOV = to->client.fov;
|
|
}
|