]> git.rm.cloudns.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Add a very basic means of displaying a client-side beam when firing vaporizer shots... Mario/mod_port
authorMario <mario.mario@y7mail.com>
Mon, 7 Jun 2021 23:42:30 +0000 (09:42 +1000)
committerMario <mario.mario@y7mail.com>
Mon, 7 Jun 2021 23:42:30 +0000 (09:42 +1000)
12 files changed:
qcsrc/client/hud/crosshair.qc
qcsrc/client/hud/crosshair.qh
qcsrc/client/main.qc
qcsrc/client/main.qh
qcsrc/client/weapons/_mod.inc
qcsrc/client/weapons/_mod.qh
qcsrc/client/weapons/tracing.qc [new file with mode: 0644]
qcsrc/client/weapons/tracing.qh [new file with mode: 0644]
qcsrc/common/weapons/weapon.qh
qcsrc/common/weapons/weapon/vaporizer.qc
qcsrc/common/wepent.qh
xonotic-client.cfg

index c697268eb4d2ef82928b800ee5771e5cc302ad8e..dddfbb0408ab14ad0e8c2af977684e9db06e221b 100644 (file)
@@ -3,6 +3,7 @@
 #include <client/draw.qh>
 #include <client/hud/panel/scoreboard.qh>
 #include <client/view.qh>
+#include <client/weapons/tracing.qh>
 #include <common/deathtypes/all.qh>
 #include <common/ent_cs.qh>
 #include <common/mapobjects/trigger/viewloc.qh>
@@ -49,23 +50,18 @@ void TrueAim_Init()
        (trueaim_rifle = new_pure(trueaim_rifle)).dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_CORPSE;
 }
 
-float EnemyHitCheck()
+int EnemyHitCheck(bool trace_crosshair)
 {
-       float t, n;
-       wcross_origin = project_3d_to_2d(trace_endpos);
-       wcross_origin.z = 0;
-       if(trace_ent)
-               n = trace_ent.entnum;
-       else
-               n = trace_networkentity;
+       if(trace_crosshair)
+               wcross_origin = vec2(project_3d_to_2d(trace_endpos));
+       int n = (trace_ent) ? trace_ent.entnum : trace_networkentity;
        if(n < 1)
                return SHOTTYPE_HITWORLD;
        if(n > maxclients)
                return SHOTTYPE_HITWORLD;
-       t = entcs_GetTeam(n - 1);
-       if(teamplay)
-               if(t == myteam)
-                       return SHOTTYPE_HITTEAM;
+       int t = entcs_GetTeam(n - 1);
+       if(teamplay && t == myteam)
+               return SHOTTYPE_HITTEAM;
        if(t == NUM_SPECTATOR)
                return SHOTTYPE_HITWORLD;
        return SHOTTYPE_HITENEMY;
@@ -100,7 +96,7 @@ float TrueAimCheck(entity wepent)
                        if(zoomscript_caught)
                        {
                                tracebox(view_origin, '0 0 0', '0 0 0', view_origin + view_forward * max_shot_distance, mv, ta);
-                               return EnemyHitCheck();
+                               return EnemyHitCheck(true);
                        }
                        break;
                case WEP_DEVASTATOR: // projectile has a size!
@@ -147,7 +143,7 @@ float TrueAimCheck(entity wepent)
        w_shotorg = trace_endpos - view_forward * nudge;
 
        tracebox(w_shotorg, mi, ma, trueaimpoint, MOVE_NORMAL, ta);
-       shottype = EnemyHitCheck();
+       shottype = EnemyHitCheck(true);
        if(shottype != SHOTTYPE_HITWORLD)
                return shottype;
 
@@ -236,6 +232,8 @@ void HUD_Crosshair(entity this)
                return;
        }
 
