X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fserver%2Fw_common.qc;h=3780fc71384d987160f8e56c3063166fdbcc1bb3;hb=451c0fbf5473c6acc88bafaa0e9c14e5afd3764e;hp=3343fa7c3f5f24d9977e41b05e4da32a498bc0ac;hpb=be41dce70d8ee22249878d50accf08becc130205;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/server/w_common.qc b/qcsrc/server/w_common.qc index 3343fa7c3..72cac7985 100644 --- a/qcsrc/server/w_common.qc +++ b/qcsrc/server/w_common.qc @@ -1,23 +1,18 @@ -void W_GiveWeapon (entity e, float wep, string name) +void W_GiveWeapon (entity e, float wep) { entity oldself; if (!wep) return; - WEPSET_OR_EW(e, wep); + e.weapons |= WepSet_FromWeapon(wep); oldself = self; self = e; - if not(g_minstagib) - if (other.classname == "player") - { - sprint (other, "You got the ^2"); - sprint (other, name); - sprint (other, "\n"); - } + if(IS_PLAYER(other)) + { Send_Notification(NOTIF_ONE, other, MSG_MULTI, ITEM_WEAPON_GOT, wep); } self = oldself; } @@ -38,6 +33,8 @@ void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, f entity pseudoprojectile; float f, ffs; + pseudoprojectile = world; + railgun_start = start; railgun_end = end; @@ -107,7 +104,10 @@ void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, f // Find all non-hit players the beam passed close by if(deathtype == WEP_MINSTANEX || deathtype == WEP_NEX) { - FOR_EACH_REALCLIENT(msg_entity) if(msg_entity != self) if(!msg_entity.railgunhit) if not(msg_entity.classname == "spectator" && 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); @@ -120,7 +120,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) @@ -165,10 +165,10 @@ void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, f trace_dphitq3surfaceflags = endq3surfaceflags; } -.float dmg_edge; .float dmg_force; .float dmg_radius; .float dmg_total; +//.float last_yoda; void W_BallisticBullet_Hit (void) { float f, q, g; @@ -183,24 +183,17 @@ void W_BallisticBullet_Hit (void) { endzcurveparticles(); - headshot = 0; yoda = 0; - damage_headshotbonus = self.dmg_edge * f; 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); - damage_headshotbonus = 0; - if(headshot) - f *= q; - if(self.dmg_edge > 0) + /*if(yoda && (time > (self.last_yoda + 5))) { - if(headshot) - AnnounceTo(self.realowner, "headshot"); - if(yoda) - AnnounceTo(self.realowner, "awesome"); - } + Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_YODA); + self.last_yoda = time; + }*/ // calculate hits for ballistic weapons if(g) @@ -227,9 +220,9 @@ void W_BallisticBullet_LeaveSolid_think() self.think = self.W_BallisticBullet_LeaveSolid_think_save; self.nextthink = max(time, self.W_BallisticBullet_LeaveSolid_nextthink_save); - self.W_BallisticBullet_LeaveSolid_think_save = SUB_Null; + self.W_BallisticBullet_LeaveSolid_think_save = func_null; - self.flags &~= FL_ONGROUND; + self.flags &= ~FL_ONGROUND; if(self.enemy.solid == SOLID_BSP) { @@ -241,31 +234,58 @@ void W_BallisticBullet_LeaveSolid_think() UpdateCSQCProjectile(self); } -float W_BallisticBullet_LeaveSolid(entity e, vector vel, float constant) +float W_BallisticBullet_LeaveSolid(float eff) { // 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); E0_m = 0.5 * v0 * v0; - maxdist = E0_m / constant; - // maxdist = 0.5 * v0 * v0 / constant - // dprint("max dist = ", ftos(maxdist), "\n"); - if(maxdist <= autocvar_g_ballistics_mindistance) - return 0; + if(constant) + { + maxdist = E0_m / constant; + // maxdist = 0.5 * v0 * v0 / constant + // dprint("max dist = ", ftos(maxdist), "\n"); - traceline_inverted (self.origin, self.origin + normalize(vel) * maxdist, MOVE_NORMAL, self); + if(maxdist <= autocvar_g_ballistics_mindistance) + return 0; + } + else + { + maxdist = vlen(other.maxs - other.mins) + 1; // any distance, as long as we leave the entity + } + 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; @@ -298,12 +318,19 @@ float W_BallisticBullet_LeaveSolid(entity e, vector vel, float constant) self.flags |= FL_ONGROUND; // prevent moving self.W_BallisticBullet_LeaveSolid_velocity = vel; + if(eff >= 0) + if(vlen(trace_endpos - self.origin) > 4) + { + endzcurveparticles(); + trailparticles(self, eff, self.origin, trace_endpos); + } + return 1; } void W_BallisticBullet_Touch (void) { - float density; + //float density; if(self.think == W_BallisticBullet_LeaveSolid_think) // skip this! return; @@ -311,6 +338,12 @@ void W_BallisticBullet_Touch (void) 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: @@ -324,19 +357,15 @@ void W_BallisticBullet_Touch (void) // 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) + if (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NONSOLID)) + if (!(trace_dphitcontents & DPCONTENTS_OPAQUE)) { remove(self); return; } - density = other.ballistics_density; - if(density == 0) - density = 1; - // go through solid! - if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius * density)) + if(!W_BallisticBullet_LeaveSolid(-1)) { remove(self); return; @@ -360,38 +389,33 @@ void fireBallisticBullet_trace_callback(vector start, vector hit, vector end) self.owner = world; } -void fireBallisticBullet(vector start, vector dir, float spread, float pSpeed, float lifetime, float damage, float headshotbonus, float force, float dtype, float tracereffects, float gravityfactor, float bulletconstant) +void fireBallisticBullet(vector start, vector dir, float spread, float pSpeed, float lifetime, float damage, float force, float dtype, float tracereffects, float bulletconstant) { - float lag, dt, savetime, density; + 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.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); + W_SetupProjectileVelocityEx(proj, dir, v_up, pSpeed, 0, 0, spread, TRUE); proj.angles = vectoangles(proj.velocity); - proj.dmg_radius = autocvar_g_ballistics_materialconstant / bulletconstant; + 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_edge = headshotbonus; proj.dmg_force = force; proj.projectiledeathtype = dtype; @@ -399,121 +423,108 @@ void fireBallisticBullet(vector start, vector dir, float spread, float pSpeed, f other = proj; MUTATOR_CALLHOOK(EditProjectile); - if(antilagging) - { - float eff; + 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"); + if(tracereffects & EF_RED) + eff = particleeffectnum("tr_rifle"); + else if(tracereffects & EF_BLUE) + eff = particleeffectnum("tr_rifle_weak"); + else + eff = particleeffectnum("tr_bullet"); + + // NOTE: this may severely throw off weapon balance + 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) + FOR_EACH_PLAYER(pl) + if(pl != self) + antilag_takeback(pl, time - lag); - // NOTE: this may severely throw off weapon balance - lag = ANTILAG_LATENCY(self); - if(lag < 0.001) - lag = 0; - if(clienttype(self) != CLIENTTYPE_REAL) - lag = 0; - if(autocvar_g_antilag == 0 || self.cvar_cl_noantilag) - lag = 0; // only do hitscan, but no antilag + oldself = self; + self = proj; - if(lag) - FOR_EACH_PLAYER(pl) - if(pl != self) - antilag_takeback(pl, time - lag); + savetime = frametime; + frametime = 0.05; - oldself = self; - self = proj; + 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; + v0 = self.velocity; + self.velocity = self.velocity * 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; + + 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 - savetime = frametime; - frametime = 0.05; + 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); - for(;;) + if(!SUB_OwnerCheck()) { - // 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; - - // hit the player - 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) + if(SUB_NoImpactCheck()) break; - density = other.ballistics_density; - if(density == 0) - density = 1; + // hit the player + W_BallisticBullet_Hit(); + } - // go through solid! - if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius * density)) - break; + if(proj.dmg_radius < 0) // these NEVER penetrate solid + break; - W_BallisticBullet_LeaveSolid_think(); - } - frametime = savetime; - self = oldself; + // 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 (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NONSOLID)) + if (!(trace_dphitcontents & DPCONTENTS_OPAQUE)) + break; - if(lag) - FOR_EACH_PLAYER(pl) - if(pl != self) - antilag_restore(pl); + // go through solid! + if(!W_BallisticBullet_LeaveSolid((other && (other.solid != SOLID_BSP)) ? eff : -1)) + break; - remove(proj); + W_BallisticBullet_LeaveSolid_think(); - return; + self.projectiledeathtype |= HITTYPE_BOUNCE; } + frametime = savetime; + self = oldself; - 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); + if(lag) + FOR_EACH_PLAYER(pl) + if(pl != self) + antilag_restore(pl); + + remove(proj); + + return; } void fireBullet (vector start, vector dir, float spread, float damage, float force, float dtype, float tracer) @@ -531,8 +542,8 @@ void fireBullet (vector start, vector dir, float spread, float damage, float for if (pointcontents (trace_endpos) != CONTENT_SKY) { - if not (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) - Damage_DamageInfo(trace_endpos, damage, 0, 0, dir * max(1, force), dtype, trace_ent.species, self); + if (!(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); } @@ -544,7 +555,7 @@ float W_CheckProjectileDamage(entity inflictor, entity projowner, float deathtyp float is_from_contents = (deathtype == DEATH_SLIME || deathtype == DEATH_LAVA); float is_from_owner = (inflictor == projowner); float is_from_exception = (exception != -1); - + //dprint(strcat("W_CheckProjectileDamage: from_contents ", ftos(is_from_contents), " : from_owner ", ftos(is_from_owner), " : exception ", strcat(ftos(is_from_exception), " (", ftos(exception), "). \n"))); if(autocvar_g_projectiles_damage <= -2) @@ -562,14 +573,14 @@ float W_CheckProjectileDamage(entity inflictor, entity projowner, float deathtyp { if(is_from_exception) return (exception); // if exception is detected, allow it to override - else if not(is_from_contents) + else if (!is_from_contents) return FALSE; // otherwise, only allow damage from contents - } + } else if(autocvar_g_projectiles_damage == 1) { if(is_from_exception) return (exception); // if exception is detected, allow it to override - else if not(is_from_contents || is_from_owner) + else if (!(is_from_contents || is_from_owner)) return FALSE; // otherwise, only allow self damage and damage from contents } else if(autocvar_g_projectiles_damage == 2) // allow any damage, but override for exceptions @@ -584,9 +595,13 @@ float W_CheckProjectileDamage(entity inflictor, entity projowner, float deathtyp void W_PrepareExplosionByDamage(entity attacker, void() explode) { self.takedamage = DAMAGE_NO; - self.event_damage = SUB_Null; - self.owner = attacker; - self.realowner = attacker; + self.event_damage = func_null; + + if(IS_CLIENT(attacker) && !autocvar_g_projectiles_keep_owner) + { + self.owner = attacker; + self.realowner = attacker; + } // do not explode NOW but in the NEXT FRAME! // because recursive calls to RadiusDamage are not allowed