}
// 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) ? 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)
{
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)
this.SendFlags |= CPSF_STATUS;
}
+bool ons_ControlPoint_Icon_Heal(entity targ, entity inflictor, float amount, float limit)
+{
+ float true_limit = ((limit) ? 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;
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
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;
this.SendFlags |= GSF_STATUS;
}
+bool ons_GeneratorHeal(entity targ, entity inflictor, float amount, float limit)
+{
+ float true_limit = ((limit) ? 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;
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;
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;
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;
_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;
}
}
+bool Monster_Heal(entity targ, entity inflictor, float amount, float limit)
+{
+ float true_limit = ((limit) ? 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)
{
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;
}
}
+bool vehicles_heal(entity targ, entity inflictor, float amount, float limit)
+{
+ float true_limit = ((limit) ? 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)
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;
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
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;
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"
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) : 0);
+ Heal(trace_ent, own, roothealth, 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)
{
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)
{
STAT(HUD, this) = HUD_NORMAL;
this.event_damage = PlayerDamage;
+ this.event_heal = PlayerHeal;
if(!this.bot_attack)
IL_PUSH(g_bot_targets, this);
.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;
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))
+ 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!
+ return healed;
+}
+
float Fire_IsBurning(entity e)
{
return (time < e.fire_endtime);
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 0 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;
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;
// set damage function to corpse damage
this.event_damage = PlayerCorpseDamage;
+ this.event_damage = 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);
}
}
+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
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);