]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/common/mutators/mutator/damagetext/sv_damagetext.qc
Merge branch 'terencehill/damagetext_stuff' into 'master'
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / mutators / mutator / damagetext / sv_damagetext.qc
index c244d170d259709ea485cc874e0a707e0c1294dc..b49cddd96012b6acb97077c5e84b9e2c3f0ed63a 100644 (file)
@@ -14,6 +14,7 @@ REGISTER_MUTATOR(damagetext, true);
 .float dent_net_health;
 .float dent_net_armor;
 .float dent_net_potential;
+.entity dent_attackers;
 
 bool write_damagetext(entity this, entity client, int sf)
 {
@@ -64,6 +65,22 @@ MUTATOR_HOOKFUNCTION(damagetext, PlayerDamaged) {
        float potential_damage = M_ARGV(6, float);
        if(DEATH_WEAPONOF(deathtype) == WEP_VAPORIZER) return;
 
+       static entity net_text_prev;
+       static float net_damagetext_prev_time;
+
+       bool multiple = (net_damagetext_prev_time == time && net_text_prev && !wasfreed(net_text_prev)
+               && net_text_prev.realowner == attacker && net_text_prev.enemy == hit
+               && net_text_prev.dent_net_deathtype == deathtype);
+
+       if (multiple)
+       {
+               // damage of multiple projectiles hitting player at the same time, e.g. shotgun
+               // is accumulated on the same damagetext entity
+               health += net_text_prev.dent_net_health;
+               armor += net_text_prev.dent_net_armor;
+               potential_damage += net_text_prev.dent_net_potential;
+       }
+
        int flags = 0;
        if (SAME_TEAM(hit, attacker)) flags |= DTFLAG_SAMETEAM;
        if (health >= DAMAGETEXT_SHORT_LIMIT) flags |= DTFLAG_BIG_HEALTH;
@@ -72,6 +89,21 @@ MUTATOR_HOOKFUNCTION(damagetext, PlayerDamaged) {
        if (!armor) flags |= DTFLAG_NO_ARMOR;
        if (almost_equals_eps(armor + health, potential_damage, 5)) flags |= DTFLAG_NO_POTENTIAL;
 
+       if (multiple)
+       {
+               net_text_prev.dent_net_flags = flags;
+               net_text_prev.dent_net_health = health;
+               net_text_prev.dent_net_armor = armor;
+               net_text_prev.dent_net_potential = potential_damage;
+               return;
+       }
+       else if (!IL_CONTAINS(hit.dent_attackers, attacker))
+       {
+               // player is hit for the first time after respawn by this attacker
+               IL_PUSH(hit.dent_attackers, attacker);
+               flags |= DTFLAG_STOP_ACCUMULATION; // forcedly stop client-side damage accumulation
+       }
+
        entity net_text = new_pure(net_damagetext);
        net_text.realowner = attacker;
        net_text.enemy = hit;
@@ -81,8 +113,33 @@ MUTATOR_HOOKFUNCTION(damagetext, PlayerDamaged) {
        net_text.dent_net_armor = armor;
        net_text.dent_net_potential = potential_damage;
 
+       net_text_prev = net_text;
+       net_damagetext_prev_time = time;
+
        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);
 }
+
+MUTATOR_HOOKFUNCTION(damagetext, ClientDisconnect)
+{
+       entity player = M_ARGV(0, entity);
+       if (player.dent_attackers)
+               IL_DELETE(player.dent_attackers);
+
+       // NOTE this player is automatically removed from dent_attackers lists of other players
+       // by intrusive list's ONREMOVE
+}
+
+MUTATOR_HOOKFUNCTION(damagetext, PlayerSpawn)
+{
+       entity player = M_ARGV(0, entity);
+       if (player.dent_attackers == NULL)
+       {
+               player.dent_attackers = IL_NEW();
+               return true;
+       }
+
+       IL_CLEAR(player.dent_attackers);
+}