#define SV_DAMAGETEXT_SPECTATORS_ONLY() (autocvar_sv_damagetext >= 1)
#define SV_DAMAGETEXT_PLAYERS() (autocvar_sv_damagetext >= 2)
#define SV_DAMAGETEXT_ALL() (autocvar_sv_damagetext >= 3)
+
+.int dent_net_flags;
+.float dent_net_deathtype;
+.float dent_net_health;
+.float dent_net_armor;
+.float dent_net_potential;
+
+bool write_damagetext(entity this, entity client, int sf)
+{
+ entity attacker = this.realowner;
+ entity hit = this.enemy;
+ int flags = this.dent_net_flags;
+ int deathtype = this.dent_net_deathtype;
+ float health = this.dent_net_health;
+ float armor = this.dent_net_armor;
+ float potential_damage = this.dent_net_potential;
+ if (!(
+ (SV_DAMAGETEXT_ALL()) ||
+ (SV_DAMAGETEXT_PLAYERS() && client == attacker) ||
+ (SV_DAMAGETEXT_SPECTATORS_ONLY() && IS_SPEC(client) && client.enemy == attacker) ||
+ (SV_DAMAGETEXT_SPECTATORS_ONLY() && IS_OBSERVER(client))
+ )) return false;
+
+ WriteHeader(MSG_ENTITY, damagetext);
+ WriteByte(MSG_ENTITY, etof(hit));
+ WriteInt24_t(MSG_ENTITY, deathtype);
+ WriteByte(MSG_ENTITY, flags);
+
+ // we need to send a few decimal places to minimize errors when accumulating damage
+ // sending them multiplied saves bandwidth compared to using WriteCoord,
+ // however if the multiplied damage would be too much for (signed) short, we send an int24
+ if (flags & DTFLAG_BIG_HEALTH) WriteInt24_t(MSG_ENTITY, health * DAMAGETEXT_PRECISION_MULTIPLIER);
+ else WriteShort(MSG_ENTITY, health * DAMAGETEXT_PRECISION_MULTIPLIER);
+ if (!(flags & DTFLAG_NO_ARMOR))
+ {
+ if (flags & DTFLAG_BIG_ARMOR) WriteInt24_t(MSG_ENTITY, armor * DAMAGETEXT_PRECISION_MULTIPLIER);
+ else WriteShort(MSG_ENTITY, armor * DAMAGETEXT_PRECISION_MULTIPLIER);
+ }
+ if (!(flags & DTFLAG_NO_POTENTIAL))
+ {
+ if (flags & DTFLAG_BIG_POTENTIAL) WriteInt24_t(MSG_ENTITY, potential_damage * DAMAGETEXT_PRECISION_MULTIPLIER);
+ else WriteShort(MSG_ENTITY, potential_damage * DAMAGETEXT_PRECISION_MULTIPLIER);
+ }
+ return true;
+}
+
MUTATOR_HOOKFUNCTION(damagetext, PlayerDamaged) {
if (SV_DAMAGETEXT_DISABLED()) return;
entity attacker = M_ARGV(0, entity);
float potential_damage = M_ARGV(6, float);
if(DEATH_WEAPONOF(deathtype) == WEP_VAPORIZER) return;
if(deathtype == DEATH_FIRE.m_id || deathtype == DEATH_BUFF.m_id) return; // TODO: exclude damage over time and thorn effects
- FOREACH_CLIENT(IS_REAL_CLIENT(it), {
- if (
- (SV_DAMAGETEXT_ALL()) ||
- (SV_DAMAGETEXT_PLAYERS() && it == attacker) ||
- (SV_DAMAGETEXT_SPECTATORS_ONLY() && IS_SPEC(it) && it.enemy == attacker) ||
- (SV_DAMAGETEXT_SPECTATORS_ONLY() && IS_OBSERVER(it))
- ) {
- int flags = 0;
- if (SAME_TEAM(hit, attacker)) flags |= DTFLAG_SAMETEAM;
- if (health >= DAMAGETEXT_SHORT_LIMIT) flags |= DTFLAG_BIG_HEALTH;
- if (armor >= DAMAGETEXT_SHORT_LIMIT) flags |= DTFLAG_BIG_ARMOR;
- if (potential_damage >= DAMAGETEXT_SHORT_LIMIT) flags |= DTFLAG_BIG_POTENTIAL;
- if (!armor) flags |= DTFLAG_NO_ARMOR;
- if (almost_equals_eps(armor + health, potential_damage, 5)) flags |= DTFLAG_NO_POTENTIAL;
-
- msg_entity = it;
- WriteHeader(MSG_ONE, damagetext);
- WriteByte(MSG_ONE, etof(hit));
- WriteInt24_t(MSG_ONE, deathtype);
- WriteByte(MSG_ONE, flags);
-
- // we need to send a few decimal places to minimize errors when accumulating damage
- // sending them multiplied saves bandwidth compared to using WriteCoord,
- // however if the multiplied damage would be too much for (signed) short, we send an int24
- if (flags & DTFLAG_BIG_HEALTH) WriteInt24_t(MSG_ONE, health * DAMAGETEXT_PRECISION_MULTIPLIER);
- else WriteShort(MSG_ONE, health * DAMAGETEXT_PRECISION_MULTIPLIER);
- if (!(flags & DTFLAG_NO_ARMOR))
- {
- if (flags & DTFLAG_BIG_ARMOR) WriteInt24_t(MSG_ONE, armor * DAMAGETEXT_PRECISION_MULTIPLIER);
- else WriteShort(MSG_ONE, armor * DAMAGETEXT_PRECISION_MULTIPLIER);
- }
- if (!(flags & DTFLAG_NO_POTENTIAL))
- {
- if (flags & DTFLAG_BIG_POTENTIAL) WriteInt24_t(MSG_ONE, potential_damage * DAMAGETEXT_PRECISION_MULTIPLIER);
- else WriteShort(MSG_ONE, potential_damage * DAMAGETEXT_PRECISION_MULTIPLIER);
- }
- }
- });
+
+ int flags = 0;
+ if (SAME_TEAM(hit, attacker)) flags |= DTFLAG_SAMETEAM;
+ if (health >= DAMAGETEXT_SHORT_LIMIT) flags |= DTFLAG_BIG_HEALTH;
+ if (armor >= DAMAGETEXT_SHORT_LIMIT) flags |= DTFLAG_BIG_ARMOR;
+ if (potential_damage >= DAMAGETEXT_SHORT_LIMIT) flags |= DTFLAG_BIG_POTENTIAL;
+ if (!armor) flags |= DTFLAG_NO_ARMOR;
+ if (almost_equals_eps(armor + health, potential_damage, 5)) flags |= DTFLAG_NO_POTENTIAL;
+
+ entity net_text = new_pure(net_damagetext);
+ net_text.realowner = attacker;
+ net_text.enemy = hit;
+ net_text.dent_net_flags = flags;
+ net_text.dent_net_deathtype = deathtype;
+ net_text.dent_net_health = health;
+ net_text.dent_net_armor = armor;
+ net_text.dent_net_potential = potential_damage;
+
+ setthink(net_text, SUB_Remove);
+ net_text.nextthink = (time > 10) ? (time + 0.5) : 10; // allow a buffer from start time for clients to load in
+
+ Net_LinkEntity(net_text, false, 0, write_damagetext);
}