diff --git a/csqc/pmove.qc b/csqc/pmove.qc index 76fff3cd..6a6edfdf 100644 --- a/csqc/pmove.qc +++ b/csqc/pmove.qc @@ -239,8 +239,9 @@ static void PM_ActivatePMS(PMS_Data *pmsd) { pm.ent.pmove_flags = 0; // With auto-bunny we only care about clearing }; +static float cache_invalidated; static PMS_Data* SimPMSD() { - return CVARF(fopm_nocache) ? &pm_s : &pm_c; + return (cache_invalidated || CVARF(fopm_nocache)) ? &pm_s : &pm_c; } void PM_InputFrame(); @@ -250,6 +251,7 @@ enum { NT_CONC, NT_DASH, NT_EXPLOSION, + NT_BOUNCE, }; enum { @@ -257,6 +259,7 @@ enum { NA_SEQ, NA_SEQTIME, NA_ITIME, + NA_CONC, // itime but with expiration driven by conc_state }; struct PM_Nudge { @@ -271,6 +274,7 @@ struct PM_Nudge { float aux; }; +const static float NUDGE_EPS = 3 * SERVER_FRAME_DT; static PM_Nudge nudges[5]; static float num_nudges; @@ -281,9 +285,10 @@ static float nudge_expired(PM_Nudge* n) { switch (n->nat) { case NA_NONE: return TRUE; - case NA_SEQ: return n->seq < servercommandframe; - case NA_SEQTIME: return n->seq + ceil(n->itime / SERVER_FRAME_DT) < servercommandframe; - case NA_ITIME: return n->itime < pstate_server.server_time; + case NA_SEQ: return n->seq >= servercommandframe; + case NA_SEQTIME: return n->seq + ceil(n->itime / SERVER_FRAME_DT) >= servercommandframe; + case NA_ITIME: return n->itime >= pstate_server.server_time + NUDGE_EPS; // Slack + case NA_CONC: return n->itime <= pstate_server.conc_state.next; } return TRUE; @@ -296,6 +301,7 @@ static PM_Nudge* find_nudge_slot() { if (!nudge_expired(n)) continue; + cache_invalidated = TRUE; n->nat = NA_NONE; n->src_no = 0; n->src = 0; @@ -330,6 +336,7 @@ void PM_AddNudgeConc(float itime, float mag, float flip) { return; n->type = NT_CONC; + n->nat = NA_CONC; n->itime = itime; n->vec.x = mag; n->vec.y = flip; @@ -381,7 +388,7 @@ static void PM_NudgeExplosion(PM_Nudge* nudge, entity ent) { // Can be either by {seq + itime} for predicted projectiles, or by {itime} for // server projectiles. -void PM_AddNudgeExplosion(float nat, float seq, float itime, entity ent, float dmg) { +static void PM_AddNudgeExplosion(float nat, float seq, float itime, entity ent, float dmg) { if (vlen(PM_Org() - ent.origin) > dmg * 3) // Fine tune this.. return; @@ -400,11 +407,58 @@ void PM_AddNudgeExplosion(float nat, float seq, float itime, entity ent, float d n->src_no = ent.entnum; ent.nudge = n; + ent.removefunc = PM_RemoveSelfNudges; +} + +static void PM_AddNudgeBounce(float itime, entity ent, float dmg) { + if (vlen(PM_Org() - ent.origin) > dmg * 3) // Fine tune this.. + return; + PM_Nudge* n = find_nudge_slot(); + if (!n) + return; + + n->type = NT_BOUNCE; + n->nat = NA_ITIME; + n->itime = itime; + n->aux = dmg; + + n->src = ent; + n->src_no = ent.entnum; + + ent.nudge = n; ent.removefunc = PM_RemoveSelfNudges; } -DEFCVAR_FLOAT(fo_beta_nudge_explosion, 0); +static void PM_NudgeBounce(PM_Nudge* nudge, entity ent) { + float dmg = nudge->aux; + entity src = nudge->src; + + if (vlen(ent.origin - src.origin) > dmg + 40) + return; + + vector targ_org = ent.origin + (ent.mins + ent.maxs) * 0.5; + float points = dmg - vlen(targ_org - src.origin) / 2; + + ent.velocity = (targ_org - src.origin) * points/20; +} + + +void PM_AddGrenadeExplosion(float itime, entity ent) { + if (!PM_Enabled() || !CVARF(fo_beta_nudge_explosion)) + return; + + FO_GrenExp exp; + FO_GrenGetExp(ent.fpp.gren_type, exp); + + float dmg = exp.dmg; + + if (exp.type == kRadiusDamage) + PM_AddNudgeExplosion(NA_ITIME, 0, itime, ent, dmg); + else + PM_AddNudgeBounce(itime, ent, dmg); +} + void PM_AddSimExplosion(float itime, entity ent) { if (!PM_Enabled() || !CVARF(fo_beta_nudge_explosion)) return; @@ -447,6 +501,7 @@ float MatchNudge(PM_Nudge* n, float seq, float sitime, float eitime) { switch (n->nat) { case NA_SEQ: return n->seq == seq; case NA_SEQTIME: return n->seq + floor(n->itime / SERVER_FRAME_DT) == seq; + case NA_CONC: case NA_ITIME: return n->itime > sitime && n->itime <= eitime; default: return FALSE; @@ -454,8 +509,7 @@ float MatchNudge(PM_Nudge* n, float seq, float sitime, float eitime) { } // TODO: This obviously wants to be smarter -static vector PM_ApplyNudges(entity ent, float seq, float sitime, float eitime) { - vector result = '0 0 0'; +static void PM_ApplyNudges(entity ent, float seq, float sitime, float eitime) { for (float i = 0; i < nudges.length; i++) { PM_Nudge* n = &nudges[i]; @@ -472,10 +526,11 @@ static vector PM_ApplyNudges(entity ent, float seq, float sitime, float eitime) case NT_EXPLOSION: PM_NudgeExplosion(n, ent); break; + case NT_BOUNCE: + PM_NudgeBounce(n, ent); + break; } } - - return result; } static void RunPlayerPhysics() { @@ -517,6 +572,9 @@ static void PM_RunMovement(PMS_Data* pmsd, float endframe) { RunPlayerPhysics(); if (!CVARF(fopm_nonudge)) { + float start_time = pm.interp_t; + if (pm.seq == pm.server_seq + 1) + start_time -= NUDGE_EPS; PM_ApplyNudges(ent, pm.seq, pm.interp_t, pm.interp_t + input_timelength); } @@ -576,7 +634,6 @@ static void PM_UpdateError() { err -= ent.origin; float nerr = vlen(err); - if (nerr > 64) { // teleport pm.error = '0 0 0'; pm.errortime = 0; @@ -657,6 +714,11 @@ static void PM_HandleRemovedNudges() { if (nudge_expired(n)) continue; + if (n->type == NT_CONC && n->itime < pstate_server.conc_state.next) { + n->expire_frame = -1; + continue; + } + for (float j = 0; j < pstate_server.num_filter_ents; j++) { if (n->src_no == pstate_server.filter_ents[j]) { n->nat = NA_SEQ; @@ -694,6 +756,7 @@ void PM_Update(float sendflags) { if (clientcommandframe > servercommandframe + 1) PM_RunMovement(&pm_s, clientcommandframe - 1); PM_SavePMS(&pm_c); + cache_invalidated = FALSE; } if (enabled && was_enabled) diff --git a/csqc/weapon_predict.qc b/csqc/weapon_predict.qc index 4ae79879..77468264 100644 --- a/csqc/weapon_predict.qc +++ b/csqc/weapon_predict.qc @@ -586,6 +586,8 @@ void PredProj_Sound(int proj_type, float vol = 1) { void PM_Update(float sendflags); +DEFCVAR_FLOAT(fo_beta_nudge_explosion, 0); + void WP_ServerUpdate(float sendflags) { pstate_server.seq = servercommandframe; @@ -604,8 +606,8 @@ void WP_ServerUpdate(float sendflags) { pstate_server.weaponframe += 1; } - if (CVARF(fo_wpp_beta) & 4) - phys_sim_dt = (pstate_server.client_ping + 2 * SERVER_FRAME_MS) / 1000.0; + if (CVARF(fo_beta_nudge_explosion)) + phys_sim_dt = (pstate_server.client_ping + 4 * SERVER_FRAME_MS) / 1000.0; else phys_sim_dt = -1; @@ -2057,7 +2059,7 @@ static void WP_UpdatePredict() { pengine.is_effectframe = TRUE; pengine.last_effectframe = pframe; PM_SyncTo(pframe); - /* Conc_Update(&pstate_pred.conc_state, world, pstate_pred.server_time); */ + Conc_Update(&pstate_pred.conc_state, world, pstate_pred.server_time); } else { pengine.is_effectframe = FALSE; } diff --git a/share/classes.qc b/share/classes.qc index 25048ce2..831dd0db 100644 --- a/share/classes.qc +++ b/share/classes.qc @@ -125,10 +125,10 @@ static inline void ConcAction(entity ent, float itime, float mag, float flip) { Conc_Stumble(ent, mag, flip); } #else -void PM_AddConcNudge(float itime, float mag, float flip); +void PM_AddNudgeConc(float itime, float mag, float flip); static inline void ConcAction(entity ent, float itime, float mag, float flip) { - PM_AddConcNudge(itime, mag, flip); + PM_AddNudgeConc(itime, mag, flip); } #endif diff --git a/share/physics.qc b/share/physics.qc index 66e887aa..f4e54746 100644 --- a/share/physics.qc +++ b/share/physics.qc @@ -60,6 +60,8 @@ static void Phys_Impact(entity e, float dt, float phys_flags) { static vector get_followed_origin() { return self.aiment.origin; } + +static void Phys_Expired(entity e, float phys_flags) {} #else DEFCVAR_FLOAT(fo_phys_debug, 0); @@ -93,7 +95,15 @@ static void Phys_Impact(entity e, float dt, float phys_flags) { get_phys_time(e)); } -static void Phys_Expired(entity e, float phys_flags) {} +void PM_AddGrenadeExplosion(float itime, entity ent); + +static void Phys_Expired(entity e, float phys_flags) { + e.voided = TRUE; + if (e.phys_sim_voided_at) // Only one of sim or !sim + return; + + PM_AddGrenadeExplosion(e.fpp.expires_at, e); +} static vector get_followed_origin() { if (!(float)getentity(self.aiment_num, GE_ACTIVE)) @@ -224,7 +234,7 @@ float Phys_Advance(entity e, float target_time, float phys_flags) { } // If there's an expiry, make sure we get to the exact position. - if (e.fpp.expires_at && target_time > e.fpp.expires_at) { + if (e.fpp.expires_at && target_time >= e.fpp.expires_at) { target_time = e.fpp.expires_at; phys_flags |= PHYSF_CONSUME_ALL; } @@ -280,6 +290,10 @@ float Phys_Advance(entity e, float target_time, float phys_flags) { if (step > 0) // Origin can have moved. setorigin(e, e.origin); + + if (e.fpp.expires_at && e.phys_time >= e.fpp.expires_at) + Phys_Expired(e, phys_flags); + return total; } @@ -301,7 +315,7 @@ void Phys_Sim(entity e) { return; float ptime = time + phys_sim_dt; - if (ptime < self.phys_sim_last + SERVER_FRAME_MS) + if (ptime < self.phys_sim_last + SERVER_FRAME_DT) return; self.phys_sim_last = ptime; diff --git a/share/prediction.qc b/share/prediction.qc index bfa0d353..9aa81c13 100644 --- a/share/prediction.qc +++ b/share/prediction.qc @@ -51,6 +51,8 @@ enumflags { PF_PMOVE_ACTIVATING, }; +static float MAX_FILTER_ENTS = 4; + struct predict_tf_state { int playerclass; int predict_flags, effects; @@ -84,9 +86,9 @@ struct predict_tf_state { float num_filter_ents; #ifdef SSQC - entity filter_ents[4]; + entity filter_ents[MAX_FILTER_ENTS]; #else - float filter_ents[4]; + float filter_ents[MAX_FILTER_ENTS]; #endif #ifdef CSQC @@ -398,8 +400,8 @@ void Predict_AddFilterEnt(entity p, entity filter) { return; predict_tf_state* ps = &p.predict_state; - if (ps->num_filter_ents >= 4) - return + if (ps->num_filter_ents >= MAX_FILTER_ENTS) + return; ps->filter_ents[ps->num_filter_ents++] = filter; } diff --git a/share/weapons.qc b/share/weapons.qc index dccb3480..8a0c09c6 100644 --- a/share/weapons.qc +++ b/share/weapons.qc @@ -843,7 +843,7 @@ const float KF_BOTH_PLAYER = KF_SRC_PLAYER | KF_TARG_PLAYER; // terrible mistake. float CalcRadiusDamage(entity src, entity targ, float flags, float damage) { - vector targ_origin = (targ.absmin + targ.absmax) * 0.5; // (*) + vector targ_origin = targ.origin + (targ.mins + targ.maxs) * 0.5; // (*) damage -= vlen(src.origin - targ_origin) / 2; if (flags & KF_SELF) @@ -857,7 +857,7 @@ vector CalcKnock(entity src, entity targ, float flags, float moment) { if (moment < 60 && (flags & (KF_BOTH_PLAYER | KF_SELF) == KF_BOTH_PLAYER)) mult = 11; - vector src_origin = (src.absmin + src.absmax) * 0.5; // (*) + vector src_origin = src.origin + (src.mins + src.maxs) * 0.5; // (*) vector dir = normalize(targ.origin - src_origin); vector result = dir * moment * mult; diff --git a/ssqc/scout.qc b/ssqc/scout.qc index 8205ea70..cd8ee594 100644 --- a/ssqc/scout.qc +++ b/ssqc/scout.qc @@ -858,6 +858,8 @@ void (entity inflictor, entity attacker, float bounce, && (points > 0)) { head.velocity = org - inflictor.origin; head.velocity = head.velocity * (points / 20); + Predict_AddFilterEnt(head, inflictor); + if (head.classname != "player") { if (head.flags & FL_ONGROUND) head.flags = head.flags - FL_ONGROUND; diff --git a/ssqc/tfort.qc b/ssqc/tfort.qc index 4a5035e6..8d061796 100644 --- a/ssqc/tfort.qc +++ b/ssqc/tfort.qc @@ -2420,6 +2420,7 @@ void () TeamFortress_ExplodePerson = { proj.fpp.gren_type = gtype; proj.fpp.flags |= FPF_NO_REWIND; + proj.fpp.expires_at = expires; proj.skin = gdesc->skin; proj.avelocity = gdesc->avelocity; proj.touch = ste->touch;