+       HUD_Crosshair_ClientBeam(this);
+
        float f, i, j;
        vector v;
        if(!scoreboard_active && !camera_active && intermission != 2 && !STAT(GAME_STOPPED) && !autocvar_cl_lockview
index 4cd7715af49883dc82deadb9eb203e6f97ad1f2a..f1e7221a814ff473ab08057f71f54f074938731d 100644 (file)
@@ -63,3 +63,5 @@ vector crosshair_getcolor(entity this, float health_stat);
 void TrueAim_Init();
 void HUD_Crosshair(entity this);
 void DrawReticle(entity this);
+
+int EnemyHitCheck(bool trace_crosshair);
index 757effd99bf766574f3fb328e25766ad5e782a90..4ab08609b16f77fbeb24d7cfffe72062d8c8c7db 100644 (file)
@@ -12,6 +12,7 @@
 #include <client/shownames.qh>
 #include <client/view.qh>
 #include <client/weapons/projectile.qh>
+#include <client/weapons/tracing.qh>
 #include <common/deathtypes/all.qh>
 #include <common/effects/all.inc>
 #include <common/effects/all.qh>
index 83417c08d9c59ecd7a54c493f8c1bc40ec999d7f..6206f49162860bbf3a81fd9ef13b9a6696eb5436 100644 (file)
@@ -137,8 +137,6 @@ const int HOOK_END =      2;
 
 .float ping, ping_packetloss, ping_movementloss;
 
-float g_trueaim_minrange;
-
 int hud;
 float view_quality;
 
index 5fb71ce1eed785f07661a7f32cabe35dbaa4fd5c..bd1e1e842994b6484c02b9c0d3191f13f9512e7f 100644 (file)
@@ -1,2 +1,3 @@
 // generated file; do not modify
 #include <client/weapons/projectile.qc>
+#include <client/weapons/tracing.qc>
index f72a914c66ba6e10b701716fa9a50b63906bdd19..09ff6e359c89985ef6fe706f6ad13ce3612f7230 100644 (file)
@@ -1,2 +1,3 @@
 // generated file; do not modify
 #include <client/weapons/projectile.qh>
+#include <client/weapons/tracing.qh>
diff --git a/qcsrc/client/weapons/tracing.qc b/qcsrc/client/weapons/tracing.qc
new file mode 100644 (file)
index 0000000..30a4bb1
--- /dev/null
@@ -0,0 +1,97 @@
+#include "tracing.qh"
+
+#include <client/csqcmodel_hooks.qh>
+#include <client/hud/crosshair.qh>
+#include <client/main.qh>
+#include <client/view.qh>
+#include <common/physics/player.qh>
+#include <common/weapons/all.qh>
+#include <common/wepent.qh>
+#include <lib/csqcmodel/common.qh>
+#include <lib/warpzone/common.qh>
+
+int W_SetupShot_Dir_ProjectileSize_Range(entity ent, entity wepent, vector s_forward, vector mi, vector ma, float antilag, float recoil, Sound snd, float chan, float maxdamage, float range)
+{
+       float nudge = 1; // added to traceline target and subtracted from result  TOOD(divVerent): do we still need this? Doesn't the engine do this now for us?
+       vector vecs;
+       int oldsolid = ent.dphitcontentsmask;
+       if (IS_PLAYER(ent) && wepent.activeweapon == WEP_RIFLE)
+               ent.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_CORPSE;
+       else
+               ent.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
+       WarpZone_TraceLine(ent.origin + ent.view_ofs, ent.origin + ent.view_ofs + s_forward * range, MOVE_NOMONSTERS, ent);
+       ent.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
+
+       vector forward, right, up;
+       forward = v_forward;
+       right = v_right;
+       up = v_up;
+       w_shotend = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos); // warpzone support
+       v_forward = forward;
+       v_right = right;
+       v_up = up;
+
+       // perform the test now as future traces will affect the accuracy of the test
+       return = EnemyHitCheck(false);
+
+       // un-adjust trueaim if shotend is too close
+       if(vdist(w_shotend - (ent.origin + ent.view_ofs), <, g_trueaim_minrange))
+               w_shotend = ent.origin + ent.view_ofs + s_forward * g_trueaim_minrange;
+
+       vector md = wepent.movedir;
+       if(md.x > 0)
+               vecs = md;
+       else
+               vecs = '0 0 0';
+
+       vector dv = right * -vecs.y + up * vecs.z;
+       w_shotorg = ent.origin + ent.view_ofs + dv;
+
+       int oldsurfaceflags = trace_dphitq3surfaceflags;
+
+       // now move the shotorg forward as much as requested if possible
+       tracebox(w_shotorg, mi, ma, w_shotorg + forward * (vecs.x + nudge), MOVE_NORMAL, ent);
+       w_shotorg = trace_endpos - forward * nudge;
+       // calculate the shotdir from the chosen shotorg
+       w_shotdir = normalize(w_shotend - w_shotorg);
+
+       // restore flags for later use
+       trace_dphitq3surfaceflags = oldsurfaceflags;
+
+       //vector prevdir = w_shotdir;
+       //vector prevorg = w_shotorg;
+       //vector prevend = w_shotend;
+
+       ent.dphitcontentsmask = oldsolid;
+
+       // nudge w_shotend so a trace to w_shotend hits
+       w_shotend = w_shotend + normalize(w_shotend - w_shotorg) * nudge;
+       //if(w_shotend != prevend) { printf("CLIENT: shotEND differs: %s - %s\n", vtos(w_shotend), vtos(prevend)); }
+       //if(w_shotorg != prevorg) { printf("CLIENT: shotORG differs: %s - %s\n", vtos(w_shotorg), vtos(prevorg)); }
+       //if(w_shotdir != prevdir) { printf("CLIENT: shotDIR differs: %s - %s\n", vtos(w_shotdir), vtos(prevdir)); }
+}
+
+void HUD_Crosshair_ClientBeam(entity this)
+{
+       // TODO: make player_blocked a generic function for use here!
+       if(time < STAT(GAMESTARTTIME) || time < STAT(ROUNDSTARTTIME) || STAT(FROZEN) || spectatee_status)
+               return;
+
+       entity localplayer = playerslots[player_localnum];
+       if(!(autocvar_cl_clientbeams > 0 || (localplayer.ping >= autocvar_cl_clientbeams_minping && autocvar_cl_clientbeams != -1)))
+               return;
+
+       bool button_atck = (input_buttons & BIT(0));
+       bool button_atck2 = (input_buttons & BIT(2));
+
+       vector viewangles = getpropertyvec(VF_CL_VIEWANGLES);
+       makevectors(viewangles);
+
+       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+       {
+               entity wepent = viewmodels[slot];
+               Weapon wep = wepent.activeweapon;
+               if(wep != WEP_Null)
+                       wep.wr_clientbeam(wep, localplayer, wepent, button_atck | (button_atck2 << 1));
+       }
+}
diff --git a/qcsrc/client/weapons/tracing.qh b/qcsrc/client/weapons/tracing.qh
new file mode 100644 (file)
index 0000000..c72c20a
--- /dev/null
@@ -0,0 +1,16 @@
+#pragma once
+
+#include <common/sounds/sound.qh>
+
+AUTOCVAR_SAVE(cl_clientbeams,                    int,   0,      "Draw a client side beam when firing some weapons. -1: always disabled, 0: disabled unless ping is high, 1: enabled");
+AUTOCVAR_SAVE(cl_clientbeams_minping,            int,   160,    "Minimum ping for the client-side beams to automatically enable");
+
+float g_trueaim_minrange;
+vector w_shotorg;
+vector w_shotdir;
+vector w_shotend;
+
+// TODO: make this a common function!
+int W_SetupShot_Dir_ProjectileSize_Range(entity ent, entity wepent, vector s_forward, vector mi, vector ma, float antilag, float recoil, Sound snd, float chan, float maxdamage, float range);
+
+void HUD_Crosshair_ClientBeam(entity this);
index 67f646cbe5b328dbbb8d7671cbef65db65189fc9..81632a709d8a7d9a948794ca04ab7add98e63cd3 100644 (file)
@@ -101,6 +101,8 @@ CLASS(Weapon, Object)
     METHOD(Weapon, wr_resetplayer, void(Weapon this, entity actor)) {}
     /** (CLIENT) impact effect for weapon explosion */
     METHOD(Weapon, wr_impacteffect, void(Weapon this, entity actor)) {}
