Merge pull request #12577 from pchote/movement-layers

Implement tunnels, elevated bridges, jumpjet and subterranean units.
This commit is contained in:
RoosterDragon
2017-01-30 21:38:09 +00:00
committed by GitHub
53 changed files with 2201 additions and 304 deletions

View File

@@ -19,26 +19,25 @@ namespace OpenRA
public struct CPos : IScriptBindable, ILuaAdditionBinding, ILuaSubtractionBinding, ILuaEqualityBinding, ILuaTableBinding, IEquatable<CPos>
{
public readonly int X, Y;
public readonly byte Layer;
public CPos(int x, int y) { X = x; Y = y; }
public static readonly CPos Zero = new CPos(0, 0);
public CPos(int x, int y) { X = x; Y = y; Layer = 0; }
public CPos(int x, int y, byte layer) { X = x; Y = y; Layer = layer; }
public static readonly CPos Zero = new CPos(0, 0, 0);
public static explicit operator CPos(int2 a) { return new CPos(a.X, a.Y); }
public static CPos operator +(CVec a, CPos b) { return new CPos(a.X + b.X, a.Y + b.Y); }
public static CPos operator +(CPos a, CVec b) { return new CPos(a.X + b.X, a.Y + b.Y); }
public static CPos operator -(CPos a, CVec b) { return new CPos(a.X - b.X, a.Y - b.Y); }
public static CPos operator +(CVec a, CPos b) { return new CPos(a.X + b.X, a.Y + b.Y, b.Layer); }
public static CPos operator +(CPos a, CVec b) { return new CPos(a.X + b.X, a.Y + b.Y, a.Layer); }
public static CPos operator -(CPos a, CVec b) { return new CPos(a.X - b.X, a.Y - b.Y, a.Layer); }
public static CVec operator -(CPos a, CPos b) { return new CVec(a.X - b.X, a.Y - b.Y); }
public static bool operator ==(CPos me, CPos other) { return me.X == other.X && me.Y == other.Y; }
public static bool operator ==(CPos me, CPos other) { return me.X == other.X && me.Y == other.Y && me.Layer == other.Layer; }
public static bool operator !=(CPos me, CPos other) { return !(me == other); }
public static CPos Max(CPos a, CPos b) { return new CPos(Math.Max(a.X, b.X), Math.Max(a.Y, b.Y)); }
public static CPos Min(CPos a, CPos b) { return new CPos(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y)); }
public override int GetHashCode() { return X.GetHashCode() ^ Y.GetHashCode() ^ Layer.GetHashCode(); }
public override int GetHashCode() { return X.GetHashCode() ^ Y.GetHashCode(); }
public bool Equals(CPos other) { return X == other.X && Y == other.Y; }
public bool Equals(CPos other) { return X == other.X && Y == other.Y && Layer == other.Layer; }
public override bool Equals(object obj) { return obj is CPos && Equals((CPos)obj); }
public override string ToString() { return X + "," + Y; }
@@ -120,6 +119,7 @@ namespace OpenRA
{
case "X": return X;
case "Y": return Y;
case "Layer": return Layer;
default: throw new LuaException("CPos does not define a member '{0}'".F(key));
}
}

View File

@@ -194,7 +194,6 @@
<Compile Include="Traits\Player\PlayerColorPalette.cs" />
<Compile Include="Traits\Player\PlayerHighlightPalette.cs" />
<Compile Include="Traits\World\ScreenMap.cs" />
<Compile Include="Traits\World\ActorMap.cs" />
<Compile Include="Scripting\ScriptContext.cs" />
<Compile Include="Scripting\ScriptActorInterface.cs" />
<Compile Include="Scripting\ScriptObjectWrapper.cs" />

View File

@@ -232,6 +232,30 @@ namespace OpenRA.Traits
}
}
public enum SubCell { Invalid = int.MinValue, Any = int.MinValue / 2, FullCell = 0, First = 1 }
public interface IActorMap
{
IEnumerable<Actor> GetActorsAt(CPos a);
IEnumerable<Actor> GetActorsAt(CPos a, SubCell sub);
bool HasFreeSubCell(CPos cell, bool checkTransient = true);
SubCell FreeSubCell(CPos cell, SubCell preferredSubCell = SubCell.Any, bool checkTransient = true);
SubCell FreeSubCell(CPos cell, SubCell preferredSubCell, Func<Actor, bool> checkIfBlocker);
bool AnyActorsAt(CPos a);
bool AnyActorsAt(CPos a, SubCell sub, bool checkTransient = true);
bool AnyActorsAt(CPos a, SubCell sub, Func<Actor, bool> withCondition);
void AddInfluence(Actor self, IOccupySpace ios);
void RemoveInfluence(Actor self, IOccupySpace ios);
int AddCellTrigger(CPos[] cells, Action<Actor> onEntry, Action<Actor> onExit);
void RemoveCellTrigger(int id);
int AddProximityTrigger(WPos pos, WDist range, WDist vRange, Action<Actor> onEntry, Action<Actor> onExit);
void RemoveProximityTrigger(int id);
void UpdateProximityTrigger(int id, WPos newPos, WDist newRange, WDist newVRange);
void AddPosition(Actor a, IOccupySpace ios);
void RemovePosition(Actor a, IOccupySpace ios);
void UpdatePosition(Actor a, IOccupySpace ios);
IEnumerable<Actor> ActorsInBox(WPos a, WPos b);
}
public interface IRenderModifier { IEnumerable<IRenderable> ModifyRender(Actor self, WorldRenderer wr, IEnumerable<IRenderable> r); }
public interface ILoadsPalettes { void LoadPalettes(WorldRenderer wr); }
public interface ILoadsPlayerPalettes { void LoadPlayerPalettes(WorldRenderer wr, string playerName, HSLColor playerColor, bool replaceExisting); }

View File

@@ -14,7 +14,6 @@ using System.Collections.Generic;
using System.Linq;
using OpenRA.Effects;
using OpenRA.FileFormats;
using OpenRA.GameRules;
using OpenRA.Graphics;
using OpenRA.Network;
using OpenRA.Orders;
@@ -107,7 +106,7 @@ namespace OpenRA
public readonly Map Map;
public readonly ActorMap ActorMap;
public readonly IActorMap ActorMap;
public readonly ScreenMap ScreenMap;
public readonly WorldType Type;
@@ -159,7 +158,7 @@ namespace OpenRA
var worldActorType = type == WorldType.Editor ? "EditorWorld" : "World";
WorldActor = CreateActor(worldActorType, new TypeDictionary());
ActorMap = WorldActor.Trait<ActorMap>();
ActorMap = WorldActor.Trait<IActorMap>();
ScreenMap = WorldActor.Trait<ScreenMap>();
// Add players

View File

@@ -771,7 +771,7 @@ namespace OpenRA.Mods.Common.AI
var path = pathfinder.FindPath(
PathSearch.Search(World, mobileInfo, harvester, true,
loc => domainIndex.IsPassable(harvester.Location, loc, passable) && harvester.CanHarvestAt(loc, resLayer, harvInfo, territory))
loc => domainIndex.IsPassable(harvester.Location, loc, mobileInfo, passable) && harvester.CanHarvestAt(loc, resLayer, harvInfo, territory))
.WithCustomCost(loc => World.FindActorsInCircle(World.Map.CenterOfCell(loc), Info.HarvesterEnemyAvoidanceRadius)
.Where(u => !u.IsDead && harvester.Owner.Stances[u.Owner] == Stance.Enemy)
.Sum(u => Math.Max(WDist.Zero.Length, Info.HarvesterEnemyAvoidanceRadius.Length - (World.Map.CenterOfCell(loc) - u.CenterPosition).Length)))

View File

