]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'TimePath/damagetext' into 'master'
authorTimePath <andrew.hardaker1995@gmail.com>
Sun, 23 Aug 2015 06:43:02 +0000 (06:43 +0000)
committerTimePath <andrew.hardaker1995@gmail.com>
Sun, 23 Aug 2015 06:43:02 +0000 (06:43 +0000)
Damage text

Nice little feature to display a running total of damage. ~~Should this be on by default? Should servers be able to disable it? Are the default settings good enough?~~ done done and done.
![](https://dl.dropboxusercontent.com/u/42745598/screenshots/2015-08-23_13-24-33.png)

See merge request !192

12 files changed:
qcsrc/client/main.qc
qcsrc/client/mutators/events.qh
qcsrc/client/progs.src
qcsrc/common/constants.qh
qcsrc/common/mutators/all.inc [new file with mode: 0644]
qcsrc/common/mutators/all.qc [new file with mode: 0644]
qcsrc/common/mutators/events.qh
qcsrc/common/mutators/mutator/damagetext.qc [new file with mode: 0644]
qcsrc/common/util-pre.qh
qcsrc/server/cl_player.qc
qcsrc/server/mutators/events.qh
qcsrc/server/progs.src

index 12157531dc038828838d2436faf96fcf0b14a585..a585a136e5b8d29ab8b9c9e77b9b126649dbedf2 100644 (file)
@@ -29,6 +29,8 @@
 #include "../common/vehicles/cl_vehicles.qh"
 #include "../common/vehicles/vehicles.qh"
 
+#include "mutators/events.qh"
+
 #include "weapons/projectile.qh"
 
 #include "../common/buffs.qh"
@@ -1259,69 +1261,55 @@ void Net_WeaponComplain()
 // Return value should be 1 if CSQC handled the temporary entity, otherwise return 0 to have the engine process the event.
 float CSQC_Parse_TempEntity()
 {
-       float bHandled;
-               bHandled  = true;
        // Acquire TE ID
-       float nTEID;
-               nTEID = ReadByte();
+       int nTEID = ReadByte();
 
-       if(autocvar_developer_csqcentities)
+       if (autocvar_developer_csqcentities)
                printf("CSQC_Parse_TempEntity() with nTEID=%d\n", nTEID);
 
-               // NOTE: Could just do return instead of break...
-       switch(nTEID)
+       switch (nTEID)
        {
+               case TE_CSQC_MUTATOR:
+                       int mutID = ReadMutator();
+                       if (MUTATOR_CALLHOOK(CSQC_Parse_TempEntity, mutID))
+                       return true;
                case TE_CSQC_TARGET_MUSIC:
                        Net_TargetMusic();
-                       bHandled = true;
-                       break;
+                       return true;
                case TE_CSQC_PICTURE:
                        Net_MapVote_Picture();
-                       bHandled = true;
-                       break;
+                       return true;
                case TE_CSQC_RACE:
                        Net_ReadRace();
-                       bHandled = true;
-                       break;
+                       return true;
                case TE_CSQC_VORTEXBEAMPARTICLE:
                        Net_ReadVortexBeamParticle();
-                       bHandled = true;
-                       break;
+                       return true;
                case TE_CSQC_TEAMNAGGER:
                        Net_TeamNagger();
-                       bHandled = true;
-                       break;
+                       return true;
                case TE_CSQC_ARC:
                        Net_ReadArc();
-                       bHandled = true;
-                       break;
+                       return true;
                case TE_CSQC_PINGPLREPORT:
                        Net_ReadPingPLReport();
-                       bHandled = true;
-                       break;
+                       return true;
                case TE_CSQC_WEAPONCOMPLAIN:
                        Net_WeaponComplain();
-                       bHandled = true;
-                       break;
+                       return true;
                case TE_CSQC_VEHICLESETUP:
                        Net_VehicleSetup();
-                       bHandled = true;
-                       break;
+                       return true;
                case TE_CSQC_SVNOTICE:
                        cl_notice_read();
-                       bHandled = true;
-                       break;
+                       return true;
                case TE_CSQC_SHOCKWAVEPARTICLE:
                        Net_ReadShockwaveParticle();
-                       bHandled = true;
-                       break;
+                       return true;
                default:
                        // No special logic for this temporary entity; return 0 so the engine can handle it
-                       bHandled = false;
-                       break;
+                       return false;
        }
-
-       return bHandled;
 }
 
 string getcommandkey(string text, string command)
index 41177cb66a321193f5eebf4ab4660b92a2be4f14..8f317c8e8a6f042476a887fbeb1869bc2b7841a7 100644 (file)
@@ -38,4 +38,15 @@ MUTATOR_HOOKABLE(CSQC_ConsoleCommand, EV_CSQC_ConsoleCommand);
 /* Called when the crosshair is being updated */
 MUTATOR_HOOKABLE(UpdateCrosshair, EV_NO_ARGS);
 
+/**
+ * Called when a temp entity is parsed
+ * NOTE: hooks MUST start with `if (MUTATOR_RETURNVALUE) return false;`
+ * NOTE: hooks MUST start with `if (!ReadMutatorEquals(mutator_argv_int_0, name_of_mutator)) return false;`
+ * NOTE: return true if you handled the command, return false to continue handling
+ */
+#define EV_CSQC_Parse_TempEntity(i, o) \
+    /** entity id */ i(int, mutator_argv_int_0) \
+    /**/
+MUTATOR_HOOKABLE(CSQC_Parse_TempEntity, EV_CSQC_Parse_TempEntity);
+
 #endif
index ea61aed0d4e903f3fd3e58a06ccb4eb2cdb7f487..43c056aa3a394132a400d8ece9468a31b64be37d 100644 (file)
@@ -60,6 +60,8 @@ weapons/projectile.qc // TODO
 
 ../common/monsters/all.qc
 
+../common/mutators/all.qc
+
 ../common/weapons/all.qc // TODO
 
 ../common/triggers/include.qc
index 1953b1f33d0c66a03341515ab044b54bca26c69c..1498e465eb4b8ca70f58302030c5a196d6556670 100644 (file)
@@ -31,6 +31,14 @@ const int AS_INT = 2;
 const int AS_FLOAT_TRUNCATED = 2;
 const int AS_FLOAT = 8;
 
+const int TE_CSQC_MUTATOR = 99;
+#define MUTATOR_HASH(s) crc16(true, s)
+#define WriteMutator(to, s) do { \
+    WriteByte(to, TE_CSQC_MUTATOR); \
+    WriteLong(to, MUTATOR_HASH(#s)); \
+} while (0)
+#define ReadMutator() ReadLong()
+#define ReadMutatorEquals(read, s) (read == MUTATOR_HASH(#s))
 const int TE_CSQC_PICTURE = 100;
 const int TE_CSQC_RACE = 101;
 const int TE_CSQC_VORTEXBEAMPARTICLE = 103;
diff --git a/qcsrc/common/mutators/all.inc b/qcsrc/common/mutators/all.inc
new file mode 100644 (file)
index 0000000..4cb1a6c
--- /dev/null
@@ -0,0 +1 @@
+#include "mutator/damagetext.qc"
diff --git a/qcsrc/common/mutators/all.qc b/qcsrc/common/mutators/all.qc
new file mode 100644 (file)
index 0000000..14530a3
--- /dev/null
@@ -0,0 +1 @@
+#include "all.inc"
index e193d5f8a0e125eacdf657da5d0869e76dc1b812..eb46486b6b8e6ca4a00ed7f4fedcd6fee7990c37 100644 (file)
@@ -3,8 +3,28 @@
 
 #define EV_NO_ARGS(i, o)
 
+#pragma noref 1
 string ret_string;
 
+#define MUTATOR_TYPES(_, x) \
+    _(x, bool) \
+    _(x, int) \
+    _(x, entity) \
+    _(x, float) \
+    _(x, vector) \
+    _(x, string) \
+    /**/
+
+#define MUTATOR_NEWGLOBAL(x, type) type mutator_argv_##type##_##x;
+
+MUTATOR_TYPES(MUTATOR_NEWGLOBAL, 0)
+MUTATOR_TYPES(MUTATOR_NEWGLOBAL, 1)
+
+#undef MUTATOR_TYPES
+#undef MUTATOR_NEWGLOBAL
+
+#pragma noref 0
+
 /** appends ":mutatorname" to ret_string for logging */
 #define EV_BuildMutatorsString(i, o) \
     /**/ i(string, ret_string) \
diff --git a/qcsrc/common/mutators/mutator/damagetext.qc b/qcsrc/common/mutators/mutator/damagetext.qc
new file mode 100644 (file)
index 0000000..aacfc73
--- /dev/null
@@ -0,0 +1,125 @@
+REGISTER_MUTATOR(damagetext, true);
+
+#ifdef CSQC
+bool autocvar_cl_damagetext = false;
+string autocvar_cl_damagetext_format = "-%3$d";
+vector autocvar_cl_damagetext_color = '1 1 0';
+float autocvar_cl_damagetext_size = 8;
+float autocvar_cl_damagetext_alpha_start = 1;
+float autocvar_cl_damagetext_alpha_lifetime = 3;
+vector autocvar_cl_damagetext_velocity = '0 0 20';
+vector autocvar_cl_damagetext_offset = '0 -40 0';
+float autocvar_cl_damagetext_accumulate_range = 30;
+STATIC_INIT(cl_damagetext) {
+    CVAR_DESCRIBE(cl_damagetext, _("Draw damage dealt. 0: disabled, 1: enabled"));
+    CVAR_DESCRIBESTR(cl_damagetext_format, _("How to format the damage text. 1$ is health, 2$ is armor, 3$ is both"));
+    CVAR_DESCRIBEVEC(cl_damagetext_color, _("Default damage text color"));
+    CVAR_DESCRIBE(cl_damagetext_size, _("Damage text font size"));
+    CVAR_DESCRIBE(cl_damagetext_alpha_start, _("Damage text initial alpha"));
+    CVAR_DESCRIBE(cl_damagetext_alpha_lifetime, _("Damage text lifetime in seconds"));
+    CVAR_DESCRIBEVEC(cl_damagetext_velocity, _("Damage text move direction"));
+    CVAR_DESCRIBEVEC(cl_damagetext_offset, _("Damage text offset"));
+    CVAR_DESCRIBE(cl_damagetext_accumulate_range, _("Damage text spawned within this range is accumulated"));
+}
+
+CLASS(DamageText, Object)
+    ATTRIB(DamageText, m_color, vector, autocvar_cl_damagetext_color)
+    ATTRIB(DamageText, m_size, float, autocvar_cl_damagetext_size)
+    ATTRIB(DamageText, alpha, float, autocvar_cl_damagetext_alpha_start)
+    ATTRIB(DamageText, fade_rate, float, 1 / autocvar_cl_damagetext_alpha_lifetime)
+    ATTRIB(DamageText, velocity, vector, autocvar_cl_damagetext_velocity)
+    ATTRIB(DamageText, m_group, int, 0)
+    ATTRIB(DamageText, m_damage, int, 0)
+    ATTRIB(DamageText, m_armordamage, int, 0)
+    ATTRIB(DamageText, time_prev, float, time)
+
+    void DamageText_draw() {
+        entity this = self;
+        float dt = time - this.time_prev;
+        this.time_prev = time;
+        setorigin(this, this.origin + dt * this.velocity);
+        this.alpha -= dt * this.fade_rate;
+        if (this.alpha < 0) remove(this);
+        vector pos = project_3d_to_2d(this.origin) + autocvar_cl_damagetext_offset;
+        if (pos.z >= 0 && this.m_size > 0) {
+            pos.z = 0;
+            string s = sprintf(autocvar_cl_damagetext_format, this.m_damage, this.m_armordamage, this.m_damage + this.m_armordamage);
+            drawcolorcodedstring2(pos, s, this.m_size * '1 1 0', this.m_color, this.alpha, DRAWFLAG_NORMAL);
+        }
+    }
+    ATTRIB(DamageText, draw2d, void(), DamageText_draw)
+
+    void DamageText_update(DamageText this, vector _origin, int _health, int _armor) {
+        this.m_damage = _health;
+        this.m_armordamage = _armor;
+        setorigin(this, _origin);
+        this.alpha = 1;
+    }
+
+    CONSTRUCTOR(DamageText, int _group, vector _origin, int _health, int _armor) {
+        CONSTRUCT(DamageText);
+        this.m_group = _group;
+        DamageText_update(this, _origin, _health, _armor);
+        return this;
+    }
+ENDCLASS(DamageText)
+#endif
+
+#ifdef SVQC
+int autocvar_sv_damagetext = 2;
+STATIC_INIT(sv_damagetext) {
+    CVAR_DESCRIBE(sv_damagetext, _("<= 0: disabled, >= 1: spectators, >= 2: players, >= 3: all players"));
+}
+#define SV_DAMAGETEXT_DISABLED()        (autocvar_sv_damagetext <= 0 /* disabled */)
+#define SV_DAMAGETEXT_SPECTATORS_ONLY() (autocvar_sv_damagetext >= 1 /* spectators only */)
+#define SV_DAMAGETEXT_PLAYERS()         (autocvar_sv_damagetext >= 2 /* players */)
+#define SV_DAMAGETEXT_ALL()             (autocvar_sv_damagetext >= 3 /* all players */)
+MUTATOR_HOOKFUNCTION(damagetext, PlayerDamaged) {
+    if (SV_DAMAGETEXT_DISABLED()) return;
+    const entity attacker = mutator_argv_entity_0;
+    const entity hit = mutator_argv_entity_1; if (hit == attacker) return;
+    const int health = mutator_argv_int_0;
+    const int armor = mutator_argv_int_1;
+    const vector location = hit.origin;
+    entity e;
+    FOR_EACH_REALCLIENT(e) if (
+        (SV_DAMAGETEXT_ALL()) ||
+        (SV_DAMAGETEXT_PLAYERS() && e == attacker) ||
+        (SV_DAMAGETEXT_SPECTATORS_ONLY() && IS_SPEC(e) && e.enemy == attacker) ||
+        (SV_DAMAGETEXT_SPECTATORS_ONLY() && IS_OBSERVER(e))
+    ) {
+        msg_entity = e;
+        WriteByte(MSG_ONE, SVC_TEMPENTITY);
+        WriteMutator(MSG_ONE, damagetext);
+        WriteShort(MSG_ONE, health);
+        WriteShort(MSG_ONE, armor);
+        WriteEntity(MSG_ONE, hit);
+        WriteCoord(MSG_ONE, location.x);
+        WriteCoord(MSG_ONE, location.y);
+        WriteCoord(MSG_ONE, location.z);
+    }
+}
+#endif
+
+#ifdef CSQC
+MUTATOR_HOOKFUNCTION(damagetext, CSQC_Parse_TempEntity) {
+    if (MUTATOR_RETURNVALUE) return false;
+    if (!ReadMutatorEquals(mutator_argv_int_0, damagetext)) return false;
+    int health = ReadShort();
+    int armor = ReadShort();
+    int group = ReadShort();
+    vector location = vec3(ReadCoord(), ReadCoord(), ReadCoord());
+    if (autocvar_cl_damagetext) {
+        if (autocvar_cl_damagetext_accumulate_range) {
+            for (entity e = findradius(location, autocvar_cl_damagetext_accumulate_range); e; e = e.chain) {
+                if (e.instanceOfDamageText && e.m_group == group) {
+                    DamageText_update(e, location, e.m_damage + health, e.m_armordamage + armor);
+                    return true;
+                }
+            }
+        }
+        NEW(DamageText, group, location, health, armor);
+    }
+    return true;
+}
+#endif
index b96e78d514f6832d6c39cfae274e678b641761e9..fc916d4dba37a17b182ee007f5ff9e21927a078a 100644 (file)
     #define BITSET(var, mask, flag) ((var) ^ (-(flag) ^ (var)) & (mask))
 #endif
 
+#define CVAR_DESCRIBE(var, desc)    localcmd(sprintf("\nset %s \"%s\" \"%s\"\n",        #var, ftos(autocvar_##var), desc))
+#define CVAR_DESCRIBESTR(var, desc) localcmd(sprintf("\nset %s \"%s\" \"%s\"\n",        #var, autocvar_##var, desc))
+#define CVAR_DESCRIBEVEC(var, desc) localcmd(sprintf("\nset %s \"%s %s %s\" \"%s\"\n",  #var, ftos(autocvar_##var##.x), ftos(autocvar_##var##.y), ftos(autocvar_##var##.z), desc))
+
 #endif
index 9bc36c8df8098fe7c37ca22ae4977794bcd46b40..e3bb91e3954bfe112009a2d0a28d2549797993f0 100644 (file)
@@ -492,6 +492,7 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, int deathtyp
                dh = dh - max(self.health, 0);
                da = da - max(self.armorvalue, 0);
                WeaponStats_LogDamage(awep, abot, self.weapon, vbot, dh + da);
+               MUTATOR_CALLHOOK(PlayerDamaged, attacker, self, dh, da, hitloc);
        }
 
        if (self.health < 1)
index 88c58f3094db0b925e93e1c0b10c6217fb5d47f3..ec9e9c99a90dc5dd52c9f1fae1b2e976259f583f 100644 (file)
@@ -260,6 +260,18 @@ float frag_mirrordamage;
 vector frag_force;
 MUTATOR_HOOKABLE(PlayerDamage_Calculate, EV_PlayerDamage_Calculate);
 
+/**
+ * Called when a player is damaged
+ */
+#define EV_PlayerDamaged(i, o) \
+    /** attacker */ i(entity, mutator_argv_entity_0) \
+    /** target */ i(entity, mutator_argv_entity_1) \
+    /** health */ i(int, mutator_argv_int_0) \
+    /** armor */ i(int, mutator_argv_int_1) \
+    /** location */ i(vector, mutator_argv_vector_0) \
+    /**/
+MUTATOR_HOOKABLE(PlayerDamaged, EV_PlayerDamaged);
+
 /** called at the end of player_powerups() in cl_client.qc, used for manipulating the values which are set by powerup items. */
 #define EV_PlayerPowerups(i, o) \
     /**/ i(entity, self) \
index 2ed1e03d604faf161cb5324f5a0a2b3d61fdd034..bb6288101ce01e94791654b75636ed6bf2072df7 100644 (file)
@@ -88,6 +88,7 @@ weapons/weaponsystem.qc
 ../common/effects.qc
 ../common/mapinfo.qc
 ../common/monsters/all.qc
+../common/mutators/all.qc
 ../common/monsters/spawn.qc
 ../common/monsters/sv_monsters.qc
 ../common/movetypes/include.qc