+    /** (CLIENT) client-side logic run every frame, useful for drawing predicted beams */
+    METHOD(Weapon, wr_clientbeam, void(Weapon this, entity actor, entity wepent, int fire)) {}
     /** (SERVER) called whenever a player dies */
     METHOD(Weapon, wr_playerdeath, void(Weapon this, entity actor, .entity weaponentity)) {}
     /** (SERVER) logic to run when weapon is lost */
index 6f4d4aebf282b6a9a596c9a0d51819543d10b678..4566020bccd4e4a29fc8ae3262eb299740c02541 100644 (file)
@@ -68,22 +68,20 @@ void VaporizerBeam_Draw(entity this)
                WarpZone_TrailParticles_WithMultiplier(NULL, particleeffectnum(EFFECT_VORTEX_BEAM), this.vorg1, this.vorg2, 1, PARTICLES_USEALPHA | PARTICLES_USEFADE);*/
 }
 
-NET_HANDLE(TE_CSQC_VAPORBEAMPARTICLE, bool isNew)
+void VaporizerBeam_Fire(entity this, bool isNew, vector org1, vector org2, bool enemy_hit, int owner_id, int shot_team)
 {
-       Net_Accept(vortex_beam);
        setthink(this, SUB_Remove);
        this.nextthink = time + bound(0, autocvar_cl_vaporizerbeam_lifetime, 10);
        this.draw = VaporizerBeam_Draw;
        if (isNew) IL_PUSH(g_drawables, this);
        this.drawmask = MASK_NORMAL;
 
-       this.vorg1 = ReadVector();
-       this.vorg2 = ReadVector();
-       this.cnt = ReadByte();
-       int myowner = ReadByte();
-       this.owner = playerslots[myowner - 1];
-       this.sv_entnum = myowner;
-       this.team = ReadByte() - 1;
+       this.vorg1 = org1;
+       this.vorg2 = org2;
+       this.cnt = enemy_hit;
+       this.owner = (owner_id) ? playerslots[owner_id - 1] : NULL;
+       this.sv_entnum = owner_id;
+       this.team = shot_team;
 
        //pointparticles(EFFECT_VORTEX_MUZZLEFLASH, this.vorg1, normalize(this.vorg2 - this.vorg1) * 1000, 1);
 
@@ -94,6 +92,19 @@ NET_HANDLE(TE_CSQC_VAPORBEAMPARTICLE, bool isNew)
                this.drawmask = MASK_NORMAL;
                delete(this);
        }
