1 #include "sv_damagetext.qh"
3 AUTOCVAR(sv_damagetext, int, 2, "<= 0: disabled, >= 1: visible to spectators, >= 2: visible to attacker, >= 3: all players see everyone's damage");
5 REGISTER_MUTATOR(damagetext, true);
7 #define SV_DAMAGETEXT_DISABLED() (autocvar_sv_damagetext <= 0)
8 #define SV_DAMAGETEXT_SPECTATORS_ONLY() (autocvar_sv_damagetext >= 1)
9 #define SV_DAMAGETEXT_PLAYERS() (autocvar_sv_damagetext >= 2)
10 #define SV_DAMAGETEXT_ALL() (autocvar_sv_damagetext >= 3)
13 .float dent_net_deathtype;
14 .float dent_net_health;
15 .float dent_net_armor;
16 .float dent_net_potential;
18 bool write_damagetext(entity this, entity client, int sf)
20 entity attacker = this.realowner;
21 entity hit = this.enemy;
22 int flags = this.dent_net_flags;
23 int deathtype = this.dent_net_deathtype;
24 float health = this.dent_net_health;
25 float armor = this.dent_net_armor;
26 float potential_damage = this.dent_net_potential;
28 (SV_DAMAGETEXT_ALL()) ||
29 (SV_DAMAGETEXT_PLAYERS() && client == attacker) ||
30 (SV_DAMAGETEXT_SPECTATORS_ONLY() && IS_SPEC(client) && client.enemy == attacker) ||
31 (SV_DAMAGETEXT_SPECTATORS_ONLY() && IS_OBSERVER(client))
34 WriteHeader(MSG_ENTITY, damagetext);
35 WriteByte(MSG_ENTITY, etof(hit));
36 WriteInt24_t(MSG_ENTITY, deathtype);
37 WriteByte(MSG_ENTITY, flags);
39 // we need to send a few decimal places to minimize errors when accumulating damage
40 // sending them multiplied saves bandwidth compared to using WriteCoord,
41 // however if the multiplied damage would be too much for (signed) short, we send an int24
42 if (flags & DTFLAG_BIG_HEALTH) WriteInt24_t(MSG_ENTITY, health * DAMAGETEXT_PRECISION_MULTIPLIER);
43 else WriteShort(MSG_ENTITY, health * DAMAGETEXT_PRECISION_MULTIPLIER);
44 if (!(flags & DTFLAG_NO_ARMOR))
46 if (flags & DTFLAG_BIG_ARMOR) WriteInt24_t(MSG_ENTITY, armor * DAMAGETEXT_PRECISION_MULTIPLIER);
47 else WriteShort(MSG_ENTITY, armor * DAMAGETEXT_PRECISION_MULTIPLIER);
49 if (!(flags & DTFLAG_NO_POTENTIAL))
51 if (flags & DTFLAG_BIG_POTENTIAL) WriteInt24_t(MSG_ENTITY, potential_damage * DAMAGETEXT_PRECISION_MULTIPLIER);
52 else WriteShort(MSG_ENTITY, potential_damage * DAMAGETEXT_PRECISION_MULTIPLIER);
57 MUTATOR_HOOKFUNCTION(damagetext, PlayerDamaged) {
58 if (SV_DAMAGETEXT_DISABLED()) return;
59 entity attacker = M_ARGV(0, entity);
60 entity hit = M_ARGV(1, entity); if (hit == attacker) return;
61 float health = M_ARGV(2, float);
62 float armor = M_ARGV(3, float);
63 int deathtype = M_ARGV(5, int);
64 float potential_damage = M_ARGV(6, float);
65 if(DEATH_WEAPONOF(deathtype) == WEP_VAPORIZER) return;
68 if (SAME_TEAM(hit, attacker)) flags |= DTFLAG_SAMETEAM;
69 if (health >= DAMAGETEXT_SHORT_LIMIT) flags |= DTFLAG_BIG_HEALTH;
70 if (armor >= DAMAGETEXT_SHORT_LIMIT) flags |= DTFLAG_BIG_ARMOR;
71 if (potential_damage >= DAMAGETEXT_SHORT_LIMIT) flags |= DTFLAG_BIG_POTENTIAL;
72 if (!armor) flags |= DTFLAG_NO_ARMOR;
73 if (almost_equals_eps(armor + health, potential_damage, 5)) flags |= DTFLAG_NO_POTENTIAL;
75 entity net_text = new_pure(net_damagetext);
76 net_text.realowner = attacker;
78 net_text.dent_net_flags = flags;
79 net_text.dent_net_deathtype = deathtype;
80 net_text.dent_net_health = health;
81 net_text.dent_net_armor = armor;
82 net_text.dent_net_potential = potential_damage;
84 setthink(net_text, SUB_Remove);
85 net_text.nextthink = (time > 10) ? (time + 0.5) : 10; // allow a buffer from start time for clients to load in
87 Net_LinkEntity(net_text, false, 0, write_damagetext);