@@ -100,7 +100,9 @@ namespace OpenRA.Mods.Common.Activities
minRange = armaments.Max(a => a.Weapon.MinRange);
maxRange = armaments.Min(a => a.MaxRange());
if (!Target.IsInRange(self.CenterPosition, maxRange) || Target.IsInRange(self.CenterPosition, minRange))
var mobile = move as Mobile;
if (!Target.IsInRange(self.CenterPosition, maxRange) || Target.IsInRange(self.CenterPosition, minRange)
|| (mobile != null && !mobile.CanInteractWithGroundLayer(self)))
{
// Try to move within range, drop the target otherwise
if (move == null)

View File

@@ -127,7 +127,7 @@ namespace OpenRA.Mods.Common.Activities
var passable = (uint)mobileInfo.GetMovementClass(self.World.Map.Rules.TileSet);
List<CPos> path;
using (var search = PathSearch.Search(self.World, mobileInfo, self, true,
loc => domainIndex.IsPassable(self.Location, loc, passable) && self.CanHarvestAt(loc, resLayer, harvInfo, territory))
loc => domainIndex.IsPassable(self.Location, loc, mobileInfo, passable) && self.CanHarvestAt(loc, resLayer, harvInfo, territory))
.WithCustomCost(loc =>
{
if ((avoidCell.HasValue && loc == avoidCell.Value) ||

View File

@@ -184,10 +184,15 @@ namespace OpenRA.Mods.Common.Activities
else
{
mobile.SetLocation(mobile.FromCell, mobile.FromSubCell, nextCell.Value.First, nextCell.Value.Second);
var from = self.World.Map.CenterOfSubCell(mobile.FromCell, mobile.FromSubCell);
var map = self.World.Map;
var from = (mobile.FromCell.Layer == 0 ? map.CenterOfCell(mobile.FromCell) :
self.World.GetCustomMovementLayers()[mobile.FromCell.Layer].CenterOfCell(mobile.FromCell)) +
map.Grid.OffsetOfSubCell(mobile.FromSubCell);
var to = Util.BetweenCells(self.World, mobile.FromCell, mobile.ToCell) +
(self.World.Map.Grid.OffsetOfSubCell(mobile.FromSubCell) +
self.World.Map.Grid.OffsetOfSubCell(mobile.ToSubCell)) / 2;
(map.Grid.OffsetOfSubCell(mobile.FromSubCell) + map.Grid.OffsetOfSubCell(mobile.ToSubCell)) / 2;
var move = new MoveFirstHalf(
this,
from,
@@ -292,6 +297,7 @@ namespace OpenRA.Mods.Common.Activities
protected readonly Move Move;
protected readonly WPos From, To;
protected readonly int FromFacing, ToFacing;
protected readonly bool EnableArc;
protected readonly WPos ArcCenter;
protected readonly int ArcFromLength;
protected readonly WAngle ArcFromAngle;
@@ -328,6 +334,7 @@ namespace OpenRA.Mods.Common.Activities
ArcFromAngle = (ArcCenter - from).Yaw;
ArcToLength = (ArcCenter - to).HorizontalLength;
ArcToAngle = (ArcCenter - to).Yaw;
EnableArc = true;
}
}
@@ -373,7 +380,7 @@ namespace OpenRA.Mods.Common.Activities
if (MoveFractionTotal != 0)
{
WPos pos;
if (FromFacing != ToFacing)
if (EnableArc)
{
var angle = WAngle.Lerp(ArcFromAngle, ArcToAngle, moveFraction, MoveFractionTotal);
var length = int2.Lerp(ArcFromLength, ArcToLength, moveFraction, MoveFractionTotal);
@@ -415,21 +422,22 @@ namespace OpenRA.Mods.Common.Activities
protected override MovePart OnComplete(Actor self, Mobile mobile, Move parent)
{
var fromSubcellOffset = self.World.Map.Grid.OffsetOfSubCell(mobile.FromSubCell);
var toSubcellOffset = self.World.Map.Grid.OffsetOfSubCell(mobile.ToSubCell);
var map = self.World.Map;
var fromSubcellOffset = map.Grid.OffsetOfSubCell(mobile.FromSubCell);
var toSubcellOffset = map.Grid.OffsetOfSubCell(mobile.ToSubCell);
var nextCell = parent.PopPath(self);
if (nextCell != null)
{
if (IsTurn(mobile, nextCell.Value.First))
{
var nextSubcellOffset = self.World.Map.Grid.OffsetOfSubCell(nextCell.Value.Second);
var nextSubcellOffset = map.Grid.OffsetOfSubCell(nextCell.Value.Second);
var ret = new MoveFirstHalf(
Move,
Util.BetweenCells(self.World, mobile.FromCell, mobile.ToCell) + (fromSubcellOffset + toSubcellOffset) / 2,
Util.BetweenCells(self.World, mobile.ToCell, nextCell.Value.First) + (toSubcellOffset + nextSubcellOffset) / 2,
mobile.Facing,
Util.GetNearestFacing(mobile.Facing, self.World.Map.FacingBetween(mobile.ToCell, nextCell.Value.First, mobile.Facing)),
Util.GetNearestFacing(mobile.Facing, map.FacingBetween(mobile.ToCell, nextCell.Value.First, mobile.Facing)),
moveFraction - MoveFractionTotal);
mobile.FinishedMoving(self);
@@ -440,10 +448,13 @@ namespace OpenRA.Mods.Common.Activities
parent.path.Add(nextCell.Value.First);
}
var toPos = mobile.ToCell.Layer == 0 ? map.CenterOfCell(mobile.ToCell) :
self.World.GetCustomMovementLayers()[mobile.ToCell.Layer].CenterOfCell(mobile.ToCell);
var ret2 = new MoveSecondHalf(
Move,
Util.BetweenCells(self.World, mobile.FromCell, mobile.ToCell) + (fromSubcellOffset + toSubcellOffset) / 2,
self.World.Map.CenterOfCell(mobile.ToCell) + toSubcellOffset,
toPos + toSubcellOffset,
mobile.Facing,
mobile.Facing,
moveFraction - MoveFractionTotal);

View File

@@ -23,7 +23,7 @@ namespace OpenRA.Mods.Common.Activities
{
static readonly List<CPos> NoPath = new List<CPos>();
readonly Mobile mobile;
protected readonly Mobile Mobile;
readonly IPathFinder pathFinder;
readonly DomainIndex domainIndex;
readonly uint movementClass;
@@ -53,10 +53,10 @@ namespace OpenRA.Mods.Common.Activities
{
Target = target;
mobile = self.Trait<Mobile>();
Mobile = self.Trait<Mobile>();
pathFinder = self.World.WorldActor.Trait<IPathFinder>();
domainIndex = self.World.WorldActor.Trait<DomainIndex>();
movementClass = (uint)mobile.Info.GetMovementClass(self.World.Map.Rules.TileSet);
movementClass = (uint)Mobile.Info.GetMovementClass(self.World.Map.Rules.TileSet);
if (target.IsValidFor(self))
targetPosition = self.World.Map.CellContaining(target.CenterPosition);
@@ -91,7 +91,7 @@ namespace OpenRA.Mods.Common.Activities
inner.Cancel(self);
self.SetTargetLine(Target.FromCell(self.World, targetPosition), Color.Green);
return ActivityUtils.RunActivity(self, new AttackMoveActivity(self, mobile.MoveTo(targetPosition, 0)));
return ActivityUtils.RunActivity(self, new AttackMoveActivity(self, Mobile.MoveTo(targetPosition, 0)));
}
// Inner move order has completed.
@@ -103,7 +103,7 @@ namespace OpenRA.Mods.Common.Activities
return NextActivity;
// Target has moved, and MoveAdjacentTo is still valid.
inner = mobile.MoveTo(() => CalculatePathToTarget(self));
inner = Mobile.MoveTo(() => CalculatePathToTarget(self));
repath = false;
}
@@ -142,14 +142,14 @@ namespace OpenRA.Mods.Common.Activities
var loc = self.Location;
foreach (var cell in targetCells)
if (domainIndex.IsPassable(loc, cell, movementClass) && mobile.CanEnterCell(cell))
if (domainIndex.IsPassable(loc, cell, Mobile.Info, movementClass) && Mobile.CanEnterCell(cell))
searchCells.Add(cell);
if (!searchCells.Any())
return NoPath;
using (var fromSrc = PathSearch.FromPoints(self.World, mobile.Info, self, searchCells, loc, true))
using (var fromDest = PathSearch.FromPoint(self.World, mobile.Info, self, loc, targetPosition, true).Reverse())
using (var fromSrc = PathSearch.FromPoints(self.World, Mobile.Info, self, searchCells, loc, true))
using (var fromDest = PathSearch.FromPoint(self.World, Mobile.Info, self, loc, targetPosition, true).Reverse())
return pathFinder.FindBidiPath(fromSrc, fromDest);
}

View File

@@ -11,6 +11,7 @@
using System.Collections.Generic;
using System.Linq;
using OpenRA.Mods.Common.Traits;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Activities
@@ -31,12 +32,13 @@ namespace OpenRA.Mods.Common.Activities
{
// We are now in range. Don't move any further!
// HACK: This works around the pathfinder not returning the shortest path
return AtCorrectRange(self.CenterPosition);
return AtCorrectRange(self.CenterPosition) && Mobile.CanInteractWithGroundLayer(self);
}
protected override bool ShouldRepath(Actor self, CPos oldTargetPosition)
{
return targetPosition != oldTargetPosition && !AtCorrectRange(self.CenterPosition);
return targetPosition != oldTargetPosition && (!AtCorrectRange(self.CenterPosition)
|| !Mobile.CanInteractWithGroundLayer(self));
}
protected override IEnumerable<CPos> CandidateMovementCells(Actor self)

View File

@@ -30,8 +30,9 @@ namespace OpenRA.Mods.Common.Lint
var deathAnimationDeathtypes = animations.SelectMany(x => x.DeathTypes.Select(y => y.Key)).ToList();
var spawnActorDeathtypes = actorInfo.Value.TraitInfos<SpawnActorOnDeathInfo>().Where(s => !string.IsNullOrEmpty(s.DeathType)).Select(a => a.DeathType);
var spawnActorOnAnyDeathType = actorInfo.Value.TraitInfos<SpawnActorOnDeathInfo>().Any(s => s.DeathType == null);
var deathTypes = deathAnimationDeathtypes.Concat(spawnActorDeathtypes).Distinct();
if (!deathTypes.Any())
if (!deathTypes.Any() || spawnActorOnAnyDeathType)
continue;
var targetable = actorInfo.Value.TraitInfos<ITargetableInfo>().SelectMany(x => x.GetTargetTypes()).ToList();

View File

@@ -793,6 +793,13 @@
<Compile Include="UtilityCommands\OutputResolvedWeaponsCommand.cs" />
<Compile Include="Traits\RevealOnFire.cs" />
<Compile Include="Traits\Conditions\GrantConditionOnDisabled.cs" />
<Compile Include="Traits\World\ActorMap.cs" />
<Compile Include="Traits\World\TerrainTunnelLayer.cs" />
<Compile Include="Traits\World\TerrainTunnel.cs" />
<Compile Include="Traits\World\SubterraneanActorLayer.cs" />
<Compile Include="Traits\World\JumpjetActorLayer.cs" />
<Compile Include="Traits\World\ElevatedBridgeLayer.cs" />
<Compile Include="Traits\World\ElevatedBridgePlaceholder.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="AfterBuild">

View File

@@ -57,21 +57,28 @@ namespace OpenRA.Mods.Common.Pathfinder
public class PooledCellInfoLayer : IDisposable
{
public CellLayer<CellInfo> Layer { get; private set; }
CellInfoLayerPool layerPool;
List<CellLayer<CellInfo>> layers = new List<CellLayer<CellInfo>>();
public PooledCellInfoLayer(CellInfoLayerPool layerPool)
{
this.layerPool = layerPool;
Layer = layerPool.GetLayer();
}
public CellLayer<CellInfo> GetLayer()
{
var layer = layerPool.GetLayer();
layers.Add(layer);
return layer;
}
public void Dispose()
{
if (Layer == null)
return;
layerPool.ReturnLayer(Layer);
Layer = null;
if (layerPool != null)
foreach (var layer in layers)
layerPool.ReturnLayer(layer);
layers = null;
layerPool = null;
}
}

View File

@@ -11,7 +11,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Mods.Common.Traits;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Pathfinder
{
@@ -84,18 +87,29 @@ namespace OpenRA.Mods.Common.Pathfinder
readonly MobileInfo mobileInfo;
readonly MobileInfo.WorldMovementInfo worldMovementInfo;
readonly CellInfoLayerPool.PooledCellInfoLayer pooledLayer;
CellLayer<CellInfo> cellInfo;
readonly bool checkTerrainHeight;
CellLayer<CellInfo> groundInfo;
readonly Dictionary<byte, Pair<ICustomMovementLayer, CellLayer<CellInfo>>> customLayerInfo =
new Dictionary<byte, Pair<ICustomMovementLayer, CellLayer<CellInfo>>>();
public PathGraph(CellInfoLayerPool layerPool, MobileInfo mobileInfo, Actor actor, World world, bool checkForBlocked)
{
pooledLayer = layerPool.Get();
cellInfo = pooledLayer.Layer;
groundInfo = pooledLayer.GetLayer();
var layers = world.GetCustomMovementLayers().Values
.Where(cml => cml.EnabledForActor(actor.Info, mobileInfo));
foreach (var cml in layers)
customLayerInfo[cml.Index] = Pair.New(cml, pooledLayer.GetLayer());
World = world;
this.mobileInfo = mobileInfo;
worldMovementInfo = mobileInfo.GetWorldMovementInfo(world);
Actor = actor;
LaneBias = 1;
checkConditions = checkForBlocked ? CellConditions.TransientActors : CellConditions.None;
checkTerrainHeight = world.Map.Grid.MaximumTerrainHeight > 0;
}
// Sets of neighbors for each incoming direction. These exclude the neighbors which are guaranteed
@@ -117,7 +131,8 @@ namespace OpenRA.Mods.Common.Pathfinder
public List<GraphConnection> GetConnections(CPos position)
{
var previousPos = cellInfo[position].PreviousPos;
var info = position.Layer == 0 ? groundInfo : customLayerInfo[position.Layer].Second;
var previousPos = info[position].PreviousPos;
var dx = position.X - previousPos.X;
var dy = position.Y - previousPos.Y;
@@ -133,6 +148,24 @@ namespace OpenRA.Mods.Common.Pathfinder
validNeighbors.Add(new GraphConnection(neighbor, movementCost));
}
if (position.Layer == 0)
{
foreach (var cli in customLayerInfo.Values)
{
var layerPosition = new CPos(position.X, position.Y, cli.First.Index);
var entryCost = cli.First.EntryMovementCost(Actor.Info, mobileInfo, layerPosition);
if (entryCost != Constants.InvalidNode)
validNeighbors.Add(new GraphConnection(layerPosition, entryCost));
}
}
else
{
var layerPosition = new CPos(position.X, position.Y, 0);
var exitCost = customLayerInfo[position.Layer].First.ExitMovementCost(Actor.Info, mobileInfo, layerPosition);
if (exitCost != Constants.InvalidNode)
validNeighbors.Add(new GraphConnection(layerPosition, exitCost));
}
return validNeighbors;
}
@@ -161,7 +194,15 @@ namespace OpenRA.Mods.Common.Pathfinder
cellCost += customCost;
}
// directional bonuses for smoother flow!
// Prevent units from jumping over height discontinuities
if (checkTerrainHeight && neighborCPos.Layer == 0)
{
var from = neighborCPos - direction;
if (Math.Abs(World.Map.Height[neighborCPos] - World.Map.Height[from]) > 1)
return Constants.InvalidNode;
}
// Directional bonuses for smoother flow!
if (LaneBias != 0)
{
var ux = neighborCPos.X + (InReverse ? 1 : 0) & 1;
@@ -179,14 +220,15 @@ namespace OpenRA.Mods.Common.Pathfinder
public CellInfo this[CPos pos]
{
get { return cellInfo[pos]; }
set { cellInfo[pos] = value; }
get { return (pos.Layer == 0 ? groundInfo : customLayerInfo[pos.Layer].Second)[pos]; }
set { (pos.Layer == 0 ? groundInfo : customLayerInfo[pos.Layer].Second)[pos] = value; }
}
public void Dispose()
{
groundInfo = null;
customLayerInfo.Clear();
pooledLayer.Dispose();
cellInfo = null;
}
}
}

View File

@@ -15,6 +15,7 @@ using System.Drawing;
using System.Linq;
using OpenRA.Activities;
using OpenRA.Mods.Common.Activities;
using OpenRA.Mods.Common.Effects;
using OpenRA.Primitives;
using OpenRA.Traits;
@@ -38,6 +39,14 @@ namespace OpenRA.Mods.Common.Traits
}
}
public static class CustomMovementLayerType
{
public const byte Tunnel = 1;
public const byte Subterranean = 2;
public const byte Jumpjet = 3;
public const byte ElevatedBridge = 4;
}
[Desc("Unit is able to move.")]
public class MobileInfo : ConditionalTraitInfo, IMoveInfo, IPositionableInfo, IOccupySpaceInfo, IFacingInfo,
UsesInit<FacingInit>, UsesInit<LocationInit>, UsesInit<SubCellInit>
@@ -73,6 +82,57 @@ namespace OpenRA.Mods.Common.Traits
[VoiceReference] public readonly string Voice = "Action";
[GrantedConditionReference]
[Desc("The condition to grant to self while inside a tunnel.")]
public readonly string TunnelCondition = null;
[Desc("Can this unit move underground?")]
public readonly bool Subterranean = false;
[GrantedConditionReference]
[Desc("The condition to grant to self while underground.")]
public readonly string SubterraneanCondition = null;
[Desc("Pathfinding cost for submerging or reemerging.")]
public readonly int SubterraneanTransitionCost = 0;
[Desc("The terrain types that this actor can transition on. Leave empty to allow any.")]
public readonly HashSet<string> SubterraneanTransitionTerrainTypes = new HashSet<string>();
[Desc("Can this actor transition on slopes?")]
public readonly bool SubterraneanTransitionOnRamps = false;
[Desc("Depth at which the subterranian condition is applied.")]
public readonly WDist SubterraneanTransitionDepth = new WDist(-1024);
[Desc("Dig animation image to play when transitioning.")]
public readonly string SubterraneanTransitionImage = null;
[SequenceReference("SubterraneanTransitionImage")]
[Desc("Dig animation image to play when transitioning.")]
public readonly string SubterraneanTransitionSequence = null;
[PaletteReference]
public readonly string SubterraneanTransitionPalette = "effect";
public readonly string SubterraneanTransitionSound = null;
[Desc("Can this unit fly over obsticals?")]
public readonly bool Jumpjet = false;
[GrantedConditionReference]
[Desc("The condition to grant to self while flying.")]
public readonly string JumpjetCondition = null;
[Desc("Pathfinding cost for taking off or landing.")]
public readonly int JumpjetTransitionCost = 0;
[Desc("The terrain types that this actor can transition on. Leave empty to allow any.")]
public readonly HashSet<string> JumpjetTransitionTerrainTypes = new HashSet<string>();
[Desc("Can this actor transition on slopes?")]
public readonly bool JumpjetTransitionOnRamps = true;
public override object Create(ActorInitializer init) { return new Mobile(init, this); }
static object LoadSpeeds(MiniYaml y)
@@ -151,15 +211,17 @@ namespace OpenRA.Mods.Common.Traits
public int MovementCostForCell(World world, CPos cell)
{
return MovementCostForCell(world.Map, TilesetTerrainInfo[world.Map.Rules.TileSet], cell);
return MovementCostForCell(world, TilesetTerrainInfo[world.Map.Rules.TileSet], cell);
}
int MovementCostForCell(Map map, TerrainInfo[] terrainInfos, CPos cell)
int MovementCostForCell(World world, TerrainInfo[] terrainInfos, CPos cell)
{
if (!map.Contains(cell))
if (!world.Map.Contains(cell))
return int.MaxValue;
var index = map.GetTerrainIndex(cell);
var index = cell.Layer == 0 ? world.Map.GetTerrainIndex(cell) :
world.GetCustomMovementLayers()[cell.Layer].GetTerrainIndex(cell);
if (index == byte.MaxValue)
return int.MaxValue;
@@ -279,7 +341,7 @@ namespace OpenRA.Mods.Common.Traits
public int MovementCostToEnterCell(WorldMovementInfo worldMovementInfo, Actor self, CPos cell, Actor ignoreActor = null, CellConditions check = CellConditions.All)
{
var cost = MovementCostForCell(worldMovementInfo.World.Map, worldMovementInfo.TerrainInfos, cell);
var cost = MovementCostForCell(worldMovementInfo.World, worldMovementInfo.TerrainInfos, cell);
if (cost == int.MaxValue || !CanMoveFreelyInto(worldMovementInfo.World, self, cell, ignoreActor, check))
return int.MaxValue;
return cost;
@@ -317,8 +379,8 @@ namespace OpenRA.Mods.Common.Traits
bool IOccupySpaceInfo.SharesCell { get { return SharesCell; } }
}
public class Mobile : ConditionalTrait<MobileInfo>, IIssueOrder, IResolveOrder, IOrderVoice, IPositionable, IMove, IFacing, ISync,
IDeathActorInitModifier, INotifyAddedToWorld, INotifyRemovedFromWorld, INotifyBlockingMove, IActorPreviewInitModifier
public class Mobile : ConditionalTrait<MobileInfo>, INotifyCreated, IIssueOrder, IResolveOrder, IOrderVoice, IPositionable, IMove,
IFacing, ISync, IDeathActorInitModifier, INotifyAddedToWorld, INotifyRemovedFromWorld, INotifyBlockingMove, IActorPreviewInitModifier
{
const int AverageTicksBeforePathing = 5;
const int SpreadTicksBeforePathing = 5;
@@ -332,6 +394,10 @@ namespace OpenRA.Mods.Common.Traits
int facing;
CPos fromCell, toCell;
public SubCell FromSubCell, ToSubCell;
int tunnelToken = ConditionManager.InvalidConditionToken;
int subterraneanToken = ConditionManager.InvalidConditionToken;
int jumpjetToken = ConditionManager.InvalidConditionToken;
ConditionManager conditionManager;
[Sync] public int Facing
{
@@ -359,6 +425,30 @@ namespace OpenRA.Mods.Common.Traits
FromSubCell = fromSub;
ToSubCell = toSub;
AddInfluence();
// Tunnel condition is added/removed when starting the transition between layers
if (toCell.Layer == CustomMovementLayerType.Tunnel && conditionManager != null &&
!string.IsNullOrEmpty(Info.TunnelCondition) && tunnelToken == ConditionManager.InvalidConditionToken)
tunnelToken = conditionManager.GrantCondition(self, Info.TunnelCondition);
else if (toCell.Layer != CustomMovementLayerType.Tunnel && tunnelToken != ConditionManager.InvalidConditionToken)
tunnelToken = conditionManager.RevokeCondition(self, tunnelToken);
// Play submerging animation as soon as it starts to submerge (before applying the condition)
if (toCell.Layer == CustomMovementLayerType.Subterranean && fromCell.Layer != CustomMovementLayerType.Subterranean)
{
if (!string.IsNullOrEmpty(Info.SubterraneanTransitionSequence))
self.World.AddFrameEndTask(w => w.Add(new SpriteEffect(self.World.Map.CenterOfCell(fromCell), self.World, Info.SubterraneanTransitionImage,
Info.SubterraneanTransitionSequence, Info.SubterraneanTransitionPalette)));
if (!string.IsNullOrEmpty(Info.SubterraneanTransitionSound))
Game.Sound.Play(SoundType.World, Info.SubterraneanTransitionSound);
}
// Grant the jumpjet condition as soon as the actor starts leaving the ground layer
// The condition is revoked from FinishedMoving
if (toCell.Layer == CustomMovementLayerType.Jumpjet && conditionManager != null &&
!string.IsNullOrEmpty(Info.JumpjetCondition) && jumpjetToken == ConditionManager.InvalidConditionToken)
jumpjetToken = conditionManager.GrantCondition(self, Info.JumpjetCondition);
}
public Mobile(ActorInitializer init, MobileInfo info)
@@ -386,6 +476,11 @@ namespace OpenRA.Mods.Common.Traits
SetVisualPosition(self, init.Get<CenterPositionInit, WPos>());
}
void INotifyCreated.Created(Actor self)
{
conditionManager = self.TraitOrDefault<ConditionManager>();
}
// Returns a valid sub-cell
public SubCell GetValidSubCell(SubCell preferred = SubCell.Any)
{
@@ -413,7 +508,12 @@ namespace OpenRA.Mods.Common.Traits
{
subCell = GetValidSubCell(subCell);
SetLocation(cell, subCell, cell, subCell);
SetVisualPosition(self, self.World.Map.CenterOfSubCell(cell, subCell));
var position = cell.Layer == 0 ? self.World.Map.CenterOfCell(cell) :
self.World.GetCustomMovementLayers()[cell.Layer].CenterOfCell(cell);
var subcellOffset = self.World.Map.Grid.OffsetOfSubCell(subCell);
SetVisualPosition(self, position + subcellOffset);
FinishedMoving(self);
}
@@ -431,6 +531,32 @@ namespace OpenRA.Mods.Common.Traits
{
CenterPosition = pos;
self.World.UpdateMaps(self, this);
// HACK: The submerging conditions must be applied part way through a move, and this is the only method that gets called
// at the right times to detect this
if (toCell.Layer == CustomMovementLayerType.Subterranean)
{
var depth = self.World.Map.DistanceAboveTerrain(self.CenterPosition);
if (subterraneanToken == ConditionManager.InvalidConditionToken && depth < Info.SubterraneanTransitionDepth && conditionManager != null
&& !string.IsNullOrEmpty(Info.SubterraneanCondition))
subterraneanToken = conditionManager.GrantCondition(self, Info.SubterraneanCondition);
}
else if (subterraneanToken != ConditionManager.InvalidConditionToken)
{
var depth = self.World.Map.DistanceAboveTerrain(self.CenterPosition);
if (depth > Info.SubterraneanTransitionDepth)
{
subterraneanToken = conditionManager.RevokeCondition(self, subterraneanToken);
// HACK: the submerging animation and sound won't play if a condition isn't defined
if (!string.IsNullOrEmpty(Info.SubterraneanTransitionSound))
Game.Sound.Play(SoundType.World, Info.SubterraneanTransitionSound);
if (!string.IsNullOrEmpty(Info.SubterraneanTransitionSequence))
self.World.AddFrameEndTask(w => w.Add(new SpriteEffect(self.World.Map.CenterOfCell(fromCell), self.World, Info.SubterraneanTransitionImage,
Info.SubterraneanTransitionSequence, Info.SubterraneanTransitionPalette)));
}
}
}
public void AddedToWorld(Actor self)
@@ -598,6 +724,11 @@ namespace OpenRA.Mods.Common.Traits
public void FinishedMoving(Actor self)
{
// Need to check both fromCell and toCell because FinishedMoving is called multiple times during the move
// and that condition guarantees that this only runs when the unit has finished landing.
if (fromCell.Layer != CustomMovementLayerType.Jumpjet && toCell.Layer != CustomMovementLayerType.Jumpjet && jumpjetToken != ConditionManager.InvalidConditionToken)
jumpjetToken = conditionManager.RevokeCondition(self, jumpjetToken);
// Only make actor crush if it is on the ground
if (!self.IsAtGroundLevel())
return;
@@ -626,7 +757,9 @@ namespace OpenRA.Mods.Common.Traits
public int MovementSpeedForCell(Actor self, CPos cell)
{
var index = self.World.Map.GetTerrainIndex(cell);
var index = cell.Layer == 0 ? self.World.Map.GetTerrainIndex(cell) :
self.World.GetCustomMovementLayers()[cell.Layer].GetTerrainIndex(cell);
if (index == byte.MaxValue)
return 0;
@@ -716,6 +849,21 @@ namespace OpenRA.Mods.Common.Traits
}
}
public bool CanInteractWithGroundLayer(Actor self)
{
// TODO: Think about extending this to support arbitrary layer-layer checks
// in a way that is compatible with the other IMove types.
// This would then allow us to e.g. have units attack other units inside tunnels.
if (ToCell.Layer == 0)
return true;
ICustomMovementLayer layer;
if (self.World.GetCustomMovementLayers().TryGetValue(ToCell.Layer, out layer))
return layer.InteractsWithDefaultLayer;
return true;
}
void IActorPreviewInitModifier.ModifyActorPreviewInit(Actor self, TypeDictionary inits)
{
if (!inits.Contains<DynamicFacingInit>() && !inits.Contains<FacingInit>())

View File

@@ -57,7 +57,7 @@ namespace OpenRA.Mods.Common.Traits
if (mobileInfo != null)
location = self.World.Map.ChooseClosestMatchingEdgeCell(self.Location,
c => mobileInfo.CanEnterCell(self.World, null, c) && domainIndex.IsPassable(c, destination, passable));
c => mobileInfo.CanEnterCell(self.World, null, c) && domainIndex.IsPassable(c, destination, mobileInfo, passable));
}
// No suitable spawn location could be found, so production has failed.

View File

@@ -18,7 +18,7 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits.Render
{
[Desc("This actor has a death animation.")]
public class WithDeathAnimationInfo : ITraitInfo, Requires<RenderSpritesInfo>
public class WithDeathAnimationInfo : ConditionalTraitInfo, Requires<RenderSpritesInfo>
{
[Desc("Sequence prefix to play when this actor is killed by a warhead.")]
[SequenceReference(null, true)] public readonly string DeathSequence = "die";
@@ -58,25 +58,24 @@ namespace OpenRA.Mods.Common.Traits.Render
: new Dictionary<string, string[]>();
}
public object Create(ActorInitializer init) { return new WithDeathAnimation(init.Self, this); }
public override object Create(ActorInitializer init) { return new WithDeathAnimation(init.Self, this); }
}
public class WithDeathAnimation : INotifyKilled, INotifyCrushed
public class WithDeathAnimation : ConditionalTrait<WithDeathAnimationInfo>, INotifyKilled, INotifyCrushed
{
public readonly WithDeathAnimationInfo Info;
readonly RenderSprites rs;
bool crushed;
public WithDeathAnimation(Actor self, WithDeathAnimationInfo info)
: base(info)
{
Info = info;
rs = self.Trait<RenderSprites>();
}
public void Killed(Actor self, AttackInfo e)
{
// Actors with Crushable trait will spawn CrushedSequence.
if (crushed)
if (crushed || IsTraitDisabled)
return;
var palette = Info.DeathSequencePalette;

View File

@@ -16,7 +16,7 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits.Sound
{
[Desc("Sounds to play when killed.")]
public class DeathSoundsInfo : ITraitInfo
public class DeathSoundsInfo : ConditionalTraitInfo
{
[Desc("Death notification voice.")]
[VoiceReference] public readonly string Voice = "Die";
@@ -28,19 +28,21 @@ namespace OpenRA.Mods.Common.Traits.Sound
"If empty, this will be used as the default sound for all death types.")]
public readonly HashSet<string> DeathTypes = new HashSet<string>();
public object Create(ActorInitializer init) { return new DeathSounds(this); }
public override object Create(ActorInitializer init) { return new DeathSounds(this); }
}
public class DeathSounds : INotifyKilled
public class DeathSounds : ConditionalTrait<DeathSoundsInfo>, INotifyKilled
{
readonly DeathSoundsInfo info;
public DeathSounds(DeathSoundsInfo info) { this.info = info; }
public DeathSounds(DeathSoundsInfo info)
: base(info) { }
public void Killed(Actor self, AttackInfo e)
{
if (info.DeathTypes.Count == 0 || e.Damage.DamageTypes.Overlaps(info.DeathTypes))
self.PlayVoiceLocal(info.Voice, info.VolumeMultiplier);
if (IsTraitDisabled)
return;
if (Info.DeathTypes.Count == 0 || e.Damage.DamageTypes.Overlaps(Info.DeathTypes))
self.PlayVoiceLocal(Info.Voice, Info.VolumeMultiplier);
}
}
}

View File

@@ -14,11 +14,10 @@ using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Traits;
namespace OpenRA.Traits
namespace OpenRA.Mods.Common.Traits
{
public enum SubCell { Invalid = int.MinValue, Any = int.MinValue / 2, FullCell = 0, First = 1 }
public class ActorMapInfo : ITraitInfo
{
[Desc("Size of partition bins (cells)")]
@@ -27,7 +26,7 @@ namespace OpenRA.Traits
public object Create(ActorInitializer init) { return new ActorMap(init.World, this); }
}
public class ActorMap : ITick
public class ActorMap : IActorMap, ITick, INotifyCreated
{
class InfluenceNode
{
@@ -166,6 +165,8 @@ namespace OpenRA.Traits
int nextTriggerId;
readonly CellLayer<InfluenceNode> influence;
readonly Dictionary<int, CellLayer<InfluenceNode>> customInfluence = new Dictionary<int, CellLayer<InfluenceNode>>();
public readonly Dictionary<int, ICustomMovementLayer> CustomMovementLayers = new Dictionary<int, ICustomMovementLayer>();
readonly Bin[] bins;
readonly int rows, cols;
@@ -193,6 +194,15 @@ namespace OpenRA.Traits
actorShouldBeRemoved = removeActorPosition.Contains;
}
void INotifyCreated.Created(Actor self)
{
foreach (var cml in self.TraitsImplementing<ICustomMovementLayer>())
{
CustomMovementLayers[cml.Index] = cml;
customInfluence.Add(cml.Index, new CellLayer<InfluenceNode>(self.World.Map));
}
}
sealed class ActorsAtEnumerator : IEnumerator<Actor>
{
InfluenceNode node;
@@ -229,7 +239,9 @@ namespace OpenRA.Traits
var uv = a.ToMPos(map);
if (!influence.Contains(uv))
return Enumerable.Empty<Actor>();
return new ActorsAtEnumerable(influence[uv]);
var layer = a.Layer == 0 ? influence : customInfluence[a.Layer];
return new ActorsAtEnumerable(layer[uv]);
}
public IEnumerable<Actor> GetActorsAt(CPos a, SubCell sub)
@@ -238,7 +250,8 @@ namespace OpenRA.Traits
if (!influence.Contains(uv))
yield break;
for (var i = influence[uv]; i != null; i = i.Next)
var layer = a.Layer == 0 ? influence : customInfluence[a.Layer];
for (var i = layer[uv]; i != null; i = i.Next)
if (!i.Actor.Disposed && (i.SubCell == sub || i.SubCell == SubCell.FullCell))
yield return i.Actor;
}
@@ -284,7 +297,8 @@ namespace OpenRA.Traits
if (!influence.Contains(uv))
return false;
return influence[uv] != null;
var layer = a.Layer == 0 ? influence : customInfluence[a.Layer];
return layer[uv] != null;
}
// NOTE: can not check aircraft
@@ -295,7 +309,8 @@ namespace OpenRA.Traits
return false;
var always = sub == SubCell.FullCell || sub == SubCell.Any;
for (var i = influence[uv]; i != null; i = i.Next)
var layer = a.Layer == 0 ? influence : customInfluence[a.Layer];
for (var i = layer[uv]; i != null; i = i.Next)
{
if (always || i.SubCell == sub || i.SubCell == SubCell.FullCell)
{
@@ -319,7 +334,8 @@ namespace OpenRA.Traits
return false;
var always = sub == SubCell.FullCell || sub == SubCell.Any;
for (var i = influence[uv]; i != null; i = i.Next)
var layer = a.Layer == 0 ? influence : customInfluence[a.Layer];
for (var i = layer[uv]; i != null; i = i.Next)
if ((always || i.SubCell == sub || i.SubCell == SubCell.FullCell) && !i.Actor.Disposed && withCondition(i.Actor))
return true;
@@ -334,7 +350,8 @@ namespace OpenRA.Traits
if (!influence.Contains(uv))
continue;
influence[uv] = new InfluenceNode { Next = influence[uv], SubCell = c.Second, Actor = self };
var layer = c.First.Layer == 0 ? influence : customInfluence[c.First.Layer];
layer[uv] = new InfluenceNode { Next = layer[uv], SubCell = c.Second, Actor = self };
List<CellTrigger> triggers;
if (cellTriggerInfluence.TryGetValue(c.First, out triggers))
@@ -351,9 +368,10 @@ namespace OpenRA.Traits
if (!influence.Contains(uv))
continue;
var temp = influence[uv];
var layer = c.First.Layer == 0 ? influence : customInfluence[c.First.Layer];
var temp = layer[uv];
RemoveInfluenceInner(ref temp, self);
influence[uv] = temp;
layer[uv] = temp;
List<CellTrigger> triggers;
if (cellTriggerInfluence.TryGetValue(c.First, out triggers))
@@ -568,4 +586,12 @@ namespace OpenRA.Traits
}
}
}
public static class ActorMapWorldExts
{
public static Dictionary<int, ICustomMovementLayer> GetCustomMovementLayers(this World world)
{
return ((ActorMap)world.ActorMap).CustomMovementLayers;
}
}
}

View File

@@ -9,6 +9,7 @@
*/
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Traits;
@@ -21,14 +22,14 @@ namespace OpenRA.Mods.Common.Traits
{
public readonly string TerrainType = "Impassable";
public object Create(ActorInitializer init) { return new CliffBackBlockingLayer(this); }
public object Create(ActorInitializer init) { return new CliffBackImpassabilityLayer(this); }
}
class CliffBackBlockingLayer : IWorldLoaded
class CliffBackImpassabilityLayer : IWorldLoaded
{
readonly CliffBackImpassabilityLayerInfo info;
public CliffBackBlockingLayer(CliffBackImpassabilityLayerInfo info)
public CliffBackImpassabilityLayer(CliffBackImpassabilityLayerInfo info)
{
this.info = info;
}
@@ -36,8 +37,17 @@ namespace OpenRA.Mods.Common.Traits
public void WorldLoaded(World w, WorldRenderer wr)
{
var tileType = w.Map.Rules.TileSet.GetTerrainIndex(info.TerrainType);
// Units are allowed behind cliffs *only* if they are part of a tunnel portal
var tunnelPortals = w.WorldActor.Info.TraitInfos<TerrainTunnelInfo>()
.SelectMany(mti => mti.PortalCells())
.ToHashSet();
foreach (var uv in w.Map.AllCells.MapCoords)
{
if (tunnelPortals.Contains(uv.ToCPos(w.Map)))
continue;
// All the map cells that visually overlap the current cell
var testCells = w.Map.ProjectedCellsCovering(uv)
.SelectMany(puv => w.Map.Unproject(puv));

View File

@@ -37,8 +37,17 @@ namespace OpenRA.Mods.Common.Traits
domainIndexes[mc] = new MovementClassDomainIndex(world, mc);
}
public bool IsPassable(CPos p1, CPos p2, uint movementClass)
public bool IsPassable(CPos p1, CPos p2, MobileInfo mi, uint movementClass)
{
// HACK: Work around units in other movement layers from being blocked
// when the point in the main layer is not pathable
if (p1.Layer != 0 || p2.Layer != 0)
return true;
// HACK: Workaround until we can generalize movement classes
if (mi.Subterranean || mi.Jumpjet)
return true;
return domainIndexes[movementClass].IsPassable(p1, p2);
}
@@ -49,6 +58,12 @@ namespace OpenRA.Mods.Common.Traits
foreach (var index in domainIndexes)
index.Value.UpdateCells(world, dirty);
}
public void AddFixedConnection(IEnumerable<CPos> cells)
{
foreach (var index in domainIndexes)
index.Value.AddFixedConnection(cells);
}
}
class MovementClassDomainIndex
@@ -119,6 +134,19 @@ namespace OpenRA.Mods.Common.Traits
CreateConnection(c1, c2);
}
public void AddFixedConnection(IEnumerable<CPos> cells)
{
// HACK: this is a temporary workaround to add a permanent connection between the domains of the listed cells.
// This is sufficient for fixed point-to-point tunnels, but not for dynamically updating custom layers
// such as destroyable elevated bridges.
// To support those the domain index will need to learn about custom movement layers, but that then requires
// a complete refactor of the domain code to deal with MobileInfo or better a shared pathfinder class type.
var cellDomains = cells.Select(c => domains[c]).ToHashSet();
foreach (var c1 in cellDomains)
foreach (var c2 in cellDomains.Where(c => c != c1))
CreateConnection(c1, c2);
}
bool HasConnection(int d1, int d2)
{
// Search our connections graph for a possible route

View File

@@ -0,0 +1,95 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Collections.Generic;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
public class ElevatedBridgeLayerInfo : ITraitInfo, Requires<DomainIndexInfo>, ILobbyCustomRulesIgnore
{
[Desc("Terrain type used by cells outside any elevated bridge footprint.")]
public readonly string ImpassableTerrainType = "Impassable";
public object Create(ActorInitializer init) { return new ElevatedBridgeLayer(init.Self, this); }
}
// For now this is mostly copies TerrainTunnelLayer. This will change once bridge destruction is implemented
public class ElevatedBridgeLayer : ICustomMovementLayer, IWorldLoaded
{
readonly Map map;
readonly CellLayer<WPos> cellCenters;
readonly CellLayer<byte> terrainIndices;
readonly HashSet<CPos> ends = new HashSet<CPos>();
bool enabled;
public ElevatedBridgeLayer(Actor self, ElevatedBridgeLayerInfo info)
{
map = self.World.Map;
cellCenters = new CellLayer<WPos>(map);
terrainIndices = new CellLayer<byte>(map);
terrainIndices.Clear(map.Rules.TileSet.GetTerrainIndex(info.ImpassableTerrainType));
}
public void WorldLoaded(World world, WorldRenderer wr)
{
var domainIndex = world.WorldActor.Trait<DomainIndex>();
foreach (var tti in world.WorldActor.Info.TraitInfos<ElevatedBridgePlaceholderInfo>())
{
enabled = true;
var terrain = map.Rules.TileSet.GetTerrainIndex(tti.TerrainType);
foreach (var c in tti.BridgeCells())
{
var uv = c.ToMPos(map);
terrainIndices[uv] = terrain;
var pos = map.CenterOfCell(c);
cellCenters[uv] = pos - new WVec(0, 0, pos.Z - 512 * tti.Height);
}
var end = tti.EndCells();
domainIndex.AddFixedConnection(end);
foreach (var c in end)
{
// Need to explicitly set both default and tunnel layers, otherwise the .Contains check will fail
ends.Add(new CPos(c.X, c.Y, 0));
ends.Add(new CPos(c.X, c.Y, CustomMovementLayerType.ElevatedBridge));
}
}
}
bool ICustomMovementLayer.EnabledForActor(ActorInfo a, MobileInfo mi) { return enabled; }
byte ICustomMovementLayer.Index { get { return CustomMovementLayerType.ElevatedBridge; } }
bool ICustomMovementLayer.InteractsWithDefaultLayer { get { return true; } }
WPos ICustomMovementLayer.CenterOfCell(CPos cell)
{
return cellCenters[cell];
}
int ICustomMovementLayer.EntryMovementCost(ActorInfo a, MobileInfo mi, CPos cell)
{
return ends.Contains(cell) ? 0 : int.MaxValue;
}
int ICustomMovementLayer.ExitMovementCost(ActorInfo a, MobileInfo mi, CPos cell)
{
return ends.Contains(cell) ? 0 : int.MaxValue;
}
byte ICustomMovementLayer.GetTerrainIndex(CPos cell)
{
return terrainIndices[cell];
}
}
}

View File

@@ -0,0 +1,75 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
public enum ElevatedBridgePlaceholderOrientation { X, Y }
[Desc("Placeholder to make static elevated bridges work.",
"Define individual trait instances for each elevated bridge footprint in the map.")]
public class ElevatedBridgePlaceholderInfo : TraitInfo<ElevatedBridgePlaceholder>, Requires<ElevatedBridgeLayerInfo>, ILobbyCustomRulesIgnore
{
[FieldLoader.Require]
[Desc("Location of the bridge")]
public readonly CPos Location = CPos.Zero;
[FieldLoader.Require]
[Desc("Orientation of the bridge.")]
public readonly ElevatedBridgePlaceholderOrientation Orientation;
[FieldLoader.Require]
[Desc("Length of the bridge")]
public readonly int Length = 0;
[FieldLoader.Require]
[Desc("Height of the bridge in map height steps.")]
public readonly byte Height = 0;
[Desc("Terrain type of the bridge.")]
public readonly string TerrainType = "Road";
public IEnumerable<CPos> BridgeCells()
{
var dimensions = Orientation == ElevatedBridgePlaceholderOrientation.X ?
new CVec(Length + 1, 3) : new CVec(3, Length + 1);
for (var y = 0; y < dimensions.Y; y++)
for (var x = 0; x < dimensions.X; x++)
yield return Location + new CVec(x, y);
}
public IEnumerable<CPos> EndCells()
{
if (Orientation == ElevatedBridgePlaceholderOrientation.X)
{
for (var y = 0; y < 3; y++)
{
yield return Location + new CVec(0, y);
yield return Location + new CVec(Length, y);
}
}
else
{
for (var x = 0; x < 3; x++)
{
yield return Location + new CVec(x, 0);
yield return Location + new CVec(x, Length);
}
}
}
}
public class ElevatedBridgePlaceholder { }
}

