Add terrain orientation support for Mobile.
This commit is contained in:
@@ -900,6 +900,14 @@ namespace OpenRA
|
||||
return new WDist(offset.Z);
|
||||
}
|
||||
|
||||
public WRot TerrainOrientation(CPos cell)
|
||||
{
|
||||
if (!Ramp.Contains(cell))
|
||||
return WRot.None;
|
||||
|
||||
return Grid.Ramps[Ramp[cell]].Orientation;
|
||||
}
|
||||
|
||||
public WVec Offset(CVec delta, int dz)
|
||||
{
|
||||
if (Grid.Type == MapGridType.Rectangular)
|
||||
|
||||
@@ -27,9 +27,11 @@ namespace OpenRA
|
||||
public readonly int CenterHeightOffset;
|
||||
public readonly WVec[] Corners;
|
||||
public readonly WVec[][] Polygons;
|
||||
public readonly WRot Orientation;
|
||||
|
||||
public CellRamp(MapGridType type, RampCornerHeight tl = RampCornerHeight.Low, RampCornerHeight tr = RampCornerHeight.Low, RampCornerHeight br = RampCornerHeight.Low, RampCornerHeight bl = RampCornerHeight.Low, RampSplit split = RampSplit.Flat)
|
||||
public CellRamp(MapGridType type, WRot orientation, RampCornerHeight tl = RampCornerHeight.Low, RampCornerHeight tr = RampCornerHeight.Low, RampCornerHeight br = RampCornerHeight.Low, RampCornerHeight bl = RampCornerHeight.Low, RampSplit split = RampSplit.Flat)
|
||||
{
|
||||
Orientation = orientation;
|
||||
if (type == MapGridType.RectangularIsometric)
|
||||
{
|
||||
Corners = new[]
|
||||
@@ -138,41 +140,52 @@ namespace OpenRA
|
||||
throw new InvalidDataException("Subcell default index must be a valid index into the offset triples and must be greater than 0 for mods with subcells");
|
||||
}
|
||||
|
||||
// Rotation axes and amounts for the different slope types
|
||||
var southEast = new WVec(724, 724, 0);
|
||||
var southWest = new WVec(-724, 724, 0);
|
||||
var south = new WVec(0, 1024, 0);
|
||||
var east = new WVec(1024, 0, 0);
|
||||
|
||||
var forward = new WAngle(64);
|
||||
var backward = -forward;
|
||||
var halfForward = new WAngle(48);
|
||||
var halfBackward = -halfForward;
|
||||
|
||||
// Slope types are hardcoded following the convention from the TS and RA2 map format
|
||||
Ramps = new[]
|
||||
{
|
||||
// Flat
|
||||
new CellRamp(Type),
|
||||
new CellRamp(Type, WRot.None),
|
||||
|
||||
// Two adjacent corners raised by half a cell
|
||||
new CellRamp(Type, tr: RampCornerHeight.Half, br: RampCornerHeight.Half),
|
||||
new CellRamp(Type, br: RampCornerHeight.Half, bl: RampCornerHeight.Half),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Half, bl: RampCornerHeight.Half),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Half, tr: RampCornerHeight.Half),
|
||||
new CellRamp(Type, new WRot(southEast, backward), tr: RampCornerHeight.Half, br: RampCornerHeight.Half),
|
||||
new CellRamp(Type, new WRot(southWest, backward), br: RampCornerHeight.Half, bl: RampCornerHeight.Half),
|
||||
new CellRamp(Type, new WRot(southEast, forward), tl: RampCornerHeight.Half, bl: RampCornerHeight.Half),
|
||||
new CellRamp(Type, new WRot(southWest, forward), tl: RampCornerHeight.Half, tr: RampCornerHeight.Half),
|
||||
|
||||
// One corner raised by half a cell
|
||||
new CellRamp(Type, br: RampCornerHeight.Half, split: RampSplit.X),
|
||||
new CellRamp(Type, bl: RampCornerHeight.Half, split: RampSplit.Y),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Half, split: RampSplit.X),
|
||||
new CellRamp(Type, tr: RampCornerHeight.Half, split: RampSplit.Y),
|
||||
new CellRamp(Type, new WRot(south, halfBackward), br: RampCornerHeight.Half, split: RampSplit.X),
|
||||
new CellRamp(Type, new WRot(east, halfForward), bl: RampCornerHeight.Half, split: RampSplit.Y),
|
||||
new CellRamp(Type, new WRot(south, halfForward), tl: RampCornerHeight.Half, split: RampSplit.X),
|
||||
new CellRamp(Type, new WRot(east, halfBackward), tr: RampCornerHeight.Half, split: RampSplit.Y),
|
||||
|
||||
// Three corners raised by half a cell
|
||||
new CellRamp(Type, tr: RampCornerHeight.Half, br: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.X),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Half, br: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.Y),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Half, tr: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.X),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Half, tr: RampCornerHeight.Half, br: RampCornerHeight.Half, split: RampSplit.Y),
|
||||
new CellRamp(Type, new WRot(south, halfBackward), tr: RampCornerHeight.Half, br: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.X),
|
||||
new CellRamp(Type, new WRot(east, halfForward), tl: RampCornerHeight.Half, br: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.Y),
|
||||
new CellRamp(Type, new WRot(south, halfForward), tl: RampCornerHeight.Half, tr: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.X),
|
||||
new CellRamp(Type, new WRot(east, halfBackward), tl: RampCornerHeight.Half, tr: RampCornerHeight.Half, br: RampCornerHeight.Half, split: RampSplit.Y),
|
||||
|
||||
// Full tile sloped (mid corners raised by half cell, far corner by full cell)
|
||||
new CellRamp(Type, tr: RampCornerHeight.Half, br: RampCornerHeight.Full, bl: RampCornerHeight.Half),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Half, br: RampCornerHeight.Half, bl: RampCornerHeight.Full),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Full, tr: RampCornerHeight.Half, bl: RampCornerHeight.Half),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Half, tr: RampCornerHeight.Full, br: RampCornerHeight.Half),
|
||||
new CellRamp(Type, new WRot(south, backward), tr: RampCornerHeight.Half, br: RampCornerHeight.Full, bl: RampCornerHeight.Half),
|
||||
new CellRamp(Type, new WRot(east, forward), tl: RampCornerHeight.Half, br: RampCornerHeight.Half, bl: RampCornerHeight.Full),
|
||||
new CellRamp(Type, new WRot(south, forward), tl: RampCornerHeight.Full, tr: RampCornerHeight.Half, bl: RampCornerHeight.Half),
|
||||
new CellRamp(Type, new WRot(east, backward), tl: RampCornerHeight.Half, tr: RampCornerHeight.Full, br: RampCornerHeight.Half),
|
||||
|
||||
// Two opposite corners raised by half a cell
|
||||
new CellRamp(Type, tr: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.Y),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Half, br: RampCornerHeight.Half, split: RampSplit.Y),
|
||||
new CellRamp(Type, tr: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.X),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Half, br: RampCornerHeight.Half, split: RampSplit.X),
|
||||
new CellRamp(Type, WRot.None, tr: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.Y),
|
||||
new CellRamp(Type, WRot.None, tl: RampCornerHeight.Half, br: RampCornerHeight.Half, split: RampSplit.Y),
|
||||
new CellRamp(Type, WRot.None, tr: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.X),
|
||||
new CellRamp(Type, WRot.None, tl: RampCornerHeight.Half, br: RampCornerHeight.Half, split: RampSplit.X),
|
||||
};
|
||||
|
||||
TilesByDistance = CreateTilesByDistance();
|
||||
|
||||
@@ -221,7 +221,12 @@ namespace OpenRA.Mods.Common.Activities
|
||||
var to = Util.BetweenCells(self.World, mobile.FromCell, mobile.ToCell) +
|
||||
(map.Grid.OffsetOfSubCell(mobile.FromSubCell) + map.Grid.OffsetOfSubCell(mobile.ToSubCell)) / 2;
|
||||
|
||||
QueueChild(new MoveFirstHalf(this, from, to, mobile.Facing, mobile.Facing, carryoverProgress));
|
||||
WRot? toTerrainOrientation = null;
|
||||
var margin = mobile.Info.TerrainOrientationAdjustmentMargin.Length;
|
||||
if (margin >= 0)
|
||||
toTerrainOrientation = WRot.SLerp(map.TerrainOrientation(mobile.FromCell), map.TerrainOrientation(mobile.ToCell), 1, 2);
|
||||
|
||||
QueueChild(new MoveFirstHalf(this, from, to, mobile.Facing, mobile.Facing, null, toTerrainOrientation, margin, carryoverProgress));
|
||||
carryoverProgress = 0;
|
||||
return false;
|
||||
}
|
||||
@@ -389,6 +394,7 @@ namespace OpenRA.Mods.Common.Activities
|
||||
protected readonly Move Move;
|
||||
protected readonly WPos From, To;
|
||||
protected readonly WAngle FromFacing, ToFacing;
|
||||
protected readonly WRot? FromTerrainOrientation, ToTerrainOrientation;
|
||||
protected readonly bool EnableArc;
|
||||
protected readonly WPos ArcCenter;
|
||||
protected readonly int ArcFromLength;
|
||||
@@ -396,17 +402,22 @@ namespace OpenRA.Mods.Common.Activities
|
||||
protected readonly int ArcToLength;
|
||||
protected readonly WAngle ArcToAngle;
|
||||
protected readonly int Distance;
|
||||
readonly int terrainOrientationMargin;
|
||||
protected int progress;
|
||||
|
||||
public MovePart(Move move, WPos from, WPos to, WAngle fromFacing, WAngle toFacing, int carryoverProgress)
|
||||
public MovePart(Move move, WPos from, WPos to, WAngle fromFacing, WAngle toFacing,
|
||||
WRot? fromTerrainOrientation, WRot? toTerrainOrientation, int terrainOrientationMargin, int carryoverProgress)
|
||||
{
|
||||
Move = move;
|
||||
From = from;
|
||||
To = to;
|
||||
FromFacing = fromFacing;
|
||||
ToFacing = toFacing;
|
||||
FromTerrainOrientation = fromTerrainOrientation;
|
||||
ToTerrainOrientation = toTerrainOrientation;
|
||||
progress = carryoverProgress;
|
||||
Distance = (to - from).Length;
|
||||
this.terrainOrientationMargin = Math.Min(terrainOrientationMargin, Distance / 2);
|
||||
|
||||
IsInterruptible = false; // See comments in Move.Cancel()
|
||||
|
||||
@@ -471,6 +482,21 @@ namespace OpenRA.Mods.Common.Activities
|
||||
pos -= new WVec(WDist.Zero, WDist.Zero, self.World.Map.DistanceAboveTerrain(pos));
|
||||
|
||||
mobile.SetCenterPosition(self, pos);
|
||||
|
||||
// Smoothly interpolate over terrain orientation changes
|
||||
if (FromTerrainOrientation.HasValue && progress < terrainOrientationMargin)
|
||||
{
|
||||
var currentCellOrientation = self.World.Map.TerrainOrientation(mobile.FromCell);
|
||||
var orientation = WRot.SLerp(FromTerrainOrientation.Value, currentCellOrientation, progress, terrainOrientationMargin);
|
||||
mobile.SetTerrainRampOrientation(self, orientation);
|
||||
}
|
||||
else if (ToTerrainOrientation.HasValue && Distance - progress < terrainOrientationMargin)
|
||||
{
|
||||
var currentCellOrientation = self.World.Map.TerrainOrientation(mobile.FromCell);
|
||||
var orientation = WRot.SLerp(ToTerrainOrientation.Value, currentCellOrientation, Distance - progress, terrainOrientationMargin);
|
||||
mobile.SetTerrainRampOrientation(self, orientation);
|
||||
}
|
||||
|
||||
mobile.Facing = WAngle.Lerp(FromFacing, ToFacing, progress, Distance);
|
||||
return false;
|
||||
}
|
||||
@@ -485,8 +511,9 @@ namespace OpenRA.Mods.Common.Activities
|
||||
|
||||
class MoveFirstHalf : MovePart
|
||||
{
|
||||
public MoveFirstHalf(Move move, WPos from, WPos to, WAngle fromFacing, WAngle toFacing, int carryoverProgress)
|
||||
: base(move, from, to, fromFacing, toFacing, carryoverProgress) { }
|
||||
public MoveFirstHalf(Move move, WPos from, WPos to, WAngle fromFacing, WAngle toFacing,
|
||||
WRot? fromTerrainOrientation, WRot? toTerrainOrientation, int terrainOrientationMargin, int carryoverProgress)
|
||||
: base(move, from, to, fromFacing, toFacing, fromTerrainOrientation, toTerrainOrientation, terrainOrientationMargin, carryoverProgress) { }
|
||||
|
||||
static bool IsTurn(Mobile mobile, CPos nextCell, Map map)
|
||||
{
|
||||
@@ -513,12 +540,20 @@ namespace OpenRA.Mods.Common.Activities
|
||||
if (!mobile.IsTraitPaused && !mobile.IsTraitDisabled && IsTurn(mobile, nextCell.Value.Cell, map))
|
||||
{
|
||||
var nextSubcellOffset = map.Grid.OffsetOfSubCell(nextCell.Value.SubCell);
|
||||
WRot? nextToTerrainOrientation = null;
|
||||
var margin = mobile.Info.TerrainOrientationAdjustmentMargin.Length;
|
||||
if (margin >= 0)
|
||||
nextToTerrainOrientation = WRot.SLerp(map.TerrainOrientation(mobile.ToCell), map.TerrainOrientation(nextCell.Value.Cell), 1, 2);
|
||||
|
||||
var ret = new MoveFirstHalf(
|
||||
Move,
|
||||
Util.BetweenCells(self.World, mobile.FromCell, mobile.ToCell) + (fromSubcellOffset + toSubcellOffset) / 2,
|
||||
Util.BetweenCells(self.World, mobile.ToCell, nextCell.Value.Cell) + (toSubcellOffset + nextSubcellOffset) / 2,
|
||||
mobile.Facing,
|
||||
map.FacingBetween(mobile.ToCell, nextCell.Value.Cell, mobile.Facing),
|
||||
ToTerrainOrientation,
|
||||
nextToTerrainOrientation,
|
||||
margin,
|
||||
progress - Distance);
|
||||
|
||||
mobile.FinishedMoving(self);
|
||||
@@ -538,6 +573,9 @@ namespace OpenRA.Mods.Common.Activities
|
||||
toPos + toSubcellOffset,
|
||||
mobile.Facing,
|
||||
mobile.Facing,
|
||||
ToTerrainOrientation,
|
||||
null,
|
||||
mobile.Info.TerrainOrientationAdjustmentMargin.Length,
|
||||
progress - Distance);
|
||||
|
||||
mobile.EnteringCell(self);
|
||||
@@ -548,8 +586,9 @@ namespace OpenRA.Mods.Common.Activities
|
||||
|
||||
class MoveSecondHalf : MovePart
|
||||
{
|
||||
public MoveSecondHalf(Move move, WPos from, WPos to, WAngle fromFacing, WAngle toFacing, int carryoverProgress)
|
||||
: base(move, from, to, fromFacing, toFacing, carryoverProgress) { }
|
||||
public MoveSecondHalf(Move move, WPos from, WPos to, WAngle fromFacing, WAngle toFacing,
|
||||
WRot? fromTerrainOrientation, WRot? toTerrainOrientation, int terrainOrientationMargin, int carryoverProgress)
|
||||
: base(move, from, to, fromFacing, toFacing, fromTerrainOrientation, toTerrainOrientation, terrainOrientationMargin, carryoverProgress) { }
|
||||
|
||||
protected override MovePart OnComplete(Actor self, Mobile mobile, Move parent)
|
||||
{
|
||||
|
||||
@@ -133,8 +133,12 @@ namespace OpenRA.Mods.Common.Graphics
|
||||
this.model = model;
|
||||
var draw = model.models.Where(v => v.IsVisible);
|
||||
|
||||
var map = wr.World.Map;
|
||||
var groundOrientation = map.TerrainOrientation(map.CellContaining(model.pos));
|
||||
var groundNormal = OpenRA.Graphics.Util.MatrixVectorMultiply(OpenRA.Graphics.Util.MakeFloatMatrix(groundOrientation.AsMatrix()), GroundNormal);
|
||||
|
||||
renderProxy = Game.Renderer.WorldModelRenderer.RenderAsync(
|
||||
wr, draw, model.camera, model.scale, GroundNormal, model.lightSource,
|
||||
wr, draw, model.camera, model.scale, groundNormal, model.lightSource,
|
||||
model.lightAmbientColor, model.lightDiffuseColor,
|
||||
model.palette, model.normalsPalette, model.shadowPalette);
|
||||
}
|
||||
|
||||
@@ -74,6 +74,10 @@ namespace OpenRA.Mods.Common.Traits
|
||||
[Desc("Boolean expression defining the condition under which this actor cannot be nudged by other actors.")]
|
||||
public readonly BooleanExpression ImmovableCondition = null;
|
||||
|
||||
[Desc("The distance from the edge of a cell over which the actor will adjust its tilt when moving between cells with different ramp types.",
|
||||
"-1 means that the actor does not tilt on slopes.")]
|
||||
public readonly WDist TerrainOrientationAdjustmentMargin = new WDist(-1);
|
||||
|
||||
IEnumerable<ActorInit> IActorPreviewInitInfo.ActorPreviewInits(ActorInfo ai, ActorPreviewType type)
|
||||
{
|
||||
yield return new FacingInit(PreviewFacing);
|
||||
@@ -183,6 +187,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
}
|
||||
#endregion
|
||||
|
||||
WRot terrainRampOrientation = WRot.None;
|
||||
WAngle oldFacing;
|
||||
WRot orientation;
|
||||
WPos oldPos;
|
||||
@@ -211,7 +216,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
set => orientation = orientation.WithYaw(value);
|
||||
}
|
||||
|
||||
public WRot Orientation => orientation;
|
||||
public WRot Orientation => orientation.Rotate(terrainRampOrientation);
|
||||
|
||||
public WAngle TurnSpeed => Info.TurnSpeed;
|
||||
|
||||
@@ -485,6 +490,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
CenterPosition = pos;
|
||||
self.World.UpdateMaps(self, this);
|
||||
|
||||
var map = self.World.Map;
|
||||
SetTerrainRampOrientation(self, map.TerrainOrientation(map.CellContaining(pos)));
|
||||
|
||||
// The first time SetCenterPosition is called is in the constructor before creation, so we need a null check here as well
|
||||
if (notifyCenterPositionChanged == null)
|
||||
return;
|
||||
@@ -493,6 +501,12 @@ namespace OpenRA.Mods.Common.Traits
|
||||
n.CenterPositionChanged(self, fromCell.Layer, toCell.Layer);
|
||||
}
|
||||
|
||||
public void SetTerrainRampOrientation(Actor self, WRot orientation)
|
||||
{
|
||||
if (Info.TerrainOrientationAdjustmentMargin.Length >= 0)
|
||||
terrainRampOrientation = orientation;
|
||||
}
|
||||
|
||||
public bool IsLeavingCell(CPos location, SubCell subCell = SubCell.Any)
|
||||
{
|
||||
return ToCell != location && fromCell == location
|
||||
|
||||
@@ -780,6 +780,7 @@
|
||||
Locomotor: wheeled
|
||||
TurnSpeed: 20
|
||||
Voice: Move
|
||||
TerrainOrientationAdjustmentMargin: 256
|
||||
Selectable:
|
||||
Bounds: 1206, 1448
|
||||
Voiced:
|
||||
|
||||
@@ -67,6 +67,7 @@ HVR:
|
||||
Mobile:
|
||||
Speed: 99
|
||||
Locomotor: hover
|
||||
TerrainOrientationAdjustmentMargin: -1
|
||||
Selectable:
|
||||
Bounds: 1206, 1448, 0, -603
|
||||
Health:
|
||||
@@ -123,6 +124,7 @@ SMECH:
|
||||
TurnSpeed: 20
|
||||
Speed: 99
|
||||
AlwaysTurnInPlace: true
|
||||
TerrainOrientationAdjustmentMargin: -1
|
||||
Health:
|
||||
HP: 17500
|
||||
Armor:
|
||||
@@ -176,6 +178,7 @@ MMCH:
|
||||
TurnSpeed: 20
|
||||
Speed: 56
|
||||
AlwaysTurnInPlace: true
|
||||
TerrainOrientationAdjustmentMargin: -1
|
||||
Health:
|
||||
HP: 40000
|
||||
Armor:
|
||||
@@ -341,6 +344,7 @@ JUGG:
|
||||
AlwaysTurnInPlace: true
|
||||
ImmovableCondition: !undeployed
|
||||
RequireForceMoveCondition: !undeployed
|
||||
TerrainOrientationAdjustmentMargin: -1
|
||||
RevealsShroud:
|
||||
RequiresCondition: !inside-tunnel
|
||||
Range: 9c0
|
||||
|
||||
Reference in New Issue
Block a user