Merge pull request #9593 from huwpascoe/HealthTrait
Moved Health to OpenRA.Mods.Common
This commit is contained in:
@@ -71,7 +71,7 @@ namespace OpenRA
|
||||
|
||||
IOccupySpace occupySpace;
|
||||
readonly IFacing facing;
|
||||
readonly Health health;
|
||||
readonly IHealth health;
|
||||
readonly IRenderModifier[] renderModifiers;
|
||||
readonly IRender[] renders;
|
||||
readonly IDisable[] disables;
|
||||
@@ -103,7 +103,7 @@ namespace OpenRA
|
||||
VisualBounds = DetermineVisualBounds();
|
||||
EffectiveOwner = TraitOrDefault<IEffectiveOwner>();
|
||||
facing = TraitOrDefault<IFacing>();
|
||||
health = TraitOrDefault<Health>();
|
||||
health = TraitOrDefault<IHealth>();
|
||||
renderModifiers = TraitsImplementing<IRenderModifier>().ToArray();
|
||||
renders = TraitsImplementing<IRender>().ToArray();
|
||||
disables = TraitsImplementing<IDisable>().ToArray();
|
||||
@@ -281,12 +281,28 @@ namespace OpenRA
|
||||
});
|
||||
}
|
||||
|
||||
public void Kill(Actor attacker)
|
||||
public DamageState GetDamageState()
|
||||
{
|
||||
if (health == null)
|
||||
if (Disposed)
|
||||
return DamageState.Dead;
|
||||
|
||||
return (health == null) ? DamageState.Undamaged : health.DamageState;
|
||||
}
|
||||
|
||||
public void InflictDamage(Actor attacker, int damage, IWarhead warhead)
|
||||
{
|
||||
if (Disposed || health == null)
|
||||
return;
|
||||
|
||||
health.InflictDamage(this, attacker, health.MaxHP, null, true);
|
||||
health.InflictDamage(this, attacker, damage, warhead, false);
|
||||
}
|
||||
|
||||
public void Kill(Actor attacker)
|
||||
{
|
||||
if (Disposed || health == null)
|
||||
return;
|
||||
|
||||
health.Kill(this, attacker);
|
||||
}
|
||||
|
||||
public bool IsDisabled()
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace OpenRA.Graphics
|
||||
wlr.DrawLine(start + r, z + r, barColor2);
|
||||
}
|
||||
|
||||
Color GetHealthColor(Health health)
|
||||
Color GetHealthColor(IHealth health)
|
||||
{
|
||||
var player = actor.World.RenderPlayer ?? actor.World.LocalPlayer;
|
||||
|
||||
@@ -104,7 +104,7 @@ namespace OpenRA.Graphics
|
||||
health.DamageState == DamageState.Heavy ? Color.Yellow : Color.LimeGreen;
|
||||
}
|
||||
|
||||
void DrawHealthBar(WorldRenderer wr, Health health, float2 start, float2 end)
|
||||
void DrawHealthBar(WorldRenderer wr, IHealth health, float2 start, float2 end)
|
||||
{
|
||||
if (health == null || health.IsDead)
|
||||
return;
|
||||
@@ -133,7 +133,7 @@ namespace OpenRA.Graphics
|
||||
wlr.DrawLine(start + q, z + q, healthColor);
|
||||
wlr.DrawLine(start + r, z + r, healthColor2);
|
||||
|
||||
if (health.DisplayHp != health.HP)
|
||||
if (health.DisplayHP != health.HP)
|
||||
{
|
||||
var deltaColor = Color.OrangeRed;
|
||||
var deltaColor2 = Color.FromArgb(
|
||||
@@ -141,7 +141,7 @@ namespace OpenRA.Graphics
|
||||
deltaColor.R / 2,
|
||||
deltaColor.G / 2,
|
||||
deltaColor.B / 2);
|
||||
var zz = float2.Lerp(start, end, (float)health.DisplayHp / health.MaxHP);
|
||||
var zz = float2.Lerp(start, end, (float)health.DisplayHP / health.MaxHP);
|
||||
|
||||
wlr.DrawLine(z + p, zz + p, deltaColor2);
|
||||
wlr.DrawLine(z + q, zz + q, deltaColor);
|
||||
@@ -155,7 +155,7 @@ namespace OpenRA.Graphics
|
||||
if (!actor.IsInWorld || actor.IsDead)
|
||||
return;
|
||||
|
||||
var health = actor.TraitOrDefault<Health>();
|
||||
var health = actor.TraitOrDefault<IHealth>();
|
||||
|
||||
var screenPos = wr.ScreenPxPosition(pos);
|
||||
var bounds = actor.VisualBounds;
|
||||
|
||||
@@ -171,7 +171,6 @@
|
||||
<Compile Include="TraitDictionary.cs" />
|
||||
<Compile Include="Traits\DrawLineToTarget.cs" />
|
||||
<Compile Include="Traits\EditorTilesetFilter.cs" />
|
||||
<Compile Include="Traits\Health.cs" />
|
||||
<Compile Include="Traits\LintAttributes.cs" />
|
||||
<Compile Include="Traits\RejectsOrders.cs" />
|
||||
<Compile Include="Traits\Player\DeveloperMode.cs" />
|
||||
|
||||
@@ -1,214 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenRA.Traits
|
||||
{
|
||||
public class HealthInfo : ITraitInfo, UsesInit<HealthInit>
|
||||
{
|
||||
[Desc("HitPoints")]
|
||||
public readonly int HP = 0;
|
||||
|
||||
[Desc("Physical size of the unit used for damage calculations. Impacts within this radius apply full damage.")]
|
||||
public readonly WDist Radius = new WDist(426);
|
||||
|
||||
[Desc("Trigger interfaces such as AnnounceOnKill?")]
|
||||
public readonly bool NotifyAppliedDamage = true;
|
||||
|
||||
public virtual object Create(ActorInitializer init) { return new Health(init, this); }
|
||||
}
|
||||
|
||||
public enum DamageState { Undamaged, Light, Medium, Heavy, Critical, Dead }
|
||||
|
||||
public class Health : ISync, ITick
|
||||
{
|
||||
public readonly HealthInfo Info;
|
||||
|
||||
[Sync] int hp;
|
||||
|
||||
public int DisplayHp { get; private set; }
|
||||
|
||||
public Health(ActorInitializer init, HealthInfo info)
|
||||
{
|
||||
Info = info;
|
||||
MaxHP = info.HP > 0 ? info.HP : 1;
|
||||
|
||||
hp = init.Contains<HealthInit>() ? init.Get<HealthInit, int>() * MaxHP / 100 : MaxHP;
|
||||
|
||||
DisplayHp = hp;
|
||||
}
|
||||
|
||||
public int HP { get { return hp; } }
|
||||
public int MaxHP;
|
||||
|
||||
public bool IsDead { get { return hp <= 0; } }
|
||||
public bool RemoveOnDeath = true;
|
||||
|
||||
public DamageState DamageState
|
||||
{
|
||||
get
|
||||
{
|
||||
if (hp <= 0)
|
||||
return DamageState.Dead;
|
||||
|
||||
if (hp < MaxHP * 0.25f)
|
||||
return DamageState.Critical;
|
||||
|
||||
if (hp < MaxHP * 0.5f)
|
||||
return DamageState.Heavy;
|
||||
|
||||
if (hp < MaxHP * 0.75f)
|
||||
return DamageState.Medium;
|
||||
|
||||
if (hp == MaxHP)
|
||||
return DamageState.Undamaged;
|
||||
|
||||
return DamageState.Light;
|
||||
}
|
||||
}
|
||||
|
||||
public void Resurrect(Actor self, Actor repairer)
|
||||
{
|
||||
if (!IsDead)
|
||||
return;
|
||||
|
||||
hp = MaxHP;
|
||||
|
||||
var ai = new AttackInfo
|
||||
{
|
||||
Attacker = repairer,
|
||||
Damage = -MaxHP,
|
||||
DamageState = DamageState,
|
||||
PreviousDamageState = DamageState.Dead,
|
||||
Warhead = null,
|
||||
};
|
||||
|
||||
foreach (var nd in self.TraitsImplementing<INotifyDamage>()
|
||||
.Concat(self.Owner.PlayerActor.TraitsImplementing<INotifyDamage>()))
|
||||
nd.Damaged(self, ai);
|
||||
|
||||
foreach (var nd in self.TraitsImplementing<INotifyDamageStateChanged>())
|
||||
nd.DamageStateChanged(self, ai);
|
||||
|
||||
if (Info.NotifyAppliedDamage && repairer != null && repairer.IsInWorld && !repairer.IsDead)
|
||||
foreach (var nd in repairer.TraitsImplementing<INotifyAppliedDamage>()
|
||||
.Concat(repairer.Owner.PlayerActor.TraitsImplementing<INotifyAppliedDamage>()))
|
||||
nd.AppliedDamage(repairer, self, ai);
|
||||
}
|
||||
|
||||
public void InflictDamage(Actor self, Actor attacker, int damage, IWarhead warhead, bool ignoreModifiers)
|
||||
{
|
||||
// Overkill! Don't count extra hits as more kills!
|
||||
if (IsDead)
|
||||
return;
|
||||
|
||||
var oldState = DamageState;
|
||||
|
||||
// Apply any damage modifiers
|
||||
if (!ignoreModifiers && damage > 0)
|
||||
{
|
||||
var modifiers = self.TraitsImplementing<IDamageModifier>()
|
||||
.Concat(self.Owner.PlayerActor.TraitsImplementing<IDamageModifier>())
|
||||
.Select(t => t.GetDamageModifier(attacker, warhead));
|
||||
|
||||
damage = Util.ApplyPercentageModifiers(damage, modifiers);
|
||||
}
|
||||
|
||||
hp = (hp - damage).Clamp(0, MaxHP);
|
||||
|
||||
var ai = new AttackInfo
|
||||
{
|
||||
Attacker = attacker,
|
||||
Damage = damage,
|
||||
DamageState = DamageState,
|
||||
PreviousDamageState = oldState,
|
||||
Warhead = warhead,
|
||||
};
|
||||
|
||||
foreach (var nd in self.TraitsImplementing<INotifyDamage>()
|
||||
.Concat(self.Owner.PlayerActor.TraitsImplementing<INotifyDamage>()))
|
||||
nd.Damaged(self, ai);
|
||||
|
||||
if (DamageState != oldState)
|
||||
foreach (var nd in self.TraitsImplementing<INotifyDamageStateChanged>())
|
||||
nd.DamageStateChanged(self, ai);
|
||||
|
||||
if (Info.NotifyAppliedDamage && attacker != null && attacker.IsInWorld && !attacker.IsDead)
|
||||
foreach (var nd in attacker.TraitsImplementing<INotifyAppliedDamage>()
|
||||
.Concat(attacker.Owner.PlayerActor.TraitsImplementing<INotifyAppliedDamage>()))
|
||||
nd.AppliedDamage(attacker, self, ai);
|
||||
|
||||
if (hp == 0)
|
||||
{
|
||||
foreach (var nd in self.TraitsImplementing<INotifyKilled>()
|
||||
.Concat(self.Owner.PlayerActor.TraitsImplementing<INotifyKilled>()))
|
||||
nd.Killed(self, ai);
|
||||
|
||||
if (RemoveOnDeath)
|
||||
self.Dispose();
|
||||
|
||||
if (attacker == null)
|
||||
Log.Write("debug", "{0} #{1} was killed.", self.Info.Name, self.ActorID);
|
||||
else
|
||||
Log.Write("debug", "{0} #{1} killed by {2} #{3}", self.Info.Name, self.ActorID, attacker.Info.Name, attacker.ActorID);
|
||||
}
|
||||
}
|
||||
|
||||
public void Tick(Actor self)
|
||||
{
|
||||
if (hp > DisplayHp)
|
||||
DisplayHp = hp;
|
||||
|
||||
if (DisplayHp > hp)
|
||||
DisplayHp = (2 * DisplayHp + hp) / 3;
|
||||
}
|
||||
}
|
||||
|
||||
public class HealthInit : IActorInit<int>
|
||||
{
|
||||
[FieldFromYamlKey] readonly int value = 100;
|
||||
readonly bool allowZero;
|
||||
public HealthInit() { }
|
||||
public HealthInit(int init, bool allowZero = false)
|
||||
{
|
||||
this.allowZero = allowZero;
|
||||
value = init;
|
||||
}
|
||||
|
||||
public int Value(World world)
|
||||
{
|
||||
if (value < 0 || (value == 0 && !allowZero))
|
||||
return 1;
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public static class HealthExts
|
||||
{
|
||||
public static DamageState GetDamageState(this Actor self)
|
||||
{
|
||||
if (self.Disposed)
|
||||
return DamageState.Dead;
|
||||
|
||||
var health = self.TraitOrDefault<Health>();
|
||||
return (health == null) ? DamageState.Undamaged : health.DamageState;
|
||||
}
|
||||
|
||||
public static void InflictDamage(this Actor self, Actor attacker, int damage, IWarhead warhead)
|
||||
{
|
||||
if (self.Disposed) return;
|
||||
var health = self.TraitOrDefault<Health>();
|
||||
if (health == null) return;
|
||||
health.InflictDamage(self, attacker, damage, warhead, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,20 @@ using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.Traits
|
||||
{
|
||||
public enum DamageState { Undamaged, Light, Medium, Heavy, Critical, Dead }
|
||||
|
||||
public interface IHealth
|
||||
{
|
||||
DamageState DamageState { get; }
|
||||
int HP { get; }
|
||||
int MaxHP { get; }
|
||||
int DisplayHP { get; }
|
||||
bool IsDead { get; }
|
||||
|
||||
void InflictDamage(Actor self, Actor attacker, int damage, IWarhead warhead, bool ignoreModifiers);
|
||||
void Kill(Actor self, Actor attacker);
|
||||
}
|
||||
|
||||
// depends on the order of pips in WorldRenderer.cs!
|
||||
public enum PipType { Transparent, Green, Yellow, Red, Gray, Blue, Ammo, AmmoEmpty }
|
||||
public enum TagType { None, Fake, Primary }
|
||||
|
||||
Reference in New Issue
Block a user