Add terrain orientation support for Mobile.

This commit is contained in:
Paul Chote
2021-08-21 10:23:57 +01:00
committed by reaperrr
parent c88d1bbefa
commit 68710e48a6
7 changed files with 113 additions and 30 deletions

View File

@@ -900,6 +900,14 @@ namespace OpenRA
return new WDist(offset.Z); 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) public WVec Offset(CVec delta, int dz)
{ {
if (Grid.Type == MapGridType.Rectangular) if (Grid.Type == MapGridType.Rectangular)

View File

@@ -27,9 +27,11 @@ namespace OpenRA
public readonly int CenterHeightOffset; public readonly int CenterHeightOffset;
public readonly WVec[] Corners; public readonly WVec[] Corners;
public readonly WVec[][] Polygons; 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) if (type == MapGridType.RectangularIsometric)
{ {
Corners = new[] 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"); 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 // Slope types are hardcoded following the convention from the TS and RA2 map format
Ramps = new[] Ramps = new[]
{ {
// Flat // Flat
new CellRamp(Type), new CellRamp(Type, WRot.None),
// Two adjacent corners raised by half a cell // Two adjacent corners raised by half a cell
new CellRamp(Type, tr: RampCornerHeight.Half, br: RampCornerHeight.Half), new CellRamp(Type, new WRot(southEast, backward), tr: RampCornerHeight.Half, br: RampCornerHeight.Half),
new CellRamp(Type, br: RampCornerHeight.Half, bl: RampCornerHeight.Half), new CellRamp(Type, new WRot(southWest, backward), br: RampCornerHeight.Half, bl: RampCornerHeight.Half),
new CellRamp(Type, tl: RampCornerHeight.Half, bl: RampCornerHeight.Half), new CellRamp(Type, new WRot(southEast, forward), tl: RampCornerHeight.Half, bl: RampCornerHeight.Half),
new CellRamp(Type, tl: RampCornerHeight.Half, tr: RampCornerHeight.Half), new CellRamp(Type, new WRot(southWest, forward), tl: RampCornerHeight.Half, tr: RampCornerHeight.Half),
// One corner raised by half a cell // One corner raised by half a cell
new CellRamp(Type, br: RampCornerHeight.Half, split: RampSplit.X), new CellRamp(Type, new WRot(south, halfBackward), br: RampCornerHeight.Half, split: RampSplit.X),
new CellRamp(Type, bl: RampCornerHeight.Half, split: RampSplit.Y), new CellRamp(Type, new WRot(east, halfForward), bl: RampCornerHeight.Half, split: RampSplit.Y),
new CellRamp(Type, tl: RampCornerHeight.Half, split: RampSplit.X), new CellRamp(Type, new WRot(south, halfForward), tl: RampCornerHeight.Half, split: RampSplit.X),
new CellRamp(Type, tr: RampCornerHeight.Half, split: RampSplit.Y), new CellRamp(Type, new WRot(east, halfBackward), tr: RampCornerHeight.Half, split: RampSplit.Y),
// Three corners raised by half a cell // 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, new WRot(south, halfBackward), 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, new WRot(east, halfForward), 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, new WRot(south, halfForward), 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(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) // 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, new WRot(south, backward), tr: RampCornerHeight.Half, br: RampCornerHeight.Full, bl: RampCornerHeight.Half),
new CellRamp(Type, tl: RampCornerHeight.Half, br: RampCornerHeight.Half, bl: RampCornerHeight.Full), new CellRamp(Type, new WRot(east, forward), tl: RampCornerHeight.Half, br: RampCornerHeight.Half, bl: RampCornerHeight.Full),
new CellRamp(Type, tl: RampCornerHeight.Full, tr: RampCornerHeight.Half, bl: RampCornerHeight.Half), new CellRamp(Type, new WRot(south, forward), 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(east, backward), tl: RampCornerHeight.Half, tr: RampCornerHeight.Full, br: RampCornerHeight.Half),
// Two opposite corners raised by half a cell // Two opposite corners raised by half a cell
new CellRamp(Type, tr: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.Y), new CellRamp(Type, WRot.None, tr: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.Y),
new CellRamp(Type, tl: RampCornerHeight.Half, br: RampCornerHeight.Half, split: RampSplit.Y), new CellRamp(Type, WRot.None, tl: RampCornerHeight.Half, br: RampCornerHeight.Half, split: RampSplit.Y),
new CellRamp(Type, tr: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.X), new CellRamp(Type, WRot.None, 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, tl: RampCornerHeight.Half, br: RampCornerHeight.Half, split: RampSplit.X),
}; };
TilesByDistance = CreateTilesByDistance(); TilesByDistance = CreateTilesByDistance();

View File

@@ -221,7 +221,12 @@ namespace OpenRA.Mods.Common.Activities
var to = Util.BetweenCells(self.World, mobile.FromCell, mobile.ToCell) + var to = Util.BetweenCells(self.World, mobile.FromCell, mobile.ToCell) +
(map.Grid.OffsetOfSubCell(mobile.FromSubCell) + map.Grid.OffsetOfSubCell(mobile.ToSubCell)) / 2; (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; carryoverProgress = 0;
return false; return false;
} }
@@ -389,6 +394,7 @@ namespace OpenRA.Mods.Common.Activities
protected readonly Move Move; protected readonly Move Move;
protected readonly WPos From, To; protected readonly WPos From, To;
protected readonly WAngle FromFacing, ToFacing; protected readonly WAngle FromFacing, ToFacing;
protected readonly WRot? FromTerrainOrientation, ToTerrainOrientation;
protected readonly bool EnableArc; protected readonly bool EnableArc;
protected readonly WPos ArcCenter; protected readonly WPos ArcCenter;
protected readonly int ArcFromLength; protected readonly int ArcFromLength;
@@ -396,17 +402,22 @@ namespace OpenRA.Mods.Common.Activities
protected readonly int ArcToLength; protected readonly int ArcToLength;
protected readonly WAngle ArcToAngle; protected readonly WAngle ArcToAngle;
protected readonly int Distance; protected readonly int Distance;
readonly int terrainOrientationMargin;
protected int progress; 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; Move = move;
From = from; From = from;
To = to; To = to;
FromFacing = fromFacing; FromFacing = fromFacing;
ToFacing = toFacing; ToFacing = toFacing;
FromTerrainOrientation = fromTerrainOrientation;
ToTerrainOrientation = toTerrainOrientation;
progress = carryoverProgress; progress = carryoverProgress;
Distance = (to - from).Length; Distance = (to - from).Length;
this.terrainOrientationMargin = Math.Min(terrainOrientationMargin, Distance / 2);
IsInterruptible = false; // See comments in Move.Cancel() 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)); pos -= new WVec(WDist.Zero, WDist.Zero, self.World.Map.DistanceAboveTerrain(pos));
mobile.SetCenterPosition(self, 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); mobile.Facing = WAngle.Lerp(FromFacing, ToFacing, progress, Distance);
return false; return false;
} }
@@ -485,8 +511,9 @@ namespace OpenRA.Mods.Common.Activities
class MoveFirstHalf : MovePart class MoveFirstHalf : MovePart
{ {
public MoveFirstHalf(Move move, WPos from, WPos to, WAngle fromFacing, WAngle toFacing, int carryoverProgress) public MoveFirstHalf(Move move, WPos from, WPos to, WAngle fromFacing, WAngle toFacing,
: base(move, from, to, fromFacing, toFacing, carryoverProgress) { } 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) 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)) if (!mobile.IsTraitPaused && !mobile.IsTraitDisabled && IsTurn(mobile, nextCell.Value.Cell, map))
{ {
var nextSubcellOffset = map.Grid.OffsetOfSubCell(nextCell.Value.SubCell); 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( var ret = new MoveFirstHalf(
Move, Move,
Util.BetweenCells(self.World, mobile.FromCell, mobile.ToCell) + (fromSubcellOffset + toSubcellOffset) / 2, Util.BetweenCells(self.World, mobile.FromCell, mobile.ToCell) + (fromSubcellOffset + toSubcellOffset) / 2,
Util.BetweenCells(self.World, mobile.ToCell, nextCell.Value.Cell) + (toSubcellOffset + nextSubcellOffset) / 2, Util.BetweenCells(self.World, mobile.ToCell, nextCell.Value.Cell) + (toSubcellOffset + nextSubcellOffset) / 2,
mobile.Facing, mobile.Facing,
map.FacingBetween(mobile.ToCell, nextCell.Value.Cell, mobile.Facing), map.FacingBetween(mobile.ToCell, nextCell.Value.Cell, mobile.Facing),
ToTerrainOrientation,
nextToTerrainOrientation,
margin,
progress - Distance); progress - Distance);
mobile.FinishedMoving(self); mobile.FinishedMoving(self);
@@ -538,6 +573,9 @@ namespace OpenRA.Mods.Common.Activities
toPos + toSubcellOffset, toPos + toSubcellOffset,
mobile.Facing, mobile.Facing,
mobile.Facing, mobile.Facing,
ToTerrainOrientation,
null,
mobile.Info.TerrainOrientationAdjustmentMargin.Length,
progress - Distance); progress - Distance);
mobile.EnteringCell(self); mobile.EnteringCell(self);
@@ -548,8 +586,9 @@ namespace OpenRA.Mods.Common.Activities
class MoveSecondHalf : MovePart class MoveSecondHalf : MovePart
{ {
public MoveSecondHalf(Move move, WPos from, WPos to, WAngle fromFacing, WAngle toFacing, int carryoverProgress) public MoveSecondHalf(Move move, WPos from, WPos to, WAngle fromFacing, WAngle toFacing,
: base(move, from, to, fromFacing, toFacing, carryoverProgress) { } 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) protected override MovePart OnComplete(Actor self, Mobile mobile, Move parent)
{ {

View File

@@ -133,8 +133,12 @@ namespace OpenRA.Mods.Common.Graphics
this.model = model; this.model = model;
var draw = model.models.Where(v => v.IsVisible); 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( 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.lightAmbientColor, model.lightDiffuseColor,
model.palette, model.normalsPalette, model.shadowPalette); model.palette, model.normalsPalette, model.shadowPalette);
} }

View File

@@ -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.")] [Desc("Boolean expression defining the condition under which this actor cannot be nudged by other actors.")]
public readonly BooleanExpression ImmovableCondition = null; 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) IEnumerable<ActorInit> IActorPreviewInitInfo.ActorPreviewInits(ActorInfo ai, ActorPreviewType type)
{ {
yield return new FacingInit(PreviewFacing); yield return new FacingInit(PreviewFacing);
@@ -183,6 +187,7 @@ namespace OpenRA.Mods.Common.Traits
} }
#endregion #endregion
WRot terrainRampOrientation = WRot.None;
WAngle oldFacing; WAngle oldFacing;
WRot orientation; WRot orientation;
WPos oldPos; WPos oldPos;
@@ -211,7 +216,7 @@ namespace OpenRA.Mods.Common.Traits
set => orientation = orientation.WithYaw(value); set => orientation = orientation.WithYaw(value);
} }
public WRot Orientation => orientation; public WRot Orientation => orientation.Rotate(terrainRampOrientation);
public WAngle TurnSpeed => Info.TurnSpeed; public WAngle TurnSpeed => Info.TurnSpeed;
@@ -485,6 +490,9 @@ namespace OpenRA.Mods.Common.Traits
CenterPosition = pos; CenterPosition = pos;
self.World.UpdateMaps(self, this); 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 // The first time SetCenterPosition is called is in the constructor before creation, so we need a null check here as well
if (notifyCenterPositionChanged == null) if (notifyCenterPositionChanged == null)
return; return;
@@ -493,6 +501,12 @@ namespace OpenRA.Mods.Common.Traits
n.CenterPositionChanged(self, fromCell.Layer, toCell.Layer); 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) public bool IsLeavingCell(CPos location, SubCell subCell = SubCell.Any)
{ {
return ToCell != location && fromCell == location return ToCell != location && fromCell == location

View File

@@ -780,6 +780,7 @@
Locomotor: wheeled Locomotor: wheeled
TurnSpeed: 20 TurnSpeed: 20
Voice: Move Voice: Move
TerrainOrientationAdjustmentMargin: 256
Selectable: Selectable:
Bounds: 1206, 1448 Bounds: 1206, 1448
Voiced: Voiced:

View File

@@ -67,6 +67,7 @@ HVR:
Mobile: Mobile:
Speed: 99 Speed: 99
Locomotor: hover Locomotor: hover
TerrainOrientationAdjustmentMargin: -1
Selectable: Selectable:
Bounds: 1206, 1448, 0, -603 Bounds: 1206, 1448, 0, -603
Health: Health:
@@ -123,6 +124,7 @@ SMECH:
TurnSpeed: 20 TurnSpeed: 20
Speed: 99 Speed: 99
AlwaysTurnInPlace: true AlwaysTurnInPlace: true
TerrainOrientationAdjustmentMargin: -1
Health: Health:
HP: 17500 HP: 17500
Armor: Armor:
@@ -176,6 +178,7 @@ MMCH:
TurnSpeed: 20 TurnSpeed: 20
Speed: 56 Speed: 56
AlwaysTurnInPlace: true AlwaysTurnInPlace: true
TerrainOrientationAdjustmentMargin: -1
Health: Health:
HP: 40000 HP: 40000
Armor: Armor:
@@ -341,6 +344,7 @@ JUGG:
AlwaysTurnInPlace: true AlwaysTurnInPlace: true
ImmovableCondition: !undeployed ImmovableCondition: !undeployed
RequireForceMoveCondition: !undeployed RequireForceMoveCondition: !undeployed
TerrainOrientationAdjustmentMargin: -1
RevealsShroud: RevealsShroud:
RequiresCondition: !inside-tunnel RequiresCondition: !inside-tunnel
Range: 9c0 Range: 9c0