diff --git a/OpenRA.Game/Actor.cs b/OpenRA.Game/Actor.cs index 1dbc155022..07f2478299 100644 --- a/OpenRA.Game/Actor.cs +++ b/OpenRA.Game/Actor.cs @@ -22,14 +22,36 @@ using OpenRA.Traits; namespace OpenRA { - public class Actor : IScriptBindable, IScriptNotifyBind, ILuaTableBinding, ILuaEqualityBinding, ILuaToStringBinding, IEquatable + public interface IActor { - public readonly ActorInfo Info; + ActorInfo Info { get; } + IWorld World { get; } + uint ActorID { get; } + Player Owner { get; set; } + + T TraitOrDefault(); + T Trait(); + IEnumerable TraitsImplementing(); + + T TraitInfo(); + + IEnumerable Render(WorldRenderer wr); + } + + public class Actor : IScriptBindable, IScriptNotifyBind, ILuaTableBinding, ILuaEqualityBinding, ILuaToStringBinding, IEquatable, IActor + { + public ActorInfo Info { get; private set; } public readonly World World; - public readonly uint ActorID; + + IWorld IActor.World + { + get { return World; } + } + + public uint ActorID { get; private set; } [Sync] - public Player Owner; + public Player Owner { get; set; } public bool IsInWorld { get; internal set; } public bool Destroyed { get; private set; } @@ -202,6 +224,11 @@ namespace OpenRA return World.TraitDict.WithInterface(this); } + public T TraitInfo() + { + return Info.Traits.Get(); + } + public bool HasTrait() { return World.TraitDict.Contains(this); @@ -235,7 +262,7 @@ namespace OpenRA { World.AddFrameEndTask(w => { - if (this.Destroyed) + if (Destroyed) return; var oldOwner = Owner; diff --git a/OpenRA.Game/CacheStorage.cs b/OpenRA.Game/CacheStorage.cs new file mode 100644 index 0000000000..e5216a9639 --- /dev/null +++ b/OpenRA.Game/CacheStorage.cs @@ -0,0 +1,19 @@ +#region Copyright & License Information +/* + * Copyright 2007-2015 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. For more information, + * see COPYING. + */ +#endregion + +namespace OpenRA +{ + public interface ICacheStorage + { + void Remove(string key); + void Store(string key, T data); + T Retrieve(string key); + } +} \ No newline at end of file diff --git a/OpenRA.Game/LogProxy.cs b/OpenRA.Game/LogProxy.cs new file mode 100644 index 0000000000..0f313470aa --- /dev/null +++ b/OpenRA.Game/LogProxy.cs @@ -0,0 +1,25 @@ +#region Copyright & License Information +/* + * Copyright 2007-2015 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. For more information, + * see COPYING. + */ +#endregion + +namespace OpenRA +{ + public interface ILog + { + void Write(string channel, string format, params object[] args); + } + + public class LogProxy : ILog + { + public void Write(string channel, string format, params object[] args) + { + Log.Write(channel, format, args); + } + } +} diff --git a/OpenRA.Game/Map/Map.cs b/OpenRA.Game/Map/Map.cs index f1b6dc4fbd..801ad43542 100644 --- a/OpenRA.Game/Map/Map.cs +++ b/OpenRA.Game/Map/Map.cs @@ -111,15 +111,33 @@ namespace OpenRA MissionSelector = 4 } - public class Map + public interface IMap + { + TileShape TileShape { get; } + + int2 MapSize { get; set; } + bool Contains(CPos cell); + CPos CellContaining(WPos pos); + WVec OffsetOfSubCell(SubCell subCell); + IEnumerable FindTilesInCircle(CPos center, int maxRange); + WPos CenterOfCell(CPos cell); + } + + public class Map : IMap { public const int MaxTilesInCircleRange = 50; public readonly TileShape TileShape; + TileShape IMap.TileShape + { + get { return TileShape; } + } + [FieldLoader.Ignore] public readonly WVec[] SubCellOffsets; public readonly SubCell DefaultSubCell; public readonly SubCell LastSubCell; - [FieldLoader.Ignore] public IFolder Container; + [FieldLoader.Ignore] + public IFolder Container; public string Path { get; private set; } // Yaml map data @@ -165,41 +183,65 @@ namespace OpenRA return videos; } - [FieldLoader.Ignore] public Lazy> Actors; + [FieldLoader.Ignore] + public Lazy> Actors; public int PlayerCount { get { return Players.Count(p => p.Value.Playable); } } public Rectangle Bounds; // Yaml map data - [FieldLoader.Ignore] public Dictionary Players = new Dictionary(); - [FieldLoader.Ignore] public Lazy> Smudges; + [FieldLoader.Ignore] + public Dictionary Players = new Dictionary(); + [FieldLoader.Ignore] + public Lazy> Smudges; - [FieldLoader.Ignore] public List RuleDefinitions = new List(); - [FieldLoader.Ignore] public List SequenceDefinitions = new List(); - [FieldLoader.Ignore] public List VoxelSequenceDefinitions = new List(); - [FieldLoader.Ignore] public List WeaponDefinitions = new List(); - [FieldLoader.Ignore] public List VoiceDefinitions = new List(); - [FieldLoader.Ignore] public List NotificationDefinitions = new List(); - [FieldLoader.Ignore] public List TranslationDefinitions = new List(); + [FieldLoader.Ignore] + public List RuleDefinitions = new List(); + [FieldLoader.Ignore] + public List SequenceDefinitions = new List(); + [FieldLoader.Ignore] + public List VoxelSequenceDefinitions = new List(); + [FieldLoader.Ignore] + public List WeaponDefinitions = new List(); + [FieldLoader.Ignore] + public List VoiceDefinitions = new List(); + [FieldLoader.Ignore] + public List NotificationDefinitions = new List(); + [FieldLoader.Ignore] + public List TranslationDefinitions = new List(); // Binary map data - [FieldLoader.Ignore] public byte TileFormat = 2; + [FieldLoader.Ignore] + public byte TileFormat = 2; public int2 MapSize; - [FieldLoader.Ignore] public Lazy> MapTiles; - [FieldLoader.Ignore] public Lazy> MapResources; - [FieldLoader.Ignore] public Lazy> MapHeight; + int2 IMap.MapSize + { + get { return MapSize; } + set { MapSize = value; } + } - [FieldLoader.Ignore] public CellLayer CustomTerrain; + [FieldLoader.Ignore] + public Lazy> MapTiles; + [FieldLoader.Ignore] + public Lazy> MapResources; + [FieldLoader.Ignore] + public Lazy> MapHeight; - [FieldLoader.Ignore] Lazy cachedTileSet; - [FieldLoader.Ignore] Lazy rules; + [FieldLoader.Ignore] + public CellLayer CustomTerrain; + + [FieldLoader.Ignore] + Lazy cachedTileSet; + [FieldLoader.Ignore] + Lazy rules; public Ruleset Rules { get { return rules != null ? rules.Value : null; } } public SequenceProvider SequenceProvider { get { return Rules.Sequences[Tileset]; } } - [FieldLoader.Ignore] public CellRegion Cells; + [FieldLoader.Ignore] + public CellRegion Cells; public static Map FromTileset(TileSet tileset) { diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index db244831f3..af61ef4bf9 100644 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -84,6 +84,8 @@ + + diff --git a/OpenRA.Game/World.cs b/OpenRA.Game/World.cs index 8467ba2439..de23db10ce 100644 --- a/OpenRA.Game/World.cs +++ b/OpenRA.Game/World.cs @@ -24,7 +24,15 @@ namespace OpenRA { public enum WorldType { Regular, Shellmap } - public class World + public interface IWorld + { + IActor WorldActor { get; } + int WorldTick { get; } + IMap Map { get; } + TileSet TileSet { get; } + } + + public class World : IWorld { class ActorIDComparer : IComparer { @@ -113,9 +121,13 @@ namespace OpenRA RenderPlayer = LocalPlayer; } - public readonly Actor WorldActor; - public readonly Map Map; - public readonly TileSet TileSet; + public Actor WorldActor { get; private set; } + IActor IWorld.WorldActor { get { return WorldActor; } } + + public Map Map { get; private set; } + IMap IWorld.Map { get { return Map; } } + + public TileSet TileSet { get; private set; } public readonly ActorMap ActorMap; public readonly ScreenMap ScreenMap; public readonly WorldType Type; diff --git a/OpenRA.Mods.Common/Traits/Mobile.cs b/OpenRA.Mods.Common/Traits/Mobile.cs index 4e3cc08759..89bd615dd7 100644 --- a/OpenRA.Mods.Common/Traits/Mobile.cs +++ b/OpenRA.Mods.Common/Traits/Mobile.cs @@ -29,8 +29,17 @@ namespace OpenRA.Mods.Common.Traits All = TransientActors | BlockedByMovers } + public interface IMobileInfo + { + int MovementCostForCell(World world, CPos cell); + bool CanEnterCell(World world, Actor self, CPos cell, out int movementCost, Actor ignoreActor = null, CellConditions check = CellConditions.All); + bool CanEnterCell(World world, Actor self, CPos cell, Actor ignoreActor = null, CellConditions check = CellConditions.All); + + int GetMovementClass(TileSet tileset); + } + [Desc("Unit is able to move.")] - public class MobileInfo : ITraitInfo, IOccupySpaceInfo, IFacingInfo, IMoveInfo, UsesInit, UsesInit, UsesInit + public class MobileInfo : ITraitInfo, IOccupySpaceInfo, IFacingInfo, IMoveInfo, UsesInit, UsesInit, UsesInit, IMobileInfo { [FieldLoader.LoadUsing("LoadSpeeds")] [Desc("Set Water: 0 for ground units and lower the value on rough terrain.")] @@ -165,11 +174,61 @@ namespace OpenRA.Mods.Common.Traits return true; } + public int TileSetMovementHash(TileSet tileSet) + { + var terrainInfos = TilesetTerrainInfo[tileSet]; + + // Compute and return the hash using aggregate + return terrainInfos.Aggregate(terrainInfos.Length, + (current, terrainInfo) => unchecked(current * 31 + terrainInfo.Cost)); + } + public bool CanEnterCell(World world, Actor self, CPos cell, Actor ignoreActor = null, CellConditions check = CellConditions.All) { if (MovementCostForCell(world, cell) == int.MaxValue) return false; + return CanMoveFreelyInto(world, self, cell, ignoreActor, check); + } + + // Determines whether the actor is blocked by other Actors + public bool CanMoveFreelyInto(World world, Actor self, CPos cell, Actor ignoreActor, CellConditions check) + { + if (SharesCell && world.ActorMap.HasFreeSubCell(cell)) + return true; + + if (check.HasFlag(CellConditions.TransientActors)) + { + var canIgnoreMovingAllies = self != null && !check.HasFlag(CellConditions.BlockedByMovers); + var needsCellExclusively = self == null || Crushes == null || !Crushes.Any(); + foreach (var a in world.ActorMap.GetUnitsAt(cell)) + { + if (a == ignoreActor) + continue; + + // Neutral/enemy units are blockers. Allied units that are moving are not blockers. + if (canIgnoreMovingAllies && self.Owner.Stances[a.Owner] == Stance.Ally && IsMovingInMyDirection(self, a)) continue; + + // Non-sharable unit can enter a cell with shareable units only if it can crush all of them. + if (needsCellExclusively) + return false; + var crushables = a.TraitsImplementing(); + if (!crushables.Any()) + return false; + foreach (var crushable in crushables) + if (!crushable.CrushableBy(Crushes, self.Owner)) + return false; + } + } + + return true; + } + + public bool CanEnterCell(World world, Actor self, CPos cell, out int movementCost, Actor ignoreActor = null, CellConditions check = CellConditions.All) + { + if ((movementCost = MovementCostForCell(world, cell)) == int.MaxValue) + return false; + if (SharesCell && world.ActorMap.HasFreeSubCell(cell)) return true; @@ -506,6 +565,11 @@ namespace OpenRA.Mods.Common.Traits return Info.CanEnterCell(self.World, self, cell, ignoreActor, checkTransientActors ? CellConditions.All : CellConditions.BlockedByMovers); } + public bool CanMoveFreely(CPos cell, Actor ignoreActor = null, bool checkTransientActors = true) + { + return Info.CanMoveFreelyInto(self.World, self, cell, ignoreActor, checkTransientActors ? CellConditions.All : CellConditions.BlockedByMovers); + } + public void EnteringCell(Actor self) { var crushables = self.World.ActorMap.GetUnitsAt(ToCell).Where(a => a != self)