#define DAMAGETEXT_PRECISION_MULTIPLIER 128
#define DAMAGETEXT_SHORT_LIMIT 256 // the smallest value that we can't send as short - 2^15 (signed short) / DAMAGETEXT_PRECISION_MULTIPLIER
+const int DTFLAG_SAMETEAM = BIT(0);
+const int DTFLAG_BIG_HEALTH = BIT(1);
+const int DTFLAG_BIG_ARMOR = BIT(2);
+const int DTFLAG_BIG_POTENTIAL = BIT(3);
+const int DTFLAG_NO_ARMOR = BIT(4);
+const int DTFLAG_NO_POTENTIAL = BIT(5);
+
REGISTER_MUTATOR(damagetext, true);
-#if defined(CSQC) || defined(MENUQC)
+// || defined(MENUQC)
+#if defined(CSQC)
// no translatable cvar description please
AUTOCVAR_SAVE(cl_damagetext, bool, true, "Draw damage dealt where you hit the enemy");
-AUTOCVAR_SAVE(cl_damagetext_format, string, "-{total}", "How to format the damage text. {health}, {armor}, {total}, {potential}, {real_damage}");
+AUTOCVAR_SAVE(cl_damagetext_format, string, "-{total}", "How to format the damage text. {health}, {armor}, {total}, {potential}: full damage not capped to target's health, {potential_health}: health damage not capped to target's health");
+AUTOCVAR_SAVE(cl_damagetext_format_verbose, bool, false, "{health} shows {potential_health} too when they differ; {total} shows {potential} too when they differ");
STATIC_INIT(DamageText_LegacyFormat) {
if (strstrofs(autocvar_cl_damagetext_format, "{", 0) < 0) autocvar_cl_damagetext_format = "-{total}";
}
Weapon w = DEATH_WEAPONOF(this.m_deathtype);
if (w != WEP_Null) rgb = w.wpcolor;
}
+ int health = rint(this.m_damage / DAMAGETEXT_PRECISION_MULTIPLIER);
+ int total = rint((this.m_damage + this.m_armordamage) / DAMAGETEXT_PRECISION_MULTIPLIER);
+ int potential = rint(this.m_potential_damage / DAMAGETEXT_PRECISION_MULTIPLIER);
+ int potential_health = rint((this.m_potential_damage - this.m_armordamage) / DAMAGETEXT_PRECISION_MULTIPLIER);
+
string s = autocvar_cl_damagetext_format;
- s = strreplace("{health}", sprintf("%d", rint(this.m_damage / DAMAGETEXT_PRECISION_MULTIPLIER)), s);
s = strreplace("{armor}", sprintf("%d", rint(this.m_armordamage / DAMAGETEXT_PRECISION_MULTIPLIER)), s);
- s = strreplace("{total}", sprintf("%d", rint((this.m_damage + this.m_armordamage) / DAMAGETEXT_PRECISION_MULTIPLIER)), s);
- s = strreplace("{potential}", sprintf("%d", rint(this.m_potential_damage/DAMAGETEXT_PRECISION_MULTIPLIER)), s);
- s = strreplace("{real_damage}", sprintf("%d", rint((this.m_potential_damage - this.m_armordamage)/DAMAGETEXT_PRECISION_MULTIPLIER)), s);
+ s = strreplace("{potential}", sprintf("%d", potential), s);
+ s = strreplace("{potential_health}", sprintf("%d", potential_health), s);
+
+ s = strreplace("{health}", (
+ (health == potential_health || !autocvar_cl_damagetext_format_verbose)
+ ? sprintf("%d", health)
+ : sprintf("%d (%d)", health, potential_health)
+ ), s);
+ s = strreplace("{total}", (
+ (total == potential || !autocvar_cl_damagetext_format_verbose)
+ ? sprintf("%d", total)
+ : sprintf("%d (%d)", total, potential)
+ ), s);
drawcolorcodedstring2_builtin(pos, s, this.m_size * '1 1 0', rgb, this.alpha, DRAWFLAG_NORMAL);
}
}
(SV_DAMAGETEXT_SPECTATORS_ONLY() && IS_SPEC(it) && it.enemy == attacker) ||
(SV_DAMAGETEXT_SPECTATORS_ONLY() && IS_OBSERVER(it))
) {
- int flags = SAME_TEAM(hit, attacker); // BIT(0)
- if (health >= DAMAGETEXT_SHORT_LIMIT) flags |= BIT(1);
- if (armor >= DAMAGETEXT_SHORT_LIMIT) flags |= BIT(2);
- if (potential_damage >= DAMAGETEXT_SHORT_LIMIT) flags |= BIT(3);
+ 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 (fabs((armor + health) - potential_damage) < 0.0001) flags |= DTFLAG_NO_POTENTIAL;
msg_entity = it;
WriteHeader(MSG_ONE, damagetext);
// 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 (health >= DAMAGETEXT_SHORT_LIMIT) WriteInt24_t(MSG_ONE, health * DAMAGETEXT_PRECISION_MULTIPLIER);
+ if (flags & DTFLAG_BIG_HEALTH) WriteInt24_t(MSG_ONE, health * DAMAGETEXT_PRECISION_MULTIPLIER);
else WriteShort(MSG_ONE, health * DAMAGETEXT_PRECISION_MULTIPLIER);
- if (armor >= DAMAGETEXT_SHORT_LIMIT) WriteInt24_t(MSG_ONE, armor * DAMAGETEXT_PRECISION_MULTIPLIER);
- else WriteShort(MSG_ONE, armor * DAMAGETEXT_PRECISION_MULTIPLIER);
- if (potential_damage >= DAMAGETEXT_SHORT_LIMIT) WriteInt24_t(MSG_ONE, potential_damage * DAMAGETEXT_PRECISION_MULTIPLIER);
- else WriteShort(MSG_ONE, potential_damage * 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);
+ }
}
));
}
vector location = vec3(ReadCoord(), ReadCoord(), ReadCoord());
int deathtype = ReadInt24_t();
int flags = ReadByte();
- bool friendlyfire = flags & 1;
+ bool friendlyfire = flags & DTFLAG_SAMETEAM;
int health, armor, potential_damage;
- if (flags & BIT(1)) health = ReadInt24_t();
+ if (flags & DTFLAG_BIG_HEALTH) health = ReadInt24_t();
else health = ReadShort();
- if (flags & BIT(2)) armor = ReadInt24_t();
+ if (flags & DTFLAG_NO_ARMOR) armor = 0;
+ else if (flags & DTFLAG_BIG_ARMOR) armor = ReadInt24_t();
else armor = ReadShort();
- if (flags & BIT(3)) potential_damage = ReadInt24_t();
+ if (flags & DTFLAG_NO_POTENTIAL) potential_damage = health + armor;
+ else if (flags & DTFLAG_BIG_POTENTIAL) potential_damage = ReadInt24_t();
else potential_damage = ReadShort();
return = true;