View File

@@ -0,0 +1,103 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Linq;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
public class JumpjetActorLayerInfo : ITraitInfo
{
[Desc("Terrain type of the airborne layer.")]
public readonly string TerrainType = "Jumpjet";
[Desc("Height offset relative to the smoothed terrain for movement.")]
public readonly WDist HeightOffset = new WDist(2304);
[Desc("Cell radius for smoothing adjacent cell heights.")]
public readonly int SmoothingRadius = 2;
public object Create(ActorInitializer init) { return new JumpjetActorLayer(init.Self, this); }
}
public class JumpjetActorLayer : ICustomMovementLayer
{
readonly Map map;
readonly byte terrainIndex;
readonly CellLayer<int> height;
public JumpjetActorLayer(Actor self, JumpjetActorLayerInfo info)
{
map = self.World.Map;
terrainIndex = self.World.Map.Rules.TileSet.GetTerrainIndex(info.TerrainType);
height = new CellLayer<int>(map);
foreach (var c in map.AllCells)
{
var neighbourCount = 0;
var neighbourHeight = 0;
for (var dy = -info.SmoothingRadius; dy <= info.SmoothingRadius; dy++)
{
for (var dx = -info.SmoothingRadius; dx <= info.SmoothingRadius; dx++)
{
var neighbour = c + new CVec(dx, dy);
if (!map.AllCells.Contains(neighbour))
continue;
neighbourCount++;
neighbourHeight += map.Height[neighbour];
}
}
height[c] = info.HeightOffset.Length + neighbourHeight * 512 / neighbourCount;
}
}
bool ICustomMovementLayer.EnabledForActor(ActorInfo a, MobileInfo mi) { return mi.Jumpjet; }
byte ICustomMovementLayer.Index { get { return CustomMovementLayerType.Jumpjet; } }
bool ICustomMovementLayer.InteractsWithDefaultLayer { get { return true; } }
WPos ICustomMovementLayer.CenterOfCell(CPos cell)
{
var pos = map.CenterOfCell(cell);
return pos + new WVec(0, 0, height[cell] - pos.Z);
}
bool ValidTransitionCell(CPos cell, MobileInfo mi)
{
var terrainType = map.GetTerrainInfo(cell).Type;
if (!mi.JumpjetTransitionTerrainTypes.Contains(terrainType) && mi.JumpjetTransitionTerrainTypes.Any())
return false;
if (mi.JumpjetTransitionOnRamps)
return true;
var tile = map.Tiles[cell];
var ti = map.Rules.TileSet.GetTileInfo(tile);
return ti == null || ti.RampType == 0;
}
int ICustomMovementLayer.EntryMovementCost(ActorInfo a, MobileInfo mi, CPos cell)
{
return ValidTransitionCell(cell, mi) ? mi.JumpjetTransitionCost : int.MaxValue;
}
int ICustomMovementLayer.ExitMovementCost(ActorInfo a, MobileInfo mi, CPos cell)
{
return ValidTransitionCell(cell, mi) ? mi.JumpjetTransitionCost : int.MaxValue;
}
byte ICustomMovementLayer.GetTerrainIndex(CPos cell)
{
return terrainIndex;
}
}
}

