Overhaul prediction routines by boxing the generic client attributes away

from the game specific code. Add some helper macros... clean a whole bunch
up.
This commit is contained in:
Marco Cawthorne 2021-05-08 17:44:16 +02:00
parent 8e4ec974e0
commit 64745eb23c
41 changed files with 720 additions and 603 deletions

View File

@ -14,7 +14,6 @@
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
vector g_hud_color;
vector g_hudmins;
vector g_hudres;

View File

@ -37,10 +37,10 @@ View_UpdateWeapon(entity vm, entity mflash)
* thus we need to update all the net variables to
* make sure these updates are recognized. this is
* vile but it'll have to do for now */
pl.net_w_attack_next = pl.w_attack_next;
pl.net_w_idle_next = pl.w_idle_next;
pl.net_viewzoom = pl.viewzoom;
pl.net_weapontime = pl.weapontime;
SAVE_STATE(pl.w_attack_next);
SAVE_STATE(pl.w_idle_next);
SAVE_STATE(pl.viewzoom);
SAVE_STATE(pl.weapontime);
/* figure out when the attachments start. in FTE attachments for
* HLMDL are treated as bones. they start at numbones + 1 */

View File

@ -16,7 +16,7 @@
/* generic function that applies damage, pain and suffering */
void
Damage_Apply(entity t, entity c, float dmg, int w, int type)
Damage_Apply(entity t, entity c, float dmg, int w, damageType_t type)
{
base_player tp = (base_player)t;

View File

@ -14,7 +14,8 @@
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
void Flashlight_Toggle(void)
void
Flashlight_Toggle(void)
{
if (cvar("sv_playerslots") != 1) {
if (cvar("mp_flashlight") != 1) {

View File

@ -14,7 +14,8 @@
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
void item_pickup::touch(void)
void
item_pickup::touch(void)
{
if (other.classname != "player") {
return;
@ -38,19 +39,22 @@ void item_pickup::touch(void)
}
}
void item_pickup::SetItem(int i)
void
item_pickup::SetItem(int i)
{
id = i;
m_oldModel = Weapons_GetWorldmodel(id);
SetModel(m_oldModel);
}
void item_pickup::SetFloating(int i)
void
item_pickup::SetFloating(int i)
{
m_bFloating = rint(bound(0, m_bFloating, 1));
}
void item_pickup::Respawn(void)
void
item_pickup::Respawn(void)
{
SetSolid(SOLID_TRIGGER);
SetOrigin(m_oldOrigin);
@ -75,7 +79,8 @@ void item_pickup::Respawn(void)
}
}
void item_pickup::item_pickup(void)
void
item_pickup::item_pickup(void)
{
CBaseTrigger::CBaseTrigger();
Respawn();

View File

@ -19,7 +19,8 @@
UseWorkaround
====================
*/
void UseWorkaround(entity eTarget)
void
UseWorkaround(entity eTarget)
{
eActivator = self;
entity eOldSelf = self;
@ -33,7 +34,8 @@ void UseWorkaround(entity eTarget)
Player_UseDown
====================
*/
void Player_UseDown(void)
void
Player_UseDown(void)
{
vector vecSrc;
@ -70,14 +72,17 @@ void Player_UseDown(void)
Player_UseUp
====================
*/
void Player_UseUp(void) {
void
Player_UseUp(void) {
if (!(self.flags & FL_USE_RELEASED)) {
self.flags |= FL_USE_RELEASED;
}
}
void Weapons_Draw(void);
void CSEv_PlayerSwitchWeapon_i(int w)
void
CSEv_PlayerSwitchWeapon_i(int w)
{
player pl = (player)self;
pl.activeweapon = w;

View File

@ -14,24 +14,28 @@
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
void info_player_start(void)
void
info_player_start(void)
{
self.solid = SOLID_TRIGGER;
setsize(self, VEC_HULL_MIN, VEC_HULL_MAX);
}
void info_player_deathmatch(void)
void
info_player_deathmatch(void)
{
self.solid = SOLID_TRIGGER;
setsize(self, VEC_HULL_MIN, VEC_HULL_MAX);
}
void info_player_team1(void)
void
info_player_team1(void)
{
self.classname = "info_player_deathmatch";
}
void info_player_team2(void)
void
info_player_team2(void)
{
self.classname = "info_player_deathmatch";
}

View File

@ -14,15 +14,20 @@
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
void Game_SpectatorThink(void)
void
Game_SpectatorThink(void)
{
}
void Game_SpectatorConnect(void)
void
Game_SpectatorConnect(void)
{
}
void Game_SpectatorDisconnect(void)
void
Game_SpectatorDisconnect(void)
{
}

View File

@ -15,7 +15,6 @@
*/
#ifdef CLIENT
void
FX_Impact_Init(void)
{
@ -24,7 +23,7 @@ FX_Impact_Init(void)
#endif
void
FX_Impact(int iType, vector vecPos, vector vNormal)
FX_Impact(impactType_t iType, vector vecPos, vector vNormal)
{
}

View File

@ -21,7 +21,8 @@ Input_Handle
Handles impulse and whatnot
=================
*/
void Game_Input(void)
void
Game_Input(void)
{
#ifdef SERVER
CGameRules rules = (CGameRules)g_grMode;

View File

@ -33,13 +33,13 @@ enumflags
PLAYER_ARMOR,
PLAYER_MOVETYPE,
PLAYER_VIEWOFS,
PLAYER_BASEFRAME,
PLAYER_FRAME,
PLAYER_AMMO1,
PLAYER_AMMO2,
PLAYER_AMMO3,
PLAYER_UNUSED1,
PLAYER_UNUSED2
PLAYER_UNUSED2,
PLAYER_UNUSED3,
PLAYER_UNUSED4,
PLAYER_UNUSED5,
PLAYER_UNUSED6,
PLAYER_UNUSED7
};
noref int input_sequence;
@ -55,7 +55,7 @@ class player:base_player
virtual void(void) draw;
virtual float() predraw;
virtual void(void) postdraw;
virtual void(float) ReceiveEntity;
virtual void(float,float) ReceiveEntity;
virtual void(void) PredictPreFrame;
virtual void(void) PredictPostFrame;
#else
@ -65,88 +65,15 @@ class player:base_player
};
#ifdef CLIENT
//void Weapons_AmmoUpdate(entity);
/*
=================
player::ReceiveEntity
=================
*/
void
player::ReceiveEntity(float new)
player::ReceiveEntity(float new, float fl)
{
float fl;
/* seed for our prediction table */
sequence = servercommandframe;
fl = readfloat();
/* HACK: we need to make this more reliable */
if (fl == UPDATE_ALL) {
/* we respawned */
gravity = __NULL__;
}
if (fl & PLAYER_MODELINDEX)
modelindex = readshort();
if (fl & PLAYER_ORIGIN) {
origin[0] = readcoord();
origin[1] = readcoord();
}
if (fl & PLAYER_ORIGIN_Z)
origin[2] = readcoord();
if (fl & PLAYER_ANGLES_X)
pitch = readfloat();
if (fl & PLAYER_ANGLES_Y)
angles[1] = readfloat();
if (fl & PLAYER_ANGLES_Z)
angles[2] = readfloat();
if (fl & PLAYER_VELOCITY) {
velocity[0] = readcoord();
velocity[1] = readcoord();
}
if (fl & PLAYER_VELOCITY_Z)
velocity[2] = readcoord();
if (fl & PLAYER_FLAGS) {
flags = readfloat();
gflags = readfloat();
}
if (fl & PLAYER_WEAPON)
activeweapon = readbyte();
if (fl & PLAYER_ITEMS)
g_items = (__variant)readfloat();
if (fl & PLAYER_HEALTH)
health = readbyte();
if (fl & PLAYER_ARMOR)
armor = readbyte();
if (fl & PLAYER_MOVETYPE)
movetype = readbyte();
if (fl & PLAYER_VIEWOFS)
view_ofs[2] = readfloat();
if (fl & PLAYER_BASEFRAME)
baseframe = readbyte();
if (fl & PLAYER_FRAME) {
frame = readbyte();
frame1time = 0.0f;
frame2time = 0.0f;
}
if (fl & PLAYER_AMMO1) {
}
if (fl & PLAYER_AMMO2) {
}
if (fl & PLAYER_AMMO3) {
}
//if (fl & PLAYER_AMMO1 || fl & PLAYER_AMMO2 || fl & PLAYER_AMMO3)
// Weapons_AmmoUpdate(this);
base_player::ReceiveEntity(new, fl);
setorigin(this, origin);
}
@ -161,6 +88,7 @@ so we can roll them back later.
void
player::PredictPreFrame(void)
{
base_player::PredictPreFrame();
}
/*
@ -173,89 +101,14 @@ Where we roll back our values to the ones last sent/verified by the server.
void
player::PredictPostFrame(void)
{
base_player::PredictPostFrame();
}
#else
void
player::EvaluateEntity(void)
{
SendFlags |= PLAYER_KEEPALIVE;
if (old_modelindex != modelindex)
SendFlags |= PLAYER_MODELINDEX;
if (old_origin[0] != origin[0])
SendFlags |= PLAYER_ORIGIN;
if (old_origin[1] != origin[1])
SendFlags |= PLAYER_ORIGIN;
if (old_origin[2] != origin[2])
SendFlags |= PLAYER_ORIGIN_Z;
if (old_angles[0] != v_angle[0])
SendFlags |= PLAYER_ANGLES_X;
if (old_angles[1] != angles[1])
SendFlags |= PLAYER_ANGLES_Y;
if (old_angles[2] != angles[2])
SendFlags |= PLAYER_ANGLES_Z;
if (old_velocity[0] != velocity[0])
SendFlags |= PLAYER_VELOCITY;
if (old_velocity[1] != velocity[1])
SendFlags |= PLAYER_VELOCITY;
if (old_velocity[2] != velocity[2])
SendFlags |= PLAYER_VELOCITY_Z;
if (old_flags != flags)
SendFlags |= PLAYER_FLAGS;
if (old_gflags != gflags)
SendFlags |= PLAYER_FLAGS;
if (old_activeweapon != activeweapon)
SendFlags |= PLAYER_WEAPON;
if (old_items != g_items)
SendFlags |= PLAYER_ITEMS;
if (old_health != health)
SendFlags |= PLAYER_HEALTH;
if (old_armor != armor)
SendFlags |= PLAYER_ARMOR;
if (old_movetype != movetype)
SendFlags |= PLAYER_MOVETYPE;
if (old_viewofs != view_ofs[2])
SendFlags |= PLAYER_VIEWOFS;
if (old_baseframe != baseframe)
SendFlags |= PLAYER_BASEFRAME;
if (old_frame != frame)
SendFlags |= PLAYER_FRAME;
old_modelindex = modelindex;
old_origin = origin;
old_angles = angles;
old_angles[0] = v_angle[0];
old_velocity = velocity;
old_flags = flags;
old_gflags = gflags;
old_activeweapon = activeweapon;
old_items = g_items;
old_health = health;
old_armor = armor;
old_movetype = movetype;
old_viewofs = view_ofs[2];
old_baseframe = baseframe;
old_frame = frame;
base_player::EvaluateEntity();
}
/*
@ -266,78 +119,29 @@ player::SendEntity
float
player::SendEntity(entity ePEnt, float fChanged)
{
/* remove our entity to other clients if we're dead */
if (health <= 0 && ePEnt != this) {
return FALSE;
}
/* target client isn't real, they have no client-side. abandon */
if (clienttype(ePEnt) != CLIENTTYPE_REAL) {
return FALSE;
}
/* other players don't need to know about these attributes */
if (ePEnt != self) {
fChanged &= ~PLAYER_ITEMS;
fChanged &= ~PLAYER_HEALTH;
fChanged &= ~PLAYER_ARMOR;
fChanged &= ~PLAYER_VIEWOFS;
fChanged &= ~PLAYER_AMMO1;
fChanged &= ~PLAYER_AMMO2;
fChanged &= ~PLAYER_AMMO3;
}
WriteByte(MSG_ENTITY, ENT_PLAYER);
WriteFloat(MSG_ENTITY, fChanged);
/* really trying to get our moneys worth with 23 bits of mantissa */
if (fChanged & PLAYER_MODELINDEX)
WriteShort(MSG_ENTITY, modelindex);
if (fChanged & PLAYER_ORIGIN) {
WriteCoord(MSG_ENTITY, origin[0]);
WriteCoord(MSG_ENTITY, origin[1]);
}
if (fChanged & PLAYER_ORIGIN_Z)
WriteCoord(MSG_ENTITY, origin[2]);
if (fChanged & PLAYER_ANGLES_X)
WriteFloat(MSG_ENTITY, v_angle[0]);
if (fChanged & PLAYER_ANGLES_Y)
WriteFloat(MSG_ENTITY, angles[1]);
if (fChanged & PLAYER_ANGLES_Z)
WriteFloat(MSG_ENTITY, angles[2]);
if (fChanged & PLAYER_VELOCITY) {
WriteCoord(MSG_ENTITY, velocity[0]);
WriteCoord(MSG_ENTITY, velocity[1]);
}
if (fChanged & PLAYER_VELOCITY_Z)
WriteCoord(MSG_ENTITY, velocity[2]);
if (fChanged & PLAYER_FLAGS) {
WriteFloat(MSG_ENTITY, flags);
WriteFloat(MSG_ENTITY, gflags);
}
if (fChanged & PLAYER_WEAPON)
WriteByte(MSG_ENTITY, activeweapon);
if (fChanged & PLAYER_ITEMS)
WriteFloat(MSG_ENTITY, (__variant)g_items);
if (fChanged & PLAYER_HEALTH)
WriteByte(MSG_ENTITY, bound(0, health, 255));
if (fChanged & PLAYER_ARMOR)
WriteByte(MSG_ENTITY, armor);
if (fChanged & PLAYER_MOVETYPE)
WriteByte(MSG_ENTITY, movetype);
if (fChanged & PLAYER_VIEWOFS)
WriteFloat(MSG_ENTITY, view_ofs[2]);
if (fChanged & PLAYER_BASEFRAME)
WriteByte(MSG_ENTITY, baseframe);
if (fChanged & PLAYER_FRAME)
WriteByte(MSG_ENTITY, frame);
if (fChanged & PLAYER_AMMO1) {
}
if (fChanged & PLAYER_AMMO2) {
}
if (fChanged & PLAYER_AMMO3) {
}
/* the generic client attributes */
base_player::SendEntity(ePEnt, fChanged);
return TRUE;
}
#endif

View File

@ -21,12 +21,14 @@
.float waterlevel;
.float watertype;
float GamePMove_Maxspeed(player target)
float
GamePMove_Maxspeed(player target)
{
return (target.flags & FL_CROUCHING) ? 135 : 270;
}
void GamePMove_Fall(player target, float impactspeed)
void
GamePMove_Fall(player target, float impactspeed)
{
if (impactspeed > 580) {
#ifdef SERVER
@ -43,7 +45,8 @@ void GamePMove_Fall(player target, float impactspeed)
}
}
void GamePMove_Jump(player target)
void
GamePMove_Jump(player target)
{
float flJumptimeDelta;
float flChainBonus;

View File

@ -50,13 +50,12 @@ void Weapons_MakeVectors(void);
vector Weapons_GetCameraPos(void);
void Weapons_ViewAnimation(int);
void Weapons_ViewPunchAngle(vector);
void sound(entity, float, string, float, float);
int Weapons_IsPresent(player, int);
void Weapons_SetModel(string);
void Weapons_SetGeomset(string);
int Weapons_GetAnimation(void);
#ifdef CLIENT
string Weapons_GetPlayermodel(int);
int Weapons_GetAnimation(void);
void Weapons_HUDPic(int, int, vector, float);
#endif

View File

@ -18,7 +18,8 @@
void Decals_Init(void);
#endif
void Weapons_Init(void)
void
Weapons_Init(void)
{
/* in the future we'll have no internal weapon table, then this will fill
* one up... */
@ -53,21 +54,24 @@ void Weapons_Init(void)
}
}
void Weapons_SetModel(string mdl)
void
Weapons_SetModel(string mdl)
{
#ifdef CLIENT
setmodel(pSeat->m_eViewModel, mdl);
#endif
}
void Weapons_SetGeomset(string set)
void
Weapons_SetGeomset(string set)
{
#ifdef CLIENT
setcustomskin(pSeat->m_eViewModel, "", set);
#endif
}
void Weapons_Draw(void)
void
Weapons_Draw(void)
{
player pl = (player)self;
int i = pl.activeweapon;
@ -89,7 +93,8 @@ void Weapons_Draw(void)
#endif
}
void Weapons_Holster(void)
void
Weapons_Holster(void)
{
player pl = (player)self;
int i = pl.activeweapon;
@ -98,7 +103,8 @@ void Weapons_Holster(void)
}
}
void Weapons_Primary(void)
void
Weapons_Primary(void)
{
player pl = (player)self;
int i = pl.activeweapon;
@ -117,7 +123,8 @@ void Weapons_Primary(void)
#endif
}
void Weapons_Secondary(void)
void
Weapons_Secondary(void)
{
player pl = (player)self;
int i = pl.activeweapon;
@ -135,7 +142,8 @@ void Weapons_Secondary(void)
#endif
}
void Weapons_Reload(void)
void
Weapons_Reload(void)
{
player pl = (player)self;
int i = pl.activeweapon;
@ -153,7 +161,8 @@ void Weapons_Reload(void)
#endif
}
void Weapons_Release(void)
void
Weapons_Release(void)
{
player pl = (player)self;
int i = pl.activeweapon;
@ -164,7 +173,8 @@ void Weapons_Release(void)
pl.gflags &= ~GF_SEMI_TOGGLED;
}
void Weapons_DrawCrosshair(void)
void
Weapons_DrawCrosshair(void)
{
player pl = (player)self;
int i = pl.activeweapon;
@ -173,7 +183,8 @@ void Weapons_DrawCrosshair(void)
}
}
string Weapons_GetWorldmodel(int id)
string
Weapons_GetWorldmodel(int id)
{
if (g_weapons[id].wmodel != __NULL__) {
return g_weapons[id].wmodel();
@ -182,7 +193,8 @@ string Weapons_GetWorldmodel(int id)
return "";
}
string Weapons_GetPlayermodel(int id)
string
Weapons_GetPlayermodel(int id)
{
if (g_weapons[id].pmodel != __NULL__) {
return g_weapons[id].pmodel();
@ -191,7 +203,8 @@ string Weapons_GetPlayermodel(int id)
return "";
}
string Weapons_GetDeathmessage(int id)
string
Weapons_GetDeathmessage(int id)
{
if (g_weapons[id].deathmsg != __NULL__) {
return g_weapons[id].deathmsg();
@ -201,7 +214,8 @@ string Weapons_GetDeathmessage(int id)
}
#ifdef SERVER
float Weapons_GetAim(int id)
float
Weapons_GetAim(int id)
{
if (g_weapons[id].aimanim != __NULL__) {
return g_weapons[id].aimanim();
@ -212,7 +226,8 @@ float Weapons_GetAim(int id)
#endif
#ifdef CLIENT
void Weapons_HUDPic(int id, int s, vector pos, float a)
void
Weapons_HUDPic(int id, int s, vector pos, float a)
{
if (g_weapons[id].hudpic != __NULL__) {
g_weapons[id].hudpic(s, pos, a);
@ -220,7 +235,8 @@ void Weapons_HUDPic(int id, int s, vector pos, float a)
}
#endif
void Weapons_MakeVectors(void)
void
Weapons_MakeVectors(void)
{
#ifdef SERVER
player pl = (player)self;
@ -230,7 +246,8 @@ void Weapons_MakeVectors(void)
#endif
}
vector Weapons_GetCameraPos(void)
vector
Weapons_GetCameraPos(void)
{
#ifdef SERVER
return self.origin + self.view_ofs;
@ -239,7 +256,8 @@ vector Weapons_GetCameraPos(void)
#endif
}
void Weapons_ViewAnimation(int i)
void
Weapons_ViewAnimation(int i)
{
#ifdef CLIENT
player pl = (player)pSeat->m_ePlayer;
@ -252,13 +270,20 @@ void Weapons_ViewAnimation(int i)
#ifdef CLIENT
int View_GetAnimation(void);
int Weapons_GetAnimation(void)
{
return View_GetAnimation();
}
#endif
void Weapons_ViewPunchAngle(vector add)
int
Weapons_GetAnimation(void)
{
#ifdef CLIENT
return View_GetAnimation();
#else
return 0;
#endif
}
void
Weapons_ViewPunchAngle(vector add)
{
#ifdef CLIENT
player pl = (player)self;
@ -266,7 +291,8 @@ void Weapons_ViewPunchAngle(vector add)
#endif
}
int Weapons_IsPresent(player pl, int w)
int
Weapons_IsPresent(player pl, int w)
{
if (pl.g_items & g_weapons[w].id) {
return TRUE;

View File

@ -150,7 +150,7 @@ CSQC_RenderScene(void)
void
CSQC_UpdateView(float w, float h, float focus)
{
player pl;
player pl = __NULL__;
spectator spec;
int s;
@ -695,16 +695,6 @@ CSQC_ConsoleCommand(string sCMD)
measurepos = [0,0,0];
}
break;
case "dev_sentence":
static CBaseEntity foo;
if (!foo) {
foo = spawn(CBaseEntity);
foo.drawmask = MASK_ENGINE;
setmodel(foo, "sprites/muzzleflash1.spr");
}
setorigin(foo, getproperty(VF_ORIGIN));
foo.Sentence(argv(1));
break;
case "vote":
if (argv(1) == "yes") {
sendevent("VoteY", "");
@ -932,7 +922,8 @@ CSQC_Ent_Update(float new)
/* any differences in things that are read below are now
officially from prediction misses. */
pl.ReceiveEntity(new);
float a = readfloat();
pl.ReceiveEntity(new, a);
break;
case ENT_SPECTATOR:
spectator spec = (spectator)self;

View File

@ -14,65 +14,13 @@
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
====================
View_ShellEject
Spawns a shell tempentity. Looking fancy
====================
*/
void
Event_EjectShell(float mtime, __inout float btime)
{
// static void Event_EjectShell_Death(void) {
// remove(self);
// }
// static void Event_EjectShell_Touch(void) {
// if (other == world)
// Sound_Play(self, CHAN_BODY, "modelevent_shell.land");
// }
// vector vEndPos;
//
// /* only play once */
// if (mtime == btime)
// return;
//
// if (pSeat->m_fEjectModel)
// if (btime <= pSeat->m_flEjectTime && (mtime > pSeat->m_flEjectTime)) {
// entity eShell = spawn();
// eShell.modelindex = pSeat->m_fEjectModel;
// eShell.solid = SOLID_BBOX;
// eShell.movetype = MOVETYPE_BOUNCE;
// eShell.drawmask = MASK_ENGINE;
// eShell.angles = [pSeat->m_eViewModel.angles[0], pSeat->m_eViewModel.angles[1], 0];
// eShell.velocity = pSeat->m_vecPredictedVelocity;
//
// makevectors(pSeat->m_eViewModel.angles);
// eShell.velocity += (v_forward * pSeat->m_vecEjectVel[0]);
// eShell.velocity += (v_right * pSeat->m_vecEjectVel[1]);
// eShell.velocity += (v_up * pSeat->m_vecEjectVel[2]);
// eShell.touch = Event_EjectShell_Touch;
//
// eShell.avelocity = [0,45,900];
// eShell.think = Event_EjectShell_Death;
// eShell.nextthink = time + 2.5f;
// pSeat->m_fEjectModel = 0;
// pSeat->m_flEjectTime = 0.0f;
// setsize(eShell, [0,0,0], [0,0,0]);
// setorigin(eShell, pSeat->m_vecEjectPos);
// Sound_Play(eShell, CHAN_BODY, "modelevent_shell.eject");
// print(sprintf("%f %f\n", mtime, btime));
//
// }
// btime = mtime;
}
/*
====================
Event_ProcessModel
Called by the engine whenever a model
tries to play an event.
Called by the engine whenever a model tries to play an event and isn't handled
by ClientGame_ModelEvent. This gives a mod the chance to override builtin
events - hook into them and all that.
====================
*/
void
@ -86,44 +34,45 @@ Event_ProcessModel(float flTimeStamp, int iCode, string strData)
pSeat->m_eMuzzleflash.alpha = 1.0f;
pSeat->m_eMuzzleflash.scale = 0.25;
pSeat->m_eMuzzleflash.skin = pSeat->m_iVMBones;
// Event_EjectShell();
break;
case 5011: /* muzzle flash on attachment 1 */
pSeat->m_eMuzzleflash.alpha = 1.0f;
pSeat->m_eMuzzleflash.scale = 0.25;
pSeat->m_eMuzzleflash.skin = pSeat->m_iVMBones + 1;
// Event_EjectShell();
break;
case 5021: /* muzzle flash on attachment 2 */
pSeat->m_eMuzzleflash.alpha = 1.0f;
pSeat->m_eMuzzleflash.scale = 0.25;
pSeat->m_eMuzzleflash.skin = pSeat->m_iVMBones + 2;
// Event_EjectShell();
break;
case 5031: /* muzzle flash on attachment 3 */
pSeat->m_eMuzzleflash.alpha = 1.0f;
pSeat->m_eMuzzleflash.scale = 0.25;
pSeat->m_eMuzzleflash.skin = pSeat->m_iVMBones + 3;
// Event_EjectShell();
break;
}
}
/*
====================
View_ShellEject
Event_Callback
Spawns a shell tempentity. Looking fancy
Calls one event based on viewmodel frame timeline, to avoid running more than once.
Prediction runs through weapon many times per frame, so we have to do call special
events based on time passed on the viewmodel.
====================
*/
void
Event_Callback(float mtime, __inout float btime)
{
/* don't bother if no valid function is set */
if (pSeat->m_pEventCall == __NULL__)
return;
/* if the sequence ain't the same anymore... */
if (pSeat->m_flEventFrame != pSeat->m_eViewModel.frame)
return;
/* if the model changed... */
if (pSeat->m_flEventMdl != pSeat->m_eViewModel.modelindex)
return;
@ -131,6 +80,7 @@ Event_Callback(float mtime, __inout float btime)
if (mtime == btime)
return;
/* call when we've passed the keyframe the first time */
if (btime <= pSeat->m_flEventTime && (mtime > pSeat->m_flEventTime)) {
pSeat->m_pEventCall();
pSeat->m_pEventCall = __NULL__;

View File

@ -14,7 +14,17 @@
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* EV_MUSICTRACK */
/* We'll let the menu handle the music, as it may want to do special stuff with
* track orders and whatnot. We also cannot predict the paths that easily
* without making wrong assumptions. */
/*
====================
Music_ParseTrack
Handles EV_MUSICTRACK, a type of music track that only plays once.
====================
*/
void
Music_ParseTrack(void)
{
@ -23,7 +33,13 @@ Music_ParseTrack(void)
localcmd(sprintf("menu_musictrack %i\n", track));
}
/* EV_MUSICLOOP */
/*
====================
Music_ParseLoop
Handles EV_MUSICLOOP, a type of music track that loops continously.
====================
*/
void
Music_ParseLoop(void)
{

View File

@ -14,7 +14,8 @@
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
class CBaseNPC:CBaseEntity
class
CBaseNPC:CBaseEntity
{
int body;
float frame_last;

View File

@ -17,8 +17,10 @@
void
player::draw(void)
{
/* mouth flapping */
this.bonecontrol5 = getplayerkeyfloat(this.entnum - 1, "voiploudness");
/* DEBUG! */
if (cvar("bonetest") == 1) {
this.bonecontrol1 = cvar("bonecontrol1");
this.bonecontrol2 = cvar("bonecontrol2");
@ -35,8 +37,10 @@ player::draw(void)
float
player::predraw(void)
{
/* Run animations regardless of rendering the player */
/* run animations regardless of rendering the player */
draw();
/* make sure we're enabling shadow rendering on us */
effects &= ~EF_NOSHADOW;
if (autocvar_cl_thirdperson == TRUE || this.entnum != player_localentnum) {
@ -47,6 +51,7 @@ player::predraw(void)
Player_PreDraw(this, FALSE);
removeentity(this);
}
return PREDRAW_NEXT;
}

View File

@ -26,19 +26,6 @@ Propagate our pmove state to whatever the current frame before its stomped on
void
Predict_PlayerPreFrame(player pl)
{
/* base player attributes/fields we're going to roll back */
pl.net_origin = pl.origin;
pl.net_velocity = pl.velocity;
pl.net_flags = pl.flags;
pl.net_gflags = pl.gflags;
pl.net_jumptime = pl.jumptime;
pl.net_teleport_time = pl.teleport_time;
pl.net_viewzoom = pl.viewzoom;
pl.net_punchangle = pl.punchangle;
pl.net_w_attack_next = pl.w_attack_next;
pl.net_w_idle_next = pl.w_idle_next;
pl.net_weapontime = pl.weapontime;
/* this is where a game/mod would decide to add more prediction rollback
* information. */
pl.PredictPreFrame();
@ -80,19 +67,6 @@ Rewind our pmove state back to before we started predicting.
void
Predict_PlayerPostFrame(player pl)
{
/* finally roll the values back */
pl.origin = pl.net_origin;
pl.velocity = pl.net_velocity;
pl.flags = pl.net_flags;
pl.gflags = pl.net_gflags;
pl.jumptime = pl.net_jumptime;
pl.teleport_time = pl.net_teleport_time;
pl.viewzoom = pl.net_viewzoom;
pl.punchangle = pl.net_punchangle;
pl.w_attack_next = pl.net_w_attack_next;
pl.w_idle_next = pl.net_w_idle_next;
pl.weapontime = pl.net_weapontime;
/* give the game/mod a chance to roll back its values too */
pl.PredictPostFrame();

View File

@ -16,7 +16,7 @@
/*
=================
HUD_DrawVoice
Voice_DrawHUD
Draws a little notification for anyone using voice chat
=================
@ -64,7 +64,9 @@ Voice_DrawHUD(void)
/*
=================
Player_PreDraw
Voice_Draw3D
Draws an image above a player in-world if they're speaking over VoIP
=================
*/
void

View File

@ -83,6 +83,8 @@ prop_dynamic::Init(void)
CBaseEntity::Init();
precache_model(model);
print(model);
print("\n");
setmodel(this, model);
setorigin(this, origin);
solid = SOLID_NOT;

View File

@ -43,9 +43,6 @@ player_weaponstrip::Trigger(entity act, int unused)
for (int i = 1; i < Weapon_GetCount(); i++) {
pl.g_items &= ~Weapon_GetBitID(i);
pl.activeweapon = 0;
pl.a_ammo1 = 0;
pl.a_ammo2 = 0;
pl.a_ammo3 = 0;
}
}

View File

@ -117,7 +117,7 @@ func_illusionary::Init(void)
/* this entity is being controlled by the server, remove it */
if (targetname) {
think = Util_Destroy;
nextthink = time;
nextthink = time + 0.01f; /* time may be 0.0 */
return;
}

View File

@ -112,7 +112,7 @@ func_wall::Init(void)
/* this entity is being controlled by the server, remove it */
if (targetname) {
think = Util_Destroy;
nextthink = time;
nextthink = time + 0.01f; /* time may be 0.0 */
return;
}

View File

@ -66,10 +66,14 @@ enumflags
};
#ifdef CLIENT
class light_dynamic
#else
class light_dynamic:CBaseTrigger
class CBaseTrigger:CBaseEntity
{
};
#endif
class light_dynamic:CBaseTrigger
{
vector m_vecLight;
float m_flIntensity;
@ -80,19 +84,21 @@ class light_dynamic:CBaseTrigger
float m_flStyle;
string m_strPattern;
int m_iState;
int m_iStartActive;
void(void) light_dynamic;
virtual void(string, string) SpawnKey;
#ifdef CLIENT
virtual void(float) ReceiveEntity;
virtual float(void) predraw;
virtual void(void) Initialized;
virtual void(void) RendererRestarted;
#else
int m_iStartActive;
virtual void(entity, int) Trigger;
virtual void(void) Respawn;
virtual float(entity, float) SendEntity;
virtual void(string, string) SpawnKey;
virtual void(entity, string, string) Input;
virtual void(void) ParentUpdate;
#endif
@ -157,6 +163,48 @@ light_dynamic::ReceiveEntity(float flFlags)
classname = "light_dynamic";
}
void
DLight_Restart(void)
{
for (entity b = world; (b = find(b, ::classname, "light_dynamic"));) {
light_dynamic pf = (light_dynamic) b;
pf.RendererRestarted();
print("RELOADED LIGHT!\n");
}
}
void
light_dynamic::RendererRestarted(void)
{
float p = dynamiclight_spawnstatic(origin, m_flDistance, m_vecLight);
dynamiclight_set(p, LFIELD_ANGLES, angles);
if (m_flStyle)
dynamiclight_set(p, LFIELD_STYLE, m_flStyle);
if (m_strPattern)
dynamiclight_set(p, LFIELD_STYLESTRING, m_strPattern);
print("I'm a static light! At ");
print(vtos(origin));
print("at a color of ");
print(vtos(m_vecLight));
print(" and an intensity of ");
print(ftos(m_flDistance));
print("!\n");
}
void
light_dynamic::Initialized(void)
{
/* we're meant to be a server controlled entity. cancel out and kill us ASAP */
if (targetname) {
think = Util_Destroy;
nextthink = time + 0.1f;
return;
}
print("static!\n");
RendererRestarted();
//remove(this);
}
#else
void
light_dynamic::ParentUpdate(void)
@ -291,6 +339,17 @@ light_dynamic::Input(entity eAct, string strInput, string strData)
CBaseTrigger::Input(eAct, strInput, strData);
}
}
void
light_dynamic::Respawn(void)
{
SetSolid(SOLID_NOT);
SetSize([-16,-16,-16], [16,16,16]);
SetOrigin(m_oldOrigin);
SetAngles(m_oldAngle);
m_iState = (m_iStartActive == 1) ? 1 : 0;
}
#endif
void
light_dynamic::SpawnKey(string strKey, string strValue)
@ -330,34 +389,28 @@ light_dynamic::SpawnKey(string strKey, string strValue)
}
}
void
light_dynamic::Respawn(void)
{
SetSolid(SOLID_NOT);
SetSize([-16,-16,-16], [16,16,16]);
SetOrigin(m_oldOrigin);
SetAngles(m_oldAngle);
m_iState = (m_iStartActive == 1) ? 1 : 0;
}
#endif
void
light_dynamic::light_dynamic(void)
{
#ifdef CLIENT
drawmask = MASK_ENGINE;
#else
m_vecLight = [255,255,255];
m_flDistance = 256;
m_iStartActive = 1;
#ifdef CLIENT
drawmask = MASK_ENGINE;
#else
CBaseTrigger::CBaseTrigger();
/* the client-side will handle dlights without targetnames */
if (!targetname) {
think = Util_Destroy;
nextthink = time + 0.1f;
}
#endif
}
/* workaround for q3map2, as it turns any entity starting with light*
into a generic static world light. */
#ifndef CLIENT
CLASSEXPORT(dynamic_light, light_dynamic)
#endif
class dynamic_light:light_dynamic
{
};

View File

@ -32,7 +32,7 @@
var int autocvar_mp_flashlight = TRUE;
void FX_Impact(int, vector, vector);
void FX_Impact(impactType_t, vector, vector);
void FX_Explosion(vector);
void FX_GibHuman(vector);
void Footsteps_Update(void);
@ -44,7 +44,7 @@ void TraceAttack_SetPenetrationPower(int);
#endif
void Damage_Radius(vector, entity, float, float, int, int);
void Damage_Apply(entity, entity, float, int, int);
void Damage_Apply(entity, entity, float, int, damageType_t);
void Client_FixAngle(entity, vector);
void Client_ShakeOnce(vector, float, float, float, float);
@ -79,7 +79,7 @@ hashtable hashMaterials;
entity g_dmg_eAttacker;
entity g_dmg_eTarget;
int g_dmg_iDamage;
int g_dmg_iHitBody;
bodyType_t g_dmg_iHitBody;
int g_dmg_iFlags;
int g_dmg_iWeapon;

View File

@ -26,4 +26,4 @@ string Plugin_ParseClientCommand(string);
int Plugin_PlayerConnect(base_player);
int Plugin_PlayerDisconnect(base_player);
int Plugin_PlayerEntered(base_player);
void Plugin_PlayerObituary(entity, entity, int, int, int);
void Plugin_PlayerObituary(entity, entity, int, bodyType_t, int);

View File

@ -319,7 +319,7 @@ Deathmessage hook
=================
*/
void
Plugin_PlayerObituary(entity attk, entity targ, int weapon, int body, int dmg)
Plugin_PlayerObituary(entity attk, entity targ, int weapon, bodyType_t body, int dmg)
{
void(entity,entity,int,int,int) vFunc;

View File

@ -31,6 +31,7 @@ TraceAttack_FireSingle(vector vecPos, vector vAngle, int iDamage, int iWeapon)
if (trace_ent.takedamage == DAMAGE_YES && trace_ent.iBleeds) {
Sound_Play(trace_ent, CHAN_BODY, "damage_bullet.hit");
FX_Blood(trace_endpos, [1,0,0]);
#ifdef CSTRIKE
player pl = (player)trace_ent;
@ -63,123 +64,120 @@ TraceAttack_FireSingle(vector vecPos, vector vAngle, int iDamage, int iWeapon)
if (trace_surface_id == BODY_HEAD)
iDamage *= 3;
#endif
} else {
switch (serverkeyfloat("*bspversion")) {
case BSPVER_HL:
surf = getsurfacenearpoint(trace_ent, trace_endpos);
tex_name = Materials_FixName(getsurfacetexture(trace_ent, surf));
/* our hashtable is the key to all this */
switch ((float)hash_get(hashMaterials, tex_name)) {
case MATID_ALIEN:
FX_Impact(IMPACT_ALIEN, trace_endpos, trace_plane_normal);
break;
case MATID_COMPUTER:
FX_Impact(IMPACT_COMPUTER, trace_endpos, trace_plane_normal);
break;
case MATID_CONCRETE:
FX_Impact(IMPACT_CONCRETE, trace_endpos, trace_plane_normal);
break;
case MATID_DIRT:
FX_Impact(IMPACT_DIRT, trace_endpos, trace_plane_normal);
break;
case MATID_BLOODYFLESH:
case MATID_FLESH:
FX_Impact(IMPACT_FLESH, trace_endpos, trace_plane_normal);
break;
case MATID_FOLIAGE:
FX_Impact(IMPACT_FOLIAGE, trace_endpos, trace_plane_normal);
break;
case MATID_GLASS:
FX_Impact(IMPACT_GLASS, trace_endpos, trace_plane_normal);
break;
case MATID_GRATE:
FX_Impact(IMPACT_GRATE, trace_endpos, trace_plane_normal);
break;
case MATID_METAL:
FX_Impact(IMPACT_METAL, trace_endpos, trace_plane_normal);
break;
case MATID_SLOSH:
FX_Impact(IMPACT_SLOSH, trace_endpos, trace_plane_normal);
break;
case MATID_SNOW:
FX_Impact(IMPACT_SNOW, trace_endpos, trace_plane_normal);
break;
case MATID_TILE:
FX_Impact(IMPACT_TILE, trace_endpos, trace_plane_normal);
break;
case MATID_VENT:
FX_Impact(IMPACT_VENT, trace_endpos, trace_plane_normal);
break;
case MATID_WOOD:
FX_Impact(IMPACT_WOOD, trace_endpos, trace_plane_normal);
break;
default:
FX_Impact(IMPACT_DEFAULT, trace_endpos, trace_plane_normal);
break;
}
break;
case BSPVER_Q3: /* Q3 */
case BSPVER_RTCW: /* RtCW */
case BSPVER_RBSP: /* RFVBSP */
switch (trace_surfaceflagsi) {
case SURF_ALIEN:
FX_Impact(IMPACT_ALIEN, trace_endpos, trace_plane_normal);
break;
case SURF_COMPUTER:
FX_Impact(IMPACT_COMPUTER, trace_endpos, trace_plane_normal);
break;
case SURF_CONCRETE:
FX_Impact(IMPACT_CONCRETE, trace_endpos, trace_plane_normal);
break;
case SURF_DIRT:
FX_Impact(IMPACT_DIRT, trace_endpos, trace_plane_normal);
break;
case SURF_BLOODYFLESH:
FX_Impact(IMPACT_FLESH, trace_endpos, trace_plane_normal);
break;
case SURF_FOLIAGE:
FX_Impact(IMPACT_FOLIAGE, trace_endpos, trace_plane_normal);
break;
case SURF_GLASS:
FX_Impact(IMPACT_GLASS, trace_endpos, trace_plane_normal);
break;
case SURF_GRATE:
FX_Impact(IMPACT_GRATE, trace_endpos, trace_plane_normal);
break;
case SURF_METAL:
FX_Impact(IMPACT_METAL, trace_endpos, trace_plane_normal);
break;
case SURF_SLOSH:
FX_Impact(IMPACT_SLOSH, trace_endpos, trace_plane_normal);
break;
case SURF_SNOW:
FX_Impact(IMPACT_SNOW, trace_endpos, trace_plane_normal);
break;
case SURF_TILE:
FX_Impact(IMPACT_TILE, trace_endpos, trace_plane_normal);
break;
case SURF_VENT:
FX_Impact(IMPACT_VENT, trace_endpos, trace_plane_normal);
break;
case SURF_WOOD:
FX_Impact(IMPACT_WOOD, trace_endpos, trace_plane_normal);
break;
default:
FX_Impact(IMPACT_DEFAULT, trace_endpos, trace_plane_normal);
break;
}
break;
default:
FX_Impact(IMPACT_DEFAULT, trace_endpos, trace_plane_normal);
}
}
if (trace_ent.takedamage == DAMAGE_YES)
Damage_Apply(trace_ent, self, iDamage, iWeapon, DMG_BULLET);
}
if (trace_ent.iBleeds == TRUE) {
FX_Blood(trace_endpos, [1,0,0]);
return;
}
switch (serverkeyfloat("*bspversion")) {
case BSPVER_HL:
surf = getsurfacenearpoint(trace_ent, trace_endpos);
tex_name = Materials_FixName(getsurfacetexture(trace_ent, surf));
/* our hashtable is the key to all this */
switch ((float)hash_get(hashMaterials, tex_name)) {
case MATID_ALIEN:
FX_Impact(IMPACT_ALIEN, trace_endpos, trace_plane_normal);
break;
case MATID_COMPUTER:
FX_Impact(IMPACT_COMPUTER, trace_endpos, trace_plane_normal);
break;
case MATID_CONCRETE:
FX_Impact(IMPACT_CONCRETE, trace_endpos, trace_plane_normal);
break;
case MATID_DIRT:
FX_Impact(IMPACT_DIRT, trace_endpos, trace_plane_normal);
break;
case MATID_BLOODYFLESH:
case MATID_FLESH:
FX_Impact(IMPACT_FLESH, trace_endpos, trace_plane_normal);
break;
case MATID_FOLIAGE:
FX_Impact(IMPACT_FOLIAGE, trace_endpos, trace_plane_normal);
break;
case MATID_GLASS:
FX_Impact(IMPACT_GLASS, trace_endpos, trace_plane_normal);
break;
case MATID_GRATE:
FX_Impact(IMPACT_GRATE, trace_endpos, trace_plane_normal);
break;
case MATID_METAL:
FX_Impact(IMPACT_METAL, trace_endpos, trace_plane_normal);
break;
case MATID_SLOSH:
FX_Impact(IMPACT_SLOSH, trace_endpos, trace_plane_normal);
break;
case MATID_SNOW:
FX_Impact(IMPACT_SNOW, trace_endpos, trace_plane_normal);
break;
case MATID_TILE:
FX_Impact(IMPACT_TILE, trace_endpos, trace_plane_normal);
break;
case MATID_VENT:
FX_Impact(IMPACT_VENT, trace_endpos, trace_plane_normal);
break;
case MATID_WOOD:
FX_Impact(IMPACT_WOOD, trace_endpos, trace_plane_normal);
break;
default:
FX_Impact(IMPACT_DEFAULT, trace_endpos, trace_plane_normal);
break;
}
break;
case BSPVER_Q3: /* Q3 */
case BSPVER_RTCW: /* RtCW */
case BSPVER_RBSP: /* RFVBSP */
switch (trace_surfaceflagsi) {
case SURF_ALIEN:
FX_Impact(IMPACT_ALIEN, trace_endpos, trace_plane_normal);
break;
case SURF_COMPUTER:
FX_Impact(IMPACT_COMPUTER, trace_endpos, trace_plane_normal);
break;
case SURF_CONCRETE:
FX_Impact(IMPACT_CONCRETE, trace_endpos, trace_plane_normal);
break;
case SURF_DIRT:
FX_Impact(IMPACT_DIRT, trace_endpos, trace_plane_normal);
break;
case SURF_BLOODYFLESH:
FX_Impact(IMPACT_FLESH, trace_endpos, trace_plane_normal);
break;
case SURF_FOLIAGE:
FX_Impact(IMPACT_FOLIAGE, trace_endpos, trace_plane_normal);
break;
case SURF_GLASS:
FX_Impact(IMPACT_GLASS, trace_endpos, trace_plane_normal);
break;
case SURF_GRATE:
FX_Impact(IMPACT_GRATE, trace_endpos, trace_plane_normal);
break;
case SURF_METAL:
FX_Impact(IMPACT_METAL, trace_endpos, trace_plane_normal);
break;
case SURF_SLOSH:
FX_Impact(IMPACT_SLOSH, trace_endpos, trace_plane_normal);
break;
case SURF_SNOW:
FX_Impact(IMPACT_SNOW, trace_endpos, trace_plane_normal);
break;
case SURF_TILE:
FX_Impact(IMPACT_TILE, trace_endpos, trace_plane_normal);
break;
case SURF_VENT:
FX_Impact(IMPACT_VENT, trace_endpos, trace_plane_normal);
break;
case SURF_WOOD:
FX_Impact(IMPACT_WOOD, trace_endpos, trace_plane_normal);
break;
default:
FX_Impact(IMPACT_DEFAULT, trace_endpos, trace_plane_normal);
break;
}
break;
default:
FX_Impact(IMPACT_DEFAULT, trace_endpos, trace_plane_normal);
}
#ifdef BULLETPENETRATION
if (iTotalPenetrations > 0) {

View File

@ -14,7 +14,7 @@
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
enumflags
typedef enumflags
{
DMG_GENERIC,
DMG_CRUSH,
@ -41,4 +41,4 @@ enumflags
DMG_SLOWFREEZE,
DMG_SKIP_ARMOR,
DMG_SKIP_RAGDOLL
};
} damageType_t;

View File

@ -17,7 +17,6 @@
#include "player.h"
#include "damage.h"
#include "flags.h"
#include "hitmesh.h"
#include "entities.h"
#include "events.h"
#include "flags.h"

View File

@ -15,7 +15,7 @@
*/
/* global hitmesh definitions */
enum
typedef enum
{
BODY_DEFAULT,
BODY_HEAD,
@ -25,4 +25,4 @@ enum
BODY_ARMRIGHT,
BODY_LEGLEFT,
BODY_LEGRIGHT
};
} bodyType_t;

View File

@ -3,4 +3,5 @@ spectator.qc
pmove.qc
sound.qc
math.qc
player.qc
#endlist

View File

@ -15,7 +15,7 @@
*/
// Impact types
enum
typedef enum
{
IMPACT_MELEE,
IMPACT_EXPLOSION,
@ -34,8 +34,7 @@ enum
IMPACT_TILE,
IMPACT_VENT,
IMPACT_WOOD
};
} impactType_t;
/* TW/NEXT specific game flags */

View File

@ -1,36 +1,69 @@
/*
* Copyright (c) 2016-2021 Marco Hladik <marco@icculus.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#define PREDICTED_INT(x) int x; int x ##_net
#define PREDICTED_FLOAT(x) float x; float x ##_net
#define PREDICTED_VECTOR(x) vector x; vector x ##_net
#define PREDICTED_INT_N(x) int x ##_net
#define PREDICTED_FLOAT_N(x) float x ##_net
#define PREDICTED_VECTOR_N(x) vector x ##_net
#define ROLL_BACK(x) x = x ##_net
#define SAVE_STATE(x) x ##_net = x
#define ATTR_CHANGED(x) (x ##_net != x)
#define VEC_CHANGED(x,y) (x ##_net[y] != x[y])
class
#ifdef SERVER
class base_player:CBaseEntity
base_player:CBaseEntity
#else
class base_player
base_player
#endif
{
float health;
float armor;
PREDICTED_FLOAT(health);
PREDICTED_FLOAT(armor);
/* When the weapon is done firing */
float w_attack_next;
/* When to play the next idle animation */
float w_idle_next;
PREDICTED_FLOAT_N(modelindex);
PREDICTED_VECTOR_N(origin);
PREDICTED_VECTOR_N(velocity);
PREDICTED_VECTOR_N(angles);
PREDICTED_FLOAT_N(flags);
PREDICTED_FLOAT_N(gflags);
PREDICTED_FLOAT(viewzoom);
PREDICTED_VECTOR_N(view_ofs);
PREDICTED_FLOAT_N(movetype);
PREDICTED_VECTOR(v_angle);
/* Magazine/Clip */
int a_ammo1;
/* Rest in the inventory */
int a_ammo2;
/* Special ammo */
int a_ammo3;
PREDICTED_FLOAT(w_attack_next);
PREDICTED_FLOAT(w_idle_next);
PREDICTED_FLOAT(teleport_time);
PREDICTED_FLOAT(weapontime);
PREDICTED_VECTOR(punchangle);
/* We can't use the default .items field, because FTE will assume
* effects of some bits. Such as invisibility, quad, etc.
* also, modders probably want 32 bits for items. */
int g_items;
PREDICTED_INT(g_items);
PREDICTED_FLOAT(activeweapon);
float activeweapon;
float viewzoom;
vector punchangle;
vector view_ofs;
float weapontime;
vector v_angle;
/* these are NOT networked */
int a_ammo1;
int a_ammo2;
int a_ammo3;
/* any mods that use hooks */
entity hook;
@ -39,43 +72,13 @@ class base_player
entity vehicle;
#ifdef CLIENT
/* Prediction */
vector net_origin;
vector net_velocity;
float net_flags;
float net_gflags;
float net_w_attack_next;
float net_w_idle_next;
float net_jumptime;
float net_teleport_time;
float net_weapontime;
float net_viewzoom;
vector net_punchangle;
int net_ammo1;
int net_ammo2;
int net_ammo3;
int sequence;
float pitch;
#else
/* conditional networking */
int old_modelindex;
vector old_origin;
vector old_angles;
vector old_velocity;
int old_flags;
int old_gflags;
int old_activeweapon;
int old_items;
float old_health;
float old_armor;
int old_movetype;
float old_viewofs;
int old_baseframe;
int old_frame;
int old_a_ammo1;
int old_a_ammo2;
int old_a_ammo3;
virtual void(float, float) ReceiveEntity;
virtual void(void) PredictPreFrame;
virtual void(void) PredictPostFrame;
#else
int voted;
int step;
@ -84,5 +87,8 @@ class base_player
float underwater_time;
float underwater_dmg;
float pain_time;
virtual void(void) EvaluateEntity;
virtual float(entity, float) SendEntity;
#endif
};

272
src/shared/player.qc Normal file
View File

@ -0,0 +1,272 @@
/*
* Copyright (c) 2016-2021 Marco Hladik <marco@icculus.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifdef CLIENT
void
base_player::ReceiveEntity(float new, float fl)
{
/* seed for our prediction table */
sequence = servercommandframe;
/* HACK: we need to make this more reliable */
if (fl == UPDATE_ALL) {
/* we respawned */
gravity = __NULL__;
}
if (fl & PLAYER_MODELINDEX)
modelindex = readshort();
if (fl & PLAYER_ORIGIN) {
origin[0] = readcoord();
origin[1] = readcoord();
}
if (fl & PLAYER_ORIGIN_Z)
origin[2] = readcoord();
if (fl & PLAYER_ANGLES_X)
pitch = readfloat();
if (fl & PLAYER_ANGLES_Y)
angles[1] = readfloat();
if (fl & PLAYER_ANGLES_Z)
angles[2] = readfloat();
if (fl & PLAYER_VELOCITY) {
velocity[0] = readcoord();
velocity[1] = readcoord();
}
if (fl & PLAYER_VELOCITY_Z)
velocity[2] = readcoord();
if (fl & PLAYER_FLAGS) {
flags = readfloat();
gflags = readfloat();
}
if (fl & PLAYER_WEAPON)
activeweapon = readbyte();
if (fl & PLAYER_ITEMS)
g_items = (__variant)readfloat();
if (fl & PLAYER_HEALTH)
health = readbyte();
if (fl & PLAYER_ARMOR)
armor = readbyte();
if (fl & PLAYER_MOVETYPE)
movetype = readbyte();
if (fl & PLAYER_VIEWOFS)
view_ofs[2] = readfloat();
/* TO OPTIMISE */
teleport_time = readfloat();
viewzoom = readfloat();
weapontime = readfloat();
w_attack_next = readfloat();
w_idle_next = readfloat();
punchangle[0] = readfloat();
punchangle[1] = readfloat();
punchangle[2] = readfloat();
}
void
base_player::PredictPreFrame(void)
{
/* base player attributes/fields we're going to roll back */
SAVE_STATE(modelindex);
SAVE_STATE(origin);
SAVE_STATE(angles);
SAVE_STATE(velocity);
SAVE_STATE(flags);
SAVE_STATE(gflags);
SAVE_STATE(activeweapon);
SAVE_STATE(g_items);
SAVE_STATE(health);
SAVE_STATE(armor);
SAVE_STATE(movetype);
SAVE_STATE(view_ofs);
/* TO OPTIMISE */
SAVE_STATE(teleport_time);
SAVE_STATE(viewzoom);
SAVE_STATE(weapontime);
SAVE_STATE(w_attack_next);
SAVE_STATE(w_idle_next);
SAVE_STATE(punchangle);
}
void
base_player::PredictPostFrame(void)
{
/* finally roll the values back */
ROLL_BACK(modelindex);
ROLL_BACK(origin);
ROLL_BACK(angles);
ROLL_BACK(velocity);
ROLL_BACK(flags);
ROLL_BACK(gflags);
ROLL_BACK(activeweapon);
ROLL_BACK(g_items);
ROLL_BACK(health);
ROLL_BACK(armor);
ROLL_BACK(movetype);
ROLL_BACK(view_ofs);
/* TO OPTIMISE */
ROLL_BACK(teleport_time);
ROLL_BACK(viewzoom);
ROLL_BACK(weapontime);
ROLL_BACK(w_attack_next);
ROLL_BACK(w_idle_next);
ROLL_BACK(punchangle);
}
#else
void
base_player::EvaluateEntity(void)
{
SendFlags |= PLAYER_KEEPALIVE;
if (ATTR_CHANGED(modelindex))
SendFlags |= PLAYER_MODELINDEX;
if (VEC_CHANGED(origin, 0))
SendFlags |= PLAYER_ORIGIN;
if (VEC_CHANGED(origin, 1))
SendFlags |= PLAYER_ORIGIN;
if (VEC_CHANGED(origin, 2))
SendFlags |= PLAYER_ORIGIN_Z;
if (VEC_CHANGED(v_angle, 0))
SendFlags |= PLAYER_ANGLES_X;
if (VEC_CHANGED(angles, 1))
SendFlags |= PLAYER_ANGLES_Y;
if (VEC_CHANGED(angles, 2))
SendFlags |= PLAYER_ANGLES_Z;
if (VEC_CHANGED(velocity, 0))
SendFlags |= PLAYER_VELOCITY;
if (VEC_CHANGED(velocity, 1))
SendFlags |= PLAYER_VELOCITY;
if (VEC_CHANGED(velocity, 2))
SendFlags |= PLAYER_VELOCITY_Z;
if (ATTR_CHANGED(flags))
SendFlags |= PLAYER_FLAGS;
if (ATTR_CHANGED(gflags))
SendFlags |= PLAYER_FLAGS;
if (ATTR_CHANGED(activeweapon))
SendFlags |= PLAYER_WEAPON;
if (ATTR_CHANGED(g_items))
SendFlags |= PLAYER_ITEMS;
if (ATTR_CHANGED(health))
SendFlags |= PLAYER_HEALTH;
if (ATTR_CHANGED(armor))
SendFlags |= PLAYER_ARMOR;
if (ATTR_CHANGED(movetype))
SendFlags |= PLAYER_MOVETYPE;
if (ATTR_CHANGED(view_ofs))
SendFlags |= PLAYER_VIEWOFS;
SAVE_STATE(modelindex);
SAVE_STATE(origin);
SAVE_STATE(angles);
SAVE_STATE(velocity);
SAVE_STATE(flags);
SAVE_STATE(gflags);
SAVE_STATE(activeweapon);
SAVE_STATE(g_items);
SAVE_STATE(health);
SAVE_STATE(armor);
SAVE_STATE(movetype);
SAVE_STATE(view_ofs);
/* TO OPTIMISE */
SAVE_STATE(teleport_time);
SAVE_STATE(viewzoom);
SAVE_STATE(weapontime);
SAVE_STATE(w_attack_next);
SAVE_STATE(w_idle_next);
SAVE_STATE(punchangle);
}
float
base_player::SendEntity(entity ePEnt, float fChanged)
{
/* really trying to get our moneys worth with 23 bits of mantissa */
if (fChanged & PLAYER_MODELINDEX)
WriteShort(MSG_ENTITY, modelindex);
if (fChanged & PLAYER_ORIGIN) {
WriteCoord(MSG_ENTITY, origin[0]);
WriteCoord(MSG_ENTITY, origin[1]);
}
if (fChanged & PLAYER_ORIGIN_Z)
WriteCoord(MSG_ENTITY, origin[2]);
if (fChanged & PLAYER_ANGLES_X)
WriteFloat(MSG_ENTITY, v_angle[0]);
if (fChanged & PLAYER_ANGLES_Y)
WriteFloat(MSG_ENTITY, angles[1]);
if (fChanged & PLAYER_ANGLES_Z)
WriteFloat(MSG_ENTITY, angles[2]);
if (fChanged & PLAYER_VELOCITY) {
WriteCoord(MSG_ENTITY, velocity[0]);
WriteCoord(MSG_ENTITY, velocity[1]);
}
if (fChanged & PLAYER_VELOCITY_Z)
WriteCoord(MSG_ENTITY, velocity[2]);
if (fChanged & PLAYER_FLAGS) {
WriteFloat(MSG_ENTITY, flags);
WriteFloat(MSG_ENTITY, gflags);
}
if (fChanged & PLAYER_WEAPON)
WriteByte(MSG_ENTITY, activeweapon);
if (fChanged & PLAYER_ITEMS)
WriteFloat(MSG_ENTITY, (__variant)g_items);
if (fChanged & PLAYER_HEALTH)
WriteByte(MSG_ENTITY, bound(0, health, 255));
if (fChanged & PLAYER_ARMOR)
WriteByte(MSG_ENTITY, bound(0, armor, 255));
if (fChanged & PLAYER_MOVETYPE)
WriteByte(MSG_ENTITY, movetype);
if (fChanged & PLAYER_VIEWOFS)
WriteFloat(MSG_ENTITY, view_ofs[2]);
/* TO OPTIMISE */
WriteFloat(MSG_ENTITY, teleport_time);
WriteFloat(MSG_ENTITY, viewzoom);
WriteFloat(MSG_ENTITY, weapontime);
WriteFloat(MSG_ENTITY, w_attack_next);
WriteFloat(MSG_ENTITY, w_idle_next);
WriteFloat(MSG_ENTITY, punchangle[0]);
WriteFloat(MSG_ENTITY, punchangle[1]);
WriteFloat(MSG_ENTITY, punchangle[2]);
return TRUE;
}
#endif

View File

@ -153,7 +153,7 @@ PMove_Categorize(void)
self.view_ofs = VEC_PLAYER_VIEWPOS;
}
tracebox(self.origin, self.mins, self.maxs, self.origin - [0,0,0.25], MOVE_NORMAL, self);
tracebox(self.origin, self.mins, self.maxs, self.origin - [0,0,1], MOVE_NORMAL, self);
if (!trace_startsolid) {
if ((trace_fraction < 1) && (trace_plane_normal[2] > 0.7)) {

View File

@ -18,7 +18,7 @@
.float flags;
var hashtable g_hashsounds;
enumflags
typedef enumflags
{
SNDFL_LOOPING, /* forceloop */
SNDFL_NODUPS, /* don't random the samples */
@ -28,7 +28,7 @@ enumflags
SNDFL_PRIVATE, /* only play on target */
SNDFL_STEP, /* volume is calculated from entity speed */
SNDFL_FOLLOW
};
} soundFlag_t;
typedef struct
{
@ -39,7 +39,7 @@ typedef struct
float pitch_max;
float shakes;
float volume;
int flags;
soundFlag_t flags;
int playc;
int sample_count;
string samples;

View File

@ -280,9 +280,9 @@ Sound_Play(entity target, int chan, string shader)
if (sample < 0) {
#ifdef SERVER
error(sprintf("Sound_Speak: shader %s is not precached (SERVER)\n", shader));
print(sprintf("^1Sound_Play: shader %s is not precached (SERVER)\n", shader));
#else
error(sprintf("Sound_Speak: shader %s is not precached (CLIENT)\n", shader));
print(sprintf("^1Sound_Play: shader %s is not precached (CLIENT)\n", shader));
#endif
return;
}
@ -388,9 +388,9 @@ Sound_PlayAt(vector pos, string shader)
if (sample < 0) {
#ifdef SERVER
error(sprintf("Sound_Speak: shader %s is not precached (SERVER)\n", shader));
print(sprintf("^1Sound_PlayAt: shader %s is not precached (SERVER)\n", shader));
#else
error(sprintf("Sound_Speak: shader %s is not precached (CLIENT)\n", shader));
print(sprintf("^1Sound_PlayAt: shader %s is not precached (CLIENT)\n", shader));
#endif
return;
}
@ -495,9 +495,9 @@ Sound_Speak(entity target, string shader)
if (sample < 0) {
#ifdef SERVER
error(sprintf("Sound_Speak: shader %s is not precached (SERVER)\n", shader));
print(sprintf("^1Sound_Speak: shader %s is not precached (SERVER)\n", shader));
#else
error(sprintf("Sound_Speak: shader %s is not precached (CLIENT)\n", shader));
print(sprintf("^1Sound_Speak: shader %s is not precached (CLIENT)\n", shader));
#endif
return;
}

View File

@ -1,19 +1,19 @@
enumflags
typedef enumflags
{
SPECFL_ORIGIN,
SPECFL_VELOCITY,
SPECFL_TARGET,
SPECFL_MODE,
SPECFL_FLAGS
};
} spectatorFlags_t;
enum
typedef enum
{
SPECMODE_FREE,
SPECMODE_THIRDPERSON,
SPECMODE_FIRSTPERSON,
SPECMODE_OVERVIEW
};
} spectatorMode_t;
#ifdef SERVER
class spectator:CBaseEntity
@ -24,8 +24,8 @@ class spectator
vector origin_net;
vector velocity_net;
float spec_ent; float spec_ent_net;
float spec_mode; float spec_mode_net;
float spec_flags; float spec_flags_net;
spectatorMode_t spec_mode; spectatorMode_t spec_mode_net;
vector spec_org;