]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/common/mutators/mutator/buffs/buffs.qc
Propagate this
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / mutators / mutator / buffs / buffs.qc
index 1c9e3a2177b9f3c07f5822392e0232d4d88ba6e7..15b725ee4b18d2bcd934728a6e0b2c2c8b233496 100644 (file)
@@ -19,6 +19,9 @@ float autocvar_g_buffs_medic_survive_health;
 float autocvar_g_buffs_medic_rot;
 float autocvar_g_buffs_medic_max;
 float autocvar_g_buffs_medic_regen;
+float autocvar_g_buffs_medic_heal_amount = 15;
+float autocvar_g_buffs_medic_heal_delay = 1;
+float autocvar_g_buffs_medic_heal_range = 400;
 float autocvar_g_buffs_vengeance_damage_multiplier;
 float autocvar_g_buffs_bash_force;
 float autocvar_g_buffs_bash_force_self;
@@ -33,7 +36,6 @@ float autocvar_g_buffs_speed_damage_take;
 float autocvar_g_buffs_speed_regen;
 float autocvar_g_buffs_vampire_damage_steal;
 float autocvar_g_buffs_invisible_alpha;
-float autocvar_g_buffs_flight_gravity;
 float autocvar_g_buffs_jump_height;
 float autocvar_g_buffs_inferno_burntime_factor;
 float autocvar_g_buffs_inferno_burntime_min_time;
@@ -42,14 +44,17 @@ float autocvar_g_buffs_inferno_burntime_target_time;
 float autocvar_g_buffs_inferno_damagemultiplier;
 float autocvar_g_buffs_swapper_range;
 float autocvar_g_buffs_magnet_range_item;
+float autocvar_g_buffs_magnet_range_buff = 200;
+float autocvar_g_buffs_luck_chance = 0.15;
+float autocvar_g_buffs_luck_damagemultiplier = 3;
 
 // ammo
 .float buff_ammo_prev_infitems;
 .int buff_ammo_prev_clipload;
 // invisible
 .float buff_invisible_prev_alpha;
-// flight
-.float buff_flight_prev_gravity;
+// medic
+.float buff_medic_healtime;
 // disability
 .float buff_disability_time;
 .float buff_disability_effect_time;
@@ -73,11 +78,11 @@ const vector BUFF_MAX = ('16 16 20');
 
 #ifdef IMPLEMENTATION
 
-#include "../../../triggers/target/music.qh"
-#include "../../../gamemodes/all.qh"
+#include <common/triggers/target/music.qh>
+#include <common/gamemodes/all.qh>
 
 .float buff_time = _STAT(BUFF_TIME);
-void buffs_DelayedInit();
+void buffs_DelayedInit(entity this);
 
 REGISTER_MUTATOR(buffs, cvar("g_buffs"))
 {
@@ -87,15 +92,6 @@ REGISTER_MUTATOR(buffs, cvar("g_buffs"))
        }
 }
 
-entity buff_FirstFromFlags(int _buffs)
-{
-       if (flags)
-       {
-               FOREACH(Buffs, it.m_itemid & _buffs, LAMBDA(return it));
-       }
-       return BUFF_Null;
-}
-
 bool buffs_BuffModel_Customize()
 {SELFPARAM();
        entity player, myowner;
@@ -191,37 +187,39 @@ void buff_SetCooldown(float cd)
        self.buff_active = !cd;
 }
 
-void buff_Respawn(entity ent)
-{SELFPARAM();
+void buff_Respawn(entity this)
+{
        if(gameover) { return; }
 
-       vector oldbufforigin = ent.origin;
+       vector oldbufforigin = this.origin;
+       this.velocity = '0 0 200';
 
-       if(!MoveToRandomMapLocation(ent, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, ((autocvar_g_buffs_random_location_attempts > 0) ? autocvar_g_buffs_random_location_attempts : 10), 1024, 256))
+       if(!MoveToRandomMapLocation(this, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY,
+               ((autocvar_g_buffs_random_location_attempts > 0) ? autocvar_g_buffs_random_location_attempts : 10), 1024, 256))
        {
                entity spot = SelectSpawnPoint(true);
-               setorigin(ent, spot.origin + '0 0 200');
-               ent.angles = spot.angles;
+               setorigin(this, spot.origin);
+               this.velocity = ((randomvec() * 100) + '0 0 200');
+               this.angles = spot.angles;
        }
 
-       tracebox(ent.origin, ent.mins * 1.5, self.maxs * 1.5, ent.origin, MOVE_NOMONSTERS, ent);
+       tracebox(this.origin, this.mins * 1.5, this.maxs * 1.5, this.origin, MOVE_NOMONSTERS, this);
 
-       setorigin(ent, trace_endpos); // attempt to unstick
+       setorigin(this, trace_endpos); // attempt to unstick
 
-       ent.movetype = MOVETYPE_TOSS;
+       this.movetype = MOVETYPE_TOSS;
 
-       makevectors(ent.angles);
-       ent.velocity = '0 0 200';
-       ent.angles = '0 0 0';
+       makevectors(this.angles);
+       this.angles = '0 0 0';
        if(autocvar_g_buffs_random_lifetime > 0)
-               ent.lifetime = time + autocvar_g_buffs_random_lifetime;
+               this.lifetime = time + autocvar_g_buffs_random_lifetime;
 
-       Send_Effect(EFFECT_ELECTRO_COMBO, oldbufforigin + ((ent.mins + ent.maxs) * 0.5), '0 0 0', 1);
-       Send_Effect(EFFECT_ELECTRO_COMBO, CENTER_OR_VIEWOFS(ent), '0 0 0', 1);
+       Send_Effect(EFFECT_ELECTRO_COMBO, oldbufforigin + ((this.mins + this.maxs) * 0.5), '0 0 0', 1);
+       Send_Effect(EFFECT_ELECTRO_COMBO, CENTER_OR_VIEWOFS(this), '0 0 0', 1);
 
-       WaypointSprite_Ping(ent.buff_waypoint);
+       WaypointSprite_Ping(this.buff_waypoint);
 
-       sound(ent, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere)
+       sound(this, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere)
 }
 
 void buff_Touch()
@@ -235,7 +233,7 @@ void buff_Touch()
        }
 
        if((self.team && DIFF_TEAM(other, self))
-       || (other.frozen)
+       || (STAT(FROZEN, other))
        || (other.vehicle)
        || (!self.buff_active)
        )
@@ -332,7 +330,7 @@ void buff_Think()
        }
 
        if(!self.buff_active && !self.buff_activetime)
-       if(!self.owner || self.owner.frozen || self.owner.deadflag != DEAD_NO || !self.owner.iscreature || !(self.owner.buffs & self.buffs))
+       if(!self.owner || STAT(FROZEN, self.owner) || IS_DEAD(self.owner) || !self.owner.iscreature || !(self.owner.buffs & self.buffs))
        {
                buff_SetCooldown(autocvar_g_buffs_cooldown_respawn + frametime);
                self.owner = world;
@@ -488,6 +486,20 @@ void buff_Vengeance_DelayedDamage()
        return;
 }
 
+// note: only really useful in teamplay
+void buff_Medic_Heal(entity this)
+{
+       FOREACH_CLIENT(IS_PLAYER(it) && it != this && vdist(it.origin - this.origin, <=, autocvar_g_buffs_medic_heal_range),
+       {
+               if(SAME_TEAM(it, this))
+               if(it.health < autocvar_g_balance_health_regenstable)
+               {
+                       Send_Effect(EFFECT_HEALING, it.origin, '0 0 0', 1);
+                       it.health = bound(0, it.health + autocvar_g_buffs_medic_heal_amount, autocvar_g_balance_health_regenstable);
+               }
+       });
+}
+
 float buff_Inferno_CalculateTime(float x, float offset_x, float offset_y, float intersect_x, float intersect_y, float base)
 {
        return offset_y + (intersect_y - offset_y) * logn(((x - offset_x) * ((base - 1) / intersect_x)) + 1, base);
@@ -543,11 +555,10 @@ MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_Calculate)
 
        if(frag_target.buffs & BUFF_BASH.m_itemid)
        if(frag_attacker != frag_target)
-       if(vlen(frag_force))
                frag_force = '0 0 0';
 
        if(frag_attacker.buffs & BUFF_BASH.m_itemid)
-       if(vlen(frag_force))
+       if(frag_force)
        if(frag_attacker == frag_target)
                frag_force *= autocvar_g_buffs_bash_force_self;
        else