View File

@@ -69,7 +69,7 @@ namespace OpenRA.Mods.Common.Traits
if (domainIndex != null)
{
var passable = mi.GetMovementClass(world.Map.Rules.TileSet);
if (!domainIndex.IsPassable(source, target, (uint)passable))
if (!domainIndex.IsPassable(source, target, mi, (uint)passable))
return EmptyPath;
}
@@ -103,7 +103,7 @@ namespace OpenRA.Mods.Common.Traits
if (domainIndex != null)
{
var passable = mi.GetMovementClass(world.Map.Rules.TileSet);
tilesInRange = new List<CPos>(tilesInRange.Where(t => domainIndex.IsPassable(source, t, (uint)passable)));
tilesInRange = new List<CPos>(tilesInRange.Where(t => domainIndex.IsPassable(source, t, mi, (uint)passable)));
if (!tilesInRange.Any())
return EmptyPath;
}

View File

@@ -0,0 +1,103 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Linq;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
public class SubterraneanActorLayerInfo : ITraitInfo
{
[Desc("Terrain type of the underground layer.")]
public readonly string TerrainType = "Subterranean";
[Desc("Height offset relative to the smoothed terrain for movement.")]
public readonly WDist HeightOffset = -new WDist(2048);
[Desc("Cell radius for smoothing adjacent cell heights.")]
public readonly int SmoothingRadius = 2;
public object Create(ActorInitializer init) { return new SubterraneanActorLayer(init.Self, this); }
}
public class SubterraneanActorLayer : ICustomMovementLayer
{
readonly Map map;
readonly byte terrainIndex;
readonly CellLayer<int> height;
public SubterraneanActorLayer(Actor self, SubterraneanActorLayerInfo info)
{
map = self.World.Map;
terrainIndex = self.World.Map.Rules.TileSet.GetTerrainIndex(info.TerrainType);
height = new CellLayer<int>(map);
foreach (var c in map.AllCells)
{
var neighbourCount = 0;
var neighbourHeight = 0;
for (var dy = -info.SmoothingRadius; dy <= info.SmoothingRadius; dy++)
{
for (var dx = -info.SmoothingRadius; dx <= info.SmoothingRadius; dx++)
{
var neighbour = c + new CVec(dx, dy);
if (!map.AllCells.Contains(neighbour))
continue;
neighbourCount++;
neighbourHeight += map.Height[neighbour];
}
}
height[c] = info.HeightOffset.Length + neighbourHeight * 512 / neighbourCount;
}
}
bool ICustomMovementLayer.EnabledForActor(ActorInfo a, MobileInfo mi) { return mi.Subterranean; }
byte ICustomMovementLayer.Index { get { return CustomMovementLayerType.Subterranean; } }
bool ICustomMovementLayer.InteractsWithDefaultLayer { get { return false; } }
WPos ICustomMovementLayer.CenterOfCell(CPos cell)
{
var pos = map.CenterOfCell(cell);
return pos + new WVec(0, 0, height[cell] - pos.Z);
}
bool ValidTransitionCell(CPos cell, MobileInfo mi)
{
var terrainType = map.GetTerrainInfo(cell).Type;
if (!mi.SubterraneanTransitionTerrainTypes.Contains(terrainType) && mi.SubterraneanTransitionTerrainTypes.Any())
return false;
if (mi.SubterraneanTransitionOnRamps)
return true;
var tile = map.Tiles[cell];
var ti = map.Rules.TileSet.GetTileInfo(tile);
return ti == null || ti.RampType == 0;
}
int ICustomMovementLayer.EntryMovementCost(ActorInfo a, MobileInfo mi, CPos cell)
{
return ValidTransitionCell(cell, mi) ? mi.SubterraneanTransitionCost : int.MaxValue;
}
int ICustomMovementLayer.ExitMovementCost(ActorInfo a, MobileInfo mi, CPos cell)
{
return ValidTransitionCell(cell, mi) ? mi.SubterraneanTransitionCost : int.MaxValue;
}
byte ICustomMovementLayer.GetTerrainIndex(CPos cell)
{
return terrainIndex;
}
}
}

