]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/g_damage.qc
a bit simpler
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / g_damage.qc
index 34b31be1a6b105d8ceab064037950cabfe4ca628..876ce8c65a27ec31b60d6ad504caacaa0d78a2dc 100644 (file)
@@ -140,7 +140,9 @@ void GiveFrags (entity attacker, entity targ, float f, float deathtype)
                // after a frag, exchange the current weapon (or the culprit, if detectable) by a new random weapon
                float culprit;
                culprit = DEATH_WEAPONOF(deathtype);
-               if(!culprit || !WEPSET_CONTAINS_EW(attacker, culprit))
+               if(!culprit)
+                       culprit = attacker.weapon;
+               else if(!WEPSET_CONTAINS_EW(attacker, culprit))
                        culprit = attacker.weapon;
 
                if(g_weaponarena_random_with_laser && culprit == WEP_LASER)
@@ -231,10 +233,10 @@ void GiveFrags (entity attacker, entity targ, float f, float deathtype)
 
 string Obituary_ExtraFragInfo(entity player) // Extra fragmessage information
 {
-       string health_output;
-       string ping_output;
-       string handicap_output;
-       string output;
+       string health_output = string_null;
+       string ping_output = string_null;
+       string handicap_output = string_null;
+       string output = string_null;
 
        if(autocvar_sv_fraginfo && ((autocvar_sv_fraginfo == 2) || inWarmupStage))
        {
@@ -352,6 +354,8 @@ void Obituary (entity attacker, entity inflictor, entity targ, float deathtype)
                {
                        if (deathtype == DEATH_TEAMCHANGE || deathtype == DEATH_AUTOTEAMCHANGE)
                                msg = ColoredTeamName(targ.team); // TODO: check if needed?
+                       else
+                               msg = "";
             if(!g_cts) // no "killed your own dumb self" message in CTS
                 Send_CSQC_KillCenterprint(targ, msg, "", deathtype, MSG_SUICIDE);
 
@@ -363,6 +367,8 @@ void Obituary (entity attacker, entity inflictor, entity targ, float deathtype)
 
                        if (targ.killcount > 2)
                                msg = ftos(targ.killcount);
+                       else
+                               msg = "";
                        if(teamplay && deathtype == DEATH_MIRRORDAMAGE)
                        {
                                if(attacker.team == COLOR_TEAM1)
@@ -386,9 +392,10 @@ void Obituary (entity attacker, entity inflictor, entity targ, float deathtype)
 
                                Send_CSQC_KillCenterprint(attacker, s, "", type, MSG_KILL);
 
-                               if (targ.killcount > 2) {
+                               if (targ.killcount > 2)
                                        msg = ftos(targ.killcount);
-                               }
+                               else
+                                       msg = "";
 
                                if (attacker.killcount > 2) {
                                        msg = ftos(attacker.killcount);
@@ -423,11 +430,12 @@ void Obituary (entity attacker, entity inflictor, entity targ, float deathtype)
 
                                attacker.taunt_soundtime = time + 1;
 
-                               // TODO: fix this?
-                               if (deathtype == DEATH_CUSTOM)
+                               if (deathtype == DEATH_HURTTRIGGER && inflictor.message2 != "")
+                                       msg = inflictor.message2;
+                               else if (deathtype == DEATH_CUSTOM)
                                        msg = deathmessage;
                                else
-                                       msg = inflictor.message2;
+                                       msg = "";
 
                                if(strstrofs(msg, "%", 0) < 0)
                                        msg = strcat("%s ", msg, " by %s");
@@ -504,6 +512,8 @@ void Obituary (entity attacker, entity inflictor, entity targ, float deathtype)
                                msg = inflictor.message;
                        else if (deathtype == DEATH_CUSTOM)
                                msg = deathmessage;
+                       else
+                               msg = "";
                        if(strstrofs(msg, "%", 0) < 0)
                                msg = strcat("%s ", msg);
 
@@ -637,9 +647,7 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, float
 
                                                        if(autocvar_g_mirrordamage_virtual)
                                                        {
-                                                               vector v;
-                                                               v = healtharmor_applydamage(attacker.armorvalue, autocvar_g_balance_armor_blockpercent, mirrordamage);
-                                                               v_z = 0; // fteqcc sucks
+                                                               vector v = healtharmor_applydamage(attacker.armorvalue, autocvar_g_balance_armor_blockpercent, mirrordamage);
                                                                attacker.dmg_take += v_x;
                                                                attacker.dmg_save += v_y;
                                                                attacker.dmg_inflictor = inflictor;
@@ -649,9 +657,7 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, float
 
                                                        if(autocvar_g_friendlyfire_virtual)
                                                        {
-                                                               vector v;
-                                                               v = healtharmor_applydamage(targ.armorvalue, autocvar_g_balance_armor_blockpercent, damage);
-                                                               v_z = 0; // fteqcc sucks
+                                                               vector v = healtharmor_applydamage(targ.armorvalue, autocvar_g_balance_armor_blockpercent, damage);
                                                                targ.dmg_take += v_x;
                                                                targ.dmg_save += v_y;
                                                                targ.dmg_inflictor = inflictor;
@@ -984,13 +990,8 @@ float RadiusDamage (entity inflictor, entity attacker, float coredamage, float e
        // Returns total damage applies to creatures
 {
        entity  targ;
-       float   finaldmg;
-       float   power;
        vector  blastorigin;
        vector  force;
-       vector  diff;
-       vector  center;
-       vector  nearest;
        float   total_damage_to_creatures;
        entity  next;
        float   tfloordmg;
@@ -1035,6 +1036,10 @@ float RadiusDamage (entity inflictor, entity attacker, float coredamage, float e
                if (targ != inflictor)
                        if (ignore != targ) if(targ.takedamage)
                        {
+                               vector nearest;
+                               vector diff;
+                               float power;
+
                                // LordHavoc: measure distance to nearest point on target (not origin)
                                // (this guarentees 100% damage on a touch impact)
                                nearest = targ.WarpZone_findradius_nearest;
@@ -1048,6 +1053,7 @@ float RadiusDamage (entity inflictor, entity attacker, float coredamage, float e
                                //      print(ftos(power), "\n");
                                if (power > 0)
                                {
+                                       float finaldmg;
                                        if (power > 1)
                                                power = 1;
                                        finaldmg = coredamage * power + edgedamage * (1 - power);
@@ -1055,74 +1061,83 @@ float RadiusDamage (entity inflictor, entity attacker, float coredamage, float e
                                        {
                                                float a;
                                                float c;
-                                               float hits;
-                                               float total;
-                                               float hitratio;
-                                               float mininv_f, mininv_d;
                                                vector hitloc;
                                                vector myblastorigin;
+                                               vector center;
+
                                                myblastorigin = WarpZone_TransformOrigin(targ, blastorigin);
-                                               center = targ.origin + (targ.mins + targ.maxs) * 0.5;
+
                                                // if it's a player, use the view origin as reference
                                                if (targ.classname == "player")
                                                        center = targ.origin + targ.view_ofs;
+                                               else
+                                                       center = targ.origin + (targ.mins + targ.maxs) * 0.5;
+
                                                force = normalize(center - myblastorigin);
                                                force = force * (finaldmg / coredamage) * forceintensity;
-
-                                               // test line of sight to multiple positions on box,
-                                               // and do damage if any of them hit
-                                               hits = 0;
                                                hitloc = nearest;
 
-                                               // we know: max stddev of hitratio = 1 / (2 * sqrt(n))
-                                               // so for a given max stddev:
-                                               // n = (1 / (2 * max stddev of hitratio))^2
+                                               if(targ != directhitentity)
+                                               {
+                                                       float hits;
+                                                       float total;
+                                                       float hitratio;
+                                                       float mininv_f, mininv_d;
 
-                                               mininv_d = (finaldmg * (1-tfloordmg)) / autocvar_g_throughfloor_damage_max_stddev;
-                                               mininv_f = (vlen(force) * (1-tfloorforce)) / autocvar_g_throughfloor_force_max_stddev;
+                                                       // test line of sight to multiple positions on box,
+                                                       // and do damage if any of them hit
+                                                       hits = 0;
 
-                                               if(autocvar_g_throughfloor_debug)
-                                                       print(sprintf("THROUGHFLOOR: D=%f F=%f max(dD)=1/%f max(dF)=1/%f", finaldmg, vlen(force), mininv_d, mininv_f));
+                                                       // we know: max stddev of hitratio = 1 / (2 * sqrt(n))
+                                                       // so for a given max stddev:
+                                                       // n = (1 / (2 * max stddev of hitratio))^2
 
-                                               total = 0.25 * pow(max(mininv_f, mininv_d), 2);
+                                                       mininv_d = (finaldmg * (1-tfloordmg)) / autocvar_g_throughfloor_damage_max_stddev;
+                                                       mininv_f = (vlen(force) * (1-tfloorforce)) / autocvar_g_throughfloor_force_max_stddev;
 
-                                               if(autocvar_g_throughfloor_debug)
-                                                       print(sprintf(" steps=%f", total));
+                                                       if(autocvar_g_throughfloor_debug)
+                                                               print(sprintf("THROUGHFLOOR: D=%f F=%f max(dD)=1/%f max(dF)=1/%f", finaldmg, vlen(force), mininv_d, mininv_f));
 
-                                               if (targ.classname == "player")
-                                                       total = ceil(bound(autocvar_g_throughfloor_min_steps_player, total, autocvar_g_throughfloor_max_steps_player));
-                                               else
-                                                       total = ceil(bound(autocvar_g_throughfloor_min_steps_other, total, autocvar_g_throughfloor_max_steps_other));
+                                                       total = 0.25 * pow(max(mininv_f, mininv_d), 2);
 
-                                               if(autocvar_g_throughfloor_debug)
-                                                       print(sprintf(" steps=%f dD=%f dF=%f", total, finaldmg * (1-tfloordmg) / (2 * sqrt(total)), vlen(force) * (1-tfloorforce) / (2 * sqrt(total))));
+                                                       if(autocvar_g_throughfloor_debug)
+                                                               print(sprintf(" steps=%f", total));
 
-                                               for(c = 0; c < total; ++c)
-                                               {
-                                                       //traceline(targ.WarpZone_findradius_findorigin, nearest, MOVE_NOMONSTERS, inflictor);
-                                                       WarpZone_TraceLine(blastorigin, WarpZone_UnTransformOrigin(targ, nearest), MOVE_NOMONSTERS, inflictor);
-                                                       if (trace_fraction == 1 || trace_ent == targ)
+                                                       if (targ.classname == "player")
+                                                               total = ceil(bound(autocvar_g_throughfloor_min_steps_player, total, autocvar_g_throughfloor_max_steps_player));
+                                                       else
+                                                               total = ceil(bound(autocvar_g_throughfloor_min_steps_other, total, autocvar_g_throughfloor_max_steps_other));
+
+                                                       if(autocvar_g_throughfloor_debug)
+                                                               print(sprintf(" steps=%f dD=%f dF=%f", total, finaldmg * (1-tfloordmg) / (2 * sqrt(total)), vlen(force) * (1-tfloorforce) / (2 * sqrt(total))));
+
+                                                       for(c = 0; c < total; ++c)
                                                        {
-                                                               ++hits;
-                                                               if (hits > 1)
-                                                                       hitloc = hitloc + nearest;
-                                                               else
-                                                                       hitloc = nearest;
+                                                               //traceline(targ.WarpZone_findradius_findorigin, nearest, MOVE_NOMONSTERS, inflictor);
+                                                               WarpZone_TraceLine(blastorigin, WarpZone_UnTransformOrigin(targ, nearest), MOVE_NOMONSTERS, inflictor);
+                                                               if (trace_fraction == 1 || trace_ent == targ)
+                                                               {
+                                                                       ++hits;
+                                                                       if (hits > 1)
+                                                                               hitloc = hitloc + nearest;
+                                                                       else
+                                                                               hitloc = nearest;
+                                                               }
+                                                               nearest_x = targ.origin_x + targ.mins_x + random() * targ.size_x;
+                                                               nearest_y = targ.origin_y + targ.mins_y + random() * targ.size_y;
+                                                               nearest_z = targ.origin_z + targ.mins_z + random() * targ.size_z;
                                                        }
-                                                       nearest_x = targ.origin_x + targ.mins_x + random() * targ.size_x;
-                                                       nearest_y = targ.origin_y + targ.mins_y + random() * targ.size_y;
-                                                       nearest_z = targ.origin_z + targ.mins_z + random() * targ.size_z;
-                                               }
 
-                                               nearest = hitloc * (1 / max(1, hits));
-                                               hitratio = (hits / total);
-                                               a = bound(0, tfloordmg + (1-tfloordmg) * hitratio, 1);
-                                               finaldmg = finaldmg * a;
-                                               a = bound(0, tfloorforce + (1-tfloorforce) * hitratio, 1);
-                                               force = force * a;
+                                                       nearest = hitloc * (1 / max(1, hits));
+                                                       hitratio = (hits / total);
+                                                       a = bound(0, tfloordmg + (1-tfloordmg) * hitratio, 1);
+                                                       finaldmg = finaldmg * a;
+                                                       a = bound(0, tfloorforce + (1-tfloorforce) * hitratio, 1);
+                                                       force = force * a;
 
-                                               if(autocvar_g_throughfloor_debug)
-                                                       print(sprintf(" D=%f F=%f\n", finaldmg, vlen(force)));
+                                                       if(autocvar_g_throughfloor_debug)
+                                                               print(sprintf(" D=%f F=%f\n", finaldmg, vlen(force)));
+                                               }
 
                                                // laser force adjustments :P
                                                if(DEATH_WEAPONOF(deathtype) == WEP_LASER)
@@ -1176,7 +1191,7 @@ float RadiusDamage (entity inflictor, entity attacker, float coredamage, float e
                                                //      print(" finaldmg ", ftos(finaldmg), " force ", vtos(force));
                                                //      print(" (", ftos(a), ")\n");
                                                //}
-                                               if(hits || tfloordmg || tfloorforce)
+                                               if(finaldmg || vlen(force))
                                                {
                                                        if(targ.iscreature)
                                                        {
@@ -1254,43 +1269,59 @@ float Fire_AddDamage(entity e, entity o, float d, float t, float dt)
 
                if(maxtime > mintime || maxdps > mindps)
                {
+                       // Constraints:
+                       
+                       // damage we have right now
                        mindamage = mindps * mintime;
-                       maxdamage = mindamage + d;
-
-                       // interval [mintime, maxtime] * [mindps, maxdps]
-                       // intersected with
-                       // [mindamage, maxdamage]
-                       // maximum of this!
 
-                       if(maxdamage >= maxtime * maxdps)
-                       {
-                               totaltime = maxtime;
-                               totaldamage = maxtime * maxdps;
-
-                               // this branch increases totaldamage if either t > mintime, or dps > mindps
-                       }
-                       else
-                       {
-                               // maxdamage is inside the interval!
-                               // first, try to use mindps; only if this fails, increase dps as needed
-                               totaltime = min(maxdamage / mindps, maxtime); // maxdamage / mindps >= mindamage / mindps = mintime
-                               totaldamage = maxdamage;
-                               // can totaldamage / totaltime be >= maxdps?
-                               // max(mindps, maxdamage / maxtime) >= maxdps?
-                               // we know maxdamage < maxtime * maxdps
-                               // so it cannot be
-
-                               // this branch ALWAYS increases totaldamage, but requires maxdamage < maxtime * maxdps
-                       }
+                       // damage we want to get
+                       maxdamage = mindamage + d;
 
-                       // total conditions for increasing:
-                       //     maxtime > mintime OR maxdps > mindps OR maxtime * maxdps > maxdamage
-                       // however:
-                       //     if maxtime = mintime, maxdps = mindps
-                       // then:
-                       //     maxdamage = mindamage + d
-                       //     mindamage = mindps * mintime = maxdps * maxtime < maxdamage!
-                       // so the last condition is not needed
+                       // but we can't exceed maxtime * maxdps!
+                       totaldamage = min(maxdamage, maxtime * maxdps);
+
+                       // LEMMA:
+                       // Look at:
+                       // totaldamage = min(mindamage + d, maxdamage * maxdps)
+                       // We see:
+                       // totaldamage <= maxdamage * maxdps
+                       // ==> totaldamage / maxdps <= maxtime.
+                       // We also see:
+                       // totaldamage / mindps = min(mindamage / mindps + d, maxtime * maxdps / mindps)
+                       //                     >= min(mintime, maxtime)
+                       // ==> totaldamage / maxdps >= mintime.
+
+                       /*
+                       // how long do we damage then?
+                       // at least as long as before
+                       // but, never exceed maxdps
+                       totaltime = max(mintime, totaldamage / maxdps); // always <= maxtime due to lemma
+                       */
+
+                       // alternate:
+                       // at most as long as maximum allowed
+                       // but, never below mindps
+                       totaltime = min(maxtime, totaldamage / mindps); // always >= mintime due to lemma
+
+                       // assuming t > mintime, dps > mindps:
+                       // we get d = t * dps = maxtime * maxdps
+                       // totaldamage = min(maxdamage, maxtime * maxdps) = min(... + d, maxtime * maxdps) = maxtime * maxdps
+                       // totaldamage / maxdps = maxtime
+                       // totaldamage / mindps > totaldamage / maxdps = maxtime
+                       // FROM THIS:
+                       // a) totaltime = max(mintime, maxtime) = maxtime
+                       // b) totaltime = min(maxtime, totaldamage / maxdps) = maxtime
+
+                       // assuming t <= mintime:
+                       // we get maxtime = mintime
+                       // a) totaltime = max(mintime, ...) >= mintime, also totaltime <= maxtime by the lemma, therefore totaltime = mintime = maxtime
+                       // b) totaltime = min(maxtime, ...) <= maxtime, also totaltime >= mintime by the lemma, therefore totaltime = mintime = maxtime
+
+                       // assuming dps <= mindps:
+                       // we get mindps = maxdps.
+                       // With this, the lemma says that mintime <= totaldamage / mindps = totaldamage / maxdps <= maxtime.
+                       // a) totaltime = max(mintime, totaldamage / maxdps) = totaldamage / maxdps
+                       // b) totaltime = min(maxtime, totaldamage / mindps) = totaldamage / maxdps
 
                        e.fire_damagepersec = totaldamage / totaltime;
                        e.fire_endtime = time + totaltime;