Implement tunnels.
This commit is contained in:
@@ -794,6 +794,8 @@
|
|||||||
<Compile Include="Traits\RevealOnFire.cs" />
|
<Compile Include="Traits\RevealOnFire.cs" />
|
||||||
<Compile Include="Traits\Conditions\GrantConditionOnDisabled.cs" />
|
<Compile Include="Traits\Conditions\GrantConditionOnDisabled.cs" />
|
||||||
<Compile Include="Traits\World\ActorMap.cs" />
|
<Compile Include="Traits\World\ActorMap.cs" />
|
||||||
|
<Compile Include="Traits\World\TerrainTunnelLayer.cs" />
|
||||||
|
<Compile Include="Traits\World\TerrainTunnel.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<Target Name="AfterBuild">
|
<Target Name="AfterBuild">
|
||||||
|
|||||||
@@ -38,6 +38,11 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class CustomMovementLayerType
|
||||||
|
{
|
||||||
|
public const byte Tunnel = 1;
|
||||||
|
}
|
||||||
|
|
||||||
[Desc("Unit is able to move.")]
|
[Desc("Unit is able to move.")]
|
||||||
public class MobileInfo : ConditionalTraitInfo, IMoveInfo, IPositionableInfo, IOccupySpaceInfo, IFacingInfo,
|
public class MobileInfo : ConditionalTraitInfo, IMoveInfo, IPositionableInfo, IOccupySpaceInfo, IFacingInfo,
|
||||||
UsesInit<FacingInit>, UsesInit<LocationInit>, UsesInit<SubCellInit>
|
UsesInit<FacingInit>, UsesInit<LocationInit>, UsesInit<SubCellInit>
|
||||||
@@ -73,6 +78,10 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
[VoiceReference] public readonly string Voice = "Action";
|
[VoiceReference] public readonly string Voice = "Action";
|
||||||
|
|
||||||
|
[GrantedConditionReference]
|
||||||
|
[Desc("The condition to grant to self while inside a tunnel.")]
|
||||||
|
public readonly string TunnelCondition = null;
|
||||||
|
|
||||||
public override object Create(ActorInitializer init) { return new Mobile(init, this); }
|
public override object Create(ActorInitializer init) { return new Mobile(init, this); }
|
||||||
|
|
||||||
static object LoadSpeeds(MiniYaml y)
|
static object LoadSpeeds(MiniYaml y)
|
||||||
@@ -319,8 +328,8 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
bool IOccupySpaceInfo.SharesCell { get { return SharesCell; } }
|
bool IOccupySpaceInfo.SharesCell { get { return SharesCell; } }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Mobile : ConditionalTrait<MobileInfo>, IIssueOrder, IResolveOrder, IOrderVoice, IPositionable, IMove, IFacing, ISync,
|
public class Mobile : ConditionalTrait<MobileInfo>, INotifyCreated, IIssueOrder, IResolveOrder, IOrderVoice, IPositionable, IMove,
|
||||||
IDeathActorInitModifier, INotifyAddedToWorld, INotifyRemovedFromWorld, INotifyBlockingMove, IActorPreviewInitModifier
|
IFacing, ISync, IDeathActorInitModifier, INotifyAddedToWorld, INotifyRemovedFromWorld, INotifyBlockingMove, IActorPreviewInitModifier
|
||||||
{
|
{
|
||||||
const int AverageTicksBeforePathing = 5;
|
const int AverageTicksBeforePathing = 5;
|
||||||
const int SpreadTicksBeforePathing = 5;
|
const int SpreadTicksBeforePathing = 5;
|
||||||
@@ -334,6 +343,8 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
int facing;
|
int facing;
|
||||||
CPos fromCell, toCell;
|
CPos fromCell, toCell;
|
||||||
public SubCell FromSubCell, ToSubCell;
|
public SubCell FromSubCell, ToSubCell;
|
||||||
|
int tunnelToken = ConditionManager.InvalidConditionToken;
|
||||||
|
ConditionManager conditionManager;
|
||||||
|
|
||||||
[Sync] public int Facing
|
[Sync] public int Facing
|
||||||
{
|
{
|
||||||
@@ -361,6 +372,12 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
FromSubCell = fromSub;
|
FromSubCell = fromSub;
|
||||||
ToSubCell = toSub;
|
ToSubCell = toSub;
|
||||||
AddInfluence();
|
AddInfluence();
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Mobile(ActorInitializer init, MobileInfo info)
|
public Mobile(ActorInitializer init, MobileInfo info)
|
||||||
@@ -388,6 +405,11 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
SetVisualPosition(self, init.Get<CenterPositionInit, WPos>());
|
SetVisualPosition(self, init.Get<CenterPositionInit, WPos>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void INotifyCreated.Created(Actor self)
|
||||||
|
{
|
||||||
|
conditionManager = self.TraitOrDefault<ConditionManager>();
|
||||||
|
}
|
||||||
|
|
||||||
// Returns a valid sub-cell
|
// Returns a valid sub-cell
|
||||||
public SubCell GetValidSubCell(SubCell preferred = SubCell.Any)
|
public SubCell GetValidSubCell(SubCell preferred = SubCell.Any)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using OpenRA.Graphics;
|
using OpenRA.Graphics;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
@@ -36,8 +37,17 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
public void WorldLoaded(World w, WorldRenderer wr)
|
public void WorldLoaded(World w, WorldRenderer wr)
|
||||||
{
|
{
|
||||||
var tileType = w.Map.Rules.TileSet.GetTerrainIndex(info.TerrainType);
|
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)
|
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
|
// All the map cells that visually overlap the current cell
|
||||||
var testCells = w.Map.ProjectedCellsCovering(uv)
|
var testCells = w.Map.ProjectedCellsCovering(uv)
|
||||||
.SelectMany(puv => w.Map.Unproject(puv));
|
.SelectMany(puv => w.Map.Unproject(puv));
|
||||||
|
|||||||
62
OpenRA.Mods.Common/Traits/World/TerrainTunnel.cs
Normal file
62
OpenRA.Mods.Common/Traits/World/TerrainTunnel.cs
Normal 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 { }
|
||||||
|
}
|
||||||
94
OpenRA.Mods.Common/Traits/World/TerrainTunnelLayer.cs
Normal file
94
OpenRA.Mods.Common/Traits/World/TerrainTunnelLayer.cs
Normal 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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -306,6 +306,7 @@
|
|||||||
Voiced:
|
Voiced:
|
||||||
VoiceSet: Infantry
|
VoiceSet: Infantry
|
||||||
Targetable:
|
Targetable:
|
||||||
|
RequiresCondition: !inside-tunnel
|
||||||
TargetTypes: Ground, Infantry
|
TargetTypes: Ground, Infantry
|
||||||
QuantizeFacingsFromSequence:
|
QuantizeFacingsFromSequence:
|
||||||
Sequence: stand
|
Sequence: stand
|
||||||
@@ -479,6 +480,7 @@
|
|||||||
Voiced:
|
Voiced:
|
||||||
VoiceSet: Vehicle
|
VoiceSet: Vehicle
|
||||||
Targetable:
|
Targetable:
|
||||||
|
RequiresCondition: !inside-tunnel
|
||||||
TargetTypes: Ground, Vehicle, Repair
|
TargetTypes: Ground, Vehicle, Repair
|
||||||
Repairable:
|
Repairable:
|
||||||
RepairBuildings: gadept
|
RepairBuildings: gadept
|
||||||
@@ -521,6 +523,7 @@
|
|||||||
RequiresCondition: criticalspeed
|
RequiresCondition: criticalspeed
|
||||||
Modifier: 60
|
Modifier: 60
|
||||||
Carryable:
|
Carryable:
|
||||||
|
RequiresCondition: !inside-tunnel
|
||||||
RevealOnFire:
|
RevealOnFire:
|
||||||
|
|
||||||
^Tank:
|
^Tank:
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ APC:
|
|||||||
Sequence: water
|
Sequence: water
|
||||||
RequiresCondition: inwater
|
RequiresCondition: inwater
|
||||||
LeavesTrails:
|
LeavesTrails:
|
||||||
|
RequiresCondition: !inside-tunnel
|
||||||
Image: wake
|
Image: wake
|
||||||
Palette: effect
|
Palette: effect
|
||||||
TerrainTypes: Water
|
TerrainTypes: Water
|
||||||
@@ -92,6 +93,7 @@ HVR:
|
|||||||
WithVoxelTurret:
|
WithVoxelTurret:
|
||||||
Hovers:
|
Hovers:
|
||||||
LeavesTrails:
|
LeavesTrails:
|
||||||
|
RequiresCondition: !inside-tunnel
|
||||||
Image: wake
|
Image: wake
|
||||||
Palette: effect
|
Palette: effect
|
||||||
TerrainTypes: Water
|
TerrainTypes: Water
|
||||||
|
|||||||
@@ -616,7 +616,9 @@ tracks16:
|
|||||||
Offset: 0, -13, 42
|
Offset: 0, -13, 42
|
||||||
ZRamp: 1
|
ZRamp: 1
|
||||||
UseTilesetExtension: true
|
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:
|
tuntop01:
|
||||||
Inherits: ^tuntop
|
Inherits: ^tuntop
|
||||||
|
|||||||
Reference in New Issue
Block a user