}
.float railgundistance;
+.vector railgunforce;
void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float mindist, float maxdist, float halflifedist, float forcehalflifedist, float deathtype)
{
local vector hitloc, force, endpoint, dir;
local entity ent, endent;
local float endq3surfaceflags;
+ float totaldmg;
+ entity o;
float length;
vector beampos;
entity pseudoprojectile;
float f, ffs;
- float hit;
-
railgun_start = start;
railgun_end = end;
// go a little bit into the wall because we need to hit this wall later
end = end + dir;
+ totaldmg = 0;
+
// trace multiple times until we hit a wall, each obstacle will be made
// non-solid so we can hit the next, while doing this we spawn effects and
// note down which entities were hit so we can damage them later
+ o = self;
while (1)
{
if(self.antilag_debug)
- WarpZone_traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
+ WarpZone_traceline_antilag (self, start, end, FALSE, o, self.antilag_debug);
else
- WarpZone_traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
+ WarpZone_traceline_antilag (self, start, end, FALSE, o, ANTILAG_LATENCY(self));
+ if(o && WarpZone_trace_firstzone)
+ {
+ o = world;
+ continue;
+ }
// if it is world we can't hurt it so stop now
if (trace_ent == world || trace_fraction == 1)
trace_ent.railgunhitloc = end;
trace_ent.railgunhitsolidbackup = trace_ent.solid;
trace_ent.railgundistance = vlen(WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos) - start);
+ trace_ent.railgunforce = WarpZone_TransformVelocity(WarpZone_trace_transform, force);
// stop if this is a wall
if (trace_ent.solid == SOLID_BSP)
if(!pseudoprojectile)
pseudoprojectile = spawn(); // we need this so the sound uses the "entchannel4" volume
- soundtoat(MSG_ONE, pseudoprojectile, beampos, CHAN_PROJECTILE, snd, VOL_BASE * f, ATTN_NONE);
+ soundtoat(MSG_ONE, pseudoprojectile, beampos, CH_SHOTS_SINGLE, snd, VOL_BASE * f, ATTN_NONE);
}
if(pseudoprojectile)
// get the details we need to call the damage function
hitloc = ent.railgunhitloc;
- //for stats so that team hit will count as a miss
- if(ent.flags & FL_CLIENT)
- if(ent.deadflag == DEAD_NO)
- hit = 1;
-
- if(teams_matter)
- if(ent.team == self.team)
- hit = 0;
-
f = ExponentialFalloff(mindist, maxdist, halflifedist, ent.railgundistance);
ffs = ExponentialFalloff(mindist, maxdist, forcehalflifedist, ent.railgundistance);
+ if(accuracy_isgooddamage(self.realowner, ent))
+ totaldmg += bdamage * f;
+
// apply the damage
if (ent.takedamage)
- Damage (ent, self, self, bdamage * f, deathtype, hitloc, force * ffs);
+ Damage (ent, self, self, bdamage * f, deathtype, hitloc, ent.railgunforce * ffs);
// create a small explosion to throw gibs around (if applicable)
//setorigin (explosion, hitloc);
}
// calculate hits and fired shots for hitscan
- if not(inWarmupStage)
- {
- self.stats_fired[self.weapon - 1] += 1;
- self.stat_fired = self.weapon + 64 * floor(self.stats_fired[self.weapon - 1]);
-
- if(hit) {
- self.stats_hit[self.weapon - 1] += 1;
- self.stat_hit = self.weapon + 64 * floor(self.stats_hit[self.weapon - 1]);
- }
- }
+ accuracy_add(self, self.weapon, 0, min(bdamage, totaldmg));
trace_endpos = endpoint;
trace_ent = endent;
.float dmg_edge;
.float dmg_force;
.float dmg_radius;
+.float dmg_total;
void W_BallisticBullet_Hit (void)
{
- float f;
+ 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)
Damage_DamageInfo(self.origin, self.dmg * f, 0, 0, max(1, self.dmg_force) * normalize(self.velocity) * f, self.projectiledeathtype, self);
headshot = 0;
yoda = 0;
- damage_headshotbonus = self.dmg_edge;
+ damage_headshotbonus = self.dmg_edge * f;
railgun_start = self.origin - 2 * frametime * self.velocity;
railgun_end = self.origin + 2 * frametime * self.velocity;
-
- Damage(other, self, self.owner, self.dmg * f, self.projectiledeathtype, self.origin, self.dmg_force * normalize(self.velocity) * f);
+ 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);
damage_headshotbonus = 0;
- if(self.dmg_edge != 0)
+ if(headshot)
+ f *= q;
+ if(self.dmg_edge > 0)
{
if(headshot)
- AnnounceTo(self.owner, "headshot");
+ AnnounceTo(self.realowner, "headshot");
if(yoda)
- AnnounceTo(self.owner, "awesome");
+ AnnounceTo(self.realowner, "awesome");
}
// calculate hits for ballistic weapons
- if (other.flags & FL_CLIENT) // is the player a client
- if (other.deadflag == DEAD_NO) // is the victim a corpse
- if ((!(teamplay)) | (other.team != self.owner.team)) // not teamplay (ctf, kh, tdm etc) or the victim is in the same team
- if not(inWarmupStage) // not in warm up stage
+ if(g)
{
- self.owner.stats_hit[self.owner.weapon - 1] += 1;
- self.owner.stat_hit = self.owner.weapon + 64 * floor(self.owner.stats_hit[self.owner.weapon - 1]);
+ // 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);
}
}
// maxdist = 0.5 * v0 * v0 / constant
// dprint("max dist = ", ftos(maxdist), "\n");
- if(maxdist <= cvar("g_ballistics_mindistance"))
+ if(maxdist <= autocvar_g_ballistics_mindistance)
return 0;
traceline_inverted (self.origin, self.origin + normalize(vel) * maxdist, MOVE_NORMAL, self);
self.W_BallisticBullet_LeaveSolid_origin = trace_endpos;
- dst = vlen(trace_endpos - self.origin);
+ 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)
PROJECTILE_TOUCH;
W_BallisticBullet_Hit ();
+ // 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;
+ }
+
density = other.ballistics_density;
if(density == 0)
density = 1;
{
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 = self;
+ proj.owner = proj.realowner = self;
PROJECTILE_MAKETRIGGER(proj);
if(gravityfactor > 0)
{
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);
+ W_SetupProjectileVelocityEx(proj, dir, v_up, pSpeed, 0, 0, spread, antilagging);
proj.angles = vectoangles(proj.velocity);
- proj.dmg_radius = cvar("g_ballistics_materialconstant") / bulletconstant;
+ proj.dmg_radius = autocvar_g_ballistics_materialconstant / bulletconstant;
// so: bulletconstant = bullet mass / area of bullet circle
setorigin(proj, start);
proj.flags = FL_PROJECTILE;
proj.oldvelocity = proj.velocity;
- if(cvar("g_antilag_bullets"))
- if(pSpeed >= cvar("g_antilag_bullets"))
+ other = proj; MUTATOR_CALLHOOK(EditProjectile);
+
+ if(antilagging)
{
float eff;
if(tracereffects & EF_RED)
eff = particleeffectnum("tr_rifle");
+ else if(tracereffects & EF_BLUE)
+ eff = particleeffectnum("tr_rifle_weak");
else
eff = particleeffectnum("tr_bullet");
lag = 0;
if(clienttype(self) != CLIENTTYPE_REAL)
lag = 0;
- if(cvar("g_antilag") == 0 || self.cvar_cl_noantilag)
+ if(autocvar_g_antilag == 0 || self.cvar_cl_noantilag)
lag = 0; // only do hitscan, but no antilag
if(lag)
savetime = frametime;
frametime = 0.05;
- // update the accuracy stats - increase shots fired by 1
- if not(inWarmupStage)
- {
- oldself.stats_fired[oldself.weapon - 1] += 1;
- oldself.stat_fired = oldself.weapon + 64 * floor(oldself.stats_fired[oldself.weapon - 1]);
- }
-
for(;;)
{
// DP tracetoss is stupid and always traces in 0.05s
trace_fraction = 0;
fireBallisticBullet_trace_callback_ent = self;
fireBallisticBullet_trace_callback_eff = eff;
- WarpZone_TraceToss_ThroughZone(self, oldself, world, fireBallisticBullet_trace_callback);
+ // FIXME can we somehow do this with just ONE trace?
+ WarpZone_TraceToss(self, self.owner);
+ if(self.owner && WarpZone_trace_firstzone)
+ {
+ self.owner = world;
+ self.velocity = v0;
+ self.gravity = g0;
+ continue;
+ }
+ WarpZone_TraceToss_ThroughZone(self, self.owner, world, fireBallisticBullet_trace_callback);
self.velocity = v0;
self.gravity = g0;
W_BallisticBullet_Hit();
}
+ // 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;
+
density = other.ballistics_density;
if(density == 0)
density = 1;
return;
}
- // update the accuracy stats
- if not(inWarmupStage)
- {
- self.stats_fired[self.weapon - 1] += 1;
- self.stat_fired = self.weapon + 64 * floor(self.stats_fired[self.weapon - 1]);
- }
-
if(tracereffects & EF_RED)
CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING_TRACER, TRUE);
else if(tracereffects & EF_BLUE)
if ((trace_fraction != 1.0) && (pointcontents (trace_endpos) != CONTENT_SKY))
{
- pointparticles(particleeffectnum("TE_KNIGHTSPIKE"),end,trace_plane_normal * 2500,1);
- if (trace_ent.solid == SOLID_BSP && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT))
- Damage_DamageInfo(trace_endpos, damage, 0, 0, dir * max(1, force), dtype, self);
+ if not (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
+ Damage_DamageInfo(trace_endpos, damage, 0, 0, dir * max(1, force), dtype, self);
+
Damage (trace_ent, self, self, damage, dtype, trace_endpos, dir * force);
- //void(float effectnum, vector org, vector vel, float howmany) pointparticles = #337; // same as in CSQC
}
trace_endpos = end;
}
self.takedamage = DAMAGE_NO;
self.event_damage = SUB_Null;
self.owner = attacker;
+ self.realowner = attacker;
// do not explode NOW but in the NEXT FRAME!
// because recursive calls to RadiusDamage are not allowed