#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>
(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;
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!
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;
return;
}
+ HUD_Crosshair_ClientBeam(this);
+
float f, i, j;
vector v;
if(!scoreboard_active && !camera_active && intermission != 2 && !STAT(GAME_STOPPED) && !autocvar_cl_lockview
void TrueAim_Init();
void HUD_Crosshair(entity this);
void DrawReticle(entity this);
+
+int EnemyHitCheck(bool trace_crosshair);
#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>
.float ping, ping_packetloss, ping_movementloss;
-float g_trueaim_minrange;
-
int hud;
float view_quality;
// generated file; do not modify
#include <client/weapons/projectile.qc>
+#include <client/weapons/tracing.qc>
// generated file; do not modify
#include <client/weapons/projectile.qh>
+#include <client/weapons/tracing.qh>
--- /dev/null
+#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));
+ }
+}
--- /dev/null
+#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);
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 */
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);
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;
}
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
#pragma once
+#include <common/weapons/weapon.qh>
+
REGISTER_NET_LINKED(ENT_CLIENT_WEPENT)
REGISTER_NET_TEMP(CLIENT_WEPENT)
.int m_skin;
+ .vector movedir;
+
// only for Porto
.bool angles_held_status;
.vector angles_held;
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"