+}
+
+NET_HANDLE(TE_CSQC_VAPORBEAMPARTICLE, bool isNew)
+{
+       Net_Accept(vortex_beam);
+
+       vector org1 = ReadVector();
+       vector org2 = ReadVector();
+       bool enemy_hit = ReadByte();
+       int myowner = ReadByte();
+       int owner_team = ReadByte() - 1;
+
+       VaporizerBeam_Fire(this, isNew, org1, org2, enemy_hit, myowner, owner_team);
 
        return true;
 }
@@ -425,5 +436,31 @@ METHOD(Vaporizer, wr_zoom, bool(entity thiswep, entity actor))
         return false;
     }
 }
+.float last_beam;
+METHOD(Vaporizer, wr_clientbeam, void(entity thiswep, entity actor, entity wepent, int fire))
+{
+       if((fire & 1) && time > actor.last_beam)
+       {
+               // for traces we need the CSQC model!
+               entity realplayer = CSQCModel_server2csqc(actor.sv_entnum);
+               if(!realplayer)
+                       realplayer = csqcplayer; // fall back to the global
+               int shottype = W_SetupShot_Dir_ProjectileSize_Range(realplayer, wepent, v_forward, '0 0 0', '0 0 0', false, 0, SND_Null, CH_WEAPON_A, 0, max_shot_distance);
+               bool hit = boolean(shottype == SHOTTYPE_HITENEMY || shottype == SHOTTYPE_HITTEAM); // also count team shots
+
+               entity e = new(VaporizerBeam);
+               VaporizerBeam_Fire(e, true, w_shotorg, w_shotend, hit, actor.sv_entnum, myteam);
+
+               if(!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY) && !hit)
+               {
+                       vector force = w_shotdir * WEP_CVAR_PRI(vaporizer, force);
+                       traceline(w_shotend - normalize(force) * 16, w_shotend + normalize(force) * 16, MOVE_NOMONSTERS, NULL);
+                       w_backoff = -1 * normalize(force);
+                       vector org2 = w_shotend + w_backoff * 6;
+                       pointparticles(EFFECT_VORTEX_IMPACT, org2, '0 0 0', 1);
+               }
+               actor.last_beam = time + WEP_CVAR_PRI(vaporizer, refire);
+       }
+}
 
 #endif
index 6d7128c33ca27323d91cd6ba8685fb6ca61ac0e4..c2872e2be44b9ca994c50b61afe142f42f2f2dcb 100644 (file)
@@ -1,5 +1,7 @@
 #pragma once
 
+#include <common/weapons/weapon.qh>
+
 REGISTER_NET_LINKED(ENT_CLIENT_WEPENT)
 REGISTER_NET_TEMP(CLIENT_WEPENT)
 
@@ -39,6 +41,8 @@ REGISTER_NET_TEMP(CLIENT_WEPENT)
 
        .int m_skin;
 
+       .vector movedir;
+
        // only for Porto
        .bool angles_held_status;
        .vector angles_held;
index c62cb55407beedd41fd03f9492cb1e78260d9a6b..93988fcb59ffe10866784aa8f30ee09a921add88 100644 (file)
@@ -901,6 +901,9 @@ set cl_useenginerefdef 0
 
 set cl_rollkillspeed 10
 
+seta cl_clientbeams 0 "Draw a client side beam when firing some weapons. -1: always disabled, 0: disabled unless ping is high, 1: enabled"
+seta cl_clientbeams_minping 160 "Minimum ping for the client-side beams to automatically enable"
+
 // Facility for config.cfg use ONLY.
 // Interpreted in post-config.cfg.
 seta menu_forced_saved_cvars "" "These cvars will always be saved, despite engine/Xonotic cvar saving status"