Merge pull request #3531 from pchote/world-targets
Convert more things to world coordinates
This commit is contained in:
@@ -34,6 +34,22 @@ namespace OpenRA
|
||||
public static bool operator ==(WPos me, WPos other) { return (me.X == other.X && me.Y == other.Y && me.Z == other.Z); }
|
||||
public static bool operator !=(WPos me, WPos other) { return !(me == other); }
|
||||
|
||||
public static WPos Lerp(WPos a, WPos b, int mul, int div) { return a + (b - a) * mul / div; }
|
||||
|
||||
public static WPos LerpQuadratic(WPos a, WPos b, WAngle pitch, int mul, int div)
|
||||
{
|
||||
// Start with a linear lerp between the points
|
||||
var ret = Lerp(a, b, mul, div);
|
||||
|
||||
if (pitch.Angle == 0)
|
||||
return ret;
|
||||
|
||||
// Add an additional quadratic variation to height
|
||||
// Uses fp to avoid integer overflow
|
||||
var offset = (int)((float)((float)(b - a).Length*pitch.Tan()*mul*(div - mul)) / (float)(1024*div*div));
|
||||
return new WPos(ret.X, ret.Y, ret.Z + offset);
|
||||
}
|
||||
|
||||
public static WPos Average(params WPos[] list)
|
||||
{
|
||||
if (list == null || list.Length == 0)
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace OpenRA
|
||||
|
||||
public WRange(int r) { Range = r; }
|
||||
public static readonly WRange Zero = new WRange(0);
|
||||
public static WRange FromCells(int cells) { return new WRange(1024*cells); }
|
||||
|
||||
public static WRange operator +(WRange a, WRange b) { return new WRange(a.Range + b.Range); }
|
||||
public static WRange operator -(WRange a, WRange b) { return new WRange(a.Range - b.Range); }
|
||||
|
||||
@@ -53,6 +53,20 @@ namespace OpenRA
|
||||
|
||||
public static WVec Lerp(WVec a, WVec b, int mul, int div) { return a + (b - a) * mul / div; }
|
||||
|
||||
public static WVec LerpQuadratic(WVec a, WVec b, WAngle pitch, int mul, int div)
|
||||
{
|
||||
// Start with a linear lerp between the points
|
||||
var ret = Lerp(a, b, mul, div);
|
||||
|
||||
if (pitch.Angle == 0)
|
||||
return ret;
|
||||
|
||||
// Add an additional quadratic variation to height
|
||||
// Uses fp to avoid integer overflow
|
||||
var offset = (int)((float)((float)(b - a).Length*pitch.Tan()*mul*(div - mul)) / (float)(1024*div*div));
|
||||
return new WVec(ret.X, ret.Y, ret.Z + offset);
|
||||
}
|
||||
|
||||
public override int GetHashCode() { return X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode(); }
|
||||
|
||||
public override bool Equals(object obj)
|
||||
|
||||
@@ -21,8 +21,6 @@ namespace OpenRA
|
||||
public readonly int X, Y;
|
||||
|
||||
public CPos(int x, int y) { X = x; Y = y; }
|
||||
public CPos(WPos a) { X = a.X / 1024; Y = a.Y / 1024; }
|
||||
|
||||
public static readonly CPos Zero = new CPos(0, 0);
|
||||
|
||||
public static explicit operator CPos(int2 a) { return new CPos(a.X, a.Y); }
|
||||
@@ -71,4 +69,9 @@ namespace OpenRA
|
||||
public static CPos TopLeftAsCPos(this Rectangle r) { return new CPos(r.Left, r.Top); }
|
||||
public static CPos BottomRightAsCPos(this Rectangle r) { return new CPos(r.Right, r.Bottom); }
|
||||
}
|
||||
}
|
||||
|
||||
public static class WPosExtensions
|
||||
{
|
||||
public static CPos ToCPos(this WPos a) { return new CPos(a.X / 1024, a.Y / 1024); }
|
||||
}
|
||||
}
|
||||
@@ -137,5 +137,37 @@ namespace OpenRA.GameRules
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public bool IsValidAgainst(Actor a)
|
||||
{
|
||||
var targetable = a.TraitOrDefault<ITargetable>();
|
||||
if (targetable == null || !ValidTargets.Intersect(targetable.TargetTypes).Any())
|
||||
return false;
|
||||
|
||||
if (Warheads.All(w => w.EffectivenessAgainst(a) <= 0))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsValidAgainst(Target target, World world)
|
||||
{
|
||||
if (!target.IsValid)
|
||||
return false;
|
||||
|
||||
if (target.IsActor)
|
||||
return IsValidAgainst(target.Actor);
|
||||
else
|
||||
{
|
||||
var cell = target.CenterPosition.ToCPos();
|
||||
if (ValidTargets.Contains("Ground") && world.GetTerrainType(cell) != "Water")
|
||||
return true;
|
||||
|
||||
if (ValidTargets.Contains("Water") && world.GetTerrainType(cell) == "Water")
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,13 +60,13 @@ namespace OpenRA.Graphics
|
||||
|
||||
// Start of the first line segment is the tail of the list - don't smooth it.
|
||||
var curPos = trail[idx(next - skip - 1)];
|
||||
var curCell = new CPos(curPos);
|
||||
var curCell = curPos.ToCPos();
|
||||
var curColor = color;
|
||||
for (var i = 0; i < length - skip - 4; i++)
|
||||
{
|
||||
var j = next - skip - i - 2;
|
||||
var nextPos = WPos.Average(trail[idx(j)], trail[idx(j-1)], trail[idx(j-2)], trail[idx(j-3)]);
|
||||
var nextCell = new CPos(nextPos);
|
||||
var nextCell = nextPos.ToCPos();
|
||||
var nextColor = Exts.ColorLerp(i * 1f / (length - 4), color, Color.Transparent);
|
||||
|
||||
if (!world.FogObscures(curCell) && !world.FogObscures(nextCell))
|
||||
|
||||
@@ -80,6 +80,31 @@ namespace OpenRA
|
||||
il.EmitCall(OpCodes.Call, ((Func<PSubVec, int>)hash_PSubVec).Method, null);
|
||||
il.Emit(OpCodes.Xor);
|
||||
}
|
||||
else if (type == typeof(WRange))
|
||||
{
|
||||
il.EmitCall(OpCodes.Call, ((Func<WRange, int>)(a => a.GetHashCode())).Method, null);
|
||||
il.Emit(OpCodes.Xor);
|
||||
}
|
||||
else if (type == typeof(WPos))
|
||||
{
|
||||
il.EmitCall(OpCodes.Call, ((Func<WPos, int>)(a => a.GetHashCode())).Method, null);
|
||||
il.Emit(OpCodes.Xor);
|
||||
}
|
||||
else if (type == typeof(WVec))
|
||||
{
|
||||
il.EmitCall(OpCodes.Call, ((Func<WVec, int>)(a => a.GetHashCode())).Method, null);
|
||||
il.Emit(OpCodes.Xor);
|
||||
}
|
||||
else if (type == typeof(WAngle))
|
||||
{
|
||||
il.EmitCall(OpCodes.Call, ((Func<WAngle, int>)(a => a.GetHashCode())).Method, null);
|
||||
il.Emit(OpCodes.Xor);
|
||||
}
|
||||
else if (type == typeof(WRot))
|
||||
{
|
||||
il.EmitCall(OpCodes.Call, ((Func<WRot, int>)(a => a.GetHashCode())).Method, null);
|
||||
il.Emit(OpCodes.Xor);
|
||||
}
|
||||
else if (type == typeof(TypeDictionary))
|
||||
{
|
||||
il.EmitCall(OpCodes.Call, ((Func<TypeDictionary, int>)hash_tdict).Method, null);
|
||||
|
||||
@@ -52,8 +52,6 @@ namespace OpenRA.Traits
|
||||
|
||||
public void RenderAfterWorld(WorldRenderer wr)
|
||||
{
|
||||
//if (self.IsIdle) return;
|
||||
|
||||
var force = Game.GetModifierKeys().HasModifier(Modifiers.Alt);
|
||||
if ((lifetime <= 0 || --lifetime <= 0) && !force)
|
||||
return;
|
||||
@@ -61,9 +59,7 @@ namespace OpenRA.Traits
|
||||
if (targets == null || targets.Count == 0)
|
||||
return;
|
||||
|
||||
var move = self.TraitOrDefault<IMove>();
|
||||
var origin = (move != null ? self.CenterLocation - new PVecInt(0, move.Altitude) : self.CenterLocation).ToFloat2();
|
||||
|
||||
var from = wr.ScreenPxPosition(self.CenterPosition);
|
||||
var wlr = Game.Renderer.WorldLineRenderer;
|
||||
|
||||
foreach (var target in targets)
|
||||
@@ -71,9 +67,10 @@ namespace OpenRA.Traits
|
||||
if (!target.IsValid)
|
||||
continue;
|
||||
|
||||
wlr.DrawLine(origin, target.CenterLocation.ToFloat2(), c, c);
|
||||
DrawTargetMarker(wlr, target.CenterLocation.ToFloat2());
|
||||
DrawTargetMarker(wlr, origin);
|
||||
var to = wr.ScreenPxPosition(target.CenterPosition);
|
||||
wlr.DrawLine(from, to, c, c);
|
||||
DrawTargetMarker(wlr, from);
|
||||
DrawTargetMarker(wlr, to);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,9 +46,9 @@ namespace OpenRA.Traits
|
||||
var Xy = new float2(bounds.Right, bounds.Top);
|
||||
|
||||
wr.DrawSelectionBox(self, Color.White);
|
||||
DrawHealthBar(self, xy, Xy);
|
||||
DrawExtraBars(self, xy, Xy);
|
||||
DrawUnitPath(self);
|
||||
DrawHealthBar(wr, self, xy, Xy);
|
||||
DrawExtraBars(wr, self, xy, Xy);
|
||||
DrawUnitPath(wr, self);
|
||||
}
|
||||
|
||||
public void DrawRollover(WorldRenderer wr, Actor self)
|
||||
@@ -61,11 +61,11 @@ namespace OpenRA.Traits
|
||||
var xy = new float2(bounds.Left, bounds.Top);
|
||||
var Xy = new float2(bounds.Right, bounds.Top);
|
||||
|
||||
DrawHealthBar(self, xy, Xy);
|
||||
DrawExtraBars(self, xy, Xy);
|
||||
DrawHealthBar(wr, self, xy, Xy);
|
||||
DrawExtraBars(wr, self, xy, Xy);
|
||||
}
|
||||
|
||||
void DrawExtraBars(Actor self, float2 xy, float2 Xy)
|
||||
void DrawExtraBars(WorldRenderer wr, Actor self, float2 xy, float2 Xy)
|
||||
{
|
||||
foreach (var extraBar in self.TraitsImplementing<ISelectionBar>())
|
||||
{
|
||||
@@ -74,12 +74,12 @@ namespace OpenRA.Traits
|
||||
{
|
||||
xy.Y += 4;
|
||||
Xy.Y += 4;
|
||||
DrawSelectionBar(self, xy, Xy, extraBar.GetValue(), extraBar.GetColor());
|
||||
DrawSelectionBar(wr, self, xy, Xy, extraBar.GetValue(), extraBar.GetColor());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawSelectionBar(Actor self, float2 xy, float2 Xy, float value, Color barColor)
|
||||
void DrawSelectionBar(WorldRenderer wr, Actor self, float2 xy, float2 Xy, float value, Color barColor)
|
||||
{
|
||||
if (!self.IsInWorld) return;
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace OpenRA.Traits
|
||||
wlr.DrawLine(xy + new float2(0, -4), z + new float2(0, -4), barColor2, barColor2);
|
||||
}
|
||||
|
||||
void DrawHealthBar(Actor self, float2 xy, float2 Xy)
|
||||
void DrawHealthBar(WorldRenderer wr, Actor self, float2 xy, float2 Xy)
|
||||
{
|
||||
if (!self.IsInWorld) return;
|
||||
|
||||
@@ -148,24 +148,21 @@ namespace OpenRA.Traits
|
||||
}
|
||||
}
|
||||
|
||||
void DrawUnitPath(Actor self)
|
||||
void DrawUnitPath(WorldRenderer wr, Actor self)
|
||||
{
|
||||
if (self.World.LocalPlayer == null ||!self.World.LocalPlayer.PlayerActor.Trait<DeveloperMode>().PathDebug) return;
|
||||
|
||||
var activity = self.GetCurrentActivity();
|
||||
var mobile = self.TraitOrDefault<IMove>();
|
||||
if (activity != null && mobile != null)
|
||||
if (activity != null)
|
||||
{
|
||||
var alt = new float2(0, -mobile.Altitude);
|
||||
var targets = activity.GetTargets(self);
|
||||
var start = self.CenterLocation.ToFloat2() + alt;
|
||||
var start = wr.ScreenPxPosition(self.CenterPosition);
|
||||
|
||||
var c = Color.Green;
|
||||
|
||||
var wlr = Game.Renderer.WorldLineRenderer;
|
||||
foreach (var step in targets.Select(p => p.CenterLocation.ToFloat2()))
|
||||
foreach (var stp in targets.Where(t => t.IsValid).Select(p => wr.ScreenPxPosition(p.CenterPosition)))
|
||||
{
|
||||
var stp = step + alt;
|
||||
wlr.DrawLine(stp + new float2(-1, -1), stp + new float2(-1, 1), c, c);
|
||||
wlr.DrawLine(stp + new float2(-1, 1), stp + new float2(1, 1), c, c);
|
||||
wlr.DrawLine(stp + new float2(1, 1), stp + new float2(1, -1), c, c);
|
||||
|
||||
@@ -8,17 +8,32 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenRA.Traits
|
||||
{
|
||||
public struct Target // a target: either an actor, or a fixed location.
|
||||
public struct Target
|
||||
{
|
||||
public static Target[] NoTargets = {};
|
||||
public static readonly Target[] NoTargets = {};
|
||||
public static readonly Target None = new Target();
|
||||
|
||||
Actor actor;
|
||||
PPos pos;
|
||||
WPos pos;
|
||||
bool valid;
|
||||
int generation;
|
||||
|
||||
public static Target FromPos(WPos p) { return new Target { pos = p, valid = true }; }
|
||||
public static Target FromPos(PPos p) { return new Target { pos = p.ToWPos(0), valid = true }; }
|
||||
public static Target FromCell(CPos c) { return new Target { pos = c.CenterPosition, valid = true }; }
|
||||
public static Target FromOrder(Order o)
|
||||
{
|
||||
return o.TargetActor != null
|
||||
? Target.FromActor(o.TargetActor)
|
||||
: Target.FromCell(o.TargetLocation);
|
||||
}
|
||||
|
||||
public static Target FromActor(Actor a)
|
||||
{
|
||||
return new Target
|
||||
@@ -28,22 +43,56 @@ namespace OpenRA.Traits
|
||||
generation = a.Generation,
|
||||
};
|
||||
}
|
||||
public static Target FromPos(PPos p) { return new Target { pos = p, valid = true }; }
|
||||
public static Target FromCell(CPos c) { return new Target { pos = Util.CenterOfCell(c), valid = true }; }
|
||||
public static Target FromOrder(Order o)
|
||||
{
|
||||
return o.TargetActor != null
|
||||
? Target.FromActor(o.TargetActor)
|
||||
: Target.FromCell(o.TargetLocation);
|
||||
}
|
||||
|
||||
public static readonly Target None = new Target();
|
||||
|
||||
public bool IsValid { get { return valid && (actor == null || (actor.IsInWorld && !actor.IsDead() && actor.Generation == generation)); } }
|
||||
public PPos PxPosition { get { return IsActor ? actor.Trait<IHasLocation>().PxPosition : pos; } }
|
||||
public PPos PxPosition { get { return IsActor ? actor.Trait<IHasLocation>().PxPosition : PPos.FromWPos(pos); } }
|
||||
public PPos CenterLocation { get { return PxPosition; } }
|
||||
|
||||
public Actor Actor { get { return IsActor ? actor : null; } }
|
||||
|
||||
// TODO: This should return true even if the actor is destroyed
|
||||
public bool IsActor { get { return actor != null && !actor.Destroyed; } }
|
||||
|
||||
// Representative position - see Positions for the full set of targetable positions.
|
||||
public WPos CenterPosition
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!IsValid)
|
||||
throw new InvalidOperationException("Attempting to query the position of an invalid Target");
|
||||
|
||||
return actor != null ? actor.CenterPosition : pos;
|
||||
}
|
||||
}
|
||||
|
||||
// Positions available to target for range checks
|
||||
static readonly WPos[] NoPositions = {};
|
||||
public IEnumerable<WPos> Positions
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!IsValid)
|
||||
return NoPositions;
|
||||
|
||||
if (actor == null)
|
||||
return new []{pos};
|
||||
|
||||
var targetable = actor.TraitOrDefault<ITargetable>();
|
||||
if (targetable == null)
|
||||
return new []{actor.CenterPosition};
|
||||
|
||||
return targetable.TargetableCells(actor).Select(c => c.CenterPosition);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsInRange(WPos origin, WRange range)
|
||||
{
|
||||
if (!IsValid)
|
||||
return false;
|
||||
|
||||
// Target ranges are calculated in 2D, so ignore height differences
|
||||
return Positions.Any(t => (t.X - origin.X)*(t.X - origin.X) +
|
||||
(t.Y - origin.Y)*(t.Y - origin.Y) <= range.Range*range.Range);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,11 @@ namespace OpenRA.Traits
|
||||
return ( facing - rot ) & 0xFF;
|
||||
}
|
||||
|
||||
public static int GetFacing(WVec d, int currentFacing)
|
||||
{
|
||||
return GetFacing(new int2(d.X, d.Y), currentFacing);
|
||||
}
|
||||
|
||||
public static int GetFacing(PVecInt d, int currentFacing)
|
||||
{
|
||||
return GetFacing(d.ToInt2(), currentFacing);
|
||||
@@ -40,7 +45,7 @@ namespace OpenRA.Traits
|
||||
return GetFacing(d.ToInt2(), currentFacing);
|
||||
}
|
||||
|
||||
public static int GetFacing( int2 d, int currentFacing )
|
||||
public static int GetFacing(int2 d, int currentFacing)
|
||||
{
|
||||
if (d == int2.Zero)
|
||||
return currentFacing;
|
||||
@@ -177,7 +182,7 @@ namespace OpenRA.Traits
|
||||
: new CPos[] {};
|
||||
|
||||
if (cells.Length == 0)
|
||||
cells = new CPos[] { target.CenterLocation.ToCPos() };
|
||||
cells = new CPos[] { target.CenterPosition.ToCPos() };
|
||||
|
||||
return Util.ExpandFootprint(cells, true);
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ namespace OpenRA.Traits
|
||||
return cells.Select(c => c.First);
|
||||
}
|
||||
|
||||
return new[] { a.CenterLocation.ToCPos() };
|
||||
return new[] { a.CenterPosition.ToCPos() };
|
||||
}
|
||||
|
||||
public void Explore(World world, CPos center, int range)
|
||||
|
||||
@@ -39,6 +39,11 @@ namespace OpenRA
|
||||
return actors.OrderBy( a => (a.CenterLocation - px).LengthSquared ).FirstOrDefault();
|
||||
}
|
||||
|
||||
public static IEnumerable<Actor> FindUnitsInCircle(this World world, WPos a, WRange r)
|
||||
{
|
||||
return world.FindUnitsInCircle(PPos.FromWPos(a), r.Range * Game.CellSize / 1024);
|
||||
}
|
||||
|
||||
public static IEnumerable<Actor> FindUnitsInCircle(this World world, PPos a, int r)
|
||||
{
|
||||
using (new PerfSample("FindUnitsInCircle"))
|
||||
|
||||
@@ -27,16 +27,15 @@ namespace OpenRA.Mods.Cnc
|
||||
readonly RenderUnit ru;
|
||||
State state;
|
||||
|
||||
PPos startDock;
|
||||
PPos endDock;
|
||||
WPos startDock, endDock;
|
||||
public HarvesterDockSequence(Actor self, Actor proc)
|
||||
{
|
||||
this.proc = proc;
|
||||
state = State.Turn;
|
||||
harv = self.Trait<Harvester>();
|
||||
ru = self.Trait<RenderUnit>();
|
||||
startDock = self.Trait<IHasLocation>().PxPosition;
|
||||
endDock = proc.Trait<IHasLocation>().PxPosition + new PVecInt(-15,8);
|
||||
startDock = self.Trait<IHasLocation>().PxPosition.ToWPos(0);
|
||||
endDock = (proc.Trait<IHasLocation>().PxPosition + new PVecInt(-15,8)).ToWPos(0);
|
||||
}
|
||||
|
||||
public override Activity Tick(Actor self)
|
||||
|
||||
@@ -34,15 +34,13 @@ namespace OpenRA.Mods.Cnc.Effects
|
||||
|
||||
public IEnumerable<IRenderable> Render(WorldRenderer wr)
|
||||
{
|
||||
yield return new SpriteRenderable(anim.Image,
|
||||
target.CenterLocation.ToFloat2() - new float2(0, 0.5f*anim.Image.size.Y - Game.CellSize),
|
||||
wr.Palette("effect"), (int)target.CenterLocation.Y);
|
||||
yield return new SpriteRenderable(anim.Image, target.CenterPosition, 1, wr.Palette("effect"), 1.0f);
|
||||
}
|
||||
|
||||
void Finish( World world )
|
||||
void Finish(World world)
|
||||
{
|
||||
world.AddFrameEndTask(w => w.Remove(this));
|
||||
Combat.DoExplosion(firedBy, "IonCannon", target.CenterLocation, 0);
|
||||
Combat.DoExplosion(firedBy, "IonCannon", target.CenterPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -518,7 +518,7 @@ namespace OpenRA.Mods.RA.AI
|
||||
if (owner.attackOrFleeFuzzy.CanAttack)
|
||||
{
|
||||
foreach(var u in owner.units)
|
||||
owner.world.IssueOrder(new Order("AttackMove", u, false) { TargetLocation = owner.Target.CenterLocation.ToCPos() });
|
||||
owner.world.IssueOrder(new Order("AttackMove", u, false) { TargetLocation = owner.Target.CenterPosition.ToCPos() });
|
||||
// We have gathered sufficient units. Attack the nearest enemy unit.
|
||||
owner.fsm.ChangeState(new GroundUnitsAttackMoveState(), true);
|
||||
return;
|
||||
@@ -560,7 +560,7 @@ namespace OpenRA.Mods.RA.AI
|
||||
{
|
||||
owner.world.IssueOrder(new Order("Stop", leader, false));
|
||||
foreach (var unit in owner.units.Where(a => !ownUnits.Contains(a)))
|
||||
owner.world.IssueOrder(new Order("AttackMove", unit, false) { TargetLocation = leader.CenterLocation.ToCPos() });
|
||||
owner.world.IssueOrder(new Order("AttackMove", unit, false) { TargetLocation = leader.CenterPosition.ToCPos() });
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace OpenRA.Mods.RA.Activities
|
||||
{
|
||||
protected Target Target;
|
||||
ITargetable targetable;
|
||||
int Range;
|
||||
WRange Range;
|
||||
bool AllowMovement;
|
||||
|
||||
int nextPathTime;
|
||||
@@ -27,7 +27,10 @@ namespace OpenRA.Mods.RA.Activities
|
||||
const int delayBetweenPathingAttempts = 20;
|
||||
const int delaySpread = 5;
|
||||
|
||||
public Attack(Target target, int range, bool allowMovement)
|
||||
public Attack(Target target, WRange range)
|
||||
: this(target, range, true) {}
|
||||
|
||||
public Attack(Target target, WRange range, bool allowMovement)
|
||||
{
|
||||
Target = target;
|
||||
if (target.IsActor)
|
||||
@@ -37,20 +40,18 @@ namespace OpenRA.Mods.RA.Activities
|
||||
AllowMovement = allowMovement;
|
||||
}
|
||||
|
||||
public Attack(Target target, int range) : this(target, range, true) {}
|
||||
|
||||
public override Activity Tick( Actor self )
|
||||
public override Activity Tick(Actor self)
|
||||
{
|
||||
var attack = self.Trait<AttackBase>();
|
||||
|
||||
var ret = InnerTick( self, attack );
|
||||
attack.IsAttacking = ( ret == this );
|
||||
var ret = InnerTick(self, attack);
|
||||
attack.IsAttacking = (ret == this);
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected virtual Activity InnerTick( Actor self, AttackBase attack )
|
||||
protected virtual Activity InnerTick(Actor self, AttackBase attack)
|
||||
{
|
||||
if (IsCanceled) return NextActivity;
|
||||
if (IsCanceled)
|
||||
return NextActivity;
|
||||
|
||||
if (!Target.IsValid)
|
||||
return NextActivity;
|
||||
@@ -61,7 +62,7 @@ namespace OpenRA.Mods.RA.Activities
|
||||
if (targetable != null && !targetable.TargetableBy(Target.Actor, self))
|
||||
return NextActivity;
|
||||
|
||||
if (!Combat.IsInRange(self.CenterLocation, Range, Target))
|
||||
if (!Target.IsInRange(self.CenterPosition, Range))
|
||||
{
|
||||
if (--nextPathTime > 0)
|
||||
return this;
|
||||
@@ -69,13 +70,13 @@ namespace OpenRA.Mods.RA.Activities
|
||||
nextPathTime = self.World.SharedRandom.Next(delayBetweenPathingAttempts - delaySpread,
|
||||
delayBetweenPathingAttempts + delaySpread);
|
||||
|
||||
return (AllowMovement) ? Util.SequenceActivities(self.Trait<Mobile>().MoveWithinRange(Target, Range), this) : NextActivity;
|
||||
return (AllowMovement) ? Util.SequenceActivities(self.Trait<Mobile>().MoveWithinRange(Target, Range.Range / 1024), this) : NextActivity;
|
||||
}
|
||||
|
||||
var desiredFacing = Util.GetFacing(Target.CenterLocation - self.CenterLocation, 0);
|
||||
var desiredFacing = Util.GetFacing(Target.CenterPosition - self.CenterPosition, 0);
|
||||
var facing = self.Trait<IFacing>();
|
||||
if (facing.Facing != desiredFacing)
|
||||
return Util.SequenceActivities( new Turn( desiredFacing ), this );
|
||||
return Util.SequenceActivities(new Turn(desiredFacing), this);
|
||||
|
||||
attack.DoAttack(self, Target);
|
||||
return this;
|
||||
|
||||
@@ -35,9 +35,9 @@ namespace OpenRA.Mods.RA.Activities
|
||||
|
||||
// Move to the middle of the target, ignoring impassable tiles
|
||||
var mobile = self.Trait<Mobile>();
|
||||
var to = target.CenterLocation;
|
||||
var from = self.CenterLocation;
|
||||
var speed = mobile.MovementSpeedForCell(self, self.Location);
|
||||
var to = target.CenterPosition;
|
||||
var from = self.CenterPosition;
|
||||
var speed = mobile.WorldMovementSpeedForCell(self, self.Location);
|
||||
var length = speed > 0 ? (int)((to - from).Length * 3 / speed) : 0;
|
||||
|
||||
return Util.SequenceActivities(
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace OpenRA.Mods.RA.Activities
|
||||
if (IsCanceled) return NextActivity;
|
||||
if (!Target.IsValid) return NextActivity;
|
||||
|
||||
var inRange = ( Target.CenterLocation.ToCPos() - self.Location ).LengthSquared < Range * Range;
|
||||
var inRange = ( Target.CenterPosition.ToCPos() - self.Location ).LengthSquared < Range * Range;
|
||||
|
||||
if( inRange ) return this;
|
||||
if (--nextPathTime > 0) return this;
|
||||
|
||||
@@ -17,10 +17,10 @@ namespace OpenRA.Mods.RA.Activities
|
||||
/* non-turreted attack */
|
||||
public class Heal : Attack
|
||||
{
|
||||
public Heal(Target target, int range, bool allowMovement)
|
||||
public Heal(Target target, WRange range, bool allowMovement)
|
||||
: base(target, range, allowMovement) {}
|
||||
|
||||
protected override Activity InnerTick( Actor self, AttackBase attack )
|
||||
protected override Activity InnerTick(Actor self, AttackBase attack)
|
||||
{
|
||||
if (Target.IsActor && Target.Actor.GetDamageState() == DamageState.Undamaged)
|
||||
return NextActivity;
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace OpenRA.Mods.RA.Activities
|
||||
|
||||
return Util.SequenceActivities(
|
||||
new MoveAdjacentTo(Target.FromActor(rearmTarget)),
|
||||
mobile.MoveTo(rearmTarget.CenterLocation.ToCPos(), rearmTarget),
|
||||
mobile.MoveTo(rearmTarget.CenterPosition.ToCPos(), rearmTarget),
|
||||
new Rearm(self),
|
||||
new Repair(rearmTarget),
|
||||
this );
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using OpenRA.GameRules;
|
||||
using OpenRA.Mods.RA.Move;
|
||||
using OpenRA.Mods.RA.Render;
|
||||
using OpenRA.Traits;
|
||||
@@ -17,40 +19,54 @@ namespace OpenRA.Mods.RA.Activities
|
||||
{
|
||||
class Leap : Activity
|
||||
{
|
||||
Target target;
|
||||
PPos initialLocation;
|
||||
Mobile mobile;
|
||||
WeaponInfo weapon;
|
||||
|
||||
int moveFraction;
|
||||
const int delay = 6;
|
||||
WPos from;
|
||||
WPos to;
|
||||
int ticks;
|
||||
int length;
|
||||
WAngle angle;
|
||||
|
||||
public Leap(Actor self, Target target)
|
||||
public Leap(Actor self, Actor target, WeaponInfo weapon, WRange speed, WAngle angle)
|
||||
{
|
||||
this.target = target;
|
||||
initialLocation = (PPos) self.Trait<Mobile>().PxPosition;
|
||||
var targetMobile = target.TraitOrDefault<Mobile>();
|
||||
if (targetMobile == null)
|
||||
throw new InvalidOperationException("Leap requires a target actor with the Mobile trait");
|
||||
|
||||
self.Trait<RenderInfantry>().Attacking(self, target);
|
||||
Sound.Play("dogg5p.aud", self.CenterLocation);
|
||||
this.weapon = weapon;
|
||||
this.angle = angle;
|
||||
mobile = self.Trait<Mobile>();
|
||||
mobile.SetLocation(mobile.fromCell, mobile.fromSubCell, targetMobile.fromCell, targetMobile.fromSubCell);
|
||||
mobile.IsMoving = true;
|
||||
|
||||
from = self.CenterPosition;
|
||||
var offset = MobileInfo.SubCellOffsets[targetMobile.fromSubCell];
|
||||
to = targetMobile.fromCell.CenterPosition + new WVec(offset.X * 1024 / Game.CellSize, offset.Y * 1024 / Game.CellSize, 0);
|
||||
length = Math.Max((to - from).Length / speed.Range, 1);
|
||||
|
||||
self.Trait<RenderInfantry>().Attacking(self, Target.FromActor(target));
|
||||
|
||||
if (weapon.Report != null && weapon.Report.Any())
|
||||
Sound.Play(weapon.Report.Random(self.World.SharedRandom), self.CenterLocation);
|
||||
}
|
||||
|
||||
public override Activity Tick(Actor self)
|
||||
{
|
||||
if( moveFraction == 0 && IsCanceled ) return NextActivity;
|
||||
if (!target.IsValid) return NextActivity;
|
||||
if (ticks == 0 && IsCanceled)
|
||||
return NextActivity;
|
||||
|
||||
self.Trait<AttackLeap>().IsLeaping = true;
|
||||
var mobile = self.Trait<Mobile>();
|
||||
++moveFraction;
|
||||
|
||||
mobile.PxPosition = PPos.Lerp(initialLocation, target.PxPosition, moveFraction, delay);
|
||||
|
||||
if (moveFraction >= delay)
|
||||
mobile.AdjustPxPosition(self, PPos.FromWPosHackZ(WPos.LerpQuadratic(from, to, angle, ++ticks, length)));
|
||||
if (ticks >= length)
|
||||
{
|
||||
self.TraitsImplementing<IMove>().FirstOrDefault()
|
||||
.SetPosition(self, target.CenterLocation.ToCPos());
|
||||
mobile.SetLocation(mobile.toCell, mobile.toSubCell, mobile.toCell, mobile.toSubCell);
|
||||
mobile.FinishedMoving(self);
|
||||
mobile.IsMoving = false;
|
||||
|
||||
self.World.ActorMap.GetUnitsAt(mobile.toCell, mobile.toSubCell)
|
||||
.Except(new []{self}).Where(t => weapon.IsValidAgainst(t))
|
||||
.Do(t => t.Kill(self));
|
||||
|
||||
if (target.IsActor)
|
||||
target.Actor.Kill(self);
|
||||
self.Trait<AttackLeap>().IsLeaping = false;
|
||||
return NextActivity;
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace OpenRA.Mods.RA.Activities
|
||||
}
|
||||
|
||||
ps1.heuristic = PathSearch.DefaultEstimator(mobile.toCell);
|
||||
var ps2 = PathSearch.FromPoint(self.World, mobile.Info, self, mobile.toCell, target.CenterLocation.ToCPos(), true);
|
||||
var ps2 = PathSearch.FromPoint(self.World, mobile.Info, self, mobile.toCell, target.CenterPosition.ToCPos(), true);
|
||||
var ret = self.World.WorldActor.Trait<PathFinder>().FindBidiPath(ps1, ps2);
|
||||
|
||||
return Util.SequenceActivities(mobile.MoveTo(() => ret), this);
|
||||
|
||||
@@ -79,23 +79,24 @@ namespace OpenRA.Mods.RA.Activities
|
||||
return this;
|
||||
|
||||
var actor = cargo.Unload(self);
|
||||
var exitPx = Util.CenterOfCell(exitTile.Value);
|
||||
var currentPx = Util.CenterOfCell(self.Location);
|
||||
var exit = exitTile.Value.CenterPosition;
|
||||
var current = self.Location.CenterPosition;
|
||||
|
||||
self.World.AddFrameEndTask(w =>
|
||||
{
|
||||
if (actor.Destroyed) return;
|
||||
if (actor.Destroyed)
|
||||
return;
|
||||
|
||||
var mobile = actor.Trait<Mobile>();
|
||||
mobile.Facing = Util.GetFacing( (exitPx - currentPx).ToInt2(), mobile.Facing );
|
||||
mobile.Facing = Util.GetFacing(exit - current, mobile.Facing );
|
||||
mobile.SetPosition(actor, exitTile.Value);
|
||||
mobile.AdjustPxPosition(actor, currentPx);
|
||||
var speed = mobile.MovementSpeedForCell(actor, exitTile.Value);
|
||||
var length = speed > 0 ? ((int)(exitPx - currentPx).Length * 3 / speed) : 0;
|
||||
mobile.AdjustPxPosition(actor, PPos.FromWPos(current));
|
||||
var speed = mobile.WorldMovementSpeedForCell(actor, exitTile.Value);
|
||||
var length = speed > 0 ? ((int)(exit - current).Length * 3 / speed) : 0;
|
||||
|
||||
w.Add(actor);
|
||||
actor.CancelActivity();
|
||||
actor.QueueActivity(new Drag(currentPx, exitPx, length));
|
||||
actor.QueueActivity(new Drag(current, exit, length));
|
||||
actor.QueueActivity(mobile.MoveTo(exitTile.Value, 0));
|
||||
|
||||
var rallyPoint = ChooseRallyPoint(actor).Value;
|
||||
|
||||
@@ -177,7 +177,7 @@ namespace OpenRA.Mods.RA.Air
|
||||
return new Order(order.OrderID, self, queued) { TargetActor = target.Actor };
|
||||
|
||||
if (order.OrderID == "Move")
|
||||
return new Order(order.OrderID, self, queued) { TargetLocation = target.CenterLocation.ToCPos() };
|
||||
return new Order(order.OrderID, self, queued) { TargetLocation = target.CenterPosition.ToCPos() };
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace OpenRA.Mods.RA.Air
|
||||
if (aircraft.Altitude <= 0)
|
||||
{
|
||||
if (info.Explosion != null)
|
||||
Combat.DoExplosion(self, info.Explosion, self.CenterLocation, 0);
|
||||
Combat.DoExplosion(self, info.Explosion, self.CenterPosition);
|
||||
|
||||
self.Destroy();
|
||||
return null;
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace OpenRA.Mods.RA.Air
|
||||
|
||||
var aircraft = self.Trait<Aircraft>();
|
||||
|
||||
var desiredFacing = Util.GetFacing(d.ToInt2(), aircraft.Facing);
|
||||
var desiredFacing = Util.GetFacing(d, aircraft.Facing);
|
||||
if (aircraft.Altitude == cruiseAltitude)
|
||||
aircraft.Facing = Util.TickFacing(aircraft.Facing, desiredFacing, aircraft.ROT);
|
||||
|
||||
|
||||
@@ -38,12 +38,12 @@ namespace OpenRA.Mods.RA.Air
|
||||
}
|
||||
|
||||
var attack = self.Trait<AttackHeli>();
|
||||
var dist = target.CenterLocation - self.CenterLocation;
|
||||
var dist = target.CenterPosition - self.CenterPosition;
|
||||
|
||||
var desiredFacing = Util.GetFacing(dist, aircraft.Facing);
|
||||
aircraft.Facing = Util.TickFacing(aircraft.Facing, desiredFacing, aircraft.ROT);
|
||||
|
||||
if (!Combat.IsInRange(self.CenterLocation, attack.GetMaximumRange(), target))
|
||||
if (!target.IsInRange(self.CenterPosition, attack.GetMaximumRange()))
|
||||
aircraft.TickMove(PSubPos.PerPx * aircraft.MovementSpeed, desiredFacing);
|
||||
|
||||
attack.DoAttack( self, target );
|
||||
|
||||
@@ -27,8 +27,8 @@ namespace OpenRA.Mods.RA.Air
|
||||
|
||||
if (IsCanceled) return NextActivity;
|
||||
|
||||
var d = Target.CenterLocation - self.CenterLocation;
|
||||
if (d.LengthSquared < 50) /* close enough */
|
||||
var d = Target.CenterPosition - self.CenterPosition;
|
||||
if (d.LengthSquared < 256*256) // close enough (1/4 cell)
|
||||
return NextActivity;
|
||||
|
||||
var aircraft = self.Trait<Aircraft>();
|
||||
|
||||
@@ -107,9 +107,17 @@ namespace OpenRA.Mods.RA
|
||||
if (limitedAmmo != null && !limitedAmmo.HasAmmo())
|
||||
return;
|
||||
|
||||
if (!Combat.IsInRange(self.CenterLocation, Weapon.Range, target)) return;
|
||||
if (Combat.IsInRange(self.CenterLocation, Weapon.MinRange, target)) return;
|
||||
if (!IsValidAgainst(self.World, target)) return;
|
||||
// TODO: Define weapon ranges as WRange
|
||||
var range = new WRange((int)(1024*Weapon.Range));
|
||||
var minRange = new WRange((int)(1024*Weapon.MinRange));
|
||||
if (!target.IsInRange(self.CenterPosition, range))
|
||||
return;
|
||||
|
||||
if (target.IsInRange(self.CenterPosition, minRange))
|
||||
return;
|
||||
|
||||
if (!Weapon.IsValidAgainst(target, self.World))
|
||||
return;
|
||||
|
||||
var barrel = Barrels[Burst % Barrels.Length];
|
||||
var destMove = target.IsActor ? target.Actor.TraitOrDefault<IMove>() : null;
|
||||
@@ -164,14 +172,6 @@ namespace OpenRA.Mods.RA
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsValidAgainst(World world, Target target)
|
||||
{
|
||||
if (target.IsActor)
|
||||
return Combat.WeaponValidForTarget(Weapon, target.Actor);
|
||||
else
|
||||
return Combat.WeaponValidForTarget(Weapon, world, target.CenterLocation.ToCPos());
|
||||
}
|
||||
|
||||
public bool IsReloading { get { return FireDelay > 0; } }
|
||||
|
||||
public WVec MuzzleOffset(Actor self, Barrel b)
|
||||
|
||||
@@ -117,7 +117,7 @@ namespace OpenRA.Mods.RA
|
||||
if (target.IsActor)
|
||||
return new Order("Attack", self, queued) { TargetActor = target.Actor };
|
||||
else
|
||||
return new Order("Attack", self, queued) { TargetLocation = target.CenterLocation.ToCPos() };
|
||||
return new Order("Attack", self, queued) { TargetLocation = target.CenterPosition.ToCPos() };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -139,10 +139,10 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
public abstract Activity GetAttackActivity(Actor self, Target newTarget, bool allowMove);
|
||||
|
||||
public bool HasAnyValidWeapons(Target t) { return Armaments.Any(a => a.IsValidAgainst(self.World, t)); }
|
||||
public float GetMaximumRange() { return Armaments.Select(a => a.Weapon.Range).Aggregate(0f, Math.Max); }
|
||||
public bool HasAnyValidWeapons(Target t) { return Armaments.Any(a => a.Weapon.IsValidAgainst(t, self.World)); }
|
||||
public WRange GetMaximumRange() { return new WRange((int)(1024*Armaments.Max(a => a.Weapon.Range))); }
|
||||
|
||||
public Armament ChooseArmamentForTarget(Target t) { return Armaments.FirstOrDefault(a => a.IsValidAgainst(self.World, t)); }
|
||||
public Armament ChooseArmamentForTarget(Target t) { return Armaments.FirstOrDefault(a => a.Weapon.IsValidAgainst(t, self.World)); }
|
||||
|
||||
public void AttackTarget(Target target, bool queued, bool allowMove)
|
||||
{
|
||||
|
||||
@@ -28,15 +28,15 @@ namespace OpenRA.Mods.RA
|
||||
public AttackFrontal(Actor self, AttackFrontalInfo info)
|
||||
: base( self ) { this.info = info; }
|
||||
|
||||
protected override bool CanAttack( Actor self, Target target )
|
||||
protected override bool CanAttack(Actor self, Target target)
|
||||
{
|
||||
if( !base.CanAttack( self, target ) )
|
||||
if (!base.CanAttack(self, target))
|
||||
return false;
|
||||
|
||||
var facing = self.Trait<IFacing>().Facing;
|
||||
var facingToTarget = Util.GetFacing(target.CenterLocation - self.CenterLocation, facing);
|
||||
var facingToTarget = Util.GetFacing(target.CenterPosition - self.CenterPosition, facing);
|
||||
|
||||
if( Math.Abs( facingToTarget - facing ) % 256 > info.FacingTolerance )
|
||||
if (Math.Abs(facingToTarget - facing) % 256 > info.FacingTolerance)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@@ -44,10 +44,13 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
public override Activity GetAttackActivity(Actor self, Target newTarget, bool allowMove)
|
||||
{
|
||||
var weapon = ChooseArmamentForTarget(newTarget);
|
||||
if (weapon == null)
|
||||
var a = ChooseArmamentForTarget(newTarget);
|
||||
if (a == null)
|
||||
return null;
|
||||
return new Activities.Attack(newTarget, Math.Max(0, (int)weapon.Weapon.Range), allowMove);
|
||||
|
||||
// TODO: Define weapon ranges as WRange
|
||||
var range = new WRange(Math.Max(0,(int)(1024*a.Weapon.Range)));
|
||||
return new Activities.Attack(newTarget, range, allowMove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Mods.RA.Activities;
|
||||
using OpenRA.Traits;
|
||||
|
||||
@@ -17,38 +18,39 @@ namespace OpenRA.Mods.RA
|
||||
{
|
||||
class AttackLeapInfo : AttackFrontalInfo
|
||||
{
|
||||
[Desc("Leap speed (in units/tick).")]
|
||||
public readonly WRange Speed = new WRange(426);
|
||||
public readonly WAngle Angle = WAngle.FromDegrees(20);
|
||||
|
||||
public override object Create(ActorInitializer init) { return new AttackLeap(init.self, this); }
|
||||
}
|
||||
|
||||
class AttackLeap : AttackFrontal, ISync
|
||||
{
|
||||
[Sync] internal bool IsLeaping;
|
||||
AttackLeapInfo info;
|
||||
|
||||
public AttackLeap(Actor self, AttackLeapInfo info)
|
||||
: base(self, info) {}
|
||||
: base(self, info)
|
||||
{
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public override void DoAttack(Actor self, Target target)
|
||||
{
|
||||
if (!CanAttack(self, target))
|
||||
if (!CanAttack(self, target) || !target.IsActor)
|
||||
return;
|
||||
|
||||
var a = ChooseArmamentForTarget(target);
|
||||
if (a == null)
|
||||
return;
|
||||
|
||||
if (!Combat.IsInRange(self.CenterLocation, a.Weapon.Range, target))
|
||||
// TODO: Define weapon ranges as WRange
|
||||
var range = new WRange((int)(1024*a.Weapon.Range));
|
||||
if (!target.IsInRange(self.CenterPosition, range))
|
||||
return;
|
||||
|
||||
self.CancelActivity();
|
||||
self.QueueActivity(new Leap(self, target));
|
||||
}
|
||||
|
||||
public override Activity GetAttackActivity(Actor self, Target newTarget, bool allowMove)
|
||||
{
|
||||
var a = ChooseArmamentForTarget(newTarget);
|
||||
if (a == null)
|
||||
return null;
|
||||
return new Activities.Attack(newTarget, Math.Max(0, (int)a.Weapon.Range), allowMove);
|
||||
self.QueueActivity(new Leap(self, target.Actor, a.Weapon, info.Speed, info.Angle));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,9 @@ namespace OpenRA.Mods.RA
|
||||
if (arm == null)
|
||||
return;
|
||||
|
||||
if (!Combat.IsInRange(self.CenterLocation, arm.Weapon.Range, target))
|
||||
// TODO: Define weapon ranges as WRange
|
||||
var range = new WRange((int)(1024*arm.Weapon.Range));
|
||||
if (!target.IsInRange(self.CenterPosition, range))
|
||||
return;
|
||||
|
||||
var move = self.TraitOrDefault<IMove>();
|
||||
|
||||
@@ -29,10 +29,13 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
public override Activity GetAttackActivity(Actor self, Target newTarget, bool allowMove)
|
||||
{
|
||||
var weapon = ChooseArmamentForTarget(newTarget);
|
||||
if (weapon == null)
|
||||
var a = ChooseArmamentForTarget(newTarget);
|
||||
if (a == null)
|
||||
return null;
|
||||
return new Activities.Heal(newTarget, Math.Max(0, (int)weapon.Weapon.Range), allowMove);
|
||||
|
||||
// TODO: Define weapon ranges as WRange
|
||||
var range = new WRange(Math.Max(0,(int)(1024*a.Weapon.Range)));
|
||||
return new Activities.Heal(newTarget, range, allowMove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,10 +19,10 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
class AutoHeal : INotifyIdle
|
||||
{
|
||||
public void TickIdle( Actor self )
|
||||
public void TickIdle(Actor self)
|
||||
{
|
||||
var attack = self.Trait<AttackBase>();
|
||||
var inRange = self.World.FindUnitsInCircle(self.CenterLocation, (int)(Game.CellSize * attack.GetMaximumRange()));
|
||||
var inRange = self.World.FindUnitsInCircle(self.CenterPosition, attack.GetMaximumRange());
|
||||
|
||||
var target = inRange
|
||||
.Where(a => a != self && a.AppearsFriendlyTo(self))
|
||||
@@ -31,8 +31,8 @@ namespace OpenRA.Mods.RA
|
||||
.Where(a => attack.HasAnyValidWeapons(Target.FromActor(a)))
|
||||
.ClosestTo( self.CenterLocation );
|
||||
|
||||
if( target != null )
|
||||
self.QueueActivity(attack.GetAttackActivity(self, Target.FromActor( target ), false ));
|
||||
if (target != null)
|
||||
self.QueueActivity(attack.GetAttackActivity(self, Target.FromActor(target), false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,8 +87,9 @@ namespace OpenRA.Mods.RA
|
||||
var target = ScanForTarget(self, null);
|
||||
if (target != null)
|
||||
{
|
||||
self.SetTargetLine(Target.FromActor(target), Color.Red, false);
|
||||
attack.AttackTarget(Target.FromActor(target), false, Info.AllowMovement && stance != UnitStance.Defend);
|
||||
var t = Target.FromActor(target);
|
||||
self.SetTargetLine(t, Color.Red, false);
|
||||
attack.AttackTarget(t, false, Info.AllowMovement && stance != UnitStance.Defend);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,10 +101,9 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
public Actor ScanForTarget(Actor self, Actor currentTarget)
|
||||
{
|
||||
var range = Info.ScanRadius > 0 ? Info.ScanRadius : attack.GetMaximumRange();
|
||||
|
||||
if (self.IsIdle || currentTarget == null || !Combat.IsInRange(self.CenterLocation, range, currentTarget))
|
||||
if(nextScanTime <= 0)
|
||||
var range = Info.ScanRadius > 0 ? WRange.FromCells(Info.ScanRadius) : attack.GetMaximumRange();
|
||||
if (self.IsIdle || currentTarget == null || !Target.FromActor(currentTarget).IsInRange(self.CenterPosition, range))
|
||||
if (nextScanTime <= 0)
|
||||
return ChooseTarget(self, range);
|
||||
|
||||
return currentTarget;
|
||||
@@ -116,11 +116,10 @@ namespace OpenRA.Mods.RA
|
||||
attack.AttackTarget(Target.FromActor(targetActor), false, Info.AllowMovement && stance != UnitStance.Defend);
|
||||
}
|
||||
|
||||
Actor ChooseTarget(Actor self, float range)
|
||||
Actor ChooseTarget(Actor self, WRange range)
|
||||
{
|
||||
nextScanTime = self.World.SharedRandom.Next(Info.MinimumScanTimeInterval, Info.MaximumScanTimeInterval);
|
||||
|
||||
var inRange = self.World.FindUnitsInCircle(self.CenterLocation, (int)(Game.CellSize * range));
|
||||
var inRange = self.World.FindUnitsInCircle(self.CenterPosition, range);
|
||||
|
||||
if (self.Owner.HasFogVisibility())
|
||||
{
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace OpenRA.Mods.RA
|
||||
{
|
||||
// Offset effective position to the top of the northernmost occupied cell
|
||||
var bi = self.Info.Traits.GetOrDefault<BuildingInfo>();
|
||||
offset = (bi != null) ? -FootprintUtils.CenterOffset(bi).Y : -512;
|
||||
offset = ((bi != null) ? -FootprintUtils.CenterOffset(bi).Y : 0) - 512;
|
||||
}
|
||||
|
||||
public IEnumerable<IRenderable> ModifyRender(Actor self, WorldRenderer wr, IEnumerable<IRenderable> r)
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace OpenRA.Mods.RA.Buildings
|
||||
{
|
||||
public class BaseProviderInfo : ITraitInfo
|
||||
{
|
||||
public readonly float Range = 10;
|
||||
public readonly int Range = 10;
|
||||
public readonly int Cooldown = 0;
|
||||
public readonly int InitialDelay = 0;
|
||||
|
||||
|
||||
@@ -38,23 +38,20 @@ namespace OpenRA.Mods.RA.Buildings
|
||||
|
||||
public object Create(ActorInitializer init) { return new Building(init, this); }
|
||||
|
||||
public PPos CenterLocation(CPos topLeft)
|
||||
public Actor FindBaseProvider(World world, Player p, CPos topLeft)
|
||||
{
|
||||
return (PPos)((2 * topLeft.ToInt2() + Dimensions) * Game.CellSize / 2);
|
||||
}
|
||||
|
||||
bool HasBaseProvider(World world, Player p, CPos topLeft)
|
||||
{
|
||||
var center = CenterLocation(topLeft);
|
||||
var center = topLeft.CenterPosition + FootprintUtils.CenterOffset(this);
|
||||
foreach (var bp in world.ActorsWithTrait<BaseProvider>())
|
||||
{
|
||||
if (bp.Actor.Owner.Stances[p] != Stance.Ally || !bp.Trait.Ready())
|
||||
continue;
|
||||
|
||||
if (Combat.IsInRange(center, bp.Trait.Info.Range, bp.Actor.CenterLocation))
|
||||
return true;
|
||||
// Range is counted from the center of the actor, not from each cell.
|
||||
var target = Target.FromPos(bp.Actor.CenterPosition);
|
||||
if (target.IsInRange(center, WRange.FromCells(bp.Trait.Info.Range)))
|
||||
return bp.Actor;
|
||||
}
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool IsCloseEnoughToBase(World world, Player p, string buildingName, CPos topLeft)
|
||||
@@ -62,7 +59,7 @@ namespace OpenRA.Mods.RA.Buildings
|
||||
if (p.PlayerActor.Trait<DeveloperMode>().BuildAnywhere)
|
||||
return true;
|
||||
|
||||
if (RequiresBaseProvider && !HasBaseProvider(world, p, topLeft))
|
||||
if (RequiresBaseProvider && FindBaseProvider(world, p, topLeft) == null)
|
||||
return false;
|
||||
|
||||
var buildingMaxBounds = (CVec)Dimensions;
|
||||
@@ -126,7 +123,9 @@ namespace OpenRA.Mods.RA.Buildings
|
||||
|
||||
occupiedCells = FootprintUtils.UnpathableTiles( self.Info.Name, Info, TopLeft )
|
||||
.Select(c => Pair.New(c, SubCell.FullCell)).ToArray();
|
||||
pxPosition = Info.CenterLocation(topLeft);
|
||||
|
||||
var position = topLeft.CenterPosition + FootprintUtils.CenterOffset(Info);
|
||||
pxPosition = PPos.FromWPosHackZ(position);
|
||||
}
|
||||
|
||||
public int GetPowerUsage()
|
||||
|
||||
@@ -65,7 +65,8 @@ namespace OpenRA.Mods.RA.Buildings
|
||||
public static WVec CenterOffset(BuildingInfo buildingInfo)
|
||||
{
|
||||
var dim = buildingInfo.Dimensions;
|
||||
return new CVec(dim.X, dim.Y).ToWVec() / 2;
|
||||
// Offset is measured relative to the center of the cell, so need to subtract an additional half cell.
|
||||
return new CVec(dim.X, dim.Y).ToWVec() / 2 - new WVec(512, 512, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,25 +14,37 @@ using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.RA
|
||||
{
|
||||
class CarpetBombInfo : TraitInfo<CarpetBomb>
|
||||
class CarpetBombInfo : ITraitInfo
|
||||
{
|
||||
[WeaponReference]
|
||||
public readonly string Weapon = null;
|
||||
public readonly int Range = 3;
|
||||
|
||||
public object Create(ActorInitializer init) { return new CarpetBomb(this); }
|
||||
}
|
||||
|
||||
class CarpetBomb : ITick, ISync // TODO: maybe integrate this better with the normal weapons system?
|
||||
// TODO: maybe integrate this better with the normal weapons system?
|
||||
class CarpetBomb : ITick, ISync
|
||||
{
|
||||
[Sync] CPos Target;
|
||||
[Sync] int dropDelay;
|
||||
CarpetBombInfo info;
|
||||
Target target;
|
||||
|
||||
public void SetTarget(CPos targetCell) { Target = targetCell; }
|
||||
[Sync] int dropDelay;
|
||||
[Sync] WRange range;
|
||||
|
||||
public CarpetBomb(CarpetBombInfo info)
|
||||
{
|
||||
this.info = info;
|
||||
|
||||
// TODO: Push this conversion into the yaml
|
||||
range = WRange.FromCells(info.Range);
|
||||
}
|
||||
|
||||
public void SetTarget(CPos targetCell) { target = Target.FromCell(targetCell); }
|
||||
|
||||
public void Tick(Actor self)
|
||||
{
|
||||
var info = self.Info.Traits.Get<CarpetBombInfo>();
|
||||
|
||||
if( !Combat.IsInRange( self.CenterLocation, info.Range, Target.ToPPos() ) )
|
||||
if (!target.IsInRange(self.CenterPosition, range))
|
||||
return;
|
||||
|
||||
var limitedAmmo = self.TraitOrDefault<LimitedAmmo>();
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
if (explosionType != null)
|
||||
world.AddFrameEndTask(
|
||||
w => w.Add(new Explosion(w, args.dest, explosionType, isWater, args.destAltitude)));
|
||||
w => w.Add(new Explosion(w, args.dest.ToWPos(args.destAltitude), explosionType)));
|
||||
|
||||
Sound.Play(GetImpactSound(warhead, isWater), args.dest);
|
||||
|
||||
@@ -139,12 +139,14 @@ namespace OpenRA.Mods.RA
|
||||
}
|
||||
}
|
||||
|
||||
public static void DoExplosion(Actor attacker, string weapontype, PPos pos, int altitude)
|
||||
public static void DoExplosion(Actor attacker, string weapontype, WPos pos)
|
||||
{
|
||||
var pxPos = PPos.FromWPos(pos);
|
||||
var altitude = pos.Z * Game.CellSize / 1024;
|
||||
var args = new ProjectileArgs
|
||||
{
|
||||
src = pos,
|
||||
dest = pos,
|
||||
src = pxPos,
|
||||
dest = pxPos,
|
||||
srcAltitude = altitude,
|
||||
destAltitude = altitude,
|
||||
firedBy = attacker,
|
||||
@@ -154,7 +156,7 @@ namespace OpenRA.Mods.RA
|
||||
};
|
||||
|
||||
if (args.weapon.Report != null && args.weapon.Report.Any())
|
||||
Sound.Play(args.weapon.Report.Random(attacker.World.SharedRandom), pos);
|
||||
Sound.Play(args.weapon.Report.Random(attacker.World.SharedRandom), pxPos);
|
||||
|
||||
DoImpacts(args);
|
||||
}
|
||||
@@ -176,7 +178,8 @@ namespace OpenRA.Mods.RA
|
||||
static float GetDamageToInflict(Actor target, ProjectileArgs args, WarheadInfo warhead, float modifier)
|
||||
{
|
||||
// don't hit air units with splash from ground explosions, etc
|
||||
if (!WeaponValidForTarget(args.weapon, target)) return 0f;
|
||||
if (!args.weapon.IsValidAgainst(target))
|
||||
return 0f;
|
||||
|
||||
var health = target.Info.Traits.GetOrDefault<HealthInfo>();
|
||||
if( health == null ) return 0f;
|
||||
@@ -188,48 +191,5 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
return (float)(rawDamage * multiplier);
|
||||
}
|
||||
|
||||
public static bool WeaponValidForTarget(WeaponInfo weapon, Actor target)
|
||||
{
|
||||
var targetable = target.TraitOrDefault<ITargetable>();
|
||||
if (targetable == null || !weapon.ValidTargets.Intersect(targetable.TargetTypes).Any())
|
||||
return false;
|
||||
|
||||
if (weapon.Warheads.All( w => w.EffectivenessAgainst(target) <= 0))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool WeaponValidForTarget(WeaponInfo weapon, World world, CPos location)
|
||||
{
|
||||
if (weapon.ValidTargets.Contains("Ground") && world.GetTerrainType(location) != "Water") return true;
|
||||
if (weapon.ValidTargets.Contains("Water") && world.GetTerrainType(location) == "Water") return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsInRange(PPos attackOrigin, float range, Actor target)
|
||||
{
|
||||
var rsq = range * range * Game.CellSize * Game.CellSize;
|
||||
foreach (var cell in target.Trait<ITargetable>().TargetableCells(target))
|
||||
if ((attackOrigin - Util.CenterOfCell(cell)).LengthSquared <= rsq)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsInRange(PPos attackOrigin, float range, PPos targetLocation)
|
||||
{
|
||||
var rsq = range * range * Game.CellSize * Game.CellSize;
|
||||
return (attackOrigin - targetLocation).LengthSquared <= rsq;
|
||||
}
|
||||
|
||||
public static bool IsInRange(PPos attackOrigin, float range, Target target)
|
||||
{
|
||||
if (!target.IsValid) return false;
|
||||
if (target.IsActor)
|
||||
return IsInRange(attackOrigin, range, target.Actor);
|
||||
else
|
||||
return IsInRange(attackOrigin, range, target.CenterLocation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
public override void Activate(Actor collector)
|
||||
{
|
||||
Combat.DoExplosion(self, (info as ExplodeCrateActionInfo).Weapon, collector.CenterLocation, 0);
|
||||
Combat.DoExplosion(self, (info as ExplodeCrateActionInfo).Weapon, collector.CenterPosition);
|
||||
base.Activate(collector);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,26 +18,20 @@ namespace OpenRA.Mods.RA.Effects
|
||||
public class Explosion : IEffect
|
||||
{
|
||||
Animation anim;
|
||||
PPos pos;
|
||||
int altitude;
|
||||
WPos pos;
|
||||
|
||||
public Explosion(World world, PPos pixelPos, string style, bool isWater, int altitude)
|
||||
public Explosion(World world, WPos pos, string style)
|
||||
{
|
||||
this.pos = pixelPos;
|
||||
this.altitude = altitude;
|
||||
this.pos = pos;
|
||||
anim = new Animation("explosion");
|
||||
anim.PlayThen(style,
|
||||
() => world.AddFrameEndTask(w => w.Remove(this)));
|
||||
anim.PlayThen(style, () => world.AddFrameEndTask(w => w.Remove(this)));
|
||||
}
|
||||
|
||||
public void Tick( World world ) { anim.Tick(); }
|
||||
|
||||
public IEnumerable<IRenderable> Render(WorldRenderer wr)
|
||||
{
|
||||
var p = pos.ToInt2() - new int2(0, altitude);
|
||||
yield return new SpriteRenderable(anim.Image, p, wr.Palette("effect"), p.Y);
|
||||
yield return new SpriteRenderable(anim.Image, pos, 0, wr.Palette("effect"), 1f);
|
||||
}
|
||||
|
||||
public Player Owner { get { return null; } }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace OpenRA.Mods.RA.Effects
|
||||
void Explode(World world)
|
||||
{
|
||||
world.AddFrameEndTask(w => w.Remove(this));
|
||||
Combat.DoExplosion(firedBy.PlayerActor, weapon, PPos.FromWPos(pos), pos.Z * Game.CellSize / 1024);
|
||||
Combat.DoExplosion(firedBy.PlayerActor, weapon, pos);
|
||||
world.WorldActor.Trait<ScreenShaker>().AddEffect(20, PPos.FromWPos(pos).ToFloat2(), 5);
|
||||
|
||||
foreach (var a in world.ActorsWithTrait<NukePaletteEffect>())
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace OpenRA.Mods.RA.Effects
|
||||
parachuteOffset = pai.Offset;
|
||||
|
||||
// Adjust x,y to match the target subcell
|
||||
cargo.Trait<ITeleportable>().SetPosition(cargo, new CPos(dropPosition));
|
||||
cargo.Trait<ITeleportable>().SetPosition(cargo, dropPosition.ToCPos());
|
||||
var cp = cargo.CenterPosition;
|
||||
pos = new WPos(cp.X, cp.Y, dropPosition.Z);
|
||||
}
|
||||
|
||||
@@ -42,11 +42,7 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
var weapon = ChooseWeaponForExplosion(self);
|
||||
if (weapon != null)
|
||||
{
|
||||
var move = self.TraitOrDefault<IMove>();
|
||||
var altitude = move != null ? move.Altitude : 0;
|
||||
Combat.DoExplosion(e.Attacker, weapon, self.CenterLocation, altitude);
|
||||
}
|
||||
Combat.DoExplosion(e.Attacker, weapon, self.CenterPosition);
|
||||
}
|
||||
|
||||
string ChooseWeaponForExplosion(Actor self)
|
||||
|
||||
@@ -250,7 +250,7 @@ namespace OpenRA.Mods.RA
|
||||
return new Order(order.OrderID, self, queued) { TargetActor = target.Actor };
|
||||
|
||||
if (order.OrderID == "Harvest")
|
||||
return new Order(order.OrderID, self, queued) { TargetLocation = target.CenterLocation.ToCPos() };
|
||||
return new Order(order.OrderID, self, queued) { TargetLocation = target.CenterPosition.ToCPos() };
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace OpenRA.Mods.RA
|
||||
{
|
||||
if( order is BeginMinefieldOrderTargeter )
|
||||
{
|
||||
var start = target.CenterLocation.ToCPos();
|
||||
var start = target.CenterPosition.ToCPos();
|
||||
self.World.OrderGenerator = new MinefieldOrderGenerator( self, start );
|
||||
return new Order("BeginMinefield", self, false) { TargetLocation = start };
|
||||
}
|
||||
|
||||
@@ -297,7 +297,7 @@ namespace OpenRA.Mods.RA.Missions
|
||||
|
||||
var enemy = FirstUnshroudedOrDefault(enemies.OrderBy(u => (self.CenterLocation - u.CenterLocation).LengthSquared), world, 10);
|
||||
if (enemy != null)
|
||||
self.QueueActivity(new AttackMove.AttackMoveActivity(self, new Attack(Target.FromActor(enemy), 3)));
|
||||
self.QueueActivity(new AttackMove.AttackMoveActivity(self, new Attack(Target.FromActor(enemy), WRange.FromCells(3))));
|
||||
}
|
||||
|
||||
void ManageSovietUnits()
|
||||
|
||||
@@ -125,7 +125,7 @@ namespace OpenRA.Mods.RA.Missions
|
||||
|
||||
if (world.FrameNumber == frameInfiltrated + 1500 * 12 && !bridgeTank.IsDead() && bridgeTank.IsInWorld && !bridge.IsDead())
|
||||
{
|
||||
bridgeTank.QueueActivity(new Attack(Target.FromPos(bridge.CenterLocation), 4));
|
||||
bridgeTank.QueueActivity(new Attack(Target.FromPos(bridge.CenterPosition), WRange.FromCells(4)));
|
||||
attackingBridge = true;
|
||||
}
|
||||
if (attackingBridge && bridge.IsDead())
|
||||
@@ -152,7 +152,7 @@ namespace OpenRA.Mods.RA.Missions
|
||||
|
||||
var enemy = enemies.OrderBy(u => (attacker.CenterLocation - u.CenterLocation).LengthSquared).FirstOrDefault();
|
||||
if (enemy != null)
|
||||
attacker.QueueActivity(new AttackMove.AttackMoveActivity(attacker, new Attack(Target.FromActor(enemy), 3)));
|
||||
attacker.QueueActivity(new AttackMove.AttackMoveActivity(attacker, new Attack(Target.FromActor(enemy), WRange.FromCells(3))));
|
||||
else
|
||||
{
|
||||
attackingTown = false;
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace OpenRA.Mods.RA.Missions
|
||||
var targetEnemy = enemies.OrderBy(u => (self.CenterLocation - u.CenterLocation).LengthSquared).FirstOrDefault();
|
||||
if (targetEnemy != null)
|
||||
{
|
||||
self.QueueActivity(new AttackMove.AttackMoveActivity(self, new Attack(Target.FromActor(targetEnemy), 6)));
|
||||
self.QueueActivity(new AttackMove.AttackMoveActivity(self, new Attack(Target.FromActor(targetEnemy), WRange.FromCells(6))));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -213,7 +213,7 @@ namespace OpenRA.Mods.RA.Missions
|
||||
|
||||
var enemy = enemies.OrderBy(u => (self.CenterLocation - u.CenterLocation).LengthSquared).FirstOrDefault();
|
||||
if (enemy != null)
|
||||
self.QueueActivity(queued, new AttackMove.AttackMoveActivity(self, new Attack(Target.FromActor(enemy), 3)));
|
||||
self.QueueActivity(queued, new AttackMove.AttackMoveActivity(self, new Attack(Target.FromActor(enemy), WRange.FromCells(3))));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -258,7 +258,7 @@ namespace OpenRA.Mods.RA.Missions
|
||||
{
|
||||
Sound.Play("reinfor1.aud");
|
||||
foreach (var unit in units)
|
||||
world.CreateActor(unit, greece, startEntryPoint.Location, Util.GetFacing(startBridgeEndPoint.CenterLocation - startEntryPoint.CenterLocation, 0))
|
||||
world.CreateActor(unit, greece, startEntryPoint.Location, Util.GetFacing(startBridgeEndPoint.CenterPosition - startEntryPoint.CenterPosition, 0))
|
||||
.QueueActivity(new Move.Move(startMovePoint.Location, 0));
|
||||
}
|
||||
|
||||
@@ -266,7 +266,7 @@ namespace OpenRA.Mods.RA.Missions
|
||||
{
|
||||
Sound.Play("reinfor1.aud");
|
||||
foreach (var unit in units)
|
||||
world.CreateActor(unit, greece, alliedBaseEntryPoint.Location, Util.GetFacing(alliedBaseMovePoint.CenterLocation - alliedBaseEntryPoint.CenterLocation, 0))
|
||||
world.CreateActor(unit, greece, alliedBaseEntryPoint.Location, Util.GetFacing(alliedBaseMovePoint.CenterPosition - alliedBaseEntryPoint.CenterPosition, 0))
|
||||
.QueueActivity(new Move.Move(alliedBaseMovePoint.Location, 0));
|
||||
}
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ namespace OpenRA.Mods.RA.Missions
|
||||
.Where(a => a.HasTrait<Bridge>() && !a.IsDead())
|
||||
.OrderBy(a => (startJeep.CenterLocation - a.CenterLocation).LengthSquared)
|
||||
.First();
|
||||
Combat.DoExplosion(bridge, "Demolish", bridge.CenterLocation, 0);
|
||||
Combat.DoExplosion(bridge, "Demolish", bridge.CenterPosition);
|
||||
world.WorldActor.Trait<ScreenShaker>().AddEffect(15, bridge.CenterLocation.ToFloat2(), 6);
|
||||
bridge.Kill(bridge);
|
||||
}));
|
||||
|
||||
@@ -214,7 +214,7 @@ namespace OpenRA.Mods.RA.Missions
|
||||
var targetEnemy = enemies.OrderBy(u => (self.CenterLocation - u.CenterLocation).LengthSquared).FirstOrDefault();
|
||||
if (targetEnemy != null)
|
||||
{
|
||||
self.QueueActivity(new AttackMove.AttackMoveActivity(self, new Attack(Target.FromActor(targetEnemy), 3)));
|
||||
self.QueueActivity(new AttackMove.AttackMoveActivity(self, new Attack(Target.FromActor(targetEnemy), WRange.FromCells(3))));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ namespace OpenRA.Mods.RA.Missions
|
||||
|
||||
var enemy = FirstUnshroudedOrDefault(enemies.OrderBy(u => (self.CenterLocation - u.CenterLocation).LengthSquared), world, 20);
|
||||
if (enemy != null)
|
||||
self.QueueActivity(new AttackMove.AttackMoveActivity(self, new Attack(Target.FromActor(enemy), 3)));
|
||||
self.QueueActivity(new AttackMove.AttackMoveActivity(self, new Attack(Target.FromActor(enemy), WRange.FromCells(3))));
|
||||
}
|
||||
|
||||
void SpawnAndAttack(string[] squad, Player owner, CPos location)
|
||||
|
||||
@@ -15,25 +15,25 @@ namespace OpenRA.Mods.RA.Move
|
||||
{
|
||||
public class Drag : Activity
|
||||
{
|
||||
PPos endLocation;
|
||||
PPos startLocation;
|
||||
WPos start, end;
|
||||
int length;
|
||||
int ticks = 0;
|
||||
|
||||
public Drag(PPos start, PPos end, int length)
|
||||
public Drag(WPos start, WPos end, int length)
|
||||
{
|
||||
startLocation = start;
|
||||
endLocation = end;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public override Activity Tick( Actor self )
|
||||
{
|
||||
var mobile = self.Trait<Mobile>();
|
||||
mobile.PxPosition = length > 1
|
||||
? PPos.Lerp(startLocation, endLocation, ticks, length - 1)
|
||||
: endLocation;
|
||||
var pos = length > 1
|
||||
? WPos.Lerp(start, end, ticks, length - 1)
|
||||
: end;
|
||||
|
||||
mobile.PxPosition = PPos.FromWPos(pos);
|
||||
if (++ticks >= length)
|
||||
{
|
||||
mobile.IsMoving = false;
|
||||
@@ -46,7 +46,7 @@ namespace OpenRA.Mods.RA.Move
|
||||
|
||||
public override IEnumerable<Target> GetTargets( Actor self )
|
||||
{
|
||||
yield return Target.FromPos(endLocation);
|
||||
yield return Target.FromPos(end);
|
||||
}
|
||||
|
||||
// Cannot be cancelled
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace OpenRA.Mods.RA.Move
|
||||
return passability.ToBits();
|
||||
}
|
||||
|
||||
public readonly Dictionary<SubCell, PVecInt> SubCellOffsets = new Dictionary<SubCell, PVecInt>()
|
||||
public static readonly Dictionary<SubCell, PVecInt> SubCellOffsets = new Dictionary<SubCell, PVecInt>()
|
||||
{
|
||||
{SubCell.TopLeft, new PVecInt(-7,-6)},
|
||||
{SubCell.TopRight, new PVecInt(6,-6)},
|
||||
@@ -170,7 +170,9 @@ namespace OpenRA.Mods.RA.Move
|
||||
|
||||
public void SetLocation(CPos from, SubCell fromSub, CPos to, SubCell toSub)
|
||||
{
|
||||
if (fromCell == from && toCell == to) return;
|
||||
if (fromCell == from && toCell == to && fromSubCell == fromSub && toSubCell == toSub)
|
||||
return;
|
||||
|
||||
RemoveInfluence();
|
||||
__fromCell = from;
|
||||
__toCell = to;
|
||||
@@ -197,7 +199,7 @@ namespace OpenRA.Mods.RA.Move
|
||||
if (init.Contains<LocationInit>())
|
||||
{
|
||||
this.__fromCell = this.__toCell = init.Get<LocationInit, CPos>();
|
||||
this.PxPosition = Util.CenterOfCell(fromCell) + info.SubCellOffsets[fromSubCell];
|
||||
this.PxPosition = Util.CenterOfCell(fromCell) + MobileInfo.SubCellOffsets[fromSubCell];
|
||||
}
|
||||
|
||||
this.Facing = init.Contains<FacingInit>() ? init.Get<FacingInit, int>() : info.InitialFacing;
|
||||
@@ -207,7 +209,7 @@ namespace OpenRA.Mods.RA.Move
|
||||
public void SetPosition(Actor self, CPos cell)
|
||||
{
|
||||
SetLocation(cell,fromSubCell, cell,fromSubCell);
|
||||
PxPosition = Util.CenterOfCell(fromCell) + Info.SubCellOffsets[fromSubCell];
|
||||
PxPosition = Util.CenterOfCell(fromCell) + MobileInfo.SubCellOffsets[fromSubCell];
|
||||
FinishedMoving(self);
|
||||
}
|
||||
|
||||
@@ -232,7 +234,7 @@ namespace OpenRA.Mods.RA.Move
|
||||
if (order is MoveOrderTargeter)
|
||||
{
|
||||
if (Info.OnRails) return null;
|
||||
return new Order("Move", self, queued) { TargetLocation = target.CenterLocation.ToCPos() };
|
||||
return new Order("Move", self, queued) { TargetLocation = target.CenterPosition.ToCPos() };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -418,6 +420,11 @@ namespace OpenRA.Mods.RA.Move
|
||||
return (int)(speed / 100);
|
||||
}
|
||||
|
||||
public int WorldMovementSpeedForCell(Actor self, CPos cell)
|
||||
{
|
||||
return MovementSpeedForCell(self, cell) * 1024 / Game.CellSize;
|
||||
}
|
||||
|
||||
public void AddInfluence()
|
||||
{
|
||||
if (self.IsInWorld)
|
||||
|
||||
@@ -58,11 +58,18 @@ namespace OpenRA.Mods.RA.Move
|
||||
this.ignoreBuilding = ignoreBuilding;
|
||||
}
|
||||
|
||||
static readonly List<CPos> NoPath = new List<CPos>();
|
||||
public Move(Target target, int range)
|
||||
{
|
||||
this.getPath = (self,mobile) => self.World.WorldActor.Trait<PathFinder>().FindUnitPathToRange(
|
||||
mobile.toCell, target.CenterLocation.ToCPos(),
|
||||
range, self);
|
||||
this.getPath = (self, mobile) =>
|
||||
{
|
||||
if (!target.IsValid)
|
||||
return NoPath;
|
||||
|
||||
return self.World.WorldActor.Trait<PathFinder>().FindUnitPathToRange(
|
||||
mobile.toCell, target.CenterPosition.ToCPos(), range, self);
|
||||
};
|
||||
|
||||
this.destination = null;
|
||||
this.nearEnough = range;
|
||||
}
|
||||
@@ -144,8 +151,8 @@ namespace OpenRA.Mods.RA.Move
|
||||
mobile.SetLocation( mobile.fromCell, mobile.fromSubCell, nextCell.Value.First, nextCell.Value.Second );
|
||||
var move = new MoveFirstHalf(
|
||||
this,
|
||||
Util.CenterOfCell( mobile.fromCell ) + mobile.Info.SubCellOffsets[mobile.fromSubCell],
|
||||
Util.BetweenCells( mobile.fromCell, mobile.toCell ) + (mobile.Info.SubCellOffsets[mobile.fromSubCell] + mobile.Info.SubCellOffsets[mobile.toSubCell] ) / 2,
|
||||
Util.CenterOfCell( mobile.fromCell ) + MobileInfo.SubCellOffsets[mobile.fromSubCell],
|
||||
Util.BetweenCells( mobile.fromCell, mobile.toCell ) + (MobileInfo.SubCellOffsets[mobile.fromSubCell] + MobileInfo.SubCellOffsets[mobile.toSubCell] ) / 2,
|
||||
mobile.Facing,
|
||||
mobile.Facing,
|
||||
0 );
|
||||
@@ -335,15 +342,15 @@ namespace OpenRA.Mods.RA.Move
|
||||
|
||||
protected override MovePart OnComplete( Actor self, Mobile mobile, Move parent )
|
||||
{
|
||||
var fromSubcellOffset = mobile.Info.SubCellOffsets[mobile.fromSubCell];
|
||||
var toSubcellOffset = mobile.Info.SubCellOffsets[mobile.toSubCell];
|
||||
var fromSubcellOffset = MobileInfo.SubCellOffsets[mobile.fromSubCell];
|
||||
var toSubcellOffset = MobileInfo.SubCellOffsets[mobile.toSubCell];
|
||||
|
||||
var nextCell = parent.PopPath( self, mobile );
|
||||
if( nextCell != null )
|
||||
{
|
||||
if(IsTurn(mobile, nextCell.Value.First))
|
||||
{
|
||||
var nextSubcellOffset = mobile.Info.SubCellOffsets[nextCell.Value.Second];
|
||||
var nextSubcellOffset = MobileInfo.SubCellOffsets[nextCell.Value.Second];
|
||||
var ret = new MoveFirstHalf(
|
||||
move,
|
||||
Util.BetweenCells( mobile.fromCell, mobile.toCell ) + (fromSubcellOffset + toSubcellOffset) / 2,
|
||||
|
||||
@@ -98,7 +98,7 @@ namespace OpenRA.Mods.RA.Orders
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
var offset = (topLeft - CPos.Zero).ToWVec() + FootprintUtils.CenterOffset(BuildingInfo);
|
||||
var offset = topLeft.CenterPosition + FootprintUtils.CenterOffset(BuildingInfo) - WPos.Zero;
|
||||
foreach (var r in preview)
|
||||
r.WithPos(r.Pos + offset).Render(wr);
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace OpenRA.Mods.RA
|
||||
if (self.World.FrameNumber - lastAttackTime > info.NotifyInterval * 25)
|
||||
Sound.PlayNotification(self.Owner, "Speech", "BaseAttack", self.Owner.Country.Race);
|
||||
|
||||
lastAttackLocation = self.CenterLocation.ToCPos();
|
||||
lastAttackLocation = self.CenterPosition.ToCPos();
|
||||
lastAttackTime = self.World.FrameNumber;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace OpenRA.Mods.RA
|
||||
if (self.World.FrameNumber - lastAttackTime > info.NotifyInterval * 25)
|
||||
Sound.PlayNotification(self.Owner, "Speech", "HarvesterAttack", self.Owner.Country.Race);
|
||||
|
||||
lastAttackLocation = self.CenterLocation.ToCPos();
|
||||
lastAttackLocation = self.CenterPosition.ToCPos();
|
||||
lastAttackTime = self.World.FrameNumber;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,18 +83,11 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
if (buildingInfo.RequiresBaseProvider)
|
||||
{
|
||||
var center = buildingInfo.CenterLocation(order.TargetLocation);
|
||||
foreach (var bp in w.ActorsWithTrait<BaseProvider>())
|
||||
{
|
||||
if (bp.Actor.Owner.Stances[self.Owner] != Stance.Ally || !bp.Trait.Ready())
|
||||
continue;
|
||||
|
||||
if (Combat.IsInRange(center, bp.Trait.Info.Range, bp.Actor.CenterLocation))
|
||||
{
|
||||
bp.Trait.BeginCooldown();
|
||||
break;
|
||||
}
|
||||
}
|
||||
// May be null if the build anywhere cheat is active
|
||||
// BuildingInfo.IsCloseEnoughToBase has already verified that this is a valid build location
|
||||
var producer = buildingInfo.FindBaseProvider(w, self.Owner, order.TargetLocation);
|
||||
if (producer != null)
|
||||
producer.Trait<BaseProvider>().BeginCooldown();
|
||||
}
|
||||
|
||||
if (GetNumBuildables(self.Owner) > prevItems)
|
||||
|
||||
@@ -50,8 +50,8 @@ namespace OpenRA.Mods.RA
|
||||
public void DoProduction(Actor self, ActorInfo producee, ExitInfo exitinfo)
|
||||
{
|
||||
var exit = self.Location + exitinfo.ExitCellVector;
|
||||
var spawn = self.Trait<IHasLocation>().PxPosition + exitinfo.SpawnOffsetVector;
|
||||
var to = Util.CenterOfCell(exit);
|
||||
var spawn = (self.Trait<IHasLocation>().PxPosition + exitinfo.SpawnOffsetVector).ToWPos(0);
|
||||
var to = exit.CenterPosition;
|
||||
|
||||
var fi = producee.Traits.Get<IFacingInfo>();
|
||||
var initialFacing = exitinfo.Facing < 0 ? Util.GetFacing(to - spawn, fi.GetInitialFacing()) : exitinfo.Facing;
|
||||
@@ -66,7 +66,7 @@ namespace OpenRA.Mods.RA
|
||||
// TODO: Move this into an *Init
|
||||
// TODO: We should be adjusting the actual position for aircraft, not just visuals.
|
||||
var teleportable = newUnit.Trait<ITeleportable>();
|
||||
teleportable.AdjustPxPosition(newUnit, spawn);
|
||||
teleportable.AdjustPxPosition(newUnit, PPos.FromWPos(spawn));
|
||||
|
||||
// TODO: Generalize this for non-mobile (e.g. aircraft) too
|
||||
// Remember to update the Enter activity too
|
||||
@@ -74,7 +74,7 @@ namespace OpenRA.Mods.RA
|
||||
if (mobile != null)
|
||||
{
|
||||
// Animate the spawn -> exit transition
|
||||
var speed = mobile.MovementSpeedForCell(newUnit, exit);
|
||||
var speed = mobile.WorldMovementSpeedForCell(newUnit, exit);
|
||||
var length = speed > 0 ? (int)((to - spawn).Length * 3 / speed) : 0;
|
||||
newUnit.QueueActivity(new Drag(spawn, to, length));
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace OpenRA.Mods.RA
|
||||
public Order IssueOrder( Actor self, IOrderTargeter order, Target target, bool queued )
|
||||
{
|
||||
if( order.OrderID == "SetRallyPoint" )
|
||||
return new Order(order.OrderID, self, false) { TargetLocation = target.CenterLocation.ToCPos() };
|
||||
return new Order(order.OrderID, self, false) { TargetLocation = target.CenterPosition.ToCPos() };
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -49,10 +49,12 @@ namespace OpenRA.Mods.RA.Render
|
||||
: base(init, info)
|
||||
{
|
||||
roof = new Animation(GetImage(init.self));
|
||||
|
||||
var bi = init.self.Info.Traits.Get<BuildingInfo>();
|
||||
|
||||
// Additional 512 units move from center -> top of cell
|
||||
var offset = FootprintUtils.CenterOffset(bi).Y + 512;
|
||||
anims.Add("roof", new AnimationWithOffset(roof, null,
|
||||
() => !buildComplete, FootprintUtils.CenterOffset(bi).Y));
|
||||
() => !buildComplete, offset));
|
||||
}
|
||||
|
||||
public void BuildingComplete( Actor self )
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace OpenRA.Mods.RA.Render
|
||||
var bi = self.Info.Traits.Get<BuildingInfo>();
|
||||
FootprintUtils.UnpathableTiles(self.Info.Name, bi, self.Location).Do(
|
||||
t => self.World.AddFrameEndTask(
|
||||
w => w.Add(new Explosion(w, Traits.Util.CenterOfCell(t), "building", false, 0))));
|
||||
w => w.Add(new Explosion(w, t.CenterPosition, "building"))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,9 +47,11 @@ namespace OpenRA.Mods.RA
|
||||
if (self.Owner != self.World.LocalPlayer)
|
||||
return;
|
||||
|
||||
// Hack: Convert world coords to cells
|
||||
var pxRange = self.Trait<AttackBase>().GetMaximumRange().Range / 1024f;
|
||||
wr.DrawRangeCircleWithContrast(
|
||||
Color.FromArgb(128, Color.Yellow),
|
||||
self.CenterLocation.ToFloat2(), self.Trait<AttackBase>().GetMaximumRange(),
|
||||
self.CenterLocation.ToFloat2(), pxRange,
|
||||
Color.FromArgb(96, Color.Black),
|
||||
1);
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
self.CancelActivity();
|
||||
self.QueueActivity(new MoveAdjacentTo(target));
|
||||
self.QueueActivity(mobile.MoveTo(order.TargetActor.CenterLocation.ToCPos(), order.TargetActor));
|
||||
self.QueueActivity(mobile.MoveTo(order.TargetActor.CenterPosition.ToCPos(), order.TargetActor));
|
||||
self.QueueActivity(new Rearm(self));
|
||||
self.QueueActivity(new Repair(order.TargetActor));
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace OpenRA.Mods.RA
|
||||
{
|
||||
var position = self.CenterPosition;
|
||||
if (position.Z > 0 && self.GetDamageState() >= info.MinDamage &&
|
||||
!self.World.FogObscures(new CPos(position)))
|
||||
!self.World.FogObscures(position.ToCPos()))
|
||||
{
|
||||
var offset = info.Offset.Rotate(body.QuantizeOrientation(self, self.Orientation));
|
||||
var pos = position + body.LocalToWorld(offset);
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace OpenRA.Mods.RA
|
||||
{
|
||||
new LocationInit( enterCell ),
|
||||
new OwnerInit( self.Owner ),
|
||||
new FacingInit( Util.GetFacing((order.TargetLocation - enterCell).ToInt2(), 0) ),
|
||||
new FacingInit( Util.GetFacing(order.TargetLocation - enterCell, 0) ),
|
||||
new AltitudeInit( Rules.Info["u2"].Traits.Get<PlaneInfo>().CruiseAltitude ),
|
||||
});
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
public virtual IEnumerable<CPos> TargetableCells( Actor self )
|
||||
{
|
||||
yield return self.CenterLocation.ToCPos();
|
||||
yield return self.CenterPosition.ToCPos();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,14 +22,20 @@ namespace OpenRA.Mods.RA
|
||||
[Desc("Initial position relative to body")]
|
||||
public readonly WVec Offset = WVec.Zero;
|
||||
|
||||
[Desc("Minimum distance to throw the particle")]
|
||||
public readonly WRange MinThrowRange = new WRange(256);
|
||||
|
||||
[Desc("Maximum distance to throw the particle")]
|
||||
public readonly WRange ThrowRange = new WRange(768);
|
||||
public readonly WRange MaxThrowRange = new WRange(768);
|
||||
|
||||
[Desc("Maximum height to throw the particle")]
|
||||
public readonly WRange ThrowHeight = new WRange(256);
|
||||
[Desc("Minimum angle to throw the particle")]
|
||||
public readonly WAngle MinThrowAngle = WAngle.FromDegrees(30);
|
||||
|
||||
[Desc("Number of ticks to animate")]
|
||||
public readonly int Length = 15;
|
||||
[Desc("Maximum angle to throw the particle")]
|
||||
public readonly WAngle MaxThrowAngle = WAngle.FromDegrees(60);
|
||||
|
||||
[Desc("Speed to throw the particle (horizontal WPos/tick)")]
|
||||
public readonly int Velocity = 75;
|
||||
|
||||
[Desc("Maximum rotation rate")]
|
||||
public readonly float ROT = 15;
|
||||
@@ -39,19 +45,19 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
class ThrowsParticle : ITick
|
||||
{
|
||||
ThrowsParticleInfo info;
|
||||
WVec pos;
|
||||
WVec initialPos;
|
||||
WVec finalPos;
|
||||
WAngle angle;
|
||||
|
||||
int tick = 0;
|
||||
int length;
|
||||
|
||||
float facing;
|
||||
float rotation;
|
||||
|
||||
public ThrowsParticle(ActorInitializer init, ThrowsParticleInfo info)
|
||||
{
|
||||
this.info = info;
|
||||
|
||||
var self = init.self;
|
||||
var rs = self.Trait<RenderSimple>();
|
||||
var body = self.Trait<IBodyOrientation>();
|
||||
@@ -62,10 +68,12 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
// Calculate final position
|
||||
var throwRotation = WRot.FromFacing(Game.CosmeticRandom.Next(1024));
|
||||
var throwOffset = new WVec((int)(Game.CosmeticRandom.Gauss1D(1)*info.ThrowRange.Range), 0, 0).Rotate(throwRotation);
|
||||
var throwDistance = Game.CosmeticRandom.Next(info.MinThrowRange.Range, info.MaxThrowRange.Range);
|
||||
|
||||
initialPos = pos = info.Offset.Rotate(body.QuantizeOrientation(self, WRot.FromFacing(bodyFacing)));
|
||||
finalPos = initialPos + throwOffset;
|
||||
finalPos = initialPos + new WVec(throwDistance, 0, 0).Rotate(throwRotation);
|
||||
angle = new WAngle(Game.CosmeticRandom.Next(info.MinThrowAngle.Angle, info.MaxThrowAngle.Angle));
|
||||
length = (finalPos - initialPos).Length / info.Velocity;
|
||||
|
||||
// Facing rotation
|
||||
rotation = Game.CosmeticRandom.Gauss1D(2) * info.ROT;
|
||||
@@ -77,15 +85,10 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
public void Tick(Actor self)
|
||||
{
|
||||
if (tick == info.Length)
|
||||
if (tick == length)
|
||||
return;
|
||||
tick++;
|
||||
|
||||
// Lerp position horizontally and height along a sinusoid using a cubic ease
|
||||
var t = (tick*tick*tick / (info.Length*info.Length) - 3*tick*tick / info.Length + 3*tick);
|
||||
var tp = WVec.Lerp(initialPos, finalPos, t, info.Length);
|
||||
var th = new WAngle(512*(info.Length - t) / info.Length).Sin()*info.ThrowHeight.Range / 1024;
|
||||
pos = new WVec(tp.X, tp.Y, th);
|
||||
pos = WVec.LerpQuadratic(initialPos, finalPos, angle, tick++, length);
|
||||
|
||||
// Spin the particle
|
||||
facing += rotation;
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
public bool FaceTarget(Actor self, Target target)
|
||||
{
|
||||
desiredFacing = Util.GetFacing(target.CenterLocation - self.CenterLocation, turretFacing);
|
||||
desiredFacing = Util.GetFacing(target.CenterPosition - self.CenterPosition, turretFacing);
|
||||
return turretFacing == desiredFacing;
|
||||
}
|
||||
|
||||
|
||||
@@ -223,6 +223,7 @@ ionsfx:
|
||||
idle:
|
||||
Start: 0
|
||||
Length: *
|
||||
Offset: 0, -78
|
||||
|
||||
bomblet:
|
||||
idle:
|
||||
|
||||
Reference in New Issue
Block a user