X-Git-Url: http://de.git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fserver%2Fweapons%2Ftracing.qc;h=c3532d3d67a4cf29bde6541827acb005189ca8f0;hb=77f03e6ce033bef39ac19e0e7cb6e606ffcb26db;hp=50dcd532fb26fee30c15a8a7c45d0a2c5003154b;hpb=76b7957bc3e84d1f80630ca2e282775a5d5a9013;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/server/weapons/tracing.qc b/qcsrc/server/weapons/tracing.qc index 50dcd532f..c3532d3d6 100644 --- a/qcsrc/server/weapons/tracing.qc +++ b/qcsrc/server/weapons/tracing.qc @@ -1,9 +1,24 @@ +#if defined(CSQC) +#elif defined(MENUQC) +#elif defined(SVQC) + #include "../../dpdefs/progsdefs.qh" + #include "../../dpdefs/dpextensions.qh" + #include "../../warpzonelib/common.qh" + #include "../../common/constants.qh" + #include "../../common/util.qh" + #include "../../common/weapons/weapons.qh" + #include "tracing.qh" + #include "../autocvars.qh" + #include "../defs.qh" + #include "../antilag.qh" +#endif + // this function calculates w_shotorg and w_shotdir based on the weapon model // offset, trueaim and antilag, and won't put w_shotorg inside a wall. // make sure you call makevectors first (FIXME?) void W_SetupShot_Dir_ProjectileSize_Range(entity ent, vector s_forward, vector mi, vector ma, float antilag, float recoil, string snd, float chan, float maxdamage, float range) { - float nudge = 1; // added to traceline target and subtracted from result + 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? float oldsolid; vector vecs, dv; oldsolid = ent.dphitcontentsmask; @@ -37,28 +52,32 @@ void W_SetupShot_Dir_ProjectileSize_Range(entity ent, vector s_forward, vector m W_HitPlotAnalysis(ent, v_forward, v_right, v_up); - if(ent.weaponentity.movedir_x > 0) + if(ent.weaponentity.movedir.x > 0) vecs = ent.weaponentity.movedir; else vecs = '0 0 0'; - dv = v_right * -vecs_y + v_up * vecs_z; + dv = v_right * -vecs.y + v_up * vecs.z; w_shotorg = ent.origin + ent.view_ofs + dv; // now move the shotorg forward as much as requested if possible if(antilag) { if(ent.antilag_debug) - tracebox_antilag(ent, w_shotorg, mi, ma, w_shotorg + v_forward * (vecs_x + nudge), MOVE_NORMAL, ent, ent.antilag_debug); + tracebox_antilag(ent, w_shotorg, mi, ma, w_shotorg + v_forward * (vecs.x + nudge), MOVE_NORMAL, ent, ent.antilag_debug); else - tracebox_antilag(ent, w_shotorg, mi, ma, w_shotorg + v_forward * (vecs_x + nudge), MOVE_NORMAL, ent, ANTILAG_LATENCY(ent)); + tracebox_antilag(ent, w_shotorg, mi, ma, w_shotorg + v_forward * (vecs.x + nudge), MOVE_NORMAL, ent, ANTILAG_LATENCY(ent)); } else - tracebox(w_shotorg, mi, ma, w_shotorg + v_forward * (vecs_x + nudge), MOVE_NORMAL, ent); + tracebox(w_shotorg, mi, ma, w_shotorg + v_forward * (vecs.x + nudge), MOVE_NORMAL, ent); w_shotorg = trace_endpos - v_forward * nudge; // calculate the shotdir from the chosen shotorg w_shotdir = normalize(w_shotend - w_shotorg); + //vector prevdir = w_shotdir; + //vector prevorg = w_shotorg; + //vector prevend = w_shotend; + if (antilag) if (!ent.cvar_cl_noantilag) { @@ -104,7 +123,7 @@ void W_SetupShot_Dir_ProjectileSize_Range(entity ent, vector s_forward, vector m ent.dphitcontentsmask = oldsolid; // restore solid type (generally SOLID_SLIDEBOX) - if (!g_norecoil) + if (!autocvar_g_norecoil) ent.punchangle_x = recoil * -1; if (snd != "") @@ -115,6 +134,9 @@ void W_SetupShot_Dir_ProjectileSize_Range(entity ent, vector s_forward, vector m // 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("SERVER: shotEND differs: %s - %s\n", vtos(w_shotend), vtos(prevend)); } + //if(w_shotorg != prevorg) { printf("SERVER: shotORG differs: %s - %s\n", vtos(w_shotorg), vtos(prevorg)); } + //if(w_shotdir != prevdir) { printf("SERVER: shotDIR differs: %s - %s\n", vtos(w_shotdir), vtos(prevdir)); } } vector W_CalculateProjectileVelocity(vector pvelocity, vector mvelocity, float forceAbsolute) @@ -133,42 +155,34 @@ vector W_CalculateProjectileVelocity(vector pvelocity, vector mvelocity, float f return outvelocity; } -#if 0 -float mspercallsum; -float mspercallsstyle; -float mspercallcount; -#endif -void W_SetupProjectileVelocityEx(entity missile, vector dir, vector upDir, float pSpeed, float pUpSpeed, float pZSpeed, float spread, float forceAbsolute) +void W_SetupProjVelocity_Explicit(entity proj, vector dir, vector upDir, float pSpeed, float pUpSpeed, float pZSpeed, float spread, float forceAbsolute) { - if(missile.owner == world) + if(proj.owner == world) error("Unowned missile"); dir = dir + upDir * (pUpSpeed / pSpeed); - dir_z += pZSpeed / pSpeed; + dir.z += pZSpeed / pSpeed; pSpeed *= vlen(dir); dir = normalize(dir); -#if 0 + #if 0 if(autocvar_g_projectiles_spread_style != mspercallsstyle) { mspercallsum = mspercallcount = 0; mspercallsstyle = autocvar_g_projectiles_spread_style; } mspercallsum -= gettime(GETTIME_HIRES); -#endif + #endif + dir = W_CalculateSpread(dir, spread, g_weaponspreadfactor, autocvar_g_projectiles_spread_style); -#if 0 + + #if 0 mspercallsum += gettime(GETTIME_HIRES); mspercallcount += 1; print("avg: ", ftos(mspercallcount / mspercallsum), " per sec\n"); -#endif + #endif - missile.velocity = W_CalculateProjectileVelocity(missile.owner.velocity, pSpeed * dir, forceAbsolute); -} - -void W_SetupProjectileVelocity(entity missile, float pSpeed, float spread) // WEAPONTODO -{ - W_SetupProjectileVelocityEx(missile, w_shotdir, v_up, pSpeed, 0, 0, spread, FALSE); + proj.velocity = W_CalculateProjectileVelocity(proj.owner.velocity, pSpeed * dir, forceAbsolute); } @@ -192,9 +206,6 @@ void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, f pseudoprojectile = world; - railgun_start = start; - railgun_end = end; - dir = normalize(end - start); length = vlen(end - start); force = dir * bforce; @@ -211,9 +222,9 @@ void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, f while (1) { if(self.antilag_debug) - WarpZone_traceline_antilag (self, start, end, FALSE, o, self.antilag_debug); + WarpZone_traceline_antilag (self, start, end, false, o, self.antilag_debug); else - WarpZone_traceline_antilag (self, start, end, FALSE, o, ANTILAG_LATENCY(self)); + WarpZone_traceline_antilag (self, start, end, false, o, ANTILAG_LATENCY(self)); if(o && WarpZone_trace_firstzone) { o = world; @@ -228,7 +239,7 @@ void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, f break; // make the entity non-solid so we can hit the next one - trace_ent.railgunhit = TRUE; + trace_ent.railgunhit = true; trace_ent.railgunhitloc = end; trace_ent.railgunhitsolidbackup = trace_ent.solid; trace_ent.railgundistance = vlen(WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos) - start); @@ -247,21 +258,24 @@ void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, f endq3surfaceflags = trace_dphitq3surfaceflags; // find all the entities the railgun hit and restore their solid state - ent = findfloat(world, railgunhit, TRUE); + ent = findfloat(world, railgunhit, true); while (ent) { // restore their solid type ent.solid = ent.railgunhitsolidbackup; - ent = findfloat(ent, railgunhit, TRUE); + ent = findfloat(ent, railgunhit, true); } // spawn a temporary explosion entity for RadiusDamage calls //explosion = spawn(); // Find all non-hit players the beam passed close by - if(deathtype == WEP_MINSTANEX || deathtype == WEP_NEX) + if(deathtype == WEP_VAPORIZER || deathtype == WEP_VORTEX) { - FOR_EACH_REALCLIENT(msg_entity) if(msg_entity != self) if(!msg_entity.railgunhit) if not(IS_SPEC(msg_entity) && msg_entity.enemy == self) // we use realclient, so spectators can hear the whoosh too + FOR_EACH_REALCLIENT(msg_entity) + if(msg_entity != self) + if(!msg_entity.railgunhit) + if(!(IS_SPEC(msg_entity) && msg_entity.enemy == self)) // we use realclient, so spectators can hear the whoosh too { // nearest point on the beam beampos = start + dir * bound(0, (msg_entity.origin - start) * dir, length); @@ -274,7 +288,7 @@ void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, f if(!pseudoprojectile) pseudoprojectile = spawn(); // we need this so the sound uses the "entchannel4" volume - soundtoat(MSG_ONE, pseudoprojectile, beampos, CH_SHOTS, snd, VOL_BASE * f, ATTN_NONE); + soundtoat(MSG_ONE, pseudoprojectile, beampos, CH_SHOTS, snd, VOL_BASE * f, ATTEN_NONE); } if(pseudoprojectile) @@ -282,7 +296,7 @@ void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, f } // find all the entities the railgun hit and hurt them - ent = findfloat(world, railgunhit, TRUE); + ent = findfloat(world, railgunhit, true); while (ent) { // get the details we need to call the damage function @@ -291,7 +305,7 @@ void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, f f = ExponentialFalloff(mindist, maxdist, halflifedist, ent.railgundistance); ffs = ExponentialFalloff(mindist, maxdist, forcehalflifedist, ent.railgundistance); - if(accuracy_isgooddamage(self.realowner, ent)) + if(accuracy_isgooddamage(self, ent)) totaldmg += bdamage * f; // apply the damage @@ -304,11 +318,11 @@ void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, f ent.railgunhitloc = '0 0 0'; ent.railgunhitsolidbackup = SOLID_NOT; - ent.railgunhit = FALSE; + ent.railgunhit = false; ent.railgundistance = 0; // advance to the next entity - ent = findfloat(ent, railgunhit, TRUE); + ent = findfloat(ent, railgunhit, true); } // calculate hits and fired shots for hitscan @@ -319,399 +333,153 @@ void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, f trace_dphitq3surfaceflags = endq3surfaceflags; } -void W_BallisticBullet_Hit (void) +void fireBullet_trace_callback(vector start, vector hit, vector end) { - float f, q, g; - - f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier - q = 1 + self.dmg_edge / self.dmg; - - if(other.solid == SOLID_BSP || other.solid == SOLID_SLIDEBOX) - Damage_DamageInfo(self.origin, self.dmg * f, 0, 0, max(1, self.dmg_force) * normalize(self.velocity) * f, self.projectiledeathtype, other.species, self); - - if(other && other != self.enemy) - { - endzcurveparticles(); - - yoda = 0; - railgun_start = self.origin - 2 * frametime * self.velocity; - railgun_end = self.origin + 2 * frametime * self.velocity; - g = accuracy_isgooddamage(self.realowner, other); - Damage(other, self, self.realowner, self.dmg * f, self.projectiledeathtype, self.origin, self.dmg_force * normalize(self.velocity) * f); - - /*if(yoda && (time > (self.last_yoda + 5))) - { - Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_YODA); - self.last_yoda = time; - }*/ - - // calculate hits for ballistic weapons - if(g) - { - // do not exceed 100% - q = min(self.dmg * q, self.dmg_total + f * self.dmg) - self.dmg_total; - self.dmg_total += f * self.dmg; - accuracy_add(self.realowner, self.realowner.weapon, 0, q); - } - } - - self.enemy = other; // don't hit the same player twice with the same bullet -} - -void W_BallisticBullet_LeaveSolid_think() -{ - setorigin(self, self.W_BallisticBullet_LeaveSolid_origin); - self.velocity = self.W_BallisticBullet_LeaveSolid_velocity; - - self.think = self.W_BallisticBullet_LeaveSolid_think_save; - self.nextthink = max(time, self.W_BallisticBullet_LeaveSolid_nextthink_save); - self.W_BallisticBullet_LeaveSolid_think_save = func_null; - - self.flags &~= FL_ONGROUND; - - if(self.enemy.solid == SOLID_BSP) - { - float f; - f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier - Damage_DamageInfo(self.origin, 0, 0, 0, max(1, self.dmg_force) * normalize(self.velocity) * -f, self.projectiledeathtype, 0, self); - } - - UpdateCSQCProjectile(self); + if(vlen(hit - start) > 16) + trailparticles(world, fireBullet_trace_callback_eff, start, hit); + WarpZone_trace_forent = world; + fireBullet_last_hit = world; } -float W_BallisticBullet_LeaveSolid(float eff) +void fireBullet(vector start, vector dir, float spread, float max_solid_penetration, float damage, float force, float dtype, int tracereffects) { - // move the entity along its velocity until it's out of solid, then let it resume - vector vel = self.velocity; - float dt, dst, velfactor, v0, vs; - float maxdist; - float E0_m, Es_m; - float constant = self.dmg_radius * (other.ballistics_density ? other.ballistics_density : 1); - - // outside the world? forget it - if(self.origin_x > world.maxs_x || self.origin_y > world.maxs_y || self.origin_z > world.maxs_z || self.origin_x < world.mins_x || self.origin_y < world.mins_y || self.origin_z < world.mins_z) - return 0; - - // special case for zero density and zero bullet constant: - - if(self.dmg_radius == 0) - { - if(other.ballistics_density < 0) - constant = 0; // infinite travel distance - else - return 0; // no penetration - } - else - { - if(other.ballistics_density < 0) - constant = 0; // infinite travel distance - else if(other.ballistics_density == 0) - constant = self.dmg_radius; - else - constant = self.dmg_radius * other.ballistics_density; - } - - // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass - v0 = vlen(vel); + vector end; - E0_m = 0.5 * v0 * v0; + dir = normalize(dir + randomvec() * spread); + end = start + dir * MAX_SHOT_DISTANCE; - if(constant) - { - maxdist = E0_m / constant; - // maxdist = 0.5 * v0 * v0 / constant - // dprint("max dist = ", ftos(maxdist), "\n"); + entity pl; + fireBullet_last_hit = world; + float solid_penetration_left = 1; + float total_damage = 0; - if(maxdist <= autocvar_g_ballistics_mindistance) - return 0; - } + if(tracereffects & EF_RED) + fireBullet_trace_callback_eff = particleeffectnum("tr_rifle"); + else if(tracereffects & EF_BLUE) + fireBullet_trace_callback_eff = particleeffectnum("tr_rifle_weak"); else + fireBullet_trace_callback_eff = particleeffectnum("tr_bullet"); + + float lag = ANTILAG_LATENCY(self); + if(lag < 0.001) + lag = 0; + if (!IS_REAL_CLIENT(self)) + lag = 0; + if(autocvar_g_antilag == 0 || self.cvar_cl_noantilag) + lag = 0; // only do hitscan, but no antilag + if(lag) { - maxdist = vlen(other.maxs - other.mins) + 1; // any distance, as long as we leave the entity + FOR_EACH_PLAYER(pl) + if(pl != self) + antilag_takeback(pl, time - lag); + FOR_EACH_MONSTER(pl) + antilag_takeback(pl, time - lag); } - traceline_inverted (self.origin, self.origin + normalize(vel) * maxdist, MOVE_NORMAL, self, TRUE); - if(trace_fraction == 1) // 1: we never got out of solid - return 0; + WarpZone_trace_forent = self; - self.W_BallisticBullet_LeaveSolid_origin = trace_endpos; - - dst = max(autocvar_g_ballistics_mindistance, vlen(trace_endpos - self.origin)); - // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass - Es_m = E0_m - constant * dst; - if(Es_m <= 0) + for (;;) { - // roundoff errors got us - return 0; - } - vs = sqrt(2 * Es_m); - velfactor = vs / v0; - - dt = dst / (0.5 * (v0 + vs)); - // this is not correct, but the differential equations have no analytic - // solution - and these times are very small anyway - //print("dt = ", ftos(dt), "\n"); - - self.W_BallisticBullet_LeaveSolid_think_save = self.think; - self.W_BallisticBullet_LeaveSolid_nextthink_save = self.nextthink; - self.think = W_BallisticBullet_LeaveSolid_think; - self.nextthink = time + dt; - - vel = vel * velfactor; + // TODO also show effect while tracing + WarpZone_TraceBox_ThroughZone(start, '0 0 0', '0 0 0', end, false, WarpZone_trace_forent, world, fireBullet_trace_callback); + dir = WarpZone_TransformVelocity(WarpZone_trace_transform, dir); + end = WarpZone_TransformOrigin(WarpZone_trace_transform, end); + start = trace_endpos; + entity hit = trace_ent; + + // When hitting sky, stop. + if (pointcontents(start) == CONTENT_SKY) + break; - self.velocity = '0 0 0'; - self.flags |= FL_ONGROUND; // prevent moving - self.W_BallisticBullet_LeaveSolid_velocity = vel; + if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) + break; - if(eff >= 0) - if(vlen(trace_endpos - self.origin) > 4) + // if we hit "weapclip", bail out + // + // rationale of this check: + // + // any shader that is solid, nodraw AND trans is meant to clip weapon + // shots and players, but has no other effect! + // + // if it is not trans, it is caulk and should not have this side effect + // + // matching shaders: + // common/weapclip (intended) + // common/noimpact (is supposed to eat projectiles, but is erased anyway) + float is_weapclip = 0; + if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NODRAW) + if (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NONSOLID)) + if (!(trace_dphitcontents & DPCONTENTS_OPAQUE)) + is_weapclip = 1; + + if(!hit || hit.solid == SOLID_BSP || hit.solid == SOLID_SLIDEBOX) + Damage_DamageInfo(start, damage * solid_penetration_left, 0, 0, max(1, force) * dir * solid_penetration_left, dtype, hit.species, self); + + if (hit && hit != WarpZone_trace_forent && hit != fireBullet_last_hit) // Avoid self-damage (except after going through a warp); avoid hitting the same entity twice (engine bug). { - endzcurveparticles(); - trailparticles(self, eff, self.origin, trace_endpos); + fireBullet_last_hit = hit; + yoda = 0; + float g = accuracy_isgooddamage(self, hit); + Damage(hit, self, self, damage * solid_penetration_left, dtype, start, force * dir * solid_penetration_left); + // calculate hits for ballistic weapons + if(g) + { + // do not exceed 100% + float added_damage = min(damage - total_damage, damage * solid_penetration_left); + total_damage += damage * solid_penetration_left; + accuracy_add(self, self.weapon, 0, added_damage); + } } - return 1; -} - -void W_BallisticBullet_Touch (void) -{ - //float density; - - if(self.think == W_BallisticBullet_LeaveSolid_think) // skip this! - return; - - PROJECTILE_TOUCH; - W_BallisticBullet_Hit (); - - if(self.dmg_radius < 0) // these NEVER penetrate solid - { - remove(self); - return; - } - - // if we hit "weapclip", bail out - // - // rationale of this check: - // - // any shader that is solid, nodraw AND trans is meant to clip weapon - // shots and players, but has no other effect! - // - // if it is not trans, it is caulk and should not have this side effect - // - // matching shaders: - // common/weapclip (intended) - // common/noimpact (is supposed to eat projectiles, but is erased farther above) - if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NODRAW) - if not(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NONSOLID) - if not(trace_dphitcontents & DPCONTENTS_OPAQUE) - { - remove(self); - return; - } - - // go through solid! - if(!W_BallisticBullet_LeaveSolid(-1)) - { - remove(self); - return; - } - - self.projectiledeathtype |= HITTYPE_BOUNCE; -} - -void endFireBallisticBullet() // WEAPONTODO -{ - endzcurveparticles(); -} - -void fireBallisticBullet_trace_callback(vector start, vector hit, vector end) -{ - if(vlen(trace_endpos - fireBallisticBullet_trace_callback_ent.origin) > 16) - zcurveparticles_from_tracetoss(fireBallisticBullet_trace_callback_eff, fireBallisticBullet_trace_callback_ent.origin, trace_endpos, fireBallisticBullet_trace_callback_ent.velocity); - WarpZone_trace_forent = world; - self.owner = world; -} - -void fireBallisticBullet(vector start, vector dir, float spread, float pSpeed, float lifetime, float damage, float force, float dtype, float tracereffects, float gravityfactor, float bulletconstant) -{ - float lag, dt, savetime; //, density; - entity pl, oldself; - float antilagging; - - antilagging = (autocvar_g_antilag_bullets && (pSpeed >= autocvar_g_antilag_bullets)); - - entity proj; - proj = spawn(); - proj.classname = "bullet"; - proj.owner = proj.realowner = self; - PROJECTILE_MAKETRIGGER(proj); - if(gravityfactor > 0) - { - proj.movetype = MOVETYPE_TOSS; - proj.gravity = gravityfactor; - } - else - proj.movetype = MOVETYPE_FLY; - proj.think = SUB_Remove; - proj.nextthink = time + lifetime; // min(pLifetime, vlen(world.maxs - world.mins) / pSpeed); - W_SetupProjectileVelocityEx(proj, dir, v_up, pSpeed, 0, 0, spread, antilagging); - proj.angles = vectoangles(proj.velocity); - if(bulletconstant > 0) - proj.dmg_radius = autocvar_g_ballistics_materialconstant / bulletconstant; - else if(bulletconstant == 0) - proj.dmg_radius = 0; - else - proj.dmg_radius = -1; - // so: bulletconstant = bullet mass / area of bullet circle - setorigin(proj, start); - proj.flags = FL_PROJECTILE; - - proj.touch = W_BallisticBullet_Touch; - proj.dmg = damage; - proj.dmg_force = force; - proj.projectiledeathtype = dtype; - - proj.oldvelocity = proj.velocity; - - other = proj; MUTATOR_CALLHOOK(EditProjectile); + if (is_weapclip) + break; - if(antilagging) - { - float eff; + // go through solid! + // outside the world? forget it + if(start.x > world.maxs.x || start.y > world.maxs.y || start.z > world.maxs.z || start.x < world.mins.x || start.y < world.mins.y || start.z < world.mins.z) + break; - if(tracereffects & EF_RED) - eff = particleeffectnum("tr_rifle"); - else if(tracereffects & EF_BLUE) - eff = particleeffectnum("tr_rifle_weak"); + float maxdist; + if(max_solid_penetration < 0) + break; + else if(hit.ballistics_density < -1) + break; // -2: no solid penetration, ever + else if(hit.ballistics_density < 0) + maxdist = vlen(hit.maxs - hit.mins) + 1; // -1: infinite travel distance + else if(hit.ballistics_density == 0) + maxdist = max_solid_penetration * solid_penetration_left; else - eff = particleeffectnum("tr_bullet"); - - // NOTE: this may severely throw off weapon balance - lag = ANTILAG_LATENCY(self); - if(lag < 0.001) - lag = 0; - if not(IS_REAL_CLIENT(self)) - lag = 0; - if(autocvar_g_antilag == 0 || self.cvar_cl_noantilag) - lag = 0; // only do hitscan, but no antilag - - if(lag) - FOR_EACH_PLAYER(pl) - if(pl != self) - antilag_takeback(pl, time - lag); - - oldself = self; - self = proj; + maxdist = max_solid_penetration * solid_penetration_left * hit.ballistics_density; - savetime = frametime; - frametime = 0.05; - - for(;;) - { - // DP tracetoss is stupid and always traces in 0.05s - // ticks. This makes it trace in 0.05*0.125s ticks - // instead. - vector v0; - float g0; - v0 = self.velocity; - g0 = self.gravity; - self.velocity = self.velocity * 0.125; - self.gravity *= 0.125 * 0.125; - trace_fraction = 0; - fireBallisticBullet_trace_callback_ent = self; - fireBallisticBullet_trace_callback_eff = eff; - WarpZone_TraceToss_ThroughZone(self, self.owner, world, fireBallisticBullet_trace_callback); - self.velocity = v0; - self.gravity = g0; - - if(trace_fraction == 1) - break; - // won't hit anything anytime soon (DP's - // tracetoss does 200 tics of, here, - // 0.05*0.125s, that is, 1.25 seconds - - other = trace_ent; - dt = WarpZone_tracetoss_time * 0.125; // this is only approximate! - setorigin(self, trace_endpos); - self.velocity = WarpZone_tracetoss_velocity * (1 / 0.125); - - if(!SUB_OwnerCheck()) - { - if(SUB_NoImpactCheck()) - break; + if(maxdist <= autocvar_g_ballistics_mindistance) + break; - // hit the player - W_BallisticBullet_Hit(); - } + // move the entity along its velocity until it's out of solid, then let it resume + // The previously hit entity is ignored here! + traceline_inverted (start, start + dir * maxdist, MOVE_NORMAL, WarpZone_trace_forent, true, hit); + if(trace_fraction == 1) // 1: we never got out of solid + break; - if(proj.dmg_radius < 0) // these NEVER penetrate solid - break; - - // if we hit "weapclip", bail out - // - // rationale of this check: - // - // any shader that is solid, nodraw AND trans is meant to clip weapon - // shots and players, but has no other effect! - // - // if it is not trans, it is caulk and should not have this side effect - // - // matching shaders: - // common/weapclip (intended) - // common/noimpact (is supposed to eat projectiles, but is erased farther above) - if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NODRAW) - if not(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NONSOLID) - if not(trace_dphitcontents & DPCONTENTS_OPAQUE) - break; - - // go through solid! - if(!W_BallisticBullet_LeaveSolid((other && (other.solid != SOLID_BSP)) ? eff : -1)) - break; - - W_BallisticBullet_LeaveSolid_think(); - - self.projectiledeathtype |= HITTYPE_BOUNCE; - } - frametime = savetime; - self = oldself; + float dist_taken = max(autocvar_g_ballistics_mindistance, vlen(trace_endpos - start)); + solid_penetration_left *= (dist_taken / maxdist); - if(lag) - FOR_EACH_PLAYER(pl) - if(pl != self) - antilag_restore(pl); + // Only show effect when going through a player (invisible otherwise) + if (hit && (hit.solid != SOLID_BSP)) + if(vlen(trace_endpos - start) > 4) + trailparticles(self, fireBullet_trace_callback_eff, start, trace_endpos); - remove(proj); + start = trace_endpos; - return; + if(hit.solid == SOLID_BSP) + Damage_DamageInfo(start, 0, 0, 0, max(1, force) * normalize(dir) * -solid_penetration_left, dtype, 0, self); } - if(tracereffects & EF_RED) - CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING_TRACER, TRUE); - else if(tracereffects & EF_BLUE) - CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING, TRUE); - else - CSQCProjectile(proj, TRUE, PROJECTILE_BULLET, TRUE); -} - -void fireBullet (vector start, vector dir, float spread, float damage, float force, float dtype, float tracer) -{ - vector end; - - dir = normalize(dir + randomvec() * spread); - end = start + dir * MAX_SHOT_DISTANCE; - if(self.antilag_debug) - traceline_antilag (self, start, end, FALSE, self, self.antilag_debug); - else - traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self)); - - end = trace_endpos; - - if (pointcontents (trace_endpos) != CONTENT_SKY) + if(lag) { - if not (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) - Damage_DamageInfo(trace_endpos, damage, 0, 0, dir * max(1, force), dtype, trace_ent.species, self); - - Damage (trace_ent, self, self, damage, dtype, trace_endpos, dir * force); + FOR_EACH_PLAYER(pl) + if(pl != self) + antilag_restore(pl); + FOR_EACH_MONSTER(pl) + antilag_restore(pl); } - trace_endpos = end; }