From 744ba0544c02e86ca9602bc974828c8434dfab48 Mon Sep 17 00:00:00 2001 From: TimePath Date: Mon, 30 Nov 2015 12:25:28 +1100 Subject: [PATCH] Viewmodels: smooth switch with high latency --- qcsrc/client/view.qc | 8 +++++-- qcsrc/common/weapons/all.qc | 2 ++ qcsrc/lib/net.qh | 2 +- qcsrc/server/defs.qh | 2 +- qcsrc/server/weapons/weaponsystem.qc | 33 ++++++++++++++-------------- 5 files changed, 26 insertions(+), 21 deletions(-) diff --git a/qcsrc/client/view.qc b/qcsrc/client/view.qc index 07fa43edd..df4a6d335 100644 --- a/qcsrc/client/view.qc +++ b/qcsrc/client/view.qc @@ -264,6 +264,8 @@ void viewmodel_animate(entity this) } .vector viewmodel_origin, viewmodel_angles; +.float weapon_nextthink; +.float weapon_eta_last; void viewmodel_draw(entity this) { @@ -300,9 +302,10 @@ void viewmodel_draw(entity this) if (!this.animstate_override) anim_set(this, this.anim_idle, true, false, false); } - float eta = (STAT(WEAPON_NEXTTHINK) - time); // TODO: / W_WeaponRateFactor(); float f = 0; // 0..1; 0: fully active - switch (this.state) + float eta = (this.weapon_nextthink - time); // TODO: / W_WeaponRateFactor(); + if (eta <= 0) f = this.weapon_eta_last; + else switch (this.state) { case WS_RAISE: { @@ -324,6 +327,7 @@ void viewmodel_draw(entity this) break; } } + this.weapon_eta_last = f; this.origin = this.viewmodel_origin; this.angles = this.viewmodel_angles; this.angles_x = (-90 * f * f); diff --git a/qcsrc/common/weapons/all.qc b/qcsrc/common/weapons/all.qc index f0b606e8b..ab613bb94 100644 --- a/qcsrc/common/weapons/all.qc +++ b/qcsrc/common/weapons/all.qc @@ -561,6 +561,7 @@ NET_HANDLE(wframe, bool isNew) bool restartanim = ReadByte(); anim_set(viewmodel, a, !restartanim, restartanim, restartanim); viewmodel.state = ReadByte(); + viewmodel.weapon_nextthink = ReadFloat(); viewmodel.alpha = ReadByte() / 255; return true; } @@ -578,6 +579,7 @@ void wframe_send(entity actor, entity weaponentity, vector a, bool restartanim) WriteCoord(channel, a.z); WriteByte(channel, restartanim); WriteByte(channel, weaponentity.state); + WriteFloat(channel, weaponentity.weapon_nextthink); WriteByte(channel, weaponentity.alpha * 255); } #endif diff --git a/qcsrc/lib/net.qh b/qcsrc/lib/net.qh index 504619457..bd1c5e14a 100644 --- a/qcsrc/lib/net.qh +++ b/qcsrc/lib/net.qh @@ -187,7 +187,7 @@ STATIC_INIT(RegisterTempEntities_renumber) } #define ReadFloat() ReadCoord() - vector ReadVector() { vector v; v.x = ReadFloat(); v_y = ReadFloat(); v.z = ReadFloat(); return v; } + vector ReadVector() { vector v; v.x = ReadFloat(); v.y = ReadFloat(); v.z = ReadFloat(); return v; } vector ReadVector2D() { vector v; v.x = ReadFloat(); v.y = ReadFloat(); v.z = 0; return v; } float ReadApproxPastTime() diff --git a/qcsrc/server/defs.qh b/qcsrc/server/defs.qh index a18dd3c43..62fec55fe 100644 --- a/qcsrc/server/defs.qh +++ b/qcsrc/server/defs.qh @@ -148,7 +148,7 @@ float client_hasweapon(entity cl, float wpn, float andammo, float complain); void w_clear(Weapon thiswep, entity actor, .entity weaponentity, int fire); void w_ready(Weapon thiswep, entity actor, .entity weaponentity, int fire); // VorteX: standalone think for weapons, so normal think on weaponentity can be reserved by weaponflashes (which needs update even player dies) -.float weapon_nextthink = _STAT(WEAPON_NEXTTHINK); +.float weapon_nextthink; .void(Weapon thiswep, entity actor, .entity weaponentity, int fire) weapon_think; diff --git a/qcsrc/server/weapons/weaponsystem.qc b/qcsrc/server/weapons/weaponsystem.qc index 54bc2c9a5..d8706b26e 100644 --- a/qcsrc/server/weapons/weaponsystem.qc +++ b/qcsrc/server/weapons/weaponsystem.qc @@ -335,18 +335,16 @@ void weapon_thinkf(entity actor, .entity weaponentity, WFRAME fr, float t, void( vector or = v_right; vector ou = v_up; + vector a = '0 0 0'; if (this) { this.wframe = fr; - vector a = '0 0 0'; if (fr == WFRAME_IDLE) a = this.anim_idle; else if (fr == WFRAME_FIRE1) a = this.anim_fire1; else if (fr == WFRAME_FIRE2) a = this.anim_fire2; else // if (fr == WFRAME_RELOAD) a = this.anim_reload; a.z *= g_weaponratefactor; - entity e; - FOR_EACH_CLIENT(e) if (e == actor || (IS_SPEC(e) && e.enemy == actor)) wframe_send(e, this, a, restartanim); } v_forward = of; @@ -371,20 +369,27 @@ void weapon_thinkf(entity actor, .entity weaponentity, WFRAME fr, float t, void( // dprint("reset weapon animation timer at ", ftos(time), "\n"); } this.weapon_nextthink += t; - if (weaponentity == weaponentities[0]) actor.weapon_nextthink = this.weapon_nextthink; + if (weaponentity == weaponentities[0]) STAT(WEAPON_NEXTTHINK, actor) = this.weapon_nextthink; this.weapon_think = func; // dprint("next ", ftos(this.weapon_nextthink), "\n"); + if (this) + { + entity e; + FOR_EACH_CLIENT(e) if (e == actor || (IS_SPEC(e) && e.enemy == actor)) wframe_send(e, this, a, restartanim); + } + if ((fr == WFRAME_FIRE1 || fr == WFRAME_FIRE2) && t) { - if ((actor.weapon == WEP_SHOCKWAVE.m_id || actor.weapon == WEP_SHOTGUN.m_id) - && fr == WFRAME_FIRE2) animdecide_setaction(actor, ANIMACTION_MELEE, restartanim); - else animdecide_setaction(actor, ANIMACTION_SHOOT, restartanim); + int act = (fr == WFRAME_FIRE2 && (actor.weapon == WEP_SHOCKWAVE.m_id || actor.weapon == WEP_SHOTGUN.m_id)) + ? ANIMACTION_MELEE + : ANIMACTION_SHOOT + ; + animdecide_setaction(actor, act, restartanim); } - else + else if (actor.anim_upper_action == ANIMACTION_SHOOT || actor.anim_upper_action == ANIMACTION_MELEE) { - if (actor.anim_upper_action == ANIMACTION_SHOOT - || actor.anim_upper_action == ANIMACTION_MELEE) actor.anim_upper_action = 0; + actor.anim_upper_action = 0; } } @@ -486,13 +491,7 @@ void W_WeaponFrame(entity actor) entity oldwep = Weapons_from(actor.weapon); // set up weapon switch think in the future, and start drop anim - if ( -#if INDEPENDENT_ATTACK_FINISHED - true -#else - ATTACK_FINISHED(actor, slot) <= time + actor.weapon_frametime * 0.5 -#endif - ) + if (INDEPENDENT_ATTACK_FINISHED || ATTACK_FINISHED(actor, weaponslot(weaponentity)) <= time + actor.weapon_frametime * 0.5) { sound(actor, CH_WEAPON_SINGLE, SND_WEAPON_SWITCH, VOL_BASE, ATTN_NORM); this.state = WS_DROP; -- 2.39.2