View File

@@ -0,0 +1,62 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
public class TerrainTunnelInfo : TraitInfo<TerrainTunnel>, Requires<TerrainTunnelLayerInfo>, ILobbyCustomRulesIgnore
{
[FieldLoader.Require]
[Desc("Location of the tunnel")]
public readonly CPos Location = CPos.Zero;
[FieldLoader.Require]
[Desc("Height of the tunnel floor in map height steps.")]
public readonly byte Height = 0;
[FieldLoader.Require]
[Desc("Size of the tunnel footprint")]
public readonly CVec Dimensions = CVec.Zero;
[FieldLoader.Require]
[Desc("Tunnel footprint.", "_ is passable, x is blocked, and o are tunnel portals.")]
public readonly string Footprint = string.Empty;
[FieldLoader.Require]
[Desc("Terrain type of the tunnel floor.")]
public readonly string TerrainType = null;
public IEnumerable<CPos> TunnelCells()
{
return CellsMatching('_').Concat(CellsMatching('o'));
}
public IEnumerable<CPos> PortalCells()
{
return CellsMatching('o');
}
IEnumerable<CPos> CellsMatching(char c)
{
var index = 0;
var footprint = Footprint.Where(x => !char.IsWhiteSpace(x)).ToArray();
for (var y = 0; y < Dimensions.Y; y++)
for (var x = 0; x < Dimensions.X; x++)
if (footprint[index++] == c)
yield return Location + new CVec(x, y);
}
}
public class TerrainTunnel { }
}

View File

@@ -0,0 +1,94 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Collections.Generic;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
public class TerrainTunnelLayerInfo : ITraitInfo, Requires<DomainIndexInfo>, ILobbyCustomRulesIgnore
{
[Desc("Terrain type used by cells outside any tunnel footprint.")]
public readonly string ImpassableTerrainType = "Impassable";
public object Create(ActorInitializer init) { return new TerrainTunnelLayer(init.Self, this); }
}
public class TerrainTunnelLayer : ICustomMovementLayer, IWorldLoaded
{
readonly Map map;
readonly CellLayer<WPos> cellCenters;
readonly CellLayer<byte> terrainIndices;
readonly HashSet<CPos> portals = new HashSet<CPos>();
bool enabled;
public TerrainTunnelLayer(Actor self, TerrainTunnelLayerInfo info)
{
map = self.World.Map;
cellCenters = new CellLayer<WPos>(map);
terrainIndices = new CellLayer<byte>(map);
terrainIndices.Clear(map.Rules.TileSet.GetTerrainIndex(info.ImpassableTerrainType));
}
public void WorldLoaded(World world, WorldRenderer wr)
{
var domainIndex = world.WorldActor.Trait<DomainIndex>();
foreach (var tti in world.WorldActor.Info.TraitInfos<TerrainTunnelInfo>())
{
enabled = true;
var terrain = map.Rules.TileSet.GetTerrainIndex(tti.TerrainType);
foreach (var c in tti.TunnelCells())
{
var uv = c.ToMPos(map);
terrainIndices[uv] = terrain;
var pos = map.CenterOfCell(c);
cellCenters[uv] = pos - new WVec(0, 0, pos.Z - 512 * tti.Height);
}
var portal = tti.PortalCells();
domainIndex.AddFixedConnection(portal);
foreach (var c in portal)
{
// Need to explicitly set both default and tunnel layers, otherwise the .Contains check will fail
portals.Add(new CPos(c.X, c.Y, 0));
portals.Add(new CPos(c.X, c.Y, CustomMovementLayerType.Tunnel));
}
}
}
bool ICustomMovementLayer.EnabledForActor(ActorInfo a, MobileInfo mi) { return enabled; }
byte ICustomMovementLayer.Index { get { return CustomMovementLayerType.Tunnel; } }
bool ICustomMovementLayer.InteractsWithDefaultLayer { get { return false; } }
WPos ICustomMovementLayer.CenterOfCell(CPos cell)
{
return cellCenters[cell];
}
int ICustomMovementLayer.EntryMovementCost(ActorInfo a, MobileInfo mi, CPos cell)
{
return portals.Contains(cell) ? 0 : int.MaxValue;
}
int ICustomMovementLayer.ExitMovementCost(ActorInfo a, MobileInfo mi, CPos cell)
{
return portals.Contains(cell) ? 0 : int.MaxValue;
}
byte ICustomMovementLayer.GetTerrainIndex(CPos cell)
{
return terrainIndices[cell];
}
}
}

View File

@@ -233,4 +233,18 @@ namespace OpenRA.Mods.Common.Traits
[RequireExplicitImplementation]
public interface IGainsExperienceModifier { int GetGainsExperienceModifier(); }
[RequireExplicitImplementation]
public interface ICustomMovementLayer
{
byte Index { get; }
bool InteractsWithDefaultLayer { get; }
bool EnabledForActor(ActorInfo a, MobileInfo mi);
int EntryMovementCost(ActorInfo a, MobileInfo mi, CPos cell);
int ExitMovementCost(ActorInfo a, MobileInfo mi, CPos cell);
byte GetTerrainIndex(CPos cell);
WPos CenterOfCell(CPos cell);
}
}

View File

@@ -12,6 +12,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Mods.Common.Traits;
using OpenRA.Support;
using OpenRA.Traits;
@@ -85,7 +86,13 @@ namespace OpenRA.Mods.Common
public static WPos BetweenCells(World w, CPos from, CPos to)
{
return WPos.Lerp(w.Map.CenterOfCell(from), w.Map.CenterOfCell(to), 1, 2);
var fromPos = from.Layer == 0 ? w.Map.CenterOfCell(from) :
w.GetCustomMovementLayers()[from.Layer].CenterOfCell(from);
var toPos = to.Layer == 0 ? w.Map.CenterOfCell(to) :
w.GetCustomMovementLayers()[to.Layer].CenterOfCell(to);
return WPos.Lerp(fromPos, toPos, 1, 2);
}
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> ts, MersenneTwister random)

View File

@@ -86,7 +86,15 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var coordinateLabel = widget.GetOrNull<LabelWidget>("COORDINATE_LABEL");
if (coordinateLabel != null)
coordinateLabel.GetText = () => worldRenderer.Viewport.ViewToWorld(Viewport.LastMousePos).ToString();
{
coordinateLabel.GetText = () =>
{
var cell = worldRenderer.Viewport.ViewToWorld(Viewport.LastMousePos);
var map = worldRenderer.World.Map;
var height = map.Height.Contains(cell) ? map.Height[cell] : 0;
return "{0},{1}".F(cell, height);
};
}
var cashLabel = widget.GetOrNull<LabelWidget>("CASH_LABEL");
if (cashLabel != null)

View File