@@ -557,18 +568,23 @@ MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_Calculate)
        if(frag_target != frag_attacker)
                frag_target.buff_disability_time = time + autocvar_g_buffs_disability_slowtime;
 
-       if(frag_attacker.buffs & BUFF_MEDIC.m_itemid)
-       if(DEATH_WEAPONOF(frag_deathtype) != WEP_ARC)
-       if(SAME_TEAM(frag_attacker, frag_target))
-       if(frag_attacker != frag_target)
+       if(frag_target.buffs & BUFF_INFERNO.m_itemid)
        {
-               frag_target.health = min(g_pickup_healthmega_max, frag_target.health + frag_damage);
-               frag_damage = 0;
+               if(frag_deathtype == DEATH_FIRE.m_id)
+                       frag_damage = 0;
+               if(frag_deathtype == DEATH_LAVA.m_id)
+                       frag_damage *= 0.5; // TODO: cvarize?
        }
 
+       if(frag_attacker.buffs & BUFF_LUCK.m_itemid)
+       if(frag_attacker != frag_target)
+       if(autocvar_g_buffs_luck_damagemultiplier > 0)
+       if(random() <= autocvar_g_buffs_luck_chance)
+               frag_damage *= autocvar_g_buffs_luck_damagemultiplier;
+
        if(frag_attacker.buffs & BUFF_INFERNO.m_itemid)
        if(frag_target != frag_attacker) {
-               float time = buff_Inferno_CalculateTime(
+               float btime = buff_Inferno_CalculateTime(
                        frag_damage,
                        0,
                        autocvar_g_buffs_inferno_burntime_min_time,
@@ -576,18 +592,17 @@ MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_Calculate)
                        autocvar_g_buffs_inferno_burntime_target_time,
                        autocvar_g_buffs_inferno_burntime_factor
                );
-               Fire_AddDamage(frag_target, frag_attacker, (frag_damage * autocvar_g_buffs_inferno_damagemultiplier) * time, time, DEATH_BUFF.m_id);
+               Fire_AddDamage(frag_target, frag_attacker, (frag_damage * autocvar_g_buffs_inferno_damagemultiplier), btime, DEATH_BUFF.m_id);
        }
 
        // this... is ridiculous (TODO: fix!)
        if(frag_attacker.buffs & BUFF_VAMPIRE.m_itemid)
        if(!frag_target.vehicle)
-       if(DEATH_WEAPONOF(frag_deathtype) != WEP_ARC)
        if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype))
-       if(frag_target.deadflag == DEAD_NO)
+       if(!IS_DEAD(frag_target))
        if(IS_PLAYER(frag_target) || IS_MONSTER(frag_target))
        if(frag_attacker != frag_target)
-       if(!frag_target.frozen)
+       if(!STAT(FROZEN, frag_target))
        if(frag_target.takedamage)
        if(DIFF_TEAM(frag_attacker, frag_target))
        {
@@ -655,17 +670,17 @@ MUTATOR_HOOKFUNCTION(buffs, MonsterMove)
 }
 
 MUTATOR_HOOKFUNCTION(buffs, PlayerDies)
-{SELFPARAM();
-       if(self.buffs)
+{
+       if(frag_target.buffs)
        {
-               int buffid = buff_FirstFromFlags(self.buffs).m_id;
-               Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, buffid);
-               self.buffs = 0;
+               int buffid = buff_FirstFromFlags(frag_target.buffs).m_id;
+               Send_Notification(NOTIF_ALL_EXCEPT, frag_target, MSG_INFO, INFO_ITEM_BUFF_LOST, frag_target.netname, buffid);
+               frag_target.buffs = 0;
 
-               if(self.buff_model)
+               if(frag_target.buff_model)
                {
-                       remove(self.buff_model);
-                       self.buff_model = world;
+                       remove(frag_target.buff_model);
+                       frag_target.buff_model = world;
                }
        }
        return false;
@@ -695,15 +710,18 @@ MUTATOR_HOOKFUNCTION(buffs, ForbidThrowCurrentWeapon)
        {
                float best_distance = autocvar_g_buffs_swapper_range;
                entity closest = world;
-               entity player;
-               FOR_EACH_PLAYER(player)
-               if(DIFF_TEAM(self, player))
-               if(player.deadflag == DEAD_NO && !player.frozen && !player.vehicle)
-               if(vlen(self.origin - player.origin) <= best_distance)
-               {
-                       best_distance = vlen(self.origin - player.origin);
-                       closest = player;
-               }
+               FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(
+                       if(!IS_DEAD(it) && !STAT(FROZEN, it) && !it.vehicle)
+                       if(DIFF_TEAM(it, self))
+                       {
+                               float test = vlen2(self.origin - it.origin);
+                               if(test <= best_distance * best_distance)
+                               {
+                                       best_distance = sqrt(test);
+                                       closest = it;
+                               }
+                       }
+               ));
 
                if(closest)
                {
@@ -737,7 +755,7 @@ MUTATOR_HOOKFUNCTION(buffs, ForbidThrowCurrentWeapon)
                        // set pusher so self gets the kill if they fall into void
                        closest.pusher = self;
                        closest.pushltime = time + autocvar_g_maxpushtime;
-                       closest.istypefrag = closest.BUTTON_CHAT;
+                       closest.istypefrag = PHYS_INPUT_BUTTON_CHAT(closest);
 
                        Send_Effect(EFFECT_ELECTRO_COMBO, their_org, '0 0 0', 1);
                        Send_Effect(EFFECT_ELECTRO_COMBO, my_org, '0 0 0', 1);
@@ -767,8 +785,8 @@ bool buffs_RemovePlayer(entity player)
 
        return false;
 }
-MUTATOR_HOOKFUNCTION(buffs, MakePlayerObserver) { return buffs_RemovePlayer(self); }
-MUTATOR_HOOKFUNCTION(buffs, ClientDisconnect) { return buffs_RemovePlayer(self); }
+MUTATOR_HOOKFUNCTION(buffs, MakePlayerObserver) { SELFPARAM(); return buffs_RemovePlayer(self); }
+MUTATOR_HOOKFUNCTION(buffs, ClientDisconnect) { SELFPARAM(); return buffs_RemovePlayer(self); }
 
 MUTATOR_HOOKFUNCTION(buffs, CustomizeWaypoint)
 {SELFPARAM();
@@ -785,11 +803,6 @@ MUTATOR_HOOKFUNCTION(buffs, CustomizeWaypoint)
 
 MUTATOR_HOOKFUNCTION(buffs, OnEntityPreSpawn, CBC_ORDER_LAST)
 {SELFPARAM();
-       if (self.classname == "item_flight" && cvar("g_buffs") && cvar("g_buffs_flight"))
-       {
-               buff_Init_Compat(self, BUFF_FLIGHT);
-               return true;
-       }
        if(autocvar_g_buffs_replace_powerups)
        switch(self.classname)
        {
@@ -828,7 +841,7 @@ MUTATOR_HOOKFUNCTION(buffs, WeaponSpeedFactor)
 
 MUTATOR_HOOKFUNCTION(buffs, PlayerPreThink)
 {SELFPARAM();
-       if(gameover || self.deadflag != DEAD_NO) { return false; }
+       if(gameover || IS_DEAD(self)) { return false; }
 
        if(time < self.buff_disability_time)
        if(time >= self.buff_disability_effect_time)
@@ -846,7 +859,7 @@ MUTATOR_HOOKFUNCTION(buffs, PlayerPreThink)
        if(time >= self.buff_time)
                buff_lost = 2;
 
-       if(self.frozen) { buff_lost = 1; }
+       if(STAT(FROZEN, self)) { buff_lost = 1; }
 
        if(buff_lost)
        {
@@ -865,27 +878,43 @@ MUTATOR_HOOKFUNCTION(buffs, PlayerPreThink)
 
        if(self.buffs & BUFF_MAGNET.m_itemid)
        {
-               vector pickup_size = '1 1 1' * autocvar_g_buffs_magnet_range_item;
-               for(other = world; (other = findflags(other, flags, FL_ITEM)); )
-               if(boxesoverlap(self.absmin - pickup_size, self.absmax + pickup_size, other.absmin, other.absmax))
+               vector pickup_size;
+               FOREACH_ENTITY_FLAGS(flags, FL_ITEM,
                {
-                       setself(other);
-                       other = this;
-                       if(self.touch)
-                               self.touch();
-                       other = self;
-                       setself(this);
-               }
+                       if(it.buffs)
+                               pickup_size = '1 1 1' * autocvar_g_buffs_magnet_range_buff;
+                       else
+                               pickup_size = '1 1 1' * autocvar_g_buffs_magnet_range_item;
+
+                       if(boxesoverlap(self.absmin - pickup_size, self.absmax + pickup_size, it.absmin, it.absmax))
+                       {
+                               if(it.touch)
+                               {
+                                       entity oldother = other;
+                                       other = self;
+                                       WITHSELF(it, it.touch());
+
+                                       other = oldother;
+                               }
+                       }
+               });
        }
 
        if(self.buffs & BUFF_AMMO.m_itemid)
        if(self.clip_size)
-               self.clip_load = self.(weapon_load[self.switchweapon]) = self.clip_size;
+               self.clip_load = self.(weapon_load[PS(self).m_switchweapon.m_id]) = self.clip_size;
 
        if((self.buffs & BUFF_INVISIBLE.m_itemid) && (self.oldbuffs & BUFF_INVISIBLE.m_itemid))
        if(self.alpha != autocvar_g_buffs_invisible_alpha)
                self.alpha = autocvar_g_buffs_invisible_alpha; // powerups reset alpha, so we must enforce this (TODO)
 
+       if(self.buffs & BUFF_MEDIC.m_itemid)
+       if(time >= self.buff_medic_healtime)
+       {
+               buff_Medic_Heal(self);
+               self.buff_medic_healtime = time + autocvar_g_buffs_medic_heal_delay;
+       }
+
 #define BUFF_ONADD(b) if ( (self.buffs & (b).m_itemid) && !(self.oldbuffs & (b).m_itemid))
 #define BUFF_ONREM(b) if (!(self.buffs & (b).m_itemid) &&  (self.oldbuffs & (b).m_itemid))
 
@@ -902,7 +931,7 @@ MUTATOR_HOOKFUNCTION(buffs, PlayerPreThink)
 
                        if(self.clip_load)
                                self.buff_ammo_prev_clipload = self.clip_load;
-                       self.clip_load = self.(weapon_load[self.switchweapon]) = self.clip_size;
+                       self.clip_load = self.(weapon_load[PS(self).m_switchweapon.m_id]) = self.clip_size;
                }
 
                BUFF_ONREM(BUFF_AMMO)
@@ -928,15 +957,6 @@ MUTATOR_HOOKFUNCTION(buffs, PlayerPreThink)
                BUFF_ONREM(BUFF_INVISIBLE)
                        self.alpha = self.buff_invisible_prev_alpha;
 
-               BUFF_ONADD(BUFF_FLIGHT)
-               {
-                       self.buff_flight_prev_gravity = self.gravity;
-                       self.gravity = autocvar_g_buffs_flight_gravity;
-               }
-
-               BUFF_ONREM(BUFF_FLIGHT)
-                       self.gravity = self.buff_flight_prev_gravity;
-
                self.oldbuffs = self.buffs;
                if(self.buffs)
                {
@@ -982,7 +1002,7 @@ MUTATOR_HOOKFUNCTION(buffs, VehicleEnter)
 {
        vh_vehicle.buffs = vh_player.buffs;
        vh_player.buffs = 0;
-       vh_vehicle.buff_time = max(0, time - vh_player.buff_time);
+       vh_vehicle.buff_time = max(0, vh_player.buff_time - time);
        vh_player.buff_time = 0;
        return false;
 }
@@ -1011,11 +1031,7 @@ MUTATOR_HOOKFUNCTION(buffs, PlayerRegen)
        return false;
 }
 
-MUTATOR_HOOKFUNCTION(buffs, GetCvars)
-{
-       GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_buffs_autoreplace, "cl_buffs_autoreplace");
-       return false;
-}
+REPLICATE(cvar_cl_buffs_autoreplace, bool, "cl_buffs_autoreplace");
 
 MUTATOR_HOOKFUNCTION(buffs, BuildMutatorsString)
 {
@@ -1029,7 +1045,7 @@ MUTATOR_HOOKFUNCTION(buffs, BuildMutatorsPrettyString)
        return false;
 }
 
-void buffs_DelayedInit()
+void buffs_DelayedInit(entity this)
 {
        if(autocvar_g_buffs_spawn_count > 0)
        if(find(world, classname, "item_buff") == world)