]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'Mario/event_heal' into 'master'
authorMario <zacjardine@y7mail.com>
Mon, 18 Jun 2018 01:23:07 +0000 (01:23 +0000)
committerMario <zacjardine@y7mail.com>
Mon, 18 Jun 2018 01:23:07 +0000 (01:23 +0000)
Merge branch Mario/event_heal (M merge request)

See merge request xonotic/xonotic-data.pk3dir!569

18 files changed:
qcsrc/common/gamemodes/gamemode/assault/assault.qc
qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc
qcsrc/common/mapobjects/trigger/heal.qc
qcsrc/common/monsters/monster/mage.qc
qcsrc/common/monsters/sv_monsters.qc
qcsrc/common/mutators/mutator/vampirehook/sv_vampirehook.qc
qcsrc/common/turrets/cl_turrets.qc
qcsrc/common/turrets/sv_turrets.qc
qcsrc/common/vehicles/sv_vehicles.qc
qcsrc/common/vehicles/vehicle/bumblebee.qc
qcsrc/common/weapons/weapon/arc.qc
qcsrc/server/client.qc
qcsrc/server/defs.qh
qcsrc/server/g_damage.qc
qcsrc/server/g_damage.qh
qcsrc/server/player.qc
qcsrc/server/player.qh
qcsrc/server/resources.qc

index 97a7b4df28d0a77c03c3e0c04d489d7c244d5543..9a2d6af1d86f4efbfb300b774d22f0ed6b9dae06 100644 (file)
@@ -331,6 +331,21 @@ spawnfunc(target_objective_decrease)
 }
 
 // destructible walls that can be used to trigger target_objective_decrease
+bool destructible_heal(entity targ, entity inflictor, float amount, float limit)
+{
+       float true_limit = ((limit != RESOURCE_LIMIT_NONE) ? limit : targ.max_health);
+       if(GetResourceAmount(targ, RESOURCE_HEALTH) <= 0 || GetResourceAmount(targ, RESOURCE_HEALTH) >= true_limit)
+               return false;
+
+       GiveResourceWithLimit(targ, RESOURCE_HEALTH, amount, true_limit);
+       if(targ.sprite)
+       {
+               WaypointSprite_UpdateHealth(targ.sprite, GetResourceAmount(targ, RESOURCE_HEALTH));
+       }
+       func_breakable_colormod(targ);
+       return true;
+}
+
 spawnfunc(func_breakable);
 spawnfunc(func_assault_destructible)
 {
@@ -338,6 +353,7 @@ spawnfunc(func_assault_destructible)
 
        this.spawnflags = 3;
        this.classname = "func_assault_destructible";
+       this.event_heal = destructible_heal;
        IL_PUSH(g_assault_destructibles, this);
 
        if(assault_attacker_team == NUM_TEAM_1)
index 15aedc732d15c95283baf0c1f58cad87efc20022..fd050df0a0f541cc0137303fe86ad15ab26ba60b 100644 (file)
@@ -447,6 +447,21 @@ void ons_ControlPoint_Icon_Damage(entity this, entity inflictor, entity attacker
        this.SendFlags |= CPSF_STATUS;
 }
 
+bool ons_ControlPoint_Icon_Heal(entity targ, entity inflictor, float amount, float limit)
+{
+       float true_limit = ((limit != RESOURCE_LIMIT_NONE) ? limit : targ.max_health);
+       if(GetResourceAmount(targ, RESOURCE_HEALTH) <= 0 || GetResourceAmount(targ, RESOURCE_HEALTH) >= true_limit)
+               return false;
+
+       GiveResourceWithLimit(targ, RESOURCE_HEALTH, amount, true_limit);
+       if(targ.owner.iscaptured)
+               WaypointSprite_UpdateHealth(targ.owner.sprite, GetResourceAmount(targ, RESOURCE_HEALTH));
+       else
+               WaypointSprite_UpdateBuildFinished(targ.owner.sprite, time + (targ.max_health - GetResourceAmount(targ, RESOURCE_HEALTH)) / (targ.count / ONS_CP_THINKRATE));
+       targ.SendFlags |= CPSF_STATUS;
+       return true;
+}
+
 void ons_ControlPoint_Icon_Think(entity this)
 {
        this.nextthink = time + ONS_CP_THINKRATE;
@@ -584,6 +599,7 @@ void ons_ControlPoint_Icon_Spawn(entity cp, entity player)
        e.bot_attack = true;
        IL_PUSH(g_bot_targets, e);
        e.event_damage = ons_ControlPoint_Icon_Damage;
+       e.event_heal = ons_ControlPoint_Icon_Heal;
        e.team = player.team;
        e.colormap = 1024 + (e.team - 1) * 17;
        e.count = (e.max_health - GetResourceAmount(e, RESOURCE_HEALTH)) * ONS_CP_THINKRATE / autocvar_g_onslaught_cp_buildtime; // how long it takes to build
@@ -910,6 +926,7 @@ void ons_GeneratorDamage(entity this, entity inflictor, entity attacker, float d
                this.isshielded = false;
                this.takedamage = DAMAGE_NO; // can't be hurt anymore
                this.event_damage = func_null; // won't do anything if hurt
+               this.event_heal = func_null;
                this.count = 0; // reset counter
                setthink(this, func_null);
                this.nextthink = 0;
@@ -944,6 +961,20 @@ void ons_GeneratorDamage(entity this, entity inflictor, entity attacker, float d
        this.SendFlags |= GSF_STATUS;
 }
 
+bool ons_GeneratorHeal(entity targ, entity inflictor, float amount, float limit)
+{
+       float true_limit = ((limit != RESOURCE_LIMIT_NONE) ? limit : targ.max_health);
+       if(GetResourceAmount(targ, RESOURCE_HEALTH) <= 0 || GetResourceAmount(targ, RESOURCE_HEALTH) >= true_limit)
+               return false;
+
+       GiveResourceWithLimit(targ, RESOURCE_HEALTH, amount, true_limit);
+       WaypointSprite_UpdateHealth(targ.sprite, GetResourceAmount(targ, RESOURCE_HEALTH));
+       targ.frame = 10 * bound(0, (1 - GetResourceAmount(targ, RESOURCE_HEALTH) / targ.max_health), 1);
+       targ.lasthealth = GetResourceAmount(targ, RESOURCE_HEALTH);
+       targ.SendFlags |= GSF_STATUS;
+       return true;
+}
+
 void ons_GeneratorThink(entity this)
 {
        this.nextthink = time + GEN_THINKRATE;
@@ -978,6 +1009,7 @@ void ons_GeneratorReset(entity this)
        this.islinked = true;
        this.isshielded = true;
        this.event_damage = ons_GeneratorDamage;
+       this.event_heal = ons_GeneratorHeal;
        setthink(this, ons_GeneratorThink);
        this.nextthink = time + GEN_THINKRATE;
 
@@ -1040,6 +1072,7 @@ void ons_GeneratorSetup(entity gen) // called when spawning a generator entity o
        gen.bot_attack = true;
        IL_PUSH(g_bot_targets, gen);
        gen.event_damage = ons_GeneratorDamage;
+       gen.event_heal = ons_GeneratorHeal;
        gen.reset = ons_GeneratorReset;
        setthink(gen, ons_GeneratorThink);
        gen.nextthink = time + GEN_THINKRATE;
index ab08a39e891cf0168bd31e2328bfab60af054f11..866fd88a569ddd4e67488ba23ba0a72e8ac5715f 100644 (file)
@@ -18,14 +18,9 @@ void trigger_heal_touch(entity this, entity toucher)
                                toucher.triggerhealtime = time + this.delay;
 
                        bool playthesound = (this.spawnflags & HEAL_SOUND_ALWAYS);
-                       if (GetResourceAmount(toucher, RESOURCE_HEALTH) < this.max_health)
-                       {
-                               playthesound = true;
-                               GiveResourceWithLimit(toucher, RESOURCE_HEALTH, GetResourceAmount(this, RESOURCE_HEALTH), this.max_health);
-                               toucher.pauserothealth_finished = max(toucher.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot);
-                       }
+                       bool healed = Heal(toucher, this, GetResourceAmount(this, RESOURCE_HEALTH), this.max_health);
 
-                       if(playthesound)
+                       if(playthesound || healed)
                                _sound (toucher, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM);
                }
        }
index b26328a54593fc325c9b940a6eb21c5fb4062c28..88120a0ea7550acbb93cd46e9b94022165b18ba2 100644 (file)
@@ -234,9 +234,11 @@ void M_Mage_Defend_Heal(entity this)
                        switch(this.skin)
                        {
                                case 0:
-                                       GiveResourceWithLimit(it, RESOURCE_HEALTH, autocvar_g_monster_mage_heal_allies, autocvar_g_balance_health_regenstable);
+                               {
+                                       Heal(it, this, autocvar_g_monster_mage_heal_allies, autocvar_g_balance_health_regenstable);
                                        fx = EFFECT_HEALING;
                                        break;
+                               }
                                case 1:
                                {
                                        if(GetResourceAmount(this, RESOURCE_CELLS)) GiveResourceWithLimit(it, RESOURCE_CELLS, 1, g_pickup_cells_max);
@@ -267,7 +269,7 @@ void M_Mage_Defend_Heal(entity this)
                else
                {
                        Send_Effect(EFFECT_HEALING, it.origin, '0 0 0', 1);
-                       GiveResourceWithLimit(it, RESOURCE_HEALTH, autocvar_g_monster_mage_heal_allies, it.max_health);
+                       Heal(it, this, autocvar_g_monster_mage_heal_allies, RESOURCE_LIMIT_NONE);
                        if(!(it.spawnflags & MONSTERFLAG_INVINCIBLE) && it.sprite)
                                WaypointSprite_UpdateHealth(it.sprite, GetResourceAmount(it, RESOURCE_HEALTH));
                }
index 63f4418dec6cc4b7dae80cd7244fcdeaea254c22..84355c7f3530ffddcf3dccde86781d487c7fd717 100644 (file)
@@ -527,6 +527,7 @@ void Monster_Dead_Fade(entity this)
                        this.pos2 = this.angles;
                }
                this.event_damage = func_null;
+               this.event_heal = func_null;
                this.takedamage = DAMAGE_NO;
                setorigin(this, this.pos1);
                this.angles = this.pos2;
@@ -956,6 +957,7 @@ void Monster_Dead(entity this, entity attacker, float gibbed)
                _setmodel(this, this.mdl_dead);
 
        this.event_damage       = ((gibbed) ? func_null : Monster_Dead_Damage);
+       this.event_heal         = func_null;
        this.solid                      = SOLID_CORPSE;
        this.takedamage         = DAMAGE_AIM;
        this.deadflag           = DEAD_DEAD;
@@ -1057,6 +1059,18 @@ void Monster_Damage(entity this, entity inflictor, entity attacker, float damage
        }
 }
 
+bool Monster_Heal(entity targ, entity inflictor, float amount, float limit)
+{
+       float true_limit = ((limit != RESOURCE_LIMIT_NONE) ? limit : targ.max_health);
+       if(GetResourceAmount(targ, RESOURCE_HEALTH) <= 0 || GetResourceAmount(targ, RESOURCE_HEALTH) >= true_limit)
+               return false;
+
+       GiveResourceWithLimit(targ, RESOURCE_HEALTH, amount, true_limit);
+       if(targ.sprite)
+               WaypointSprite_UpdateHealth(targ.sprite, GetResourceAmount(targ, RESOURCE_HEALTH));
+       return true;
+}
+
 // don't check for enemies, just keep walking in a straight line
 void Monster_Move_2D(entity this, float mspeed, bool allow_jumpoff)
 {
@@ -1353,6 +1367,7 @@ bool Monster_Spawn(entity this, bool check_appear, int mon_id)
        this.damagedbycontents  = true;
        this.monsterid                  = mon_id;
        this.event_damage               = Monster_Damage;
+       this.event_heal                 = Monster_Heal;
        settouch(this, Monster_Touch);
        this.use                                = Monster_Use;
        this.solid                              = SOLID_BBOX;
index 319b518665fff22505ba0afaf5256187257b443c..115e6ca9109341fcaa4c61974eaafe416b342f15 100644 (file)
@@ -27,10 +27,8 @@ MUTATOR_HOOKFUNCTION(vh, GrappleHookThink)
                thehook.last_dmg = time + autocvar_g_vampirehook_damagerate;
                thehook.owner.damage_dealt += autocvar_g_vampirehook_damage;
                Damage(dmgent, thehook, thehook.owner, autocvar_g_vampirehook_damage, WEP_HOOK.m_id, DMG_NOWEP, thehook.origin, '0 0 0');
-               if(SAME_TEAM(thehook.owner, thehook.aiment))
-                       GiveResourceWithLimit(thehook.aiment, RESOURCE_HEALTH, autocvar_g_vampirehook_health_steal, g_pickup_healthsmall_max);
-               else
-                       GiveResourceWithLimit(thehook.owner, RESOURCE_HEALTH, autocvar_g_vampirehook_health_steal, g_pickup_healthsmall_max);
+               entity targ = ((SAME_TEAM(thehook.owner, thehook.aiment)) ? thehook.aiment : thehook.owner);
+               Heal(targ, thehook.owner, autocvar_g_vampirehook_health_steal, g_pickup_healthsmall_max);
 
                if(dmgent == thehook.owner)
                        TakeResource(dmgent, RESOURCE_HEALTH, autocvar_g_vampirehook_damage); // FIXME: friendly fire?!
index fb27cd12137fef8e36cd30dfb09f133077c3cf6c..ac68003a6cde83c1a6b652959af1f349240654e1 100644 (file)
@@ -425,8 +425,10 @@ NET_HANDLE(ENT_CLIENT_TURRET, bool isnew)
                float myhp = GetResourceAmount(this, RESOURCE_HEALTH);
                if(_tmp == 0 && myhp != 0)
                        turret_die(this);
-               else if(myhp && myhp != _tmp)
+               else if(myhp && myhp > _tmp)
                        this.helpme = servertime + 10;
+               else if(myhp && myhp < _tmp)
+                       this.helpme = 0; // we're being healed, don't spam help me waypoints
 
                SetResourceAmountExplicit(this, RESOURCE_HEALTH, _tmp);
        }
index a02ce07aca2454367cacbc86cea4242fb6bf05b5..b68aca16feddd93b6ea01b555b49aa6b17d2c53a 100644 (file)
@@ -182,6 +182,7 @@ void turret_die(entity this)
        this.tur_head.solid      = this.solid;
 
        this.event_damage                 = func_null;
+       this.event_heal = func_null;
        this.takedamage                  = DAMAGE_NO;
 
        SetResourceAmountExplicit(this, RESOURCE_HEALTH, 0);
@@ -248,6 +249,8 @@ void turret_damage(entity this, entity inflictor, entity attacker, float damage,
        {
                this.event_damage                 = func_null;
                this.tur_head.event_damage = func_null;
+               this.event_heal = func_null;
+               this.tur_head.event_heal = func_null;
                this.takedamage                  = DAMAGE_NO;
                this.nextthink = time;
                setthink(this, turret_die);
@@ -256,6 +259,17 @@ void turret_damage(entity this, entity inflictor, entity attacker, float damage,
        this.SendFlags  |= TNSF_STATUS;
 }
 
+bool turret_heal(entity targ, entity inflictor, float amount, float limit)
+{
+       float true_limit = ((limit != RESOURCE_LIMIT_NONE) ? limit : targ.max_health);
+       if(GetResourceAmount(targ, RESOURCE_HEALTH) <= 0 || GetResourceAmount(targ, RESOURCE_HEALTH) >= true_limit)
+               return false;
+
+       GiveResourceWithLimit(targ, RESOURCE_HEALTH, amount, true_limit);
+       targ.SendFlags |= TNSF_STATUS;
+       return true;
+}
+
 void turret_think(entity this);
 void turret_respawn(entity this)
 {
@@ -268,6 +282,7 @@ void turret_respawn(entity this)
        this.solid                                      = SOLID_BBOX;
        this.takedamage                         = DAMAGE_AIM;
        this.event_damage                       = turret_damage;
+       this.event_heal                         = turret_heal;
        this.avelocity                          = '0 0 0';
        this.tur_head.avelocity         = this.avelocity;
        this.tur_head.angles            = this.idle_aim;
@@ -1360,6 +1375,7 @@ bool turret_initialize(entity this, Turret tur)
        this.idle_aim                           = '0 0 0';
        this.turret_firecheckfunc       = turret_firecheck;
        this.event_damage                       = turret_damage;
+       this.event_heal                         = turret_heal;
        this.use                                        = turret_use;
        this.bot_attack                         = true;
        this.nextthink                          = time + 1;
index 6c34741cc710733fe7886c837dddf7859b5aacc8..df0f5d91693946c7230fb95d37f0dfb6b773aecd 100644 (file)
@@ -727,6 +727,20 @@ void vehicles_damage(entity this, entity inflictor, entity attacker, float damag
        }
 }
 
+bool vehicles_heal(entity targ, entity inflictor, float amount, float limit)
+{
+       float true_limit = ((limit != RESOURCE_LIMIT_NONE) ? limit : targ.max_health);
+       //if(GetResourceAmount(targ, RESOURCE_HEALTH) <= 0 || GetResourceAmount(targ, RESOURCE_HEALTH) >= true_limit)
+       if(targ.vehicle_health <= 0 || targ.vehicle_health >= true_limit)
+               return false;
+
+       targ.vehicle_health = min(targ.vehicle_health + amount, true_limit);
+       //GiveResourceWithLimit(targ, RESOURCE_HEALTH, amount, true_limit);
+       //if(targ.owner)
+               //targ.owner.vehicle_health = (targ.vehicle_health / targ.max_health) * 100;
+       return true;
+}
+
 bool vehicles_crushable(entity e)
 {
        if(IS_PLAYER(e) && time >= e.vehicle_enter_delay)
@@ -1005,6 +1019,7 @@ void vehicles_enter(entity pl, entity veh)
        setsize(pl, STAT(PL_MIN, pl), STAT(PL_MAX, pl));
 
        veh.event_damage        = vehicles_damage;
+       veh.event_heal          = vehicles_heal;
        veh.nextthink           = 0;
        pl.items &= ~IT_USING_JETPACK;
        pl.angles                       = veh.angles;
@@ -1118,6 +1133,7 @@ void vehicles_spawn(entity this)
        this.owner                              = NULL;
        settouch(this, vehicles_touch);
        this.event_damage               = vehicles_damage;
+       this.event_heal                 = vehicles_heal;
        this.reset                              = vehicles_reset;
        this.iscreature                 = true;
        this.teleportable               = false; // no teleporting for vehicles, too buggy
@@ -1230,6 +1246,7 @@ bool vehicle_initialize(entity this, Vehicle info, bool nodrop)
        this.vehicleid                          = info.vehicleid;
        this.PlayerPhysplug                     = info.PlayerPhysplug;
        this.event_damage                       = func_null;
+       this.event_heal                         = func_null;
        settouch(this, vehicles_touch);
        setthink(this, vehicles_spawn);
        this.nextthink                          = time;
index da21a4bcb42d5e599fc5fdf4aae1a4fbbb49b172..c340d947035617492da6d224bbede286baf8e7be 100644 (file)
@@ -544,36 +544,27 @@ bool bumblebee_pilot_frame(entity this, float dt)
                        else
                        {
                                if(!IS_DEAD(trace_ent))
+                               {
                                        if((teamplay && trace_ent.team == this.team) || !teamplay)
                                        {
+                                               if(autocvar_g_vehicle_bumblebee_healgun_hps)
+                                               {
+                                                       float hplimit = ((IS_PLAYER(trace_ent)) ? autocvar_g_vehicle_bumblebee_healgun_hmax : RESOURCE_LIMIT_NONE);
+                                                       Heal(trace_ent, this, autocvar_g_vehicle_bumblebee_healgun_hps * dt, hplimit);
+                                               }
 
                                                if(IS_VEHICLE(trace_ent))
                                                {
                                                        if(autocvar_g_vehicle_bumblebee_healgun_sps && trace_ent.vehicle_health <= trace_ent.max_health)
                                                                trace_ent.vehicle_shield = min(trace_ent.vehicle_shield + autocvar_g_vehicle_bumblebee_healgun_sps * dt, trace_ent.tur_head.max_health);
-
-                                                       if(autocvar_g_vehicle_bumblebee_healgun_hps)
-                                                               trace_ent.vehicle_health = min(trace_ent.vehicle_health + autocvar_g_vehicle_bumblebee_healgun_hps * dt, trace_ent.max_health);
                                                }
                                                else if(IS_CLIENT(trace_ent))
                                                {
-                                                       if(GetResourceAmount(trace_ent, RESOURCE_HEALTH) <= autocvar_g_vehicle_bumblebee_healgun_hmax && autocvar_g_vehicle_bumblebee_healgun_hps)
-                                                               GiveResourceWithLimit(trace_ent, RESOURCE_HEALTH, autocvar_g_vehicle_bumblebee_healgun_hps * dt, autocvar_g_vehicle_bumblebee_healgun_hmax);
-
                                                        if(GetResourceAmount(trace_ent, RESOURCE_ARMOR) <= autocvar_g_vehicle_bumblebee_healgun_amax && autocvar_g_vehicle_bumblebee_healgun_aps)
                                                                GiveResourceWithLimit(trace_ent, RESOURCE_ARMOR, autocvar_g_vehicle_bumblebee_healgun_aps * dt, autocvar_g_vehicle_bumblebee_healgun_amax);
-
-                                                       GiveResourceWithLimit(trace_ent, RESOURCE_HEALTH, autocvar_g_vehicle_bumblebee_healgun_hps * dt, autocvar_g_vehicle_bumblebee_healgun_hmax);
-                                               }
-                                               else if(IS_TURRET(trace_ent))
-                                               {
-                                                       if(GetResourceAmount(trace_ent, RESOURCE_HEALTH) <= trace_ent.max_health && autocvar_g_vehicle_bumblebee_healgun_hps)
-                                                               GiveResourceWithLimit(trace_ent, RESOURCE_HEALTH, autocvar_g_vehicle_bumblebee_healgun_hps * dt, trace_ent.max_health);
-                                                       //else ..hmmm what? ammo?
-
-                                                       trace_ent.SendFlags |= TNSF_STATUS;
                                                }
                                        }
+                               }
                        }
                }
 
index d6604623d2615ae31467d9592e0d7037c8557c6e..40f9ea765a6dda88428db9731dc965685004a210 100644 (file)
@@ -410,7 +410,7 @@ void W_Arc_Beam_Think(entity this)
                beam_endpos = WarpZone_TransformOrigin(WarpZone_trace_transform, beam_endpos);
                new_dir = WarpZone_TransformVelocity(WarpZone_trace_transform, new_dir);
 
-               float is_player = (
+               bool is_player = (
                        IS_PLAYER(trace_ent)
                        ||
                        trace_ent.classname == "body"
@@ -418,129 +418,42 @@ void W_Arc_Beam_Think(entity this)
                        IS_MONSTER(trace_ent)
                );
 
-               // TODO: takedamage flag for things that can be healed?
-               if(trace_ent && (trace_ent.takedamage || trace_ent.classname == "onslaught_generator" || trace_ent.classname == "onslaught_controlpoint_icon") && (is_player || WEP_CVAR(arc, beam_nonplayerdamage)))
+               if(trace_ent)
                {
-                       // calculate our own hit origin as trace_endpos tends to jump around annoyingly (to player origin?)
-                       // NO. trace_endpos should be just fine. If not,
-                       // that's an engine bug that needs proper debugging.
-                       vector hitorigin = trace_endpos;
-
-                       float falloff = ExponentialFalloff(
-                               WEP_CVAR(arc, beam_falloff_mindist),
-                               WEP_CVAR(arc, beam_falloff_maxdist),
-                               WEP_CVAR(arc, beam_falloff_halflifedist),
-                               vlen(WarpZone_UnTransformOrigin(WarpZone_trace_transform, hitorigin) - w_shotorg)
-                       );
-
-                       // TODO: api or event for things that can be healed
-                       if(IS_VEHICLE(trace_ent) && SAME_TEAM(own, trace_ent))
+                       if(SAME_TEAM(own, trace_ent))
                        {
                                float roothealth = ((burst) ? WEP_CVAR(arc, burst_healing_hps) : WEP_CVAR(arc, beam_healing_hps));
-                               // not handling shield, since it's not exactly a defensive stat
-
-                               if(trace_ent.vehicle_health <= trace_ent.max_health && roothealth)
+                               float rootarmor = ((burst) ? WEP_CVAR(arc, burst_healing_aps) : WEP_CVAR(arc, beam_healing_aps));
+                               float hplimit = ((IS_PLAYER(trace_ent)) ? WEP_CVAR(arc, beam_healing_hmax) : RESOURCE_LIMIT_NONE);
+                               Heal(trace_ent, own, (roothealth * coefficient), hplimit);
+                               if(IS_PLAYER(trace_ent) && rootarmor)
                                {
-                                       trace_ent.vehicle_health = min(
-                                                       trace_ent.vehicle_health + (roothealth * coefficient),
-                                                       trace_ent.max_health
-                                               );
-                                       if(trace_ent.owner)
-                                               trace_ent.owner.vehicle_health = (trace_ent.vehicle_health / trace_ent.max_health) * 100;
-                                       new_beam_type = ARC_BT_HEAL;
-                               }
-                       }
-                       else if(trace_ent.classname == "onslaught_generator" && SAME_TEAM(own, trace_ent))
-                       {
-                               float roothealth = ((burst) ? WEP_CVAR(arc, burst_healing_hps) : WEP_CVAR(arc, beam_healing_hps));
-                               if(roothealth)
-                               {
-                                       if(GetResourceAmount(trace_ent, RESOURCE_HEALTH) <= trace_ent.max_health)
+                                       if(GetResourceAmount(trace_ent, RESOURCE_ARMOR) <= WEP_CVAR(arc, beam_healing_amax))
                                        {
-                                               GiveResourceWithLimit(trace_ent, RESOURCE_HEALTH, (roothealth * coefficient), trace_ent.max_health);
-                                               WaypointSprite_UpdateHealth(trace_ent.sprite, GetResourceAmount(trace_ent, RESOURCE_HEALTH));
-                                               trace_ent.frame = 10 * bound(0, (1 - GetResourceAmount(trace_ent, RESOURCE_HEALTH) / trace_ent.max_health), 1);
-                                               trace_ent.lasthealth = GetResourceAmount(trace_ent, RESOURCE_HEALTH);
-                                               trace_ent.SendFlags |= GSF_STATUS;
-                                       }
-                                       new_beam_type = ARC_BT_HEAL;
-                               }
-                       }
-                       else if(trace_ent.classname == "onslaught_controlpoint_icon" && SAME_TEAM(own, trace_ent))
-                       {
-                               float roothealth = ((burst) ? WEP_CVAR(arc, burst_healing_hps) : WEP_CVAR(arc, beam_healing_hps));
-                               if(roothealth)
-                               {
-                                       if(GetResourceAmount(trace_ent, RESOURCE_HEALTH) <= trace_ent.max_health)
-                                       {
-                                               GiveResourceWithLimit(trace_ent, RESOURCE_HEALTH, (roothealth * coefficient), trace_ent.max_health);
-                                               if(trace_ent.owner.iscaptured)
-                                                       WaypointSprite_UpdateHealth(trace_ent.owner.sprite, GetResourceAmount(trace_ent, RESOURCE_HEALTH));
-                                               else
-                                                       WaypointSprite_UpdateBuildFinished(trace_ent.owner.sprite, time + (trace_ent.max_health - GetResourceAmount(trace_ent, RESOURCE_HEALTH)) / (trace_ent.count / ONS_CP_THINKRATE));
-                                       }
-                                       new_beam_type = ARC_BT_HEAL;
-                               }
-                       }
-                       else if(trace_ent.classname == "func_assault_destructible" && SAME_TEAM(own, trace_ent))
-                       {
-                               float roothealth = ((burst) ? WEP_CVAR(arc, burst_healing_hps) : WEP_CVAR(arc, beam_healing_hps));
-
-                               if(roothealth)
-                               {
-                                       if(GetResourceAmount(trace_ent, RESOURCE_HEALTH) <= trace_ent.max_health)
-                                       {
-                                               GiveResourceWithLimit(trace_ent, RESOURCE_HEALTH, (roothealth * coefficient), trace_ent.max_health);
-                                               if(trace_ent.sprite)
-                                               {
-                                                       WaypointSprite_UpdateHealth(trace_ent.sprite, GetResourceAmount(trace_ent, RESOURCE_HEALTH));
-                                               }
-                                               func_breakable_colormod(trace_ent);
+                                               GiveResourceWithLimit(trace_ent, RESOURCE_ARMOR, (rootarmor * coefficient), WEP_CVAR(arc, beam_healing_amax));
+                                               trace_ent.pauserotarmor_finished = max(
+                                                       trace_ent.pauserotarmor_finished,
+                                                       time + autocvar_g_balance_pause_armor_rot
+                                               );
                                        }
-                                       new_beam_type = ARC_BT_HEAL;
-                               }
-                       }
-                       else if(is_player && SAME_TEAM(own, trace_ent))
-                       {
-                               float roothealth, rootarmor;
-                               float maxhp;
-                               if(burst)
-                               {
-                                       roothealth = WEP_CVAR(arc, burst_healing_hps);
-                                       rootarmor = WEP_CVAR(arc, burst_healing_aps);
-                               }
-                               else
-                               {
-                                       roothealth = WEP_CVAR(arc, beam_healing_hps);
-                                       rootarmor = WEP_CVAR(arc, beam_healing_aps);
-                               }
-                               maxhp = ((IS_MONSTER(trace_ent)) ? trace_ent.max_health : WEP_CVAR(arc, beam_healing_hmax));
-
-                               if(GetResourceAmount(trace_ent, RESOURCE_HEALTH) <= maxhp && roothealth)
-                               {
-                                       GiveResourceWithLimit(trace_ent, RESOURCE_HEALTH, (roothealth * coefficient), maxhp);
-                               }
-                               if(GetResourceAmount(trace_ent, RESOURCE_ARMOR) <= WEP_CVAR(arc, beam_healing_amax) && rootarmor && !IS_MONSTER(trace_ent))
-                               {
-                                       GiveResourceWithLimit(trace_ent, RESOURCE_ARMOR, (rootarmor * coefficient), WEP_CVAR(arc, beam_healing_amax));
                                }
-
-                               // stop rot, set visual effect
                                if(roothealth || rootarmor)
-                               {
-                                       trace_ent.pauserothealth_finished = max(
-                                               trace_ent.pauserothealth_finished,
-                                               time + autocvar_g_balance_pause_health_rot
-                                       );
-                                       trace_ent.pauserotarmor_finished = max(
-                                               trace_ent.pauserotarmor_finished,
-                                               time + autocvar_g_balance_pause_armor_rot
-                                       );
                                        new_beam_type = ARC_BT_HEAL;
-                               }
                        }
-                       else
+                       else if(trace_ent.takedamage && (is_player || WEP_CVAR(arc, beam_nonplayerdamage)))
                        {
+                               // calculate our own hit origin as trace_endpos tends to jump around annoyingly (to player origin?)
+                               // NO. trace_endpos should be just fine. If not,
+                               // that's an engine bug that needs proper debugging.
+                               vector hitorigin = trace_endpos;
+
+                               float falloff = ExponentialFalloff(
+                                       WEP_CVAR(arc, beam_falloff_mindist),
+                                       WEP_CVAR(arc, beam_falloff_maxdist),
+                                       WEP_CVAR(arc, beam_falloff_halflifedist),
+                                       vlen(WarpZone_UnTransformOrigin(WarpZone_trace_transform, hitorigin) - w_shotorg)
+                               );
+
                                float rootdamage;
                                if(is_player)
                                {
index 0c740545c08d3593af6c5fe8f098125bf0544f2d..42d72edf415e00183eb1236bef310a6361f8461b 100644 (file)
@@ -380,6 +380,7 @@ void PutObserverInServer(entity this)
        this.oldvelocity = this.velocity;
        this.fire_endtime = -1;
        this.event_damage = func_null;
+       this.event_heal = func_null;
 
        for(int slot = 0; slot < MAX_AXH; ++slot)
        {
@@ -674,6 +675,7 @@ void PutPlayerInServer(entity this)
        STAT(HUD, this) = HUD_NORMAL;
 
        this.event_damage = PlayerDamage;
+       this.event_heal = PlayerHeal;
 
        if(!this.bot_attack)
                IL_PUSH(g_bot_targets, this);
index 1db5dd0c5b637244ef97e3a68271975bd393b8d0..4c23aaeb3b956f6706b8dcea57db48f1c4231f01 100644 (file)
@@ -36,6 +36,8 @@ float server_is_dedicated;
 
 .void(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) event_damage;
 
+.bool(entity targ, entity inflictor, float amount, float limit) event_heal;
+
 //.string      wad;
 //.string      map;
 
index 2efe1475a69aa9cef99b6a7fe37e98cdca5173f9..cc0c71be5d56e32a1ad721a1e8fe1a9726645a43 100644 (file)
@@ -1061,6 +1061,20 @@ float RadiusDamage (entity inflictor, entity attacker, float coredamage, float e
        return RadiusDamageForSource (inflictor, (inflictor.origin + (inflictor.mins + inflictor.maxs) * 0.5), inflictor.velocity, attacker, coredamage, edgedamage, rad, cantbe, mustbe, false, forceintensity, deathtype, weaponentity, directhitentity);
 }
 
+bool Heal(entity targ, entity inflictor, float amount, float limit)
+{
+       if(game_stopped || (IS_CLIENT(targ) && CS(targ).killcount == FRAGS_SPECTATOR) || STAT(FROZEN, targ) || IS_DEAD(targ))
+               return false;
+
+       bool healed = false;
+       if(targ.event_heal)
+               healed = targ.event_heal(targ, inflictor, amount, limit);
+       // TODO: additional handling? what if the healing kills them? should this abort if healing would do so etc
+       // TODO: healing fx!
+       // TODO: armor healing?
+       return healed;
+}
+
 float Fire_IsBurning(entity e)
 {
        return (time < e.fire_endtime);
index 9ab817853b0754a5ead77fccd73ff8cd16624eb0..88fbf134422134492c3ee1756549d4b2131fa8ab 100644 (file)
@@ -96,6 +96,10 @@ float RadiusDamageForSource (entity inflictor, vector inflictororigin, vector in
 
 float RadiusDamage (entity inflictor, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, float forceintensity, int deathtype, .entity weaponentity, entity directhitentity);
 
+// Calls .event_heal on the target so that they can handle healing themselves
+// a limit of RESOURCE_LIMIT_NONE should be handled by the entity as its max health (if applicable)
+bool Heal(entity targ, entity inflictor, float amount, float limit);
+
 .float fire_damagepersec;
 .float fire_endtime;
 .float fire_deathtype;
index 4e39cf6b779297478ec2d3ed55170e2e13cfc90c..cc58cdc6d7ca7e2bc493b9c2bd1518a3aaa3bbe2 100644 (file)
@@ -74,6 +74,7 @@ void CopyBody(entity this, float keepvelocity)
        clone.effects = this.effects;
        clone.glowmod = this.glowmod;
        clone.event_damage = this.event_damage;
+       clone.event_heal = this.event_heal;
        clone.anim_state = this.anim_state;
        clone.anim_time = this.anim_time;
        clone.anim_lower_action = this.anim_lower_action;
@@ -629,6 +630,7 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
 
                // set damage function to corpse damage
                this.event_damage = PlayerCorpseDamage;
+               this.event_heal = func_null;
                // call the corpse damage function just in case it wants to gib
                this.event_damage(this, inflictor, attacker, excess, deathtype, weaponentity, hitloc, force);
 
@@ -664,6 +666,15 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
        }
 }
 
+bool PlayerHeal(entity targ, entity inflictor, float amount, float limit)
+{
+       if(GetResourceAmount(targ, RESOURCE_HEALTH) <= 0 || GetResourceAmount(targ, RESOURCE_HEALTH) >= limit)
+               return false;
+
+       GiveResourceWithLimit(targ, RESOURCE_HEALTH, amount, limit);
+       return true;
+}
+
 bool MoveToTeam(entity client, int team_colour, int type)
 {
        int lockteams_backup = lockteams;  // backup any team lock
index 5e6642e0471a78220a1af579a705e7b50d74b76b..8c9bac9b62aaa8e94ea4a594e33f6b680f67d74c 100644 (file)
@@ -39,4 +39,6 @@ bool MoveToTeam(entity client, float team_colour, float type);
 
 void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force);
 
+bool PlayerHeal(entity targ, entity inflictor, float amount, float limit);
+
 int Say(entity source, float teamsay, entity privatesay, string msgin, float floodcontrol);
index 3614daf49e9fc1f4778c411d39e104ff656c93e7..b3b19095ae8b14e7832b386d1f5614c891a3d93f 100644 (file)
@@ -182,7 +182,7 @@ void GiveResourceWithLimit(entity receiver, int resource_type, float amount,
                return;
        }
        float current_amount = GetResourceAmount(receiver, resource_type);
-       if (current_amount + amount > limit)
+       if (current_amount + amount > limit && limit != RESOURCE_LIMIT_NONE)
        {
                amount = limit - current_amount;
        }