@@ -100,8 +100,8 @@ namespace OpenRA.Mods.RA.Traits
static IEnumerable<CPos> GetMinefieldCells(CPos start, CPos end, WDist depth)
{
var mins = CPos.Min(start, end);
var maxs = CPos.Max(start, end);
var mins = new CPos(Math.Min(start.X, end.X), Math.Min(start.Y, end.Y));
var maxs = new CPos(Math.Max(start.X, end.X), Math.Max(start.Y, end.Y));
/* TODO: proper endcaps, if anyone cares (which won't happen unless depth is large) */

View File

@@ -31,10 +31,8 @@ namespace OpenRA.Mods.TS.UtilityCommands
{
{ 0x01, "gasand" },
{ 0x03, "gawall" },
/*
{ 0x18, "bridge1" },
{ 0x19, "bridge2" },
*/
{ 0x1A, "nawall" },
{ 0x27, "tracks01" },
{ 0x28, "tracks02" },
@@ -56,10 +54,8 @@ namespace OpenRA.Mods.TS.UtilityCommands
{ 0x38, "tracktunnel02" },
{ 0x39, "tracktunnel03" },
{ 0x3A, "tracktunnel04" },
/*
{ 0x3B, "railbrdg1" },
{ 0x3C, "railbrdg2" },
*/
{ 0x3D, "crat01" },
{ 0x3E, "crat02" },
{ 0x3F, "crat03" },

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 47 KiB

View File

@@ -119,26 +119,6 @@ Actors:
Owner: Neutral
Health: 100
Facing: 96
Actor12: cabhut
Location: 174,2
Owner: Neutral
Health: 100
Facing: 96
Actor13: cabhut
Location: 183,6
Owner: Neutral
Health: 100
Facing: 96
Actor14: cabhut
Location: 239,2
Owner: Neutral
Health: 100
Facing: 96
Actor15: cabhut
Location: 229,-2
Owner: Neutral
Health: 100
Facing: 96
Actor16: tibtre01
Location: 133,-102
Owner: Neutral
@@ -1582,8 +1562,59 @@ Actors:
Actor623: tuntop02
Owner: Neutral
Location: 115,-15
Actor494: bridge1
Owner: Neutral
Location: 232,0
Actor495: bridge1
Owner: Neutral
Location: 233,0
Actor496: bridge1
Owner: Neutral
Location: 234,0
Actor497: bridge1
Owner: Neutral
Location: 235,0
Actor500: bridge1
Owner: Neutral
Location: 236,0
Actor501: bridge1
Owner: Neutral
Location: 177,4
Actor502: bridge1
Owner: Neutral
Location: 178,4
Actor503: bridge1
Owner: Neutral
Location: 179,4
Actor505: bridge1
Owner: Neutral
Location: 180,4
Rules:
World:
GlobalLightingPaletteEffect:
Ambient: 0.72
ElevatedBridgeLayer:
TerrainTunnelLayer:
ElevatedBridgePlaceholder@a:
Location: 176, 3
Height: 6
Orientation: X
Length: 5
ElevatedBridgePlaceholder@b:
Location: 231, -1
Height: 6
Orientation: X
Length: 6
TerrainTunnel@a:
Location: 117, -15
Dimensions: 3, 25
Footprint: ooo ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ooo
Height: 2
TerrainType: Clear
TerrainTunnel@b:
Location: 146, -19
Dimensions: 3, 25
Footprint: ooo ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ooo
Height: 2
TerrainType: Clear

View File

@@ -14,7 +14,7 @@ Bounds: 2,4,141,226
Visibility: Lobby
Categories: Conquest
Categories: Conquest, Playable, Original FS Map
Players:
PlayerReference@Neutral:
@@ -69,56 +69,6 @@ Players:
Enemies: Creeps
Actors:
Actor0: cabhut
Location: 126,-52
Owner: Neutral
Health: 100
Facing: 96
Actor1: cabhut
Location: 109,-56
Owner: Neutral
Health: 100
Facing: 96
Actor2: cabhut
Location: 155,-83
Owner: Neutral
Health: 100
Facing: 96
Actor3: cabhut
Location: 159,-106
Owner: Neutral
Health: 100
Facing: 96
Actor4: cabhut
Location: 101,75
Owner: Neutral
Health: 100
Facing: 96
Actor5: cabhut
Location: 115,79
Owner: Neutral
Health: 100
Facing: 96
Actor6: cabhut
Location: 154,32
Owner: Neutral
Health: 100
Facing: 96
Actor7: cabhut
Location: 158,18
Owner: Neutral
Health: 100
Facing: 96
Actor8: cabhut
Location: 221,-41
Owner: Neutral
Health: 100
Facing: 96
Actor9: cabhut
Location: 225,-55
Owner: Neutral
Health: 100
Facing: 96
Actor10: city15
Location: 58,43
Owner: Neutral
@@ -214,16 +164,6 @@ Actors:
Owner: Neutral
Health: 50
Facing: 96
Actor29: cabhut
Location: 53,29
Owner: Neutral
Health: 100
Facing: 96
Actor30: cabhut
Location: 49,5
Owner: Neutral
Health: 100
Facing: 96
Actor31: car
Location: 94,42
Owner: Neutral
@@ -837,8 +777,268 @@ Actors:
Actor233: waypoint
Location: 105,21
Owner: Neutral
Actor234: bridge2
Owner: Neutral
Location: 51,8
Actor235: bridge2
Owner: Neutral
Location: 51,9
Actor236: bridge2
Owner: Neutral
Location: 51,10
Actor237: bridge2
Owner: Neutral
Location: 51,11
Actor238: bridge2
Owner: Neutral
Location: 51,12
Actor240: bridge2
Owner: Neutral
Location: 51,13
Actor239: bridge2
Owner: Neutral
Location: 51,26
Actor241: bridge2
Owner: Neutral
Location: 51,25
Actor242: bridge2
Owner: Neutral
Location: 51,24
Actor243: bridge2
Owner: Neutral
Location: 51,23
Actor244: bridge2
Owner: Neutral
Location: 51,22
Actor245: bridge2
Owner: Neutral
Location: 51,21
Actor246: bridge2
Owner: Neutral
Location: 51,20
Actor247: bridge2
Owner: Neutral
Location: 51,19
Actor248: bridge2
Owner: Neutral
Location: 51,18
Actor249: bridge2
Owner: Neutral
Location: 51,17
Actor250: bridge2
Owner: Neutral
Location: 51,16
Actor252: bridge2
Owner: Neutral
Location: 51,15
Actor253: bridge2
Owner: Neutral
Location: 51,14
Actor251: bridge1
Owner: Neutral
Location: 104,77
Actor254: bridge1
Owner: Neutral
Location: 105,77
Actor255: bridge1
Owner: Neutral
Location: 106,77
Actor256: bridge1
Owner: Neutral
Location: 107,77
Actor257: bridge1
Owner: Neutral
Location: 108,77
Actor258: bridge1
Owner: Neutral
Location: 109,77
Actor259: bridge1
Owner: Neutral
Location: 110,77
Actor261: bridge1
Owner: Neutral
Location: 111,77
Actor262: bridge1
Owner: Neutral
Location: 112,77
Actor263: bridge2
Owner: Neutral
Location: 156,21
Actor264: bridge2
Owner: Neutral
Location: 156,22
Actor265: bridge2
Owner: Neutral
Location: 156,23
Actor266: bridge2
Owner: Neutral
Location: 156,24
Actor267: bridge2
Owner: Neutral
Location: 156,25
Actor268: bridge2
Owner: Neutral
Location: 156,26
Actor269: bridge2
Owner: Neutral
Location: 156,27
Actor270: bridge2
Owner: Neutral
Location: 156,28
Actor271: bridge2
Owner: Neutral
Location: 156,29
Actor272: bridge1
Owner: Neutral
Location: 112,-54
Actor273: bridge1
Owner: Neutral
Location: 113,-54
Actor274: bridge1
Owner: Neutral
Location: 114,-54
Actor275: bridge1
Owner: Neutral
Location: 115,-54
Actor276: bridge1
Owner: Neutral
Location: 116,-54
Actor277: bridge1
Owner: Neutral
Location: 117,-54
Actor279: bridge1
Owner: Neutral
Location: 123,-54
Actor280: bridge1
Owner: Neutral
Location: 122,-54
Actor281: bridge1
Owner: Neutral
Location: 121,-54
Actor283: bridge1
Owner: Neutral
Location: 120,-54
Actor282: bridge1
Owner: Neutral
Location: 119,-54
Actor284: bridge1
Owner: Neutral
Location: 118,-54
Actor278: bridge2
Owner: Neutral
Location: 157,-103
Actor285: bridge2
Owner: Neutral
Location: 157,-102
Actor287: bridge2
Owner: Neutral
Location: 157,-101
Actor286: bridge2
Owner: Neutral
Location: 157,-100
Actor289: bridge2
Owner: Neutral
Location: 157,-99
Actor288: bridge2
Owner: Neutral
Location: 157,-98
Actor290: bridge2
Owner: Neutral
Location: 157,-97
Actor291: bridge2
Owner: Neutral
Location: 157,-96
Actor292: bridge2
Owner: Neutral
Location: 157,-95
Actor293: bridge2
Owner: Neutral
Location: 157,-94
Actor294: bridge2
Owner: Neutral
Location: 157,-93
Actor295: bridge2
Owner: Neutral
Location: 157,-92
Actor296: bridge2
Owner: Neutral
Location: 157,-91
Actor297: bridge2
Owner: Neutral
Location: 157,-90
Actor298: bridge2
Owner: Neutral
Location: 157,-89
Actor299: bridge2
Owner: Neutral
Location: 157,-88
Actor300: bridge2
Owner: Neutral
Location: 157,-87
Actor301: bridge2
Owner: Neutral
Location: 157,-86
Actor302: bridge2
Owner: Neutral
Location: 223,-52
Actor303: bridge2
Owner: Neutral
Location: 223,-51
Actor304: bridge2
Owner: Neutral
Location: 223,-50
Actor305: bridge2
Owner: Neutral
Location: 223,-49
Actor306: bridge2
Owner: Neutral
Location: 223,-48
Actor307: bridge2
Owner: Neutral
Location: 223,-47
Actor309: bridge2
Owner: Neutral
Location: 223,-46
Actor308: bridge2
Owner: Neutral
Location: 223,-45
Actor310: bridge2
Owner: Neutral
Location: 223,-44
Rules:
World:
GlobalLightingPaletteEffect:
Ambient: 0.93
TerrainTunnelLayer:
ElevatedBridgeLayer:
ElevatedBridgePlaceholder@a:
Location: 50, 7
Height: 6
Orientation: Y
Length: 20
ElevatedBridgePlaceholder@b:
Location: 103, 76
Height: 6
Orientation: X
Length: 10
ElevatedBridgePlaceholder@c:
Location: 111, -55
Height: 6
Orientation: X
Length: 13
ElevatedBridgePlaceholder@d:
Location: 155, 20
Height: 6
Orientation: Y
Length: 10
ElevatedBridgePlaceholder@e:
Location: 156, -104
Height: 6
Orientation: Y
Length: 19
ElevatedBridgePlaceholder@f:
Location: 222, -53
Height: 6
Orientation: Y
Length: 10

View File

@@ -14,7 +14,7 @@ Bounds: 2,8,77,126
Visibility: Lobby
Categories: Conquest, Original TS Map
Categories: Conquest, Original FS Map, Playable
Players:
PlayerReference@Neutral:
@@ -816,44 +816,93 @@ Actors:
Actor222: tuntop03
Owner: Neutral
Location: 109,4
Actor223: tuntop02
Owner: Neutral
Location: 106,-16
Actor224: tuntop04
Owner: Neutral
Location: 87,28
Actor225: tuntop03
Owner: Neutral
Location: 73,34
Actor226: tuntop02
Owner: Neutral
Location: 86,13
Actor227: tuntop01
Owner: Neutral
Location: 60,34
Actor228: tuntop04
Owner: Neutral
Location: 42,13
Actor229: tuntop02
Owner: Neutral
Location: 40,-2
Actor230: tuntop01
Owner: Neutral
Location: 51,-6
Actor231: tuntop02
Owner: Neutral
Location: 72,-28
Actor232: tuntop01
Owner: Neutral
Location: 77,-38
Actor233: tuntop03
Owner: Neutral
Location: 93,-37
Actor234: tuntop01
Actor237: tuntop02
Owner: Neutral
Location: 95,3
Location: 107,-16
Actor238: tuntop01
Owner: Neutral
Location: 77,-37
Actor239: tuntop01
Owner: Neutral
Location: 95,4
Actor240: tuntop02
Owner: Neutral
Location: 73,-28
Actor234: tuntop02
Owner: Neutral
Location: 42,-2
Actor236: tuntop01
Owner: Neutral
Location: 51,-5
Actor235: tuntop02
Owner: Neutral
Location: 88,13
Rules:
World:
GlobalLightingPaletteEffect:
Ambient: 0.62
TerrainTunnelLayer:
TerrainTunnel@a:
Location: 60, 35
Dimensions: 14, 3
Footprint: o____________o o____________o o____________o
Height: 2
TerrainType: Clear
TerrainTunnel@b:
Location: 43, -2
Dimensions: 3, 16
Footprint: ooo ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ooo
Height: 2
TerrainType: Clear
TerrainTunnel@c:
Location: 88, 13
Dimensions: 4, 16
Footprint: xooo x___ x___ x___ x___ x___ x___ x___ x___ x___ x___ x___ x___ x___ x___ ooox
Height: 2
TerrainType: Clear
TerrainTunnel@d:
Location: 51, -4
Dimensions: 18, 3
Footprint: o________________o o________________o o________________o
Height: 2
TerrainType: Clear
TerrainTunnel@e:
Location: 95, 5
Dimensions: 15, 3
Footprint: o_____________o o_____________o o_____________o
Height: 2
TerrainType: Clear
TerrainTunnel@f:
Location: 74, -28
Dimensions: 3, 17
Footprint: ooo ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ooo
Height: 2
TerrainType: Clear
TerrainTunnel@g:
Location: 108, -16
Dimensions: 3, 10
Footprint: ooo ___ ___ ___ ___ ___ ___ ___ ___ ooo
Height: 2
TerrainType: Clear
TerrainTunnel@h:
Location: 77, -36
Dimensions: 17, 3
Footprint: o_______________o o_______________o o_______________o
Height: 2
TerrainType: Clear

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 51 KiB

View File

@@ -14,7 +14,7 @@ Bounds: 2,4,161,224
Visibility: Lobby
Categories: Conquest, Original TS Map
Categories: Conquest, Original TS Map, Playable
Players:
PlayerReference@Neutral:
@@ -184,76 +184,11 @@ Actors:
Owner: Neutral
Health: 100
Facing: 96
Actor23: cabhut
Location: 112,15
Owner: Neutral
Health: 100
Facing: 96
Actor24: cabhut
Location: 116,-9
Owner: Neutral
Health: 100
Facing: 96
Actor25: cabhut
Location: 96,34
Owner: Neutral
Health: 100
Facing: 96
Actor26: cabhut
Location: 121,44
Owner: Neutral
Health: 100
Facing: 96
Actor27: cabhut
Location: 126,30
Owner: Neutral
Health: 100
Facing: 96
Actor28: cabhut
Location: 85,11
Owner: Neutral
Health: 100
Facing: 96
Actor29: cabhut
Location: 130,-13
Owner: Neutral
Health: 100
Facing: 96
Actor30: cabhut
Location: 146,-9
Owner: Neutral
Health: 100
Facing: 96
Actor31: cabhut
Location: 151,-25
Owner: Neutral
Health: 100
Facing: 96
Actor32: cabhut
Location: 155,-42
Owner: Neutral
Health: 100
Facing: 96
Actor33: cabhut
Location: 145,-66
Owner: Neutral
Health: 100
Facing: 96
Actor34: cabhut
Location: 159,-69
Owner: Neutral
Health: 100
Facing: 96
Actor35: cabhut
Location: 173,-66
Owner: Neutral
Health: 100
Facing: 96
Actor36: cabhut
Location: 181,-34
Owner: Neutral
Health: 100
Facing: 96
Actor37: cabhut
Location: 163,-137
Owner: Neutral
@@ -1209,41 +1144,633 @@ Actors:
Actor341: lobrdg_r_ne
Location: 160,-137
Owner: Neutral
Actor345: lobrdg_a
Actor342: lobrdg_a
Location: 160,-136
Owner: Neutral
Actor346: lobrdg_a
Actor343: lobrdg_a
Location: 160,-135
Owner: Neutral
Actor351: lobrdg_a
Actor344: lobrdg_a
Location: 160,-134
Owner: Neutral
Actor352: lobrdg_a
Actor345: lobrdg_a
Location: 160,-133
Owner: Neutral
Actor357: lobrdg_a
Actor346: lobrdg_a
Location: 160,-132
Owner: Neutral
Actor358: lobrdg_a
Actor347: lobrdg_a
Location: 160,-131
Owner: Neutral
Actor363: lobrdg_a
Actor348: lobrdg_a
Location: 160,-130
Owner: Neutral
Actor364: lobrdg_a
Actor349: lobrdg_a
Location: 160,-129
Owner: Neutral
Actor369: lobrdg_r_sw
Actor350: lobrdg_r_sw
Location: 160,-128
Owner: Neutral
Actor351: bridge1
Location: 129,-64
Owner: Neutral
Actor352: bridge1
Location: 130,-64
Owner: Neutral
Actor353: bridge1
Location: 131,-64
Owner: Neutral
Actor354: bridge1
Location: 132,-64
Owner: Neutral
Actor355: bridge2
Location: 161,-90
Owner: Neutral
Actor356: bridge2
Location: 161,-89
Owner: Neutral
Actor357: bridge2
Location: 161,-88
Owner: Neutral
Actor358: bridge2
Location: 161,-87
Owner: Neutral
Actor359: bridge2
Location: 161,-86
Owner: Neutral
Actor360: bridge1
Location: 63,13
Owner: Neutral
Actor361: bridge1
Location: 140,-64
Owner: Neutral
Actor362: bridge1
Location: 141,-64
Owner: Neutral
Actor363: bridge1
Location: 142,-64
Owner: Neutral
Actor364: bridge2
Location: 161,-75
Owner: Neutral
Actor365: bridge2
Location: 161,-74
Owner: Neutral
Actor366: bridge2
Location: 161,-73
Owner: Neutral
Actor367: bridge2
Location: 161,-72
Owner: Neutral
Actor368: bridge1
Location: 80,13
Owner: Neutral
Actor369: bridge1
Location: 81,13
Owner: Neutral
Actor370: bridge1
Location: 82,13
Owner: Neutral
Actor371: crat03
Location: 158,-61
Owner: Neutral
Actor372: crat01
Actor372: bridge2
Location: 114,-6
Owner: Neutral
Actor373: bridge1
Location: 176,-68
Owner: Neutral
Actor374: bridge2
Location: 114,-5
Owner: Neutral
Actor375: bridge1
Location: 177,-68
Owner: Neutral
Actor376: bridge2
Location: 114,-4
Owner: Neutral
Actor377: bridge1
Location: 178,-68
Owner: Neutral
Actor378: bridge2
Location: 114,-3
Owner: Neutral
Actor379: bridge2
Location: 114,-2
Owner: Neutral
Actor380: bridge2
Location: 114,-1
Owner: Neutral
Actor381: bridge2
Location: 114,0
Owner: Neutral
Actor382: bridge2
Location: 153,-39
Owner: Neutral
Actor383: bridge2
Location: 114,1
Owner: Neutral
Actor384: bridge2
Location: 153,-38
Owner: Neutral
Actor385: bridge1
Location: 183,-68
Owner: Neutral
Actor386: bridge2
Location: 114,2
Owner: Neutral
Actor387: bridge2
Location: 153,-37
Owner: Neutral
Actor388: bridge1
Location: 184,-68
Owner: Neutral
Actor389: bridge2
Location: 153,-36
Owner: Neutral
Actor390: bridge2
Location: 114,4
Owner: Neutral
Actor391: bridge2
Location: 153,-35
Owner: Neutral
Actor392: bridge2
Location: 114,5
Owner: Neutral
Actor393: bridge2
Location: 153,-34
Owner: Neutral
Actor394: bridge2
Location: 114,6
Owner: Neutral
Actor395: bridge2
Location: 153,-33
Owner: Neutral
Actor396: bridge2
Location: 114,7
Owner: Neutral
Actor397: bridge2
Location: 153,-32
Owner: Neutral
Actor398: bridge2
Location: 114,8
Owner: Neutral
Actor399: bridge1
Location: 133,-11
Owner: Neutral
Actor400: bridge2
Location: 153,-31
Owner: Neutral
Actor401: bridge2
Location: 114,9
Owner: Neutral
Actor402: bridge1
Location: 134,-11
Owner: Neutral
Actor403: bridge2
Location: 153,-30
Owner: Neutral
Actor404: bridge2
Location: 114,10
Owner: Neutral
Actor405: bridge1
Location: 135,-11
Owner: Neutral
Actor406: bridge2
Location: 153,-29
Owner: Neutral
Actor407: bridge2
Location: 114,11
Owner: Neutral
Actor408: bridge1
Location: 136,-11
Owner: Neutral
Actor409: bridge2
Location: 153,-28
Owner: Neutral
Actor410: bridge2
Location: 114,12
Owner: Neutral
Actor411: bridge1
Location: 137,-11
Owner: Neutral
Actor412: bridge1
Location: 138,-11
Owner: Neutral
Actor413: bridge1
Location: 139,-11
Owner: Neutral
Actor414: bridge1
Location: 140,-11
Owner: Neutral
Actor415: bridge1
Location: 141,-11
Owner: Neutral
Actor416: bridge2
Location: 94,37
Owner: Neutral
Actor417: bridge1
Location: 142,-11
Owner: Neutral
Actor418: bridge2
Location: 94,38
Owner: Neutral
Actor419: bridge1
Location: 143,-11
Owner: Neutral
Actor420: bridge2
Location: 94,39
Owner: Neutral
Actor421: bridge2
Location: 94,40
Owner: Neutral
Actor422: bridge2
Location: 94,41
Owner: Neutral
Actor423: bridge2
Location: 94,42
Owner: Neutral
Actor424: crat01
Location: 123,20
Owner: Neutral
Actor425: bridge2
Location: 94,54
Owner: Neutral
Actor426: bridge2
Location: 94,55
Owner: Neutral
Actor427: bridge2
Location: 94,56
Owner: Neutral
Actor428: bridge2
Location: 94,57
Owner: Neutral
Actor429: bridge1
Location: 184,-32
Owner: Neutral
Actor430: bridge1
Location: 185,-32
Owner: Neutral
Actor431: bridge1
Location: 186,-32
Owner: Neutral
Actor432: bridge1
Location: 187,-32
Owner: Neutral
Actor433: bridge1
Location: 129,28
Owner: Neutral
Actor434: bridge1
Location: 130,28
Owner: Neutral
Actor435: bridge1
Location: 131,28
Owner: Neutral
Actor436: bridge1
Location: 132,28
Owner: Neutral
Actor437: bridge1
Location: 133,28
Owner: Neutral
Actor438: bridge1
Location: 193,-32
Owner: Neutral
Actor439: bridge1
Location: 134,28
Owner: Neutral
Actor440: bridge1
Location: 194,-32
Owner: Neutral
Actor441: bridge1
Location: 135,28
Owner: Neutral
Actor442: bridge1
Location: 195,-32
Owner: Neutral
Actor443: bridge1
Location: 196,-32
Owner: Neutral
Actor444: bridge1
Location: 197,-32
Owner: Neutral
Actor445: bridge2
Location: 123,47
Owner: Neutral
Actor446: bridge2
Location: 123,48
Owner: Neutral
Actor447: bridge2
Location: 123,49
Owner: Neutral
Actor448: bridge2
Location: 123,50
Owner: Neutral
Actor449: bridge1
Location: 145,28
Owner: Neutral
Actor450: bridge2
Location: 123,51
Owner: Neutral
Actor451: bridge1
Location: 146,28
Owner: Neutral
Actor452: bridge1
Location: 147,28
Owner: Neutral
Actor453: bridge1
Location: 148,28
Owner: Neutral
Actor454: bridge2
Location: 123,61
Owner: Neutral
Actor455: bridge2
Location: 123,62
Owner: Neutral
Actor456: bridge2
Location: 123,63
Owner: Neutral
Actor457: bridge2
Location: 123,64
Owner: Neutral
Actor458: bridge2
Location: 123,65
Owner: Neutral
Actor459: bridge2
Location: 123,66
Owner: Neutral
Actor460: bridge2
Location: 123,67
Owner: Neutral
Actor461: bridge1
Owner: Neutral
Location: 74,13
Actor462: bridge1
Owner: Neutral
Location: 75,13
Actor463: bridge1
Owner: Neutral
Location: 76,13
Actor464: bridge1
Owner: Neutral
Location: 77,13
Actor465: bridge1
Owner: Neutral
Location: 78,13
Actor466: bridge1
Owner: Neutral
Location: 79,13
Actor467: bridge1
Owner: Neutral
Location: 64,13
Actor468: bridge1
Owner: Neutral
Location: 65,13
Actor469: bridge1
Owner: Neutral
Location: 66,13
Actor470: bridge1
Owner: Neutral
Location: 67,13
Actor471: bridge1
Owner: Neutral
Location: 68,13
Actor472: bridge1
Owner: Neutral
Location: 69,13
Actor473: bridge1
Owner: Neutral
Location: 70,13
Actor474: bridge1
Owner: Neutral
Location: 71,13
Actor475: bridge1
Owner: Neutral
Location: 72,13
Actor476: bridge2
Owner: Neutral
Location: 94,43
Actor477: bridge2
Owner: Neutral
Location: 94,44
Actor478: bridge2
Owner: Neutral
Location: 94,45
Actor479: bridge2
Owner: Neutral
Location: 94,46
Actor480: bridge2
Owner: Neutral
Location: 94,48
Actor481: bridge2
Owner: Neutral
Location: 94,49
Actor482: bridge2
Owner: Neutral
Location: 94,50
Actor483: bridge2
Owner: Neutral
Location: 94,51
Actor484: bridge2
Owner: Neutral
Location: 94,52
Actor485: bridge2
Owner: Neutral
Location: 94,53
Actor486: bridge2
Owner: Neutral
Location: 123,52
Actor487: bridge2
Owner: Neutral
Location: 123,53
Actor488: bridge2
Owner: Neutral
Location: 123,54
Actor489: bridge2
Owner: Neutral
Location: 123,55
Actor490: bridge2
Owner: Neutral
Location: 123,56
Actor491: bridge2
Owner: Neutral
Location: 123,57
Actor492: bridge2
Owner: Neutral
Location: 123,58
Actor493: bridge2
Owner: Neutral
Location: 123,59
Actor494: bridge2
Owner: Neutral
Location: 123,60
Actor495: bridge1
Owner: Neutral
Location: 136,28
Actor496: bridge1
Owner: Neutral
Location: 137,28
Actor497: bridge1
Owner: Neutral
Location: 139,28
Actor498: bridge1
Owner: Neutral
Location: 140,28
Actor499: bridge1
Owner: Neutral
Location: 141,28
Actor500: bridge1
Owner: Neutral
Location: 142,28
Actor501: bridge1
Owner: Neutral
Location: 143,28
Actor502: bridge1
Owner: Neutral
Location: 144,28
Actor503: bridge1
Owner: Neutral
Location: 188,-32
Actor504: bridge1
Owner: Neutral
Location: 189,-32
Actor505: bridge1
Owner: Neutral
Location: 190,-32
Actor506: bridge1
Owner: Neutral
Location: 191,-32
Actor507: bridge1
Owner: Neutral
Location: 192,-32
Actor508: bridge2
Owner: Neutral
Location: 161,-85
Actor509: bridge2
Owner: Neutral
Location: 161,-84
Actor510: bridge2
Owner: Neutral
Location: 161,-83
Actor511: bridge2
Owner: Neutral
Location: 161,-82
Actor512: bridge2
Owner: Neutral
Location: 161,-81
Actor513: bridge2
Owner: Neutral
Location: 161,-80
Actor514: bridge2
Owner: Neutral
Location: 161,-79
Actor515: bridge2
Owner: Neutral
Location: 161,-78
Actor516: bridge2
Owner: Neutral
Location: 161,-77
Actor517: bridge2
Owner: Neutral
Location: 161,-76
Actor518: bridge1
Owner: Neutral
Location: 133,-64
Actor519: bridge1
Owner: Neutral
Location: 134,-64
Actor520: bridge1
Owner: Neutral
Location: 135,-64
Actor521: bridge1
Owner: Neutral
Location: 136,-64
Actor522: bridge1
Owner: Neutral
Location: 137,-64
Actor523: bridge1
Owner: Neutral
Location: 137,-64
Actor524: bridge1
Owner: Neutral
Location: 138,-64
Actor525: bridge1
Owner: Neutral
Location: 139,-64
Actor526: bridge1
Owner: Neutral
Location: 179,-68
Actor527: bridge1
Owner: Neutral
Location: 180,-68
Actor528: bridge1
Owner: Neutral
Location: 181,-68
Actor529: bridge1
Owner: Neutral
Location: 182,-68
Rules:
World:
GlobalLightingPaletteEffect:
Ambient: 0.75
ElevatedBridgeLayer:
ElevatedBridgePlaceholder@a:
Location: 62, 12
Height: 6
Orientation: X
Length: 23
ElevatedBridgePlaceholder@b:
Location: 93, 36
Height: 6
Orientation: Y
Length: 24
ElevatedBridgePlaceholder@c:
Location: 122, 46
Height: 6
Orientation: Y
Length: 23
ElevatedBridgePlaceholder@d:
Location: 128, 27
Height: 6
Orientation: X
Length: 21
ElevatedBridgePlaceholder@e:
Location: 113, -7
Height: 6
Orientation: Y
Length: 20
ElevatedBridgePlaceholder@f:
Location: 132, -12
Height: 6
Orientation: X
Length: 12
ElevatedBridgePlaceholder@g:
Location: 152, -40
Height: 6
Orientation: Y
Length: 13
ElevatedBridgePlaceholder@h:
Location: 142, -40
Height: 6
Orientation: Y
Length: 13
ElevatedBridgePlaceholder@i:
Location: 128, -63
Height: 6
Orientation: X
Length: 15
ElevatedBridgePlaceholder@j:
Location: 160, -91
Height: 6
Orientation: Y
Length: 21
ElevatedBridgePlaceholder@k:
Location: 175, -69
Height: 6
Orientation: X
Length: 10
ElevatedBridgePlaceholder@l:
Location: 183, -33
Height: 6
Orientation: X
Length: 15

View File

@@ -152,3 +152,34 @@ LOBRDG_R_SW:
NeighbourOffsets: 1,-1
EditorOnlyTooltip:
Name: Bridge Ramp
^ElevatedBridgePlaceholder:
AlwaysVisible:
RenderSprites:
Palette: terraindecoration
WithSpriteBody:
AutoSelectionSize:
AppearsOnRadar:
RadarColorFromTerrain:
Terrain: Bridge
BodyOrientation:
UseClassicPerspectiveFudge: false
QuantizedFacings: 1
Tooltip:
Name: Bridge
Immobile:
OccupiesSpace: false
CustomSelectionSize:
CustomBounds: 96, 144
BRIDGE1:
Inherits: ^ElevatedBridgePlaceholder
BRIDGE2:
Inherits: ^ElevatedBridgePlaceholder
RAILBRDG1:
Inherits: ^ElevatedBridgePlaceholder
RAILBRDG2:
Inherits: ^ElevatedBridgePlaceholder

View File

@@ -17,6 +17,7 @@
RevealsShroud:
Range: 6c0
MaxHeightDelta: 3
RequiresCondition: !inside-tunnel
Turreted:
TurnSpeed: 3
Armament@PRIMARY:
@@ -50,6 +51,7 @@
Mobile:
Speed: 56
RevealsShroud:
RequiresCondition: !inside-tunnel
Range: 5c0
MaxHeightDelta: 3
@@ -73,6 +75,7 @@ ICBM:
Speed: 85
TurnSpeed: 5
RevealsShroud:
RequiresCondition: !inside-tunnel
Range: 7c0
MaxHeightDelta: 3
Transforms:
@@ -98,6 +101,7 @@ BUS:
Armor:
Type: Light
RevealsShroud:
RequiresCondition: !inside-tunnel
Range: 5c0
MaxHeightDelta: 3
Cargo:
@@ -123,6 +127,7 @@ PICK:
Armor:
Type: Light
RevealsShroud:
RequiresCondition: !inside-tunnel
Range: 5c0
MaxHeightDelta: 3
Cargo:
@@ -148,6 +153,7 @@ CAR:
Armor:
Type: Light
RevealsShroud:
RequiresCondition: !inside-tunnel
Range: 5c0
MaxHeightDelta: 3
Cargo:
@@ -173,6 +179,7 @@ WINI:
Armor:
Type: Light
RevealsShroud:
RequiresCondition: !inside-tunnel
Range: 5c0
MaxHeightDelta: 3
Cargo:

View File

@@ -279,6 +279,7 @@
Valued:
Cost: 10
RevealsShroud:
RequiresCondition: !inside-tunnel
Range: 2c0
MaxHeightDelta: 3
Mobile:
@@ -298,6 +299,7 @@
BlueTiberium: 90
PathingCost: 90
Veins: 50
TunnelCondition: inside-tunnel
WithTextControlGroupDecoration:
SelectionDecorations:
Palette: pips
@@ -306,6 +308,7 @@
Voiced:
VoiceSet: Infantry
Targetable:
RequiresCondition: !inside-tunnel
TargetTypes: Ground, Infantry
QuantizeFacingsFromSequence:
Sequence: stand
@@ -471,6 +474,7 @@
TurnSpeed: 5
Voice: Move
RequiresCondition: !empdisable
TunnelCondition: inside-tunnel
Selectable:
Bounds: 40,24
WithTextControlGroupDecoration:
@@ -479,6 +483,7 @@
Voiced:
VoiceSet: Vehicle
Targetable:
RequiresCondition: !inside-tunnel
TargetTypes: Ground, Vehicle, Repair
Repairable:
RepairBuildings: gadept
@@ -521,6 +526,7 @@
RequiresCondition: criticalspeed
Modifier: 60
Carryable:
RequiresCondition: !inside-tunnel
RevealOnFire:
^Tank:
@@ -770,6 +776,7 @@
Voice: Move
Speed: 113
RequiresCondition: !empdisable
TunnelCondition: inside-tunnel
Cargo:
Types: Infantry
UnloadVoice: Unload
@@ -780,6 +787,7 @@
RevealsShroud:
Range: 5c0
MaxHeightDelta: 3
RequiresCondition: !inside-tunnel
Selectable:
Bounds: 40,24
WithTextControlGroupDecoration:

View File

@@ -77,6 +77,11 @@ JUMPJET:
VoiceSet: JumpJet
Mobile:
Speed: 56
Jumpjet: True
JumpjetTransitionCost: 100
JumpjetCondition: airborne
TerrainSpeeds:
Jumpjet: 110
Health:
HP: 120
Armor:
@@ -91,10 +96,71 @@ JUMPJET:
AttackFrontal:
Voice: Attack
WithInfantryBody:
RequiresCondition: !airborne
DefaultAttackSequence: attack
WithInfantryBody@flying:
RequiresCondition: airborne
DefaultAttackSequence: flying-attack
StandSequences: flying
MoveSequence: flying
-TakeCover:
Hovers:
RequiresCondition: airborne
ProducibleWithLevel:
Prerequisites: barracks.upgraded
Targetable:
RequiresCondition: !airborne && !inside-tunnel
Targetable@AIRBORNE:
TargetTypes: Air
RequiresCondition: airborne
SpawnActorOnDeath@airborne:
Actor: JUMPJET.Husk
RequiresCondition: airborne
DeathSounds@airborne:
RequiresCondition: airborne
WithDeathAnimation@normal:
RequiresCondition: !airborne
WithDeathAnimation@explosion:
RequiresCondition: !airborne
WithDeathAnimation@energy:
RequiresCondition: !airborne
WithDeathAnimation:
RequiresCondition: !airborne
DeathSounds@NORMAL:
RequiresCondition: !airborne
DeathSounds@EXPLOSION:
RequiresCondition: !airborne
DeathSounds@BURNED:
RequiresCondition: !airborne
DeathSounds@ZAPPED:
RequiresCondition: !airborne
SpawnActorOnDeath:
RequiresCondition: !airborne
SpawnActorOnDeath@FLAMEGUY:
RequiresCondition: !airborne
JUMPJET.Husk:
RenderSprites:
BodyOrientation:
QuantizedFacings: 1
Aircraft:
HiddenUnderFog:
Type: GroundPosition
AutoTargetIgnore:
ScriptTriggers:
Tooltip:
Name: Jumpjet Infantry
FallsToEarth:
Velocity: 86
Explosion:
Aircraft:
Speed: 186
CanHover: True
RenderSprites:
Image: jumpjet
AutoSelectionSize:
WithSpriteBody:
Sequence: die-falling
GHOST:
Inherits: ^Soldier

View File

@@ -21,6 +21,7 @@ APC:
Armor:
Type: Heavy
RevealsShroud:
RequiresCondition: !inside-tunnel
Range: 5c0
MaxHeightDelta: 3
Turreted:
@@ -41,6 +42,7 @@ APC:
Sequence: water
RequiresCondition: inwater
LeavesTrails:
RequiresCondition: !inside-tunnel
Image: wake
Palette: effect
TerrainTypes: Water
@@ -78,6 +80,7 @@ HVR:
Armor:
Type: Wood
RevealsShroud:
RequiresCondition: !inside-tunnel
Range: 7c0
MaxHeightDelta: 3
Armament:
@@ -92,6 +95,7 @@ HVR:
WithVoxelTurret:
Hovers:
LeavesTrails:
RequiresCondition: !inside-tunnel
Image: wake
Palette: effect
TerrainTypes: Water
@@ -121,6 +125,7 @@ SMECH:
Armor:
Type: Light
RevealsShroud:
RequiresCondition: !inside-tunnel
Range: 6c0
MaxHeightDelta: 3
AttackFrontal:
@@ -164,6 +169,7 @@ MMCH:
Armor:
Type: Heavy
RevealsShroud:
RequiresCondition: !inside-tunnel
Range: 8c0
MaxHeightDelta: 3
BodyOrientation:
@@ -216,6 +222,7 @@ HMEC:
Armor:
Type: Heavy
RevealsShroud:
RequiresCondition: !inside-tunnel
Range: 8c0
MaxHeightDelta: 3
AttackFrontal:
@@ -259,6 +266,7 @@ SONIC:
Armor:
Type: Heavy
RevealsShroud:
RequiresCondition: !inside-tunnel
Range: 7c0
MaxHeightDelta: 3
Armament:

View File

@@ -19,6 +19,7 @@ BGGY:
Armor:
Type: Light
RevealsShroud:
RequiresCondition: !inside-tunnel
Range: 6c0
MaxHeightDelta: 3
Armament:
@@ -54,6 +55,7 @@ BIKE:
Armor:
Type: Wood
RevealsShroud:
RequiresCondition: !inside-tunnel
Range: 5c0
MaxHeightDelta: 3
Armament@PRIMARY:
@@ -107,6 +109,7 @@ TTNK:
MuzzleSequence: muzzle
WithMuzzleOverlay:
RevealsShroud:
RequiresCondition: !inside-tunnel
Range: 5c0
MaxHeightDelta: 3
RenderSprites:
@@ -188,6 +191,7 @@ ART2:
Speed: 71
TurnSpeed: 2
RevealsShroud:
RequiresCondition: !inside-tunnel
Range: 9c0
MaxHeightDelta: 3
RenderVoxels:
@@ -217,6 +221,7 @@ REPAIR:
Speed: 85
TurnSpeed: 5
RevealsShroud:
RequiresCondition: !inside-tunnel
Range: 5c0
MaxHeightDelta: 3
Armament:
@@ -262,6 +267,7 @@ WEED:
Armor:
Type: Heavy
RevealsShroud:
RequiresCondition: !inside-tunnel
Range: 4c0
MaxHeightDelta: 3
-WithVoxelBody:
@@ -285,11 +291,21 @@ SAPC:
TurnSpeed: 5
Speed: 71
RequiresCondition: !empdisable && !loading
Subterranean: true
SubterraneanCondition: submerged
SubterraneanTransitionTerrainTypes: Clear, Rough
SubterraneanTransitionCost: 120
SubterraneanTransitionSound: subdril1.aud
SubterraneanTransitionImage: dig
SubterraneanTransitionSequence: idle
TerrainSpeeds:
Subterranean: 120
Health:
HP: 175
Armor:
Type: Heavy
RevealsShroud:
RequiresCondition: !inside-tunnel && !submerged
Range: 5c0
MaxHeightDelta: 3
Cargo:
@@ -299,6 +315,10 @@ SAPC:
UnloadVoice: Unload
LoadingCondition: loading
EjectOnDeath: true
WithVoxelBody:
RequiresCondition: !submerged
Targetable:
RequiresCondition: !inside-tunnel && !submerged
SUBTANK:
Inherits: ^Tank
@@ -317,11 +337,21 @@ SUBTANK:
TurnSpeed: 6
Speed: 71
Crushes: wall, crate, infantry
Subterranean: true
SubterraneanCondition: submerged
SubterraneanTransitionTerrainTypes: Clear, Rough
SubterraneanTransitionCost: 120
SubterraneanTransitionSound: subdril1.aud
SubterraneanTransitionImage: dig
SubterraneanTransitionSequence: idle
TerrainSpeeds:
Subterranean: 120
Health:
HP: 300
Armor:
Type: Light
RevealsShroud:
RequiresCondition: !inside-tunnel && !submerged
Range: 5c0
MaxHeightDelta: 3
Armament:
@@ -329,6 +359,10 @@ SUBTANK:
AttackFrontal:
Voice: Attack
AutoTarget:
WithVoxelBody:
RequiresCondition: !submerged
Targetable:
RequiresCondition: !inside-tunnel && !submerged
STNK:
Inherits: ^Tank
@@ -352,6 +386,7 @@ STNK:
Armor:
Type: Light
RevealsShroud:
RequiresCondition: !inside-tunnel
Range: 5c0
MaxHeightDelta: 3
Cloak:

View File

@@ -19,6 +19,7 @@ MCV:
Mobile:
Speed: 85
RevealsShroud:
RequiresCondition: !inside-tunnel
Range: 4c0
MaxHeightDelta: 3
MustBeDestroyed:
@@ -84,6 +85,7 @@ HARV:
Armor:
Type: Heavy
RevealsShroud:
RequiresCondition: !inside-tunnel
Range: 4c0
MaxHeightDelta: 3
-WithVoxelBody:
@@ -129,6 +131,7 @@ LPST:
TurnSpeed: 5
RequiresCondition: !empdisable && undeployed
RevealsShroud:
RequiresCondition: !inside-tunnel
Range: 7c0
MaxHeightDelta: 3
GrantCondition@PREVIEWWORKAROUND:

View File

@@ -60,6 +60,8 @@
TerrainGeometryOverlay:
ExitsDebugOverlayManager:
CliffBackImpassabilityLayer:
SubterraneanActorLayer:
JumpjetActorLayer:
World:
Inherits: ^BaseWorld

View File

@@ -62,3 +62,36 @@ lobrdg_r_sw:
idle: lobrdg25
damaged-idle: lobrdg26
bridge1:
idle: bridge
# Disabled to avoid glitches until shadow rendering is fixed
# ShadowStart: 18
UseTilesetExtension: true
ZRamp: 1
Offset: 0, -13, 3.5
bridge2:
idle: bridge
Start: 9
# Disabled to avoid glitches until shadow rendering is fixed
# ShadowStart: 27
UseTilesetExtension: true
ZRamp: 1
Offset: 0, -25, 3.5
railbrdg1:
idle: railbrdg
# Disabled to avoid glitches until shadow rendering is fixed
# ShadowStart: 18
UseTilesetExtension: true
ZRamp: 1
Offset: 0, -13, 3.5
railbrdg2:
idle: railbrdg
Start: 9
# Disabled to avoid glitches until shadow rendering is fixed
# ShadowStart: 27
UseTilesetExtension: true
ZRamp: 1
Offset: 0, -25, 3.5

View File

@@ -359,10 +359,18 @@ jumpjet:
prone-stand:
Facings: 8
ShadowStart: 451
flying:
Facings: 8
Length: 6
Start: 292
ShadowStart: 743
die-twirling: # TODO: animation for falling from sky starts at 436
Start: 445
Length: 6
ShadowStart: 896
die-falling:
Start: 436
Length: 9
die-flying: # TODO: animation for falling from sky starts at 436
Start: 445
Length: 6
@@ -379,6 +387,11 @@ jumpjet:
Length: 6
Facings: 8
ShadowStart: 615
flying-attack:
Start: 388
Facings: 8
Length: 6
ShadowStart: 839
prone-attack:
Start: 212
Length: 6

View File

@@ -616,7 +616,9 @@ tracks16:
Offset: 0, -13, 42
ZRamp: 1
UseTilesetExtension: true
ShadowStart: 1
# Disabled to avoid glitches until shadow rendering is fixed
# Appears to be unused in the original game too
# ShadowStart: 1
tuntop01:
Inherits: ^tuntop
@@ -629,3 +631,9 @@ tuntop03:
tuntop04:
Inherits: ^tuntop
dig:
idle:
Length: *
ZOffset: 511
Offset: 0, 0, 24

View File

@@ -72,6 +72,12 @@ Terrain:
Type: Rock
TargetTypes: Ground
Color: 44443C
TerrainType@Subterranean:
Type: Subterranean
Color: C7C9FA
TerrainType@Jumpjet:
Type: Jumpjet
Color: C7C9FA
# Automatically generated. DO NOT EDIT!
Templates:
@@ -7580,7 +7586,7 @@ Templates:
Template@296:
Category: Bridges
Id: 296
Images: ovrps01.sno, ovrps01a.sno
Images: ovrps01.sno
Size: 3, 5
Tiles:
0: Cliff
@@ -7669,7 +7675,7 @@ Templates:
Template@297:
Category: Bridges
Id: 297
Images: ovrps02.sno, ovrps02a.sno
Images: ovrps02.sno
Size: 3, 5
Tiles:
0: Cliff
@@ -7758,7 +7764,7 @@ Templates:
Template@298:
Category: Bridges
Id: 298
Images: ovrps03.sno, ovrps03a.sno
Images: ovrps03.sno
Size: 2, 5
Tiles:
0: Cliff
@@ -7822,7 +7828,7 @@ Templates:
Template@299:
Category: Bridges
Id: 299
Images: ovrps04.sno, ovrps04a.sno
Images: ovrps04.sno
Size: 5, 3
Tiles:
0: Cliff
@@ -7911,7 +7917,7 @@ Templates:
Template@300:
Category: Bridges
Id: 300
Images: ovrps05.sno, ovrps05a.sno
Images: ovrps05.sno
Size: 5, 3
Tiles:
0: Cliff
@@ -8000,7 +8006,7 @@ Templates:
Template@301:
Category: Bridges
Id: 301
Images: ovrps06.sno, ovrps06a.sno
Images: ovrps06.sno
Size: 5, 2
Tiles:
0: Cliff
@@ -14328,7 +14334,7 @@ Templates:
Template@695:
Category: TrainBridges
Id: 695
Images: tovrps01.sno, tovrps01a.sno
Images: tovrps01.sno
Size: 3, 5
Tiles:
0: Cliff
@@ -14417,7 +14423,7 @@ Templates:
Template@696:
Category: TrainBridges
Id: 696
Images: tovrps02.sno, tovrps02a.sno
Images: tovrps02.sno
Size: 3, 5
Tiles:
0: Cliff
@@ -14506,7 +14512,7 @@ Templates:
Template@697:
Category: TrainBridges
Id: 697
Images: tovrps03.sno, tovrps03a.sno
Images: tovrps03.sno
Size: 2, 5
Tiles:
0: Cliff
@@ -14570,7 +14576,7 @@ Templates:
Template@698:
Category: TrainBridges
Id: 698
Images: tovrps04.sno, tovrps04a.sno
Images: tovrps04.sno
Size: 5, 3
Tiles:
0: Cliff
@@ -14659,7 +14665,7 @@ Templates:
Template@699:
Category: TrainBridges
Id: 699
Images: tovrps05.sno, tovrps05a.sno
Images: tovrps05.sno
Size: 5, 3
Tiles:
0: Cliff
@@ -14748,7 +14754,7 @@ Templates:
Template@700:
Category: TrainBridges
Id: 700
Images: tovrps06.sno, tovrps06a.sno
Images: tovrps06.sno
Size: 5, 2
Tiles:
0: Cliff

View File

@@ -72,6 +72,12 @@ Terrain:
Type: Rock
TargetTypes: Ground
Color: 44443C
TerrainType@Subterranean:
Type: Subterranean
Color: 745537
TerrainType@Jumpjet:
Type: Jumpjet
Color: 745537
# Automatically generated. DO NOT EDIT!
Templates:
@@ -7580,7 +7586,7 @@ Templates:
Template@296:
Category: Bridges
Id: 296
Images: ovrps01.tem, ovrps01a.tem
Images: ovrps01.tem
Size: 3, 5
Tiles:
0: Cliff
@@ -7669,7 +7675,7 @@ Templates:
Template@297:
Category: Bridges
Id: 297
Images: ovrps02.tem, ovrps02a.tem
Images: ovrps02.tem
Size: 3, 5
Tiles:
0: Cliff
@@ -7758,7 +7764,7 @@ Templates:
Template@298:
Category: Bridges
Id: 298
Images: ovrps03.tem, ovrps03a.tem
Images: ovrps03.tem
Size: 2, 5
Tiles:
0: Cliff
@@ -7822,7 +7828,7 @@ Templates:
Template@299:
Category: Bridges
Id: 299
Images: ovrps04.tem, ovrps04a.tem
Images: ovrps04.tem
Size: 5, 3
Tiles:
0: Cliff
@@ -7911,7 +7917,7 @@ Templates:
Template@300:
Category: Bridges
Id: 300
Images: ovrps05.tem, ovrps05a.tem
Images: ovrps05.tem
Size: 5, 3
Tiles:
0: Cliff
@@ -8000,7 +8006,7 @@ Templates:
Template@301:
Category: Bridges
Id: 301
Images: ovrps06.tem, ovrps06a.tem
Images: ovrps06.tem
Size: 5, 2
Tiles:
0: Cliff
@@ -13365,7 +13371,7 @@ Templates:
Template@566:
Category: TrainBridges
Id: 566
Images: tovrps01.tem, tovrps01a.tem
Images: tovrps01.tem
Size: 3, 5
Tiles:
0: Cliff
@@ -13454,7 +13460,7 @@ Templates:
Template@567:
Category: TrainBridges
Id: 567
Images: tovrps02.tem, tovrps02a.tem
Images: tovrps02.tem
Size: 3, 5
Tiles:
0: Cliff
@@ -13543,7 +13549,7 @@ Templates:
Template@568:
Category: TrainBridges
Id: 568
Images: tovrps03.tem, tovrps03a.tem
Images: tovrps03.tem
Size: 2, 5
Tiles:
0: Cliff
@@ -13607,7 +13613,7 @@ Templates:
Template@569:
Category: TrainBridges
Id: 569
Images: tovrps04.tem, tovrps04a.tem
Images: tovrps04.tem
Size: 5, 3
Tiles:
0: Cliff
@@ -13696,7 +13702,7 @@ Templates:
Template@570:
Category: TrainBridges
Id: 570
Images: tovrps05.tem, tovrps05a.tem
Images: tovrps05.tem
Size: 5, 3
Tiles:
0: Cliff
@@ -13785,7 +13791,7 @@ Templates:
Template@571:
Category: TrainBridges
Id: 571
Images: tovrps06.tem, tovrps06a.tem
Images: tovrps06.tem
Size: 5, 2
Tiles:
0: Cliff