diff --git a/Makefile b/Makefile index f259e91d6d..f59d70d6a8 100644 --- a/Makefile +++ b/Makefile @@ -140,9 +140,9 @@ mod_common: $(mod_common_TARGET) test_dll_SRCS := $(shell find OpenRA.Test/ -iname '*.cs') test_dll_TARGET = OpenRA.Test.dll test_dll_KIND = library -test_dll_DEPS = $(game_TARGET) +test_dll_DEPS = $(game_TARGET) $(mod_common_TARGET) test_dll_FLAGS = -warn:1 -test_dll_LIBS = $(COMMON_LIBS) $(game_TARGET) $(NUNIT_LIBS) +test_dll_LIBS = $(COMMON_LIBS) $(game_TARGET) $(mod_common_TARGET) $(NUNIT_LIBS) PROGRAMS += test_dll test_dll: $(test_dll_TARGET) diff --git a/OpenRA.Mods.Common/HitShapes/Capsule.cs b/OpenRA.Mods.Common/HitShapes/Capsule.cs new file mode 100644 index 0000000000..614ecce97e --- /dev/null +++ b/OpenRA.Mods.Common/HitShapes/Capsule.cs @@ -0,0 +1,93 @@ +#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; +using System.Drawing; +using OpenRA.Graphics; +using OpenRA.Mods.Common.Graphics; + +namespace OpenRA.Mods.Common.HitShapes +{ + public class CapsuleShape : IHitShape + { + public WDist OuterRadius { get; private set; } + + [FieldLoader.Require] + public readonly int2 PointA; + + [FieldLoader.Require] + public readonly int2 PointB; + + public readonly WDist Radius = new WDist(426); + + int2 ab; + int abLenSq; + + public CapsuleShape() { } + + public CapsuleShape(int2 a, int2 b, WDist radius) + { + PointA = a; + PointB = b; + Radius = radius; + } + + public void Initialize() + { + ab = PointB - PointA; + abLenSq = ab.LengthSquared / 1024; + + if (abLenSq == 0) + throw new YamlException("This Capsule describes a circle. Use a Circle HitShape instead."); + + OuterRadius = Radius + new WDist(Math.Max(PointA.Length, PointB.Length)); + } + + public WDist DistanceFromEdge(WVec v) + { + var p = new int2(v.X, v.Y); + + var t = int2.Dot(p - PointA, ab) / abLenSq; + + if (t < 0) + return new WDist(Math.Max(0, (PointA - p).Length - Radius.Length)); + if (t > 1024) + return new WDist(Math.Max(0, (PointB - p).Length - Radius.Length)); + + var projection = PointA + new int2( + (ab.X * t) / 1024, + (ab.Y * t) / 1024); + + var distance = (projection - p).Length; + + return new WDist(Math.Max(0, distance - Radius.Length)); + } + + public WDist DistanceFromEdge(WPos pos, Actor actor) + { + return DistanceFromEdge((pos - actor.CenterPosition).Rotate(-actor.Orientation)); + } + + public void DrawCombatOverlay(WorldRenderer wr, RgbaColorRenderer wcr, Actor actor) + { + var a = actor.CenterPosition + new WVec(PointA.X, PointA.Y, 0).Rotate(actor.Orientation); + var b = actor.CenterPosition + new WVec(PointB.X, PointB.Y, 0).Rotate(actor.Orientation); + + var offset = new WVec(a.Y - b.Y, b.X - a.X, 0); + offset = offset * Radius.Length / offset.Length; + + var c = Color.Yellow; + RangeCircleRenderable.DrawRangeCircle(wr, a, Radius, 1, c, 0, c); + RangeCircleRenderable.DrawRangeCircle(wr, b, Radius, 1, c, 0, c); + wcr.DrawLine(new[] { wr.ScreenPosition(a - offset), wr.ScreenPosition(b - offset) }, 1, c); + wcr.DrawLine(new[] { wr.ScreenPosition(a + offset), wr.ScreenPosition(b + offset) }, 1, c); + } + } +} \ No newline at end of file diff --git a/OpenRA.Mods.Common/HitShapes/Circle.cs b/OpenRA.Mods.Common/HitShapes/Circle.cs new file mode 100644 index 0000000000..1636350537 --- /dev/null +++ b/OpenRA.Mods.Common/HitShapes/Circle.cs @@ -0,0 +1,46 @@ +#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; +using System.Drawing; +using OpenRA.Graphics; +using OpenRA.Mods.Common.Graphics; + +namespace OpenRA.Mods.Common.HitShapes +{ + public class CircleShape : IHitShape + { + public WDist OuterRadius { get { return Radius; } } + + [FieldLoader.Require] + public readonly WDist Radius = new WDist(426); + + public CircleShape() { } + + public CircleShape(WDist radius) { Radius = radius; } + + public void Initialize() { } + + public WDist DistanceFromEdge(WVec v) + { + return new WDist(Math.Max(0, v.Length - Radius.Length)); + } + + public WDist DistanceFromEdge(WPos pos, Actor actor) + { + return DistanceFromEdge(pos - actor.CenterPosition); + } + + public void DrawCombatOverlay(WorldRenderer wr, RgbaColorRenderer wcr, Actor actor) + { + RangeCircleRenderable.DrawRangeCircle(wr, actor.CenterPosition, Radius, 1, Color.Yellow, 0, Color.Yellow); + } + } +} diff --git a/OpenRA.Mods.Common/HitShapes/IHitShape.cs b/OpenRA.Mods.Common/HitShapes/IHitShape.cs new file mode 100644 index 0000000000..b60c23a3ab --- /dev/null +++ b/OpenRA.Mods.Common/HitShapes/IHitShape.cs @@ -0,0 +1,25 @@ +#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 OpenRA.Graphics; + +namespace OpenRA.Mods.Common.HitShapes +{ + public interface IHitShape + { + WDist OuterRadius { get; } + + WDist DistanceFromEdge(WVec v); + WDist DistanceFromEdge(WPos pos, Actor actor); + + void Initialize(); + void DrawCombatOverlay(WorldRenderer wr, RgbaColorRenderer wcr, Actor actor); + } +} diff --git a/OpenRA.Mods.Common/HitShapes/Rectangle.cs b/OpenRA.Mods.Common/HitShapes/Rectangle.cs new file mode 100644 index 0000000000..776a8d8bf7 --- /dev/null +++ b/OpenRA.Mods.Common/HitShapes/Rectangle.cs @@ -0,0 +1,80 @@ +#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; +using System.Drawing; +using System.Linq; +using OpenRA.Graphics; + +namespace OpenRA.Mods.Common.HitShapes +{ + public class RectangleShape : IHitShape + { + public WDist OuterRadius { get; private set; } + + [FieldLoader.Require] + public readonly int2 TopLeft; + + [FieldLoader.Require] + public readonly int2 BottomRight; + + int2 quadrantSize; + int2 center; + + WVec[] combatOverlayVerts; + + public RectangleShape() { } + + public RectangleShape(int2 tl, int2 br) + { + TopLeft = tl; + BottomRight = br; + } + + public void Initialize() + { + if (TopLeft.X >= BottomRight.X || TopLeft.Y >= BottomRight.Y) + throw new YamlException("TopLeft and BottomRight points are invalid."); + + quadrantSize = (BottomRight - TopLeft) / 2; + center = TopLeft + quadrantSize; + + OuterRadius = new WDist(Math.Max(TopLeft.Length, BottomRight.Length)); + + combatOverlayVerts = new WVec[] + { + new WVec(TopLeft.X, TopLeft.Y, 0), + new WVec(BottomRight.X, TopLeft.Y, 0), + new WVec(BottomRight.X, BottomRight.Y, 0), + new WVec(TopLeft.X, BottomRight.Y, 0) + }; + } + + public WDist DistanceFromEdge(WVec v) + { + var r = new int2( + Math.Max(Math.Abs(v.X - center.X) - quadrantSize.X, 0), + Math.Max(Math.Abs(v.Y - center.Y) - quadrantSize.Y, 0)); + + return new WDist(r.Length); + } + + public WDist DistanceFromEdge(WPos pos, Actor actor) + { + return DistanceFromEdge((pos - actor.CenterPosition).Rotate(-actor.Orientation)); + } + + public void DrawCombatOverlay(WorldRenderer wr, RgbaColorRenderer wcr, Actor actor) + { + var verts = combatOverlayVerts.Select(v => wr.ScreenPosition(actor.CenterPosition + v.Rotate(actor.Orientation))); + wcr.DrawPolygon(verts.ToArray(), 1, Color.Yellow); + } + } +} diff --git a/OpenRA.Mods.Common/Lint/CheckTargetHealthRadius.cs b/OpenRA.Mods.Common/Lint/CheckTargetHealthRadius.cs index e13081670e..15eb1232b7 100644 --- a/OpenRA.Mods.Common/Lint/CheckTargetHealthRadius.cs +++ b/OpenRA.Mods.Common/Lint/CheckTargetHealthRadius.cs @@ -44,7 +44,7 @@ namespace OpenRA.Mods.Common.Lint if (!warhead.ValidTargets.Overlaps(targetable)) continue; - if (healthTraits.Where(x => x.Radius.Length > warhead.TargetExtraSearchRadius.Length).Any()) + if (healthTraits.Where(x => x.Shape.OuterRadius.Length > warhead.TargetExtraSearchRadius.Length).Any()) emitError("Actor type `{0}` has a health radius exceeding the victim scan radius of a warhead on `{1}`!" .F(actorInfo.Key, weaponInfo.Key)); } diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index b77937ff83..5a48825910 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -177,6 +177,10 @@ + + + + diff --git a/OpenRA.Mods.Common/Traits/CombatDebugOverlay.cs b/OpenRA.Mods.Common/Traits/CombatDebugOverlay.cs index ed0d04ec8c..e016bf9620 100644 --- a/OpenRA.Mods.Common/Traits/CombatDebugOverlay.cs +++ b/OpenRA.Mods.Common/Traits/CombatDebugOverlay.cs @@ -12,7 +12,6 @@ using System; using System.Drawing; using OpenRA.Graphics; using OpenRA.Mods.Common.Effects; -using OpenRA.Mods.Common.Graphics; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -48,13 +47,12 @@ namespace OpenRA.Mods.Common.Traits if (devMode == null || !devMode.ShowCombatGeometry) return; - if (healthInfo != null) - RangeCircleRenderable.DrawRangeCircle(wr, self.CenterPosition, healthInfo.Radius, - 1, Color.Red, 0, Color.Red); - var wcr = Game.Renderer.WorldRgbaColorRenderer; var iz = 1 / wr.Viewport.Zoom; + if (healthInfo != null) + healthInfo.Shape.DrawCombatOverlay(wr, wcr, self); + if (blockInfo != null) { var hc = Color.Orange; diff --git a/OpenRA.Mods.Common/Traits/Health.cs b/OpenRA.Mods.Common/Traits/Health.cs index 7da9ddcc96..0eaf6d65ba 100644 --- a/OpenRA.Mods.Common/Traits/Health.cs +++ b/OpenRA.Mods.Common/Traits/Health.cs @@ -8,7 +8,9 @@ */ #endregion +using System; using System.Linq; +using OpenRA.Mods.Common.HitShapes; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -17,13 +19,39 @@ namespace OpenRA.Mods.Common.Traits { [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; + [FieldLoader.LoadUsing("LoadShape")] + public readonly IHitShape Shape; + + static object LoadShape(MiniYaml yaml) + { + IHitShape ret; + + var shapeNode = yaml.Nodes.Find(n => n.Key == "Shape"); + var shape = shapeNode != null ? shapeNode.Value.Value : string.Empty; + + if (!string.IsNullOrEmpty(shape)) + { + ret = Game.CreateObject(shape + "Shape"); + + try + { + FieldLoader.Load(ret, shapeNode.Value); + } + catch (YamlException e) + { + throw new YamlException("HitShape {0}: {1}".F(shape, e.Message)); + } + } + else + ret = new CircleShape(); + + ret.Initialize(); + return ret; + } + public virtual object Create(ActorInitializer init) { return new Health(init, this); } } diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs index 68a1d504ea..e7ccf570e3 100644 --- a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs +++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs @@ -2788,6 +2788,25 @@ namespace OpenRA.Mods.Common.UtilityCommands } } + // Refactored Health.Radius to HitShapes + if (engineVersion < 20151227) + { + if (node.Key.StartsWith("Health")) + { + var radius = node.Value.Nodes.FirstOrDefault(x => x.Key == "Radius"); + if (radius != null) + { + var radiusValue = FieldLoader.GetValue("Radius", radius.Value.Value); + node.Value.Nodes.Add(new MiniYamlNode("Shape", "Circle")); + + var shape = node.Value.Nodes.First(x => x.Key == "Shape"); + shape.Value.Nodes.Add(new MiniYamlNode("Radius", radiusValue)); + + node.Value.Nodes.Remove(radius); + } + } + } + UpgradeActorRules(engineVersion, ref node.Value.Nodes, node, depth + 1); } } diff --git a/OpenRA.Mods.Common/Warheads/CreateEffectWarhead.cs b/OpenRA.Mods.Common/Warheads/CreateEffectWarhead.cs index 63d2401d7f..63279db33c 100644 --- a/OpenRA.Mods.Common/Warheads/CreateEffectWarhead.cs +++ b/OpenRA.Mods.Common/Warheads/CreateEffectWarhead.cs @@ -78,7 +78,7 @@ namespace OpenRA.Mods.Common.Warheads continue; // If the impact position is within any actor's health radius, we have a direct hit - if ((unit.CenterPosition - pos).LengthSquared <= healthInfo.Radius.LengthSquared) + if ((unit.CenterPosition - pos).LengthSquared <= healthInfo.Shape.OuterRadius.LengthSquared) return true; } diff --git a/OpenRA.Mods.Common/Warheads/SpreadDamageWarhead.cs b/OpenRA.Mods.Common/Warheads/SpreadDamageWarhead.cs index 9330404895..982640b225 100644 --- a/OpenRA.Mods.Common/Warheads/SpreadDamageWarhead.cs +++ b/OpenRA.Mods.Common/Warheads/SpreadDamageWarhead.cs @@ -66,9 +66,8 @@ namespace OpenRA.Mods.Common.Warheads if (healthInfo == null) continue; - var localModifiers = damageModifiers; - var distance = Math.Max(0, (victim.CenterPosition - pos).Length - healthInfo.Radius.Length); - localModifiers = localModifiers.Append(GetDamageFalloff(distance)); + var distance = healthInfo.Shape.DistanceFromEdge(pos, victim); + var localModifiers = damageModifiers.Append(GetDamageFalloff(distance.Length)); DoImpact(victim, firedBy, localModifiers); } diff --git a/OpenRA.Mods.Common/WorldExtensions.cs b/OpenRA.Mods.Common/WorldExtensions.cs index 5015813b7c..979755341f 100644 --- a/OpenRA.Mods.Common/WorldExtensions.cs +++ b/OpenRA.Mods.Common/WorldExtensions.cs @@ -46,7 +46,7 @@ namespace OpenRA.Mods.Common var actorWidth = 0; var healthInfo = currActor.Info.TraitInfoOrDefault(); if (healthInfo != null) - actorWidth = healthInfo.Radius.Length; + actorWidth = healthInfo.Shape.OuterRadius.Length; var projection = MinimumPointLineProjection(lineStart, lineEnd, currActor.CenterPosition); var distance = (currActor.CenterPosition - projection).HorizontalLength; diff --git a/OpenRA.Test/OpenRA.Mods.Common/ShapeTest.cs b/OpenRA.Test/OpenRA.Mods.Common/ShapeTest.cs new file mode 100644 index 0000000000..5788aee1c0 --- /dev/null +++ b/OpenRA.Test/OpenRA.Mods.Common/ShapeTest.cs @@ -0,0 +1,95 @@ +#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 NUnit.Framework; +using OpenRA.Mods.Common.HitShapes; + +namespace OpenRA.Test +{ + [TestFixture] + public class ShapeTest + { + IHitShape shape; + + [TestCase(TestName = "CircleShape reports accurate distance")] + public void Circle() + { + shape = new CircleShape(new WDist(1234)); + shape.Initialize(); + + Assert.That(shape.DistanceFromEdge(new WVec(100, 100, 0)).Length, + Is.EqualTo(0)); + + Assert.That(shape.DistanceFromEdge(new WVec(1000, 0, 0)).Length, + Is.EqualTo(0)); + + Assert.That(shape.DistanceFromEdge(new WVec(2000, 2000, 0)).Length, + Is.EqualTo(1594)); + + Assert.That(new CircleShape(new WDist(73)) + .DistanceFromEdge(new WVec(150, -100, 0)).Length, + Is.EqualTo(107)); + + Assert.That(new CircleShape(new WDist(55)) + .DistanceFromEdge(new WVec(30, -45, 0)).Length, + Is.EqualTo(0)); + } + + [TestCase(TestName = "CapsuleShape report accurate distance")] + public void Capsule() + { + shape = new CapsuleShape(new int2(-50, 0), new int2(500, 235), new WDist(50)); + shape.Initialize(); + + Assert.That(shape.DistanceFromEdge(new WVec(300, 100, 0)).Length, + Is.EqualTo(0)); + + Assert.That(shape.DistanceFromEdge(new WVec(-50, 0, 0)).Length, + Is.EqualTo(0)); + + Assert.That(shape.DistanceFromEdge(new WVec(518, 451, 0)).Length, + Is.EqualTo(166)); + + Assert.That(shape.DistanceFromEdge(new WVec(-50, -50, 0)).Length, + Is.EqualTo(0)); + + Assert.That(shape.DistanceFromEdge(new WVec(-41, 97, 0)).Length, + Is.EqualTo(35)); + + Assert.That(shape.DistanceFromEdge(new WVec(339, 41, 0)).Length, + Is.EqualTo(64)); + } + + [TestCase(TestName = "RectangleShape report accurate distance")] + public void Rectangle() + { + shape = new RectangleShape(new int2(-123, -456), new int2(100, 100)); + shape.Initialize(); + + Assert.That(shape.DistanceFromEdge(new WVec(10, 10, 0)).Length, + Is.EqualTo(0)); + + Assert.That(shape.DistanceFromEdge(new WVec(-100, 50, 0)).Length, + Is.EqualTo(0)); + + Assert.That(shape.DistanceFromEdge(new WVec(0, 200, 0)).Length, + Is.EqualTo(100)); + + Assert.That(shape.DistanceFromEdge(new WVec(123, 0, 0)).Length, + Is.EqualTo(24)); + + Assert.That(shape.DistanceFromEdge(new WVec(-100, -400, 0)).Length, + Is.EqualTo(0)); + + Assert.That(shape.DistanceFromEdge(new WVec(-1000, -400, 0)).Length, + Is.EqualTo(877)); + } + } +} diff --git a/OpenRA.Test/OpenRA.Test.csproj b/OpenRA.Test/OpenRA.Test.csproj index c74f9b429d..ec093e2fc8 100644 --- a/OpenRA.Test/OpenRA.Test.csproj +++ b/OpenRA.Test/OpenRA.Test.csproj @@ -49,6 +49,7 @@ + @@ -58,6 +59,11 @@ OpenRA.Game False + + {fe6c8cc0-2f07-442a-b29f-17617b3b7fc6} + OpenRA.Mods.Common + False + diff --git a/mods/cnc/rules/defaults.yaml b/mods/cnc/rules/defaults.yaml index e31ef10108..1b2375ccc2 100644 --- a/mods/cnc/rules/defaults.yaml +++ b/mods/cnc/rules/defaults.yaml @@ -163,7 +163,8 @@ Inherits@2: ^GainsExperience Inherits@3: ^SpriteActor Health: - Radius: 128 + Shape: Circle + Radius: 128 Armor: Type: None RevealsShroud: @@ -286,8 +287,9 @@ Inherits@1: ^ExistsInWorld Inherits@2: ^SpriteActor Health: - Radius: 128 HP: 1000 + Shape: Circle + Radius: 128 Armor: Type: Wood Buildable: @@ -339,8 +341,9 @@ Inherits@1: ^ExistsInWorld Inherits@2: ^SpriteActor Health: - Radius: 427 HP: 300 + Shape: Circle + Radius: 427 Armor: Type: Wood RevealsShroud: diff --git a/mods/d2k/rules/defaults.yaml b/mods/d2k/rules/defaults.yaml index 93b3974a14..ba26ddd6c3 100644 --- a/mods/d2k/rules/defaults.yaml +++ b/mods/d2k/rules/defaults.yaml @@ -160,7 +160,8 @@ Inherits@2: ^GainsExperience Inherits@3: ^SpriteActor Health: - Radius: 96 + Shape: Circle + Radius: 96 Armor: Type: none RevealsShroud: diff --git a/mods/ra/rules/defaults.yaml b/mods/ra/rules/defaults.yaml index 6a6da42e55..2e9849ad09 100644 --- a/mods/ra/rules/defaults.yaml +++ b/mods/ra/rules/defaults.yaml @@ -165,8 +165,9 @@ Inherits@3: ^SpriteActor DrawLineToTarget: Health: - Radius: 128 HP: 25 + Shape: Circle + Radius: 128 Armor: Type: None RevealsShroud: diff --git a/mods/ra/rules/infantry.yaml b/mods/ra/rules/infantry.yaml index 59e1d52dd2..946a6f270c 100644 --- a/mods/ra/rules/infantry.yaml +++ b/mods/ra/rules/infantry.yaml @@ -591,7 +591,8 @@ Ant: VisualBounds: 30,30,0,-2 Health: HP: 750 - Radius: 469 + Shape: Circle + Radius: 469 Mobile: Speed: 99 ROT: 12 diff --git a/mods/ts/rules/civilian-infantry.yaml b/mods/ts/rules/civilian-infantry.yaml index 6e2d74cfb9..c6f4b425e1 100644 --- a/mods/ts/rules/civilian-infantry.yaml +++ b/mods/ts/rules/civilian-infantry.yaml @@ -211,8 +211,9 @@ DOGGIE: Tooltip: Name: Tiberian Fiend Health: - Radius: 213 HP: 250 + Shape: Circle + Radius: 213 PoisonedByTiberium: Weapon: TiberiumHeal Valued: diff --git a/mods/ts/rules/defaults.yaml b/mods/ts/rules/defaults.yaml index 20c1d2f629..9161b90d82 100644 --- a/mods/ts/rules/defaults.yaml +++ b/mods/ts/rules/defaults.yaml @@ -231,8 +231,9 @@ Inherits@3: ^SpriteActor DrawLineToTarget: Health: - Radius: 128 HP: 50 + Shape: Circle + Radius: 128 Armor: Type: None Valued: @@ -518,7 +519,8 @@ Inherits@2: ^SpriteActor DrawLineToTarget: Health: - Radius: 256 + Shape: Circle + Radius: 256 Armor: Type: Light Mobile: