if (MUTATOR_CALLHOOK(ClientObituary, inflictor, attacker, targ, deathtype, attacker.(weaponentity))) { CS(targ).killcount = 0; return; }
notif_anonymous = M_ARGV(5, bool);
+ // TODO: Replace "???" with a translatable "Anonymous player" string
+ // https://gitlab.com/xonotic/xonotic-data.pk3dir/-/issues/2839
if(notif_anonymous)
- attacker_name = "Anonymous player";
+ attacker_name = "???";
#ifdef NOTIFICATIONS_DEBUG
Debug_Notification(
FOREACH_CLIENT(IS_PLAYER(it),
{
for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
- {
- .entity weaponentity = weaponentities[slot];
- if(it.(weaponentity).hook.aiment == targ)
- RemoveHook(it.(weaponentity).hook);
- }
+ {
+ .entity weaponentity = weaponentities[slot];
+ if(it.(weaponentity).hook.aiment == targ)
+ RemoveHook(it.(weaponentity).hook);
+ }
});
// add waypoint
FOREACH_CLIENT(IS_PLAYER(it),
{
for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
- {
- .entity weaponentity = weaponentities[slot];
- if(it.(weaponentity).hook.aiment == targ)
- RemoveHook(it.(weaponentity).hook);
- }
+ {
+ .entity weaponentity = weaponentities[slot];
+ if(it.(weaponentity).hook.aiment == targ)
+ RemoveHook(it.(weaponentity).hook);
+ }
});
// remove the ice block
// These are ALWAYS lethal
// No damage modification here
- // Instead, prepare the victim for his death...
+ // Instead, prepare the victim for their death...
if(deathtype == DEATH_TEAMCHANGE.m_id || deathtype == DEATH_AUTOTEAMCHANGE.m_id)
{
SetResourceExplicit(targ, RES_ARMOR, 0);
if(IS_PLAYER(targ) && damage > 0 && attacker)
{
for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
- {
- .entity went = weaponentities[slot];
- if(targ.(went).hook && targ.(went).hook.aiment == attacker)
- RemoveHook(targ.(went).hook);
- }
+ {
+ .entity went = weaponentities[slot];
+ if(targ.(went).hook && targ.(went).hook.aiment == attacker)
+ RemoveHook(targ.(went).hook);
+ }
}
if(STAT(FROZEN, targ) && !ITEM_DAMAGE_NEEDKILL(deathtype)
else
force = normalize(force);
if(forceintensity >= 0)
- Damage_DamageInfo(inflictororigin, false, coredamage, edgedamage, rad, forceintensity * force, deathtype, 0, attacker);
+ Damage_DamageInfo(inflictororigin, coredamage, edgedamage, rad, forceintensity * force, deathtype, 0, attacker);
else
- Damage_DamageInfo(inflictororigin, false, coredamage, edgedamage, -rad, (-forceintensity) * force, deathtype, 0, attacker);
+ Damage_DamageInfo(inflictororigin, coredamage, edgedamage, -rad, (-forceintensity) * force, deathtype, 0, attacker);
}
stat_damagedone = 0;
if (((cantbe != targ) && !mustbe) || (mustbe == targ))
if (targ.takedamage)
{
- // measure distance from nearest point on target (not origin)
- // to nearest point on inflictor (not origin)
+ // calculate distance from nearest point on target to nearest point on inflictor
+ // instead of origin to ensure full damage on impacts
+
vector nearest = targ.WarpZone_findradius_nearest;
+
+ // optimize code by getting inflictororigin_wz from WarpZone_FindRadius calculations instead of
+ //vector inflictororigin_wz = WarpZone_TransformOrigin(targ, inflictororigin);
+
+ vector inflictororigin_wz = targ.WarpZone_findradius_nearest + targ.WarpZone_findradius_dist;
vector inflictornearest = NearestPointOnBoundingBox(
- inflictororigin - (inflictor.maxs - inflictor.mins) * 0.5,
- inflictororigin + (inflictor.maxs - inflictor.mins) * 0.5,
- nearest);
+ inflictororigin_wz + inflictor.mins, inflictororigin_wz + inflictor.maxs, nearest);
vector diff = inflictornearest - nearest;
// round up a little on the damage to ensure full damage on impacts
float a;
float c;
vector hitloc;
- vector myblastorigin;
- vector center;
-
- myblastorigin = WarpZone_TransformOrigin(targ, inflictororigin);
// if it's a player, use the view origin as reference
- center = CENTER_OR_VIEWOFS(targ);
+ vector center = CENTER_OR_VIEWOFS(targ);
+
+ if (autocvar_g_player_damageplayercenter)
+ {
+ if (targ != attacker)
+ {
+ // always use target's bbox centerpoint
+ center = targ.origin + ((targ.mins + targ.maxs) * 0.5);
+ }
+ else // targ == attacker
+ {
+ #if 0
+ // code stolen from W_SetupShot_Dir_ProjectileSize_Range()
+ vector md = targ.(weaponentity).movedir;
+ vector dv = v_right * -md.y + v_up * md.z;
+ vector mi = '0 0 0', ma = '0 0 0';
+
+ if(IS_CLIENT(targ)) // no antilag for non-clients!
+ {
+ if(CS(targ).antilag_debug)
+ tracebox_antilag(targ, center, mi, ma, center + dv, MOVE_NORMAL, targ, CS(targ).antilag_debug);
+ else
+ tracebox_antilag(targ, center, mi, ma, center + dv, MOVE_NORMAL, targ, ANTILAG_LATENCY(targ));
+ }
+ else
+ tracebox(center, mi, ma, center + dv, MOVE_NORMAL, targ);
+
+ center.z = trace_endpos.z;
+ #else
+ // very cheap way but it skips move into solid checks which is fine most of the time for now AFAIK
+ // this should only really be an issue with some rare edge cases where
+ // shot origin was prevented from going into a ceiling but it still explodes at the ceiling
+ // shot origin wasn't raised as high as possible and the shooter gets upwards knockback
+ // TL;DR: no bugs if vertical shot origin is always within player bbox
+ center.z = center.z + targ.(weaponentity).movedir.z;
+ #endif
+ }
+ }
- force = normalize(center - myblastorigin);
- force = force * (finaldmg / coredamage) * forceintensity;
+ /* debug prints
+ print(sprintf("origin vec %v\n", targ.origin));
+ print(sprintf("movedir vec %v\n", targ.(weaponentity).movedir));
+ print(sprintf("old def vec %v\n", CENTER_OR_VIEWOFS(targ)));
+ print(sprintf("origin+vofs %v\n", targ.origin + targ.view_ofs));
+ print(sprintf("bbox center %v\n", (targ.origin + ((targ.mins + targ.maxs) * 0.5))));
+ print(sprintf("center vec %v\n", center));
+ print(sprintf("shotorg vec %v\n", w_shotorg));
+ print("\n");
+ */
+
+ force = normalize(center - inflictororigin_wz);
+ force = force * (finaldmg / max(coredamage, edgedamage)) * forceintensity;
hitloc = nearest;
// apply special scaling along the z axis if set
if(autocvar_g_throughfloor_debug)
LOG_INFOF(" D=%f F=%f", finaldmg, vlen(force));
+
+ /*if (targ == attacker)
+ {
+ print("hits ", ftos(hits), " / ", ftos(total));
+ print(" finaldmg ", ftos(finaldmg), " force ", ftos(vlen(force)));
+ print(" (", vtos(force), ") (", ftos(a), ")\n");
+ }*/
}
- //if (targ == attacker)
- //{
- // print("hits ", ftos(hits), " / ", ftos(total));
- // print(" finaldmg ", ftos(finaldmg), " force ", vtos(force));
- // print(" (", ftos(a), ")\n");
- //}
if(finaldmg || force)
{
if(targ.iscreature)
RadiusDamage_running = 0;
if(!DEATH_ISSPECIAL(deathtype))
- accuracy_add(attacker, DEATH_WEAPONOF(deathtype), 0, min(coredamage, stat_damagedone));
+ accuracy_add(attacker, DEATH_WEAPONOF(deathtype), 0, min(max(coredamage, edgedamage), stat_damagedone));
return total_damage_to_creatures;
}
float RadiusDamage(entity inflictor, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, float forceintensity, int deathtype, .entity weaponentity, entity directhitentity)
{
- return RadiusDamageForSource(inflictor, (inflictor.origin + (inflictor.mins + inflictor.maxs) * 0.5), inflictor.velocity, attacker, coredamage, edgedamage, rad,
+ return RadiusDamageForSource(inflictor, (inflictor.origin + (inflictor.mins + inflictor.maxs) * 0.5), inflictor.velocity, attacker, coredamage, edgedamage, rad,
cantbe, mustbe, false, forceintensity, 1, deathtype, weaponentity, directhitentity);
}