From 143a00e94ea0f6c15365a576434e20680032118a Mon Sep 17 00:00:00 2001 From: Marco Hladik Date: Thu, 17 Mar 2022 21:05:47 -0700 Subject: [PATCH] Fix light_dynamic (static) from not spawning in CSQC. Add r_skipDiffuse to rtlight.glsl, add SURF_PENETRATE, which will make bullets pass right through them when BULLETPENETRATION is set. Go over and refactor a lot of base_client, player and spectator code to facilitate both permanent and temporary spectators better. Add experimental env_glow lens flare code. Fix material ID detection bug for Q3 based BSPs. A couple helper functions were added to src/client/utils.cpp but they are not yet final. --- base/src/client/scoreboard.qc | 2 +- platform/base_glsl.pk3dir/glsl/rtlight.glsl | 8 +- .../scripts/custinfoparms.txt | 62 ++++----- .../textures/common/clipmetal.mat | 1 + src/client/defs.h | 1 + src/client/entry.qc | 58 +++++---- src/client/include.src | 1 + src/client/player.qc | 4 +- src/client/predict.qc | 44 ------- src/client/util.cpp | 40 ++++++ src/client/util.h | 17 +++ src/client/view.qc | 26 ++-- src/gs-entbase/client/env_glow.qc | 18 ++- src/gs-entbase/client/env_sun.qc | 51 +++++++- src/gs-entbase/shared/NSEntity.qc | 7 + src/gs-entbase/shared/light_dynamic.qc | 2 +- src/server/gamerules.qc | 6 + src/server/traceattack.qc | 3 +- src/shared/client.h | 17 ++- src/shared/client.qc | 22 +++- src/shared/defs.h | 17 ++- src/shared/materials.h | 2 +- src/shared/player.h | 9 +- src/shared/player.qc | 120 +++++++++++++++++- src/shared/player_pmove.qc | 6 +- src/shared/spectator.h | 23 ++-- src/shared/spectator.qc | 101 ++++++++------- src/shared/surfaceproperties.qc | 1 + 28 files changed, 484 insertions(+), 185 deletions(-) create mode 100644 src/client/util.cpp create mode 100644 src/client/util.h diff --git a/base/src/client/scoreboard.qc b/base/src/client/scoreboard.qc index 2a6e50a8..d32e42ed 100644 --- a/base/src/client/scoreboard.qc +++ b/base/src/client/scoreboard.qc @@ -160,7 +160,7 @@ Scores_Draw(void) pos = video_mins + [(video_res[0] / 2) - 145, 30]; } - if (serverkeyfloat("teams") > 0) { + if (Util_IsTeamPlay()) { Scores_DrawTeam(pl, pos); } else { Scores_DrawNormal(pl, pos); diff --git a/platform/base_glsl.pk3dir/glsl/rtlight.glsl b/platform/base_glsl.pk3dir/glsl/rtlight.glsl index c48f78d2..a4e8773e 100644 --- a/platform/base_glsl.pk3dir/glsl/rtlight.glsl +++ b/platform/base_glsl.pk3dir/glsl/rtlight.glsl @@ -22,6 +22,8 @@ !!samps =PCF shadowmap !!samps =CUBE projectionmap +!!cvardf r_skipDiffuse + #include "sys/defs.h" //if there's no vertex normals known, disable some stuff. @@ -125,7 +127,11 @@ varying vec3 lightvector; #if defined(FLAT) vec4 bases = vec4(FLAT, FLAT, FLAT, 1.0); #else - vec4 bases = texture2D(s_diffuse, tex_c); + #if r_skipDiffuse == 0 + vec4 bases = texture2D(s_diffuse, tex_c); + #else + vec4 bases = vec4(1.0, 1.0, 1.0, 1.0); + #endif #endif #ifdef BUMP diff --git a/platform/base_scripts.pk3dir/scripts/custinfoparms.txt b/platform/base_scripts.pk3dir/scripts/custinfoparms.txt index f133ca03..f9b9439a 100644 --- a/platform/base_scripts.pk3dir/scripts/custinfoparms.txt +++ b/platform/base_scripts.pk3dir/scripts/custinfoparms.txt @@ -1,32 +1,32 @@ -{ - climb 0x00004000 - vehicleclip 0x00008000 +{ + climb 0x00004000 + vehicleclip 0x00008000 grenadeclip 0x00000080 -} -{ - leakssteam 0x00080000 - leakswater 0x00100000 - fl_r1 0x00200000 - fl_r2 0x00400000 - fl_r3 0x00800000 - fl_r4 0x01000000 - fl_r5 0x02000000 - fl_r6 0x04000000 - fl_r7 0x08000000 - - alien 0x10000000 - flesh 0x20000000 - foliage 0x30000000 - computer 0x40000000 - dirt 0x50000000 - vent 0x60000000 - grate 0x70000000 - metal 0x80000000 - glass 0x90000000 - sand 0xA0000000 - slosh 0xB0000000 - snow 0xC0000000 - tile 0xD0000000 - wood 0xE0000000 - concrete 0xF0000000 -} +} +{ + leakssteam 0x00080000 + leakswater 0x00100000 + fl_r1 0x00200000 + fl_r2 0x00400000 + fl_r3 0x00800000 + fl_r4 0x01000000 + fl_r5 0x02000000 + fl_r6 0x04000000 + penetrate 0x08000000 + + alien 0x10000000 + flesh 0x20000000 + foliage 0x30000000 + computer 0x40000000 + dirt 0x50000000 + vent 0x60000000 + grate 0x70000000 + metal 0x80000000 + glass 0x90000000 + sand 0xA0000000 + slosh 0xB0000000 + snow 0xC0000000 + tile 0xD0000000 + wood 0xE0000000 + concrete 0xF0000000 +} diff --git a/platform/vmap_tex.pk3dir/textures/common/clipmetal.mat b/platform/vmap_tex.pk3dir/textures/common/clipmetal.mat index 6071074a..8188527a 100644 --- a/platform/vmap_tex.pk3dir/textures/common/clipmetal.mat +++ b/platform/vmap_tex.pk3dir/textures/common/clipmetal.mat @@ -5,6 +5,7 @@ surfaceParm nodraw surfaceParm trans surfaceParm metal + surfaceParm penetrate surfaceParm nolightmap nomipmaps } diff --git a/src/client/defs.h b/src/client/defs.h index b1025569..bca73fdd 100644 --- a/src/client/defs.h +++ b/src/client/defs.h @@ -19,6 +19,7 @@ #include "font.h" #include "fade.h" #include "cmd.h" +#include "util.h" /* flags for 2d drawing */ #define DRAWFLAG_NORMAL 0 diff --git a/src/client/entry.qc b/src/client/entry.qc index 62be0ed6..f5f09287 100644 --- a/src/client/entry.qc +++ b/src/client/entry.qc @@ -143,6 +143,7 @@ void CSQC_UpdateView(float w, float h, float focus) { player pl = __NULL__; + base_client cl = __NULL__; spectator spec; int s; @@ -202,6 +203,7 @@ CSQC_UpdateView(float w, float h, float focus) pSeat->m_ePlayer = self = findfloat(world, entnum, player_localentnum); pl = (player)self; + cl = (base_client)self; /* player slot not present */ if (!self) { @@ -210,8 +212,9 @@ CSQC_UpdateView(float w, float h, float focus) /* this needs to be moved into a base_client method */ #if 1 - if (self.classname == "player") { - Predict_PlayerPreFrame(pl); + cl.PreFrame(); + + if (!Client_IsSpectator(pl)) { pSeat->m_vecPredictedOrigin = pl.origin; pSeat->m_vecPredictedVelocity = pl.velocity; @@ -252,9 +255,8 @@ CSQC_UpdateView(float w, float h, float focus) pl.viewzoom = oldzoom; View_PreDraw(); - } else if (self.classname == "spectator") { + } else if (Client_IsSpectator(pl)) { spec = (spectator)self; - Predict_SpectatorPreFrame(spec); if (spec.spec_mode == SPECMODE_FIRSTPERSON || spec.spec_mode == SPECMODE_THIRDPERSON) { entity c = findfloat(world, ::entnum, spec.spec_ent); @@ -279,7 +281,8 @@ CSQC_UpdateView(float w, float h, float focus) setproperty(VF_CL_VIEWANGLES, view_angles); setproperty(VF_ANGLES, view_angles); } else { - if (getplayerkeyvalue(pl.entnum-1, "*spec") == "0") { + /* we're not spectating at all */ + if (!Client_IsSpectator(pl)) { setproperty(VF_ORIGIN, pSeat->m_vecPredictedOrigin + pl.view_ofs); if (pl.flags & FL_INVEHICLE) { @@ -299,7 +302,7 @@ CSQC_UpdateView(float w, float h, float focus) Shake_Update(pl); setproperty(VF_ANGLES, view_angles + pl.punchangle); - } else if (getplayerkeyvalue(pl.entnum-1, "*spec") == "1") { + } else if (Client_IsSpectator(pl)) { spec = (spectator)self; switch (spec.spec_mode) { case SPECMODE_THIRDPERSON: @@ -328,13 +331,9 @@ CSQC_UpdateView(float w, float h, float focus) default: setproperty(VF_ORIGIN, pSeat->m_vecPredictedOrigin); } - } else if (getplayerkeyvalue(pl.entnum-1, "*spec") == "2") { - setproperty(VF_ORIGIN, pSeat->m_vecPredictedOrigin); - Shake_Update(pl); - setproperty(VF_ANGLES, view_angles + pl.punchangle); } - if (g_iIntermission) { + if (Client_InIntermission()) { view_angles = pSeat->m_vecCameraAngle; view_angles += [sin(time), sin(time * 2)]; setproperty(VF_ORIGIN, pSeat->m_vecCameraOrigin); @@ -357,10 +356,27 @@ CSQC_UpdateView(float w, float h, float focus) setproperty(VF_AFOV, autocvar_r_viewmodelfov); setproperty(VF_ORIGIN, pSeat->m_vecPredictedOrigin + pl.view_ofs); View_DrawViewModel(); - } else if (pl.health > 0) { - View_DrawViewModel(); - } else if (getplayerkeyvalue(pl.entnum-1, "*dead") == "1") { - pl.UpdateDeathcam(); + } else { + if (Client_IsSpectator(pl)) { + /* 0 means world */ + if (spec.spec_ent) { + spec = (spectator)self; + entity c = findfloat(world, ::entnum, spec.spec_ent); + + /* we found them */ + if (c && c != spec) { + player ps = (player)c; + if (ps.health <= 0) + pl.UpdateDeathcam(); + else + View_DrawViewModel(); + } + } + } else if (Client_IsDead(pl)) { + pl.UpdateDeathcam(); + } else { + View_DrawViewModel(); + } } /* this is running whenever we're doing 'buildcubemaps' */ @@ -368,7 +384,6 @@ CSQC_UpdateView(float w, float h, float focus) setproperty(VF_ORIGIN, g_vecCubePos); setproperty(VF_SIZE_X, g_dCubeSize); setproperty(VF_SIZE_Y, g_dCubeSize); - print(sprintf("cubesize: %d\n", g_dCubeSize)); setproperty(VF_AFOV, 90); } @@ -385,15 +400,15 @@ CSQC_UpdateView(float w, float h, float focus) Fade_Update((int)video_mins[0],(int)video_mins[1], (int)w, (int)h); View_PostDraw(); - if (g_iIntermission) { + if (Client_InIntermission()) { Scores_Draw(); } else if (focus == TRUE) { GameText_Draw(); PointMessage_Draw(); - if (getplayerkeyvalue(pl.entnum-1, "*spec") == "0") { + if (!Client_IsSpectator(pl)) { HUD_Draw(); - } else if (self.classname == "player") { + } else { HUD_DrawSpectator(); } @@ -412,10 +427,7 @@ CSQC_UpdateView(float w, float h, float focus) /* move this into base_client methods */ #if 1 - if (self.classname == "player") - Predict_PlayerPostFrame((player)self); - else if (self.classname == "spectator") - Predict_SpectatorPostFrame((spectator)self); + cl.PostFrame(); #endif } diff --git a/src/client/include.src b/src/client/include.src index b47ecb3f..c54d25c5 100644 --- a/src/client/include.src +++ b/src/client/include.src @@ -26,4 +26,5 @@ shake.qc cmd.qc event.qc entry.qc +util.cpp #endlist diff --git a/src/client/player.qc b/src/client/player.qc index 58ea2382..00a3a9c9 100644 --- a/src/client/player.qc +++ b/src/client/player.qc @@ -46,8 +46,10 @@ player::predraw(void) /* make sure we're enabling shadow rendering on us */ effects &= ~EF_NOSHADOW; + base_client cl = (base_client)pSeat->m_ePlayer; - if (getplayerkeyvalue(pSeat->m_ePlayer.entnum-1, "*spec") == "1") { + /* it's either us or us spectating */ + if (Client_IsSpectator(cl)) { spectator spec = (spectator)pSeat->m_ePlayer; if (entnum == spec.spec_ent && spec.spec_mode == SPECMODE_FIRSTPERSON) { this_us = 1; diff --git a/src/client/predict.qc b/src/client/predict.qc index e7420029..080b570c 100644 --- a/src/client/predict.qc +++ b/src/client/predict.qc @@ -74,39 +74,7 @@ Propagate our pmove state to whatever the current frame before its stomped on void Predict_PlayerPreFrame(player pl) { - /* this is where a game/mod would decide to add more prediction rollback - * information. */ - pl.PredictPreFrame(); - if (pl.flags & FL_INVEHICLE) - if (pl.vehicle) { - NSVehicle veh = (NSVehicle)pl.vehicle; - veh.PredictPreFrame(); - } - - /* run physics code for all the input frames which we've not heard back - * from yet. This continues on in Player_ReceiveEntity! */ - for (int i = pl.sequence + 1; i <= clientcommandframe; i++) { - float flSuccess = getinputstate(i); - if (flSuccess == FALSE) { - continue; - } - - /* don't do partial frames, aka incomplete input packets */ - if (input_timelength == 0) { - break; - } - - if (i==clientcommandframe){ - CSQC_Input_Frame(); - } - - /* this global is for our shared random number seed */ - input_sequence = i; - - /* run our custom physics */ - pl.Physics_Run(); - } } /* @@ -121,17 +89,7 @@ Rewind our pmove state back to before we started predicting. void Predict_PlayerPostFrame(player pl) { - /* give the game/mod a chance to roll back its values too */ - pl.PredictPostFrame(); - if (pl.flags & FL_INVEHICLE) - if (pl.vehicle) { - NSVehicle veh = (NSVehicle)pl.vehicle; - veh.PredictPostFrame(); - } - - /* update bounds */ - setorigin(pl, pl.origin); } /* @@ -146,7 +104,6 @@ Propagate our pmove state to whatever the current frame before its stomped on void Predict_SpectatorPreFrame(spectator pl) { - pl.PreFrame(); } /* @@ -161,5 +118,4 @@ Rewind our pmove state back to before we started predicting. void Predict_SpectatorPostFrame(spectator pl) { - pl.PostFrame(); } diff --git a/src/client/util.cpp b/src/client/util.cpp new file mode 100644 index 00000000..ac831b2c --- /dev/null +++ b/src/client/util.cpp @@ -0,0 +1,40 @@ + +float +Client_IsSpectator(base_client cl) +{ + return (getplayerkeyfloat(cl.entnum - 1, "*spec") > 0) ? TRUE : FALSE; +} + +float +Client_IsRealSpectator(base_client cl) +{ + return (cl.classname == "spectator") ? TRUE : FALSE; +} + +float +Client_IsFakeSpectator(base_client cl) +{ + return (getplayerkeyvalue(cl.entnum - 1, "*spec") == "2") ? TRUE : FALSE; +} + +float +Client_IsDead(base_client cl) +{ + if (Client_IsSpectator(cl) == TRUE) + return FALSE; + else + return (getplayerkeyvalue(cl.entnum - 1, "*dead") == "1") ? TRUE : FALSE; +} + +float +Client_IsPlayer(base_client cl) +{ + return (cl.classname == "player") ? TRUE : FALSE; +} + + +float +Client_InIntermission(void) +{ + return g_iIntermission; +} diff --git a/src/client/util.h b/src/client/util.h new file mode 100644 index 00000000..4f3f3b27 --- /dev/null +++ b/src/client/util.h @@ -0,0 +1,17 @@ +/* Returns if the specified client is a spectator, doesn't matter if real or fake */ +float Client_IsSpectator(base_client); + +/* Returns if we're a permanent spectator, USE THIS if you want to access spectator class attributes */ +float Client_IsRealSpectator(base_client cl); + +/* Returns if we're a fake spectator, in case you need to be certain */ +float Client_IsFakeSpectator(base_client cl); + +/* Return if the specified client is dead. If they're a spectator they're always alive. */ +float Client_IsDead(base_client); + +/* Returns if the specified client is a playable client class */ +float Client_IsPlayer(base_client cl); + +/* Are we in an intermission? (Match ending screen) */ +float Client_InIntermission(void); diff --git a/src/client/view.qc b/src/client/view.qc index 1c409b0a..297e6fdf 100644 --- a/src/client/view.qc +++ b/src/client/view.qc @@ -129,9 +129,10 @@ View_DrawViewModel(void) entity m_eMuzzleflashL = pSeat->m_eMuzzleflashL; player pl = __NULL__; + base_client cl = (base_client)pSeat->m_ePlayer; /* it's either us or us spectating */ - if (self.classname == "spectator") { + if (Client_IsSpectator(cl)) { spectator spec = (spectator)self; pl = (player)findfloat(world, ::entnum, spec.spec_ent); @@ -139,27 +140,24 @@ View_DrawViewModel(void) return; } else { pl = (player)self; - - if (pl.health <= 0) { - return; - } } if (!pl) return; + if (pl.health <= 0) { + return; + } + if (cvar("r_drawviewmodel") == 0 || autocvar_cl_thirdperson == TRUE) { return; } -// print(sprintf("%s: %f %d\n", pl.classname, pl.weapontime, pl.activeweapon)); - View_UpdateWeapon(pl, m_eViewModel, m_eMuzzleflash); - View_UpdateWeapon(pl, m_eViewModelL, m_eMuzzleflashL); float fBaseTime2 = m_eViewModel.frame1time; float fBaseTime = m_eViewModel.frame1time; - m_eViewModel.frame = pl.weaponframe; + m_eViewModelL.frame = m_eViewModel.frame = pl.weaponframe; m_eViewModelL.frame1time = m_eViewModelL.frame2time = m_eViewModel.frame2time = @@ -326,6 +324,16 @@ View_PlayAnimation(int iSequence) pSeat->m_eViewModel.frame = pSeat->m_eViewModelL.frame = (float)iSequence; } +void +View_PlayAnimationLeft(int iSequence) +{ + pSeat->m_eViewModelL.frame = (float)iSequence; +} +void +View_PlayAnimationRight(int iSequence) +{ + pSeat->m_eViewModel.frame = (float)iSequence; +} int View_GetAnimation(void) diff --git a/src/gs-entbase/client/env_glow.qc b/src/gs-entbase/client/env_glow.qc index caff88cc..8888179d 100644 --- a/src/gs-entbase/client/env_glow.qc +++ b/src/gs-entbase/client/env_glow.qc @@ -28,7 +28,8 @@ Client-side glare/glow orb effect like the flares in 1997's Unreal. This entity was introduced in Half-Life (1998). */ -var int autocvar_r_skipFlares = 0; +var int autocvar_r_skipGlows = 0; +var int autocvar_r_skipLensFlares = 0; class env_glow:NSEntity /* change to renderablentity? */ { @@ -44,10 +45,19 @@ class env_glow:NSEntity /* change to renderablentity? */ void(void) env_glow; virtual float() predraw; + virtual void() postdraw; virtual void(string, string) SpawnKey; virtual void(void) RendererRestarted; }; +void env_sun_lensflare(vector, float, vector); +void +env_glow::postdraw(void) +{ + if (!autocvar_r_skipLensFlares) + env_sun_lensflare(origin, m_flAlpha, m_vecColor); +} + void env_glow::RendererRestarted(void) { @@ -74,7 +84,7 @@ env_glow::predraw(void) vector fsize; vector vecPlayer; - if (autocvar_r_skipFlares) + if (autocvar_r_skipGlows) return PREDRAW_NEXT; int s = (float)getproperty(VF_ACTIVESEAT); @@ -108,9 +118,9 @@ env_glow::predraw(void) makevectors(vectoangles(origin - vecPlayer)); forg = origin + (v_forward * -16); - if (spawnflags & 1) + if (spawnflags & 1) { makevectors(m_vecOrientation+[0,0,angles[2]]); - else { + } else { makevectors(view_angles+[0,0,angles[2]]); } diff --git a/src/gs-entbase/client/env_sun.qc b/src/gs-entbase/client/env_sun.qc index 26baa73c..b149eee6 100644 --- a/src/gs-entbase/client/env_sun.qc +++ b/src/gs-entbase/client/env_sun.qc @@ -164,4 +164,53 @@ env_sun::env_sun(void) setsize(this, [0,0,0], [0,0,0]); setorigin(this, origin); Init(); -} \ No newline at end of file +} + +void +env_sun_lensflare(vector m_vecLensPos, float m_flLensAlpha, vector vecColor) +{ + vector lens_pos = project(m_vecLensPos); + vector lens_1 = lens_pos - (FLARE_SIZE / 2); + vector player_pos = getproperty(VF_ORIGIN); + vector player_angle = getproperty(VF_CL_VIEWANGLES); + + m_flLensAlpha *= 0.15f; + + if (m_flLensAlpha <= 0.0f) { + return; + } + +{ + vector delta; + float fov; + + makevectors(player_angle); + delta = normalize (m_vecLensPos - player_pos); + fov = delta * v_forward; + + /* we're in field-of-view */ + if (fov > 0.3) { + traceline(player_pos, m_vecLensPos, TRUE, self); + if (trace_fraction != 1.0) { + return; + } + } else { + return; + } +} + player_angle[0] *= -1; + + vector test1, test2; + makevectors(vectoangles(m_vecLensPos - player_pos)); + test1 = v_forward; + makevectors(player_angle); + test2 = v_forward; + + vector c = (test1 - test2) * 512; + drawpic(lens_1, "textures/sfx/flare1", FLARE_SIZE, vecColor * m_flLensAlpha, 1.0f, DRAWFLAG_ADDITIVE); + drawpic(lens_1 - c * 0.1, "textures/sfx/flare2", FLARE_SIZE, vecColor * m_flLensAlpha, 1.0f, DRAWFLAG_ADDITIVE); + drawpic(lens_1 + c * 0.2, "textures/sfx/flare3", FLARE_SIZE, vecColor * m_flLensAlpha, 1.0f, DRAWFLAG_ADDITIVE); + drawpic(lens_1 - c * 0.3, "textures/sfx/flare4", FLARE_SIZE, vecColor * m_flLensAlpha, 1.0f, DRAWFLAG_ADDITIVE); + drawpic(lens_1 + c * 0.4, "textures/sfx/flare2", FLARE_SIZE, vecColor * m_flLensAlpha, 1.0f, DRAWFLAG_ADDITIVE); + drawpic(lens_1 - c * 0.5, "textures/sfx/flare3", FLARE_SIZE, vecColor * m_flLensAlpha, 1.0f, DRAWFLAG_ADDITIVE); +} diff --git a/src/gs-entbase/shared/NSEntity.qc b/src/gs-entbase/shared/NSEntity.qc index ceeb48b7..fc44b015 100644 --- a/src/gs-entbase/shared/NSEntity.qc +++ b/src/gs-entbase/shared/NSEntity.qc @@ -480,6 +480,7 @@ NSEntity::Respawn(void) void NSEntity::Save(float handle) { + SaveFloat(handle, "spawnflags", spawnflags); SaveVector(handle, "origin", origin); SaveVector(handle, "absmin", absmin); SaveVector(handle, "absmax", absmax); @@ -498,6 +499,9 @@ void NSEntity::Restore(string strKey, string strValue) { switch (strKey) { + case "spawnflags": + spawnflags = stof(strValue); + break; case "origin": origin = stov(strValue); setorigin(this, origin); @@ -589,6 +593,9 @@ NSEntity::SpawnKey(string strKey, string strValue) /* we do re-read a lot of the builtin fields in case we want to set defaults. just in case anybody is wondering. */ switch (strKey) { + case "spawnflags": + spawnflags = stof(strValue); + break; case "origin": origin = stov(strValue); break; diff --git a/src/gs-entbase/shared/light_dynamic.qc b/src/gs-entbase/shared/light_dynamic.qc index 5d62c67e..ad4c0432 100644 --- a/src/gs-entbase/shared/light_dynamic.qc +++ b/src/gs-entbase/shared/light_dynamic.qc @@ -174,7 +174,7 @@ light_dynamic::ReceiveEntity(float flNew, float flFlags) void light_dynamic::RendererRestarted(void) { -#if 0 +#if 1 float p = dynamiclight_spawnstatic(origin, m_flDistance, m_vecLight / 255); dynamiclight_set(p, LFIELD_ANGLES, angles); diff --git a/src/server/gamerules.qc b/src/server/gamerules.qc index 52772a0a..90489b55 100644 --- a/src/server/gamerules.qc +++ b/src/server/gamerules.qc @@ -372,3 +372,9 @@ CGameRules::CGameRules(void) /* our currently running mode */ CGameRules g_grMode; + +int +Gamerules_IsTeamPlay(void) +{ + return (g_grMode.IsTeamPlay()) ? TRUE : FALSE; +} diff --git a/src/server/traceattack.qc b/src/server/traceattack.qc index ce23a0aa..2a8cba04 100644 --- a/src/server/traceattack.qc +++ b/src/server/traceattack.qc @@ -138,7 +138,8 @@ TraceAttack_FireSingle(vector vecPos, vector vAngle, int iDamage, int iWeapon, f #ifdef BULLETPENETRATION if (iTotalPenetrations > 0) { - iTotalPenetrations -= 1; + if (!(trace_surfaceflagsi & SURF_PENETRATE)) + iTotalPenetrations -= 1; TraceAttack_FireSingle(trace_endpos + (v_forward * 2), vAngle, iDamage / 2, iWeapon, dist); } #endif diff --git a/src/shared/client.h b/src/shared/client.h index 9cff2134..4aca2d33 100644 --- a/src/shared/client.h +++ b/src/shared/client.h @@ -3,13 +3,28 @@ class base_client:NSSurfacePropEntity { + vector origin_net; + vector velocity_net; + void(void) base_client; - virtual void(void) ClientInputFrame; + /* final input handling of the client */ + virtual void(void) ClientInput; + + virtual void(void) PreFrame; + virtual void(void) PostFrame; + + virtual int(void) IsFakeSpectator; #ifdef CLIENT + /* gives the chance to override input variables before networking */ + virtual void(void) ClientInputFrame; + + /* our camera when we're dead */ virtual void(void) UpdateDeathcam; + + /* run every frame before renderscene() */ virtual float(void) predraw; #endif }; diff --git a/src/shared/client.qc b/src/shared/client.qc index 14e5feb8..bad0962c 100644 --- a/src/shared/client.qc +++ b/src/shared/client.qc @@ -1,10 +1,30 @@ void -base_client::ClientInputFrame(void) +base_client::ClientInput(void) { +} +void +base_client::PreFrame(void) +{ +} + +void +base_client::PostFrame(void) +{ +} + +int +base_client::IsFakeSpectator(void) +{ + return (FALSE); } #ifdef CLIENT +void +base_client::ClientInputFrame(void) +{ +} + void base_client::UpdateDeathcam(void) { diff --git a/src/shared/defs.h b/src/shared/defs.h index dd931898..be538311 100644 --- a/src/shared/defs.h +++ b/src/shared/defs.h @@ -39,6 +39,7 @@ #include "../gs-entbase/shared/baseentity.h" #include "client.h" +#include "spectator.h" #include "player.h" #include "damage.h" #include "flags.h" @@ -50,7 +51,6 @@ #include "math.h" #include "pmove.h" #include "memory.h" -#include "spectator.h" #include "platform.h" #include "propdata.h" #include "surfaceproperties.h" @@ -138,6 +138,21 @@ Util_TimeToString(float fTime) } } + +/* returns whether or not the mode we're playing is a team game */ +#ifdef SERVER +int Gamerules_IsTeamPlay(void); +#endif +int +Util_IsTeamPlay(void) +{ +#ifdef SERVER + return Gamerules_IsTeamPlay(); +#else + return (serverkeyfloat("teams") > 0) ? TRUE : FALSE; +#endif +} + __wrap void dprint(string m) { diff --git a/src/shared/materials.h b/src/shared/materials.h index b3a106b8..50a2c794 100644 --- a/src/shared/materials.h +++ b/src/shared/materials.h @@ -132,7 +132,7 @@ typedef enum #define SURF_RESERVED4 0x01000000i #define SURF_RESERVED5 0x02000000i #define SURF_RESERVED6 0x04000000i -#define SURF_RESERVED7 0x08000000i +#define SURF_PENETRATE 0x08000000i /* material surfaceflags (need to be masked) */ #define SURF_MASK 0x0FFFFFFFi diff --git a/src/shared/player.h b/src/shared/player.h index 293ff055..c7010afc 100644 --- a/src/shared/player.h +++ b/src/shared/player.h @@ -15,7 +15,7 @@ */ class -base_player:base_client +base_player:spectator { PREDICTED_INT(weaponframe); PREDICTED_FLOAT(health); @@ -53,6 +53,11 @@ base_player:base_client void(void) base_player; + virtual void(void) ClientInput; + + virtual void(void) PreFrame; + virtual void(void) PostFrame; + virtual void(float) Physics_Fall; virtual void(void) Physics_Crouch; virtual void(void) Physics_Jump; @@ -65,6 +70,8 @@ base_player:base_client virtual void(void) Physics_InputPostMove; virtual void(void) Physics_Run; + virtual int(void) IsFakeSpectator; + #ifdef CLIENT int sequence; diff --git a/src/shared/player.qc b/src/shared/player.qc index 393e8392..ad2c7922 100644 --- a/src/shared/player.qc +++ b/src/shared/player.qc @@ -14,6 +14,93 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +int +base_player::IsFakeSpectator(void) +{ +#ifdef SERVER + if (infokey(this, "*spec") == "2") +#else + if (getplayerkeyfloat(entnum - 1, "*spec") == 2) +#endif + return (TRUE); + + return (FALSE); +} + +void +base_player::PreFrame(void) +{ +#ifdef CLIENT + /* this is where a game/mod would decide to add more prediction rollback + * information. */ + PredictPreFrame(); + + if (flags & FL_INVEHICLE) + if (vehicle) { + NSVehicle veh = (NSVehicle)vehicle; + veh.PredictPreFrame(); + } + + /* run physics code for all the input frames which we've not heard back + * from yet. This continues on in Player_ReceiveEntity! */ + for (int i = sequence + 1; i <= clientcommandframe; i++) { + float flSuccess = getinputstate(i); + if (flSuccess == FALSE) { + continue; + } + + /* don't do partial frames, aka incomplete input packets */ + if (input_timelength == 0) { + break; + } + + if (i==clientcommandframe){ + CSQC_Input_Frame(); + } + + /* this global is for our shared random number seed */ + input_sequence = i; + + /* run our custom physics */ + Physics_Run(); + } +#endif +} +void +base_player::PostFrame(void) +{ +#ifdef CLIENT + /* give the game/mod a chance to roll back its values too */ + PredictPostFrame(); + + if (flags & FL_INVEHICLE) + if (vehicle) { + NSVehicle veh = (NSVehicle)vehicle; + veh.PredictPostFrame(); + } + + /* update bounds */ + setorigin(this, origin); +#endif +} + + +void +base_player::ClientInput(void) +{ + if (IsFakeSpectator()) { + spectator::ClientInput(); + SpectatorTrackPlayer(); + return; + } + + /* allow vehicles to prevent weapon logic from happening */ + Vehicle_Input(this); + + /* weapon/item logic of what the player controls */ + Game_Input((player)this); +} + #ifdef CLIENT void base_player::ClientRemove(void) @@ -32,6 +119,11 @@ This is basically CSQC_Input_Frame! So games can override this as they please. void base_player::ClientInputFrame(void) { + if (IsFakeSpectator()) { + spectator::ClientInputFrame(); + return; + } + /* If we are inside a VGUI, don't let the client do stuff outside */ if (VGUI_Active()) { input_impulse = 0; @@ -177,6 +269,11 @@ base_player::ReceiveEntity(float new, float fl) punchangle[1] = readfloat(); punchangle[2] = readfloat(); vehicle = findfloat(world, ::entnum, readentitynum()); + + /* FIXME: Make this temp spec only */ + spec_ent = readbyte(); + spec_mode = readbyte(); + spec_flags = readbyte(); } /* @@ -217,6 +314,10 @@ base_player::PredictPreFrame(void) SAVE_STATE(w_idle_next); SAVE_STATE(punchangle); SAVE_STATE(vehicle); + + SAVE_STATE(spec_ent); + SAVE_STATE(spec_mode); + SAVE_STATE(spec_flags); } /* @@ -258,6 +359,10 @@ base_player::PredictPostFrame(void) ROLL_BACK(w_idle_next); ROLL_BACK(punchangle); ROLL_BACK(vehicle); + + ROLL_BACK(spec_ent); + ROLL_BACK(spec_mode); + ROLL_BACK(spec_flags); } #else void @@ -439,10 +544,9 @@ base_player::Death(void) /* ================= -base_player::MakeTempSpectator +base_player::MakePlayer -This is what dead players in round matches become, or when we spawn -for the first time before selecting a loadout or something. +True participating player, can walk around and everything. ================= */ void @@ -571,6 +675,11 @@ base_player::EvaluateEntity(void) SAVE_STATE(w_idle_next); SAVE_STATE(punchangle); SAVE_STATE(vehicle); + + /* FIXME: Make this temp spec only */ + SAVE_STATE(spec_ent); + SAVE_STATE(spec_mode); + SAVE_STATE(spec_flags); } /* @@ -663,6 +772,11 @@ base_player::SendEntity(entity ePEnt, float fChanged) else WriteEntity(MSG_ENTITY, __NULL__); + /* FIXME: Make this fake spectator only. */ + WriteByte(MSG_ENTITY, spec_ent); + WriteByte(MSG_ENTITY, spec_mode); + WriteByte(MSG_ENTITY, spec_flags); + return (1); } #endif diff --git a/src/shared/player_pmove.qc b/src/shared/player_pmove.qc index fde1ba87..5dc917e2 100644 --- a/src/shared/player_pmove.qc +++ b/src/shared/player_pmove.qc @@ -265,11 +265,7 @@ base_player::Physics_InputPostMove(void) flags &= ~FL_FROZEN; - /* allow vehicles to prevent weapon logic from happening */ - Vehicle_Input(this); - - /* weapon/item logic of what the player controls */ - Game_Input((player)this); + ClientInput(); } /* the main physics routine, the head */ diff --git a/src/shared/spectator.h b/src/shared/spectator.h index 42f827a6..bac0433a 100644 --- a/src/shared/spectator.h +++ b/src/shared/spectator.h @@ -15,12 +15,15 @@ typedef enum SPECMODE_OVERVIEW } spectatorMode_t; +typedef enumflags +{ + SPECFLAG_BUTTON_RELEASED, +}; + class spectator:base_client { - vector origin_net; - vector velocity_net; - float spec_ent; float spec_ent_net; - float spec_flags; float spec_flags_net; + PREDICTED_FLOAT(spec_ent); + PREDICTED_FLOAT(spec_flags); spectatorMode_t spec_mode; spectatorMode_t spec_mode_net; vector spec_org; @@ -29,18 +32,20 @@ class spectator:base_client void(void) spectator; + virtual void(void) ClientInput; + virtual void(void) InputNext; virtual void(void) InputPrevious; virtual void(void) InputMode; - virtual void(void) PreFrame; - virtual void(void) PostFrame; - - virtual void(void) SpectatorInput; - virtual void(void) WarpToTarget; + virtual void(void) PreFrame; + virtual void(void) PostFrame; + virtual void(void) SpectatorTrackPlayer; + #ifdef SERVER + virtual void(void) EvaluateEntity; virtual float(entity, float) SendEntity; virtual void(void) RunClientCommand; #else diff --git a/src/shared/spectator.qc b/src/shared/spectator.qc index cfa1c329..b5e01958 100644 --- a/src/shared/spectator.qc +++ b/src/shared/spectator.qc @@ -15,15 +15,7 @@ */ void -spectator::WarpToTarget(void) -{ - entity b = edict_num(spec_ent); - - setorigin(this, b.origin); -} - -void -spectator::SpectatorInput(void) +spectator::ClientInput(void) { if (input_buttons & INPUT_BUTTON0) { InputNext(); @@ -32,10 +24,19 @@ spectator::SpectatorInput(void) } else if (input_buttons & INPUT_BUTTON2) { InputMode(); } else { - spec_flags &= ~GF_SEMI_TOGGLED; + spec_flags &= ~SPECFLAG_BUTTON_RELEASED; } input_buttons = 0; + //crossprint(sprintf("%d %d %d\n", spec_ent, spec_mode, spec_flags)); +} + +void +spectator::WarpToTarget(void) +{ + entity b = edict_num(spec_ent); + + setorigin(this, b.origin); } #ifdef SERVER @@ -81,7 +82,7 @@ void spectator::RunClientCommand(void) { runstandardplayerphysics(this); - SpectatorInput(); + ClientInput(); } #else @@ -103,7 +104,6 @@ spectator::ClientInputFrame(void) void spectator::ReceiveEntity(float new, float fl) { - if (new == FALSE) { /* Go through all the physics code between the last received frame * and the newest frame and keep the changes this time around instead @@ -121,7 +121,7 @@ spectator::ReceiveEntity(float new, float fl) } input_sequence = i; runstandardplayerphysics(this); - SpectatorInput(); + ClientInput(); } /* any differences in things that are read below are now @@ -164,7 +164,7 @@ spectator::predraw(void) void spectator::InputNext(void) { - if (spec_flags & GF_SEMI_TOGGLED) + if (spec_flags & SPECFLAG_BUTTON_RELEASED) return; #if 0 @@ -190,14 +190,14 @@ spectator::InputNext(void) if (i <= sep && best == 0) { f = edict_num(i); - if (f && f.classname == "player") { + if (f && f.classname == "player" && f != this) { best = i; } } if (i > sep) { f = edict_num(i); - if (f && f.classname == "player") { + if (f && f.classname == "player" && f != this) { best = i; break; } @@ -209,7 +209,7 @@ spectator::InputNext(void) spec_ent = best; #endif - spec_flags |= GF_SEMI_TOGGLED; + spec_flags |= SPECFLAG_BUTTON_RELEASED; WarpToTarget(); if (spec_mode == SPECMODE_FREE) @@ -219,7 +219,7 @@ spectator::InputNext(void) void spectator::InputPrevious(void) { - if (spec_flags & GF_SEMI_TOGGLED) + if (spec_flags & SPECFLAG_BUTTON_RELEASED) return; #if 0 float max_edict; @@ -264,7 +264,7 @@ spectator::InputPrevious(void) spec_ent = best; #endif - spec_flags |= GF_SEMI_TOGGLED; + spec_flags |= SPECFLAG_BUTTON_RELEASED; WarpToTarget(); @@ -275,29 +275,28 @@ spectator::InputPrevious(void) void spectator::InputMode(void) { - if (spec_flags & GF_SEMI_TOGGLED) + if (spec_flags & SPECFLAG_BUTTON_RELEASED) return; + crossprint("MODE\n"); spec_mode++; if (spec_mode > SPECMODE_FIRSTPERSON) spec_mode = SPECMODE_FREE; - spec_flags |= GF_SEMI_TOGGLED; + spec_flags |= SPECFLAG_BUTTON_RELEASED; } void spectator::PreFrame(void) { -#ifdef SERVER - -#else +#ifdef CLIENT /* base player attributes/fields we're going to roll back */ - origin_net = origin; - velocity_net = velocity; - spec_ent_net = spec_ent; - spec_mode_net = spec_mode; - spec_flags_net = spec_flags; + SAVE_STATE(origin); + SAVE_STATE(velocity); + SAVE_STATE(spec_ent); + SAVE_STATE(spec_mode); + SAVE_STATE(spec_flags); /* run physics code for all the input frames which we've not heard back * from yet. This continues on in Player_ReceiveEntity! */ @@ -321,10 +320,16 @@ spectator::PreFrame(void) /* run our custom physics */ runstandardplayerphysics(this); - SpectatorInput(); + ClientInput(); } #endif + SpectatorTrackPlayer(); +} + +void +spectator::SpectatorTrackPlayer(void) +{ if (spec_mode == SPECMODE_THIRDPERSON || spec_mode == SPECMODE_FIRSTPERSON ) { entity b; @@ -345,10 +350,10 @@ spectator::PreFrame(void) } } -void -spectator::PostFrame(void) -{ #ifdef SERVER +void +spectator::EvaluateEntity(void) +{ /* check for which values have changed in this frame and announce to network said changes */ if (origin != origin_net) @@ -366,19 +371,23 @@ spectator::PostFrame(void) if (spec_flags != spec_flags_net) SetSendFlags(SPECFL_FLAGS); - origin_net = origin; - velocity_net = velocity; - spec_ent_net = spec_ent; - spec_mode_net = spec_mode; - spec_flags_net = spec_flags; -#else - /* finally roll the values back */ - origin = origin_net; - velocity = velocity_net; - spec_ent = spec_ent_net; - spec_mode = spec_mode_net; - spec_flags = spec_flags_net; - setorigin(this, origin); + SAVE_STATE(origin); + SAVE_STATE(velocity); + SAVE_STATE(spec_ent); + SAVE_STATE(spec_mode); + SAVE_STATE(spec_flags); +} +#endif + +void +spectator::PostFrame(void) +{ +#ifdef CLIENT + ROLL_BACK(origin); + ROLL_BACK(velocity); + ROLL_BACK(spec_ent); + ROLL_BACK(spec_mode); + ROLL_BACK(spec_flags); #endif } diff --git a/src/shared/surfaceproperties.qc b/src/shared/surfaceproperties.qc index 55b99640..734ddd03 100644 --- a/src/shared/surfaceproperties.qc +++ b/src/shared/surfaceproperties.qc @@ -475,6 +475,7 @@ SurfData_Impact(entity e, int fl, vector org, vector ang) case BSPVER_Q3: /* Q3 */ case BSPVER_RTCW: /* RtCW */ case BSPVER_RBSP: /* RFVBSP */ + fl &= ~SURF_MASK; FX_Impact(SurfData_SurfaceFlagtoImpact(fl), org, ang); break; default: