public IReadOnlyDictionary<CPos, SubCell> OccupiedCells(ActorInfo info, CPos location, SubCell subCell = SubCell.Any) { var occupied = OccupiesSpace ? new Dictionary<CPos, SubCell>() { { location, SubCell.FullCell } } : new Dictionary<CPos, SubCell>(); return new ReadOnlyDictionary<CPos, SubCell>(occupied); }
public List<CPos> FindUnitPathToRange(CPos source, SubCell srcSub, WPos target, WDist range, Actor self) { var mi = self.Info.TraitInfo<MobileInfo>(); var targetCell = world.Map.CellContaining(target); // Correct for SubCell offset target -= world.Map.OffsetOfSubCell(srcSub); // Select only the tiles that are within range from the requested SubCell // This assumes that the SubCell does not change during the path traversal var tilesInRange = world.Map.FindTilesInCircle(targetCell, range.Length / 1024 + 1) .Where(t => (world.Map.CenterOfCell(t) - target).LengthSquared <= range.LengthSquared && mi.CanEnterCell(self.World, self, t)); // See if there is any cell within range that does not involve a cross-domain request // Really, we only need to check the circle perimeter, but it's not clear that would be a performance win var domainIndex = world.WorldActor.TraitOrDefault<DomainIndex>(); if (domainIndex != null) { var passable = mi.GetMovementClass(world.TileSet); tilesInRange = new List<CPos>(tilesInRange.Where(t => domainIndex.IsPassable(source, t, (uint)passable))); if (!tilesInRange.Any()) return EmptyPath; } using (var fromSrc = PathSearch.FromPoints(world, mi, self, tilesInRange, source, true)) using (var fromDest = PathSearch.FromPoint(world, mi, self, source, targetCell, true).Reverse()) return FindBidiPath(fromSrc, fromDest); }
public bool AnyUnitsAt(CPos a, SubCell sub) { for( var i = influence[ a.X, a.Y ] ; i != null ; i = i.next ) if (i.subCell == sub || i.subCell == SubCell.FullCell) return true; return false; }
public IEnumerable<Actor> GetUnitsAt( int2 a, SubCell sub ) { if (!map.IsInMap(a)) yield break; for( var i = influence[ a.X, a.Y ] ; i != null ; i = i.next ) if (!i.actor.Destroyed && (i.subCell == sub || i.subCell == SubCell.FullCell)) yield return i.actor; }
public IEnumerable<Actor> GetUnitsAt(CPos a, SubCell sub) { if (!map.Contains(a)) yield break; for (var i = influence[a]; i != null; i = i.Next) if (!i.Actor.Destroyed && (i.SubCell == sub || i.SubCell == SubCell.FullCell)) yield return i.Actor; }
public List<CPos> FindUnitPathToRange(CPos source, SubCell srcSub, WPos target, WDist range, Actor self) { using (new PerfSample("Pathfinder")) { var key = "FindUnitPathToRange" + self.ActorID + source.X + source.Y + target.X + target.Y; var cachedPath = cacheStorage.Retrieve(key); if (cachedPath != null) return cachedPath; var pb = pathFinder.FindUnitPathToRange(source, srcSub, target, range, self); cacheStorage.Store(key, pb); return pb; } }
public Mobile(ActorInitializer init, MobileInfo info) { this.self = init.self; this.Info = info; toSubCell = fromSubCell = info.SharesCell ? SubCell.Center : SubCell.FullCell; if (init.Contains<SubCellInit>()) { this.fromSubCell = this.toSubCell = init.Get<SubCellInit, SubCell>(); } if (init.Contains<LocationInit>()) { this.__fromCell = this.__toCell = init.Get<LocationInit, int2>(); this.PxPosition = Util.CenterOfCell(fromCell) + info.SubCellOffsets[fromSubCell]; } this.Facing = init.Contains<FacingInit>() ? init.Get<FacingInit, int>() : info.InitialFacing; this.Altitude = init.Contains<AltitudeInit>() ? init.Get<AltitudeInit, int>() : 0; }
public SubCell GetAvailableSubCell(Actor self, CPos cell, BlockedByActor check, SubCell preferredSubCell = SubCell.Any, Actor ignoreActor = null) { if (MovementCostForCell(cell) == short.MaxValue) { return(SubCell.Invalid); } if (check > BlockedByActor.None) { Func <Actor, bool> checkTransient = otherActor => IsBlockedBy(self, otherActor, ignoreActor, cell, check, GetCache(cell).CellFlag); if (!sharesCell) { return(world.ActorMap.AnyActorsAt(cell, SubCell.FullCell, checkTransient) ? SubCell.Invalid : SubCell.FullCell); } return(world.ActorMap.FreeSubCell(cell, preferredSubCell, checkTransient)); } if (!sharesCell) { return(world.ActorMap.AnyActorsAt(cell, SubCell.FullCell) ? SubCell.Invalid : SubCell.FullCell); } return(world.ActorMap.FreeSubCell(cell, preferredSubCell)); }
public WVec OffsetOfSubCell(SubCell subCell) { return SubCellOffsets[(int)subCell]; }
public Activity MoveIntoWorld(Actor self, CPos cell, SubCell subCell = SubCell.Any) { return new Fly(self, Target.FromCell(self.World, cell)); }
public SubCell GetValidSubCell(SubCell preferred = SubCell.Any) { return(SubCell.FullCell); }
public void SetPosition(Actor self, CPos cell, SubCell subCell = SubCell.Any) { SetPosition(self, self.World.Map.CenterOfCell(cell)); }
public SubCell GetAvailableSubCell(CPos cell, SubCell preferredSubCell = SubCell.Any, Actor ignoreActor = null, bool checkTransientActors = true) { return(info.GetAvailableSubCell(self.World, cell, ignoreActor, checkTransientActors)); }
public IReadOnlyDictionary <CPos, SubCell> OccupiedCells(ActorInfo info, CPos location, SubCell subCell = SubCell.Any) { return(OccupiesSpace ? new Dictionary <CPos, SubCell>() { { location, SubCell.FullCell } } : new Dictionary <CPos, SubCell>()); }
public bool CanMoveFreelyInto(Actor actor, CPos cell, SubCell subCell, BlockedByActor check, Actor ignoreActor) { var cellCache = GetCache(cell); var cellFlag = cellCache.CellFlag; // If the check allows: We are not blocked by transient actors. if (check == BlockedByActor.None) { return(true); } // No actor in the cell or free SubCell. if (cellFlag == CellFlag.HasFreeSpace) { return(true); } // If actor is null we're just checking what would happen theoretically. // In such a scenario - we'll just assume any other actor in the cell will block us by default. // If we have a real actor, we can then perform the extra checks that allow us to avoid being blocked. if (actor == null) { return(false); } // All actors that may be in the cell can be crushed. if (cellCache.Crushable.Overlaps(actor.Owner.PlayerMask)) { return(true); } // If the check allows: We are not blocked by moving units. if (check <= BlockedByActor.Stationary && !cellFlag.HasCellFlag(CellFlag.HasStationaryActor)) { return(true); } // If the check allows: We are not blocked by units that we can force to move out of the way. if (check <= BlockedByActor.Immovable && !cellCache.Immovable.Overlaps(actor.Owner.PlayerMask)) { return(true); } // Cache doesn't account for ignored actors, temporary blockers, or subcells - these must use the slow path. if (ignoreActor == null && !cellFlag.HasCellFlag(CellFlag.HasTemporaryBlocker) && subCell == SubCell.FullCell) { // We already know there are uncrushable actors in the cell so we are always blocked. if (check == BlockedByActor.All) { return(false); } // We already know there are either immovable or stationary actors which the check does not allow. if (!cellFlag.HasCellFlag(CellFlag.HasCrushableActor)) { return(false); } // All actors in the cell are immovable and some cannot be crushed. if (!cellFlag.HasCellFlag(CellFlag.HasMovableActor)) { return(false); } // All actors in the cell are stationary and some cannot be crushed. if (check == BlockedByActor.Stationary && !cellFlag.HasCellFlag(CellFlag.HasMovingActor)) { return(false); } } var otherActors = subCell == SubCell.FullCell ? world.ActorMap.GetActorsAt(cell) : world.ActorMap.GetActorsAt(cell, subCell); foreach (var otherActor in otherActors) { if (IsBlockedBy(actor, otherActor, ignoreActor, cell, check, cellFlag)) { return(false); } } return(true); }
public bool IsLeavingCell(CPos location, SubCell subCell = SubCell.Any) { return(false); } // TODO: Handle landing
// Used to determine if actor can spawn public bool CanEnterCell(World world, Actor self, CPos cell, SubCell subCell = SubCell.FullCell, Actor ignoreActor = null, BlockedByActor check = BlockedByActor.All) { return(world.Map.Contains(cell)); }
public SubCell GetAvailableSubCell(CPos a, SubCell preferredSubCell = SubCell.Any, Actor ignoreActor = null, BlockedByActor check = BlockedByActor.All) { return(Locomotor.GetAvailableSubCell(self, a, check, preferredSubCell, ignoreActor)); }
public static Target FromCell(World w, CPos c, SubCell subCell = SubCell.FullCell) { return new Target { pos = w.Map.CenterOfSubCell(c, subCell), type = TargetType.Terrain }; }
public static Target FromCell(World w, CPos c, SubCell subCell = SubCell.FullCell) { return(new Target(w, c, subCell)); }
public SubCell FreeSubCell(CPos a, SubCell preferredSubCell, Func<Actor, bool> checkIfBlocker) { if (preferredSubCell > SubCell.Any && !AnyUnitsAt(a, preferredSubCell, checkIfBlocker)) return preferredSubCell; if (!AnyUnitsAt(a)) return map.DefaultSubCell; for (var i = (int)SubCell.First; i < map.SubCellOffsets.Length; i++) if (i != (int)preferredSubCell && !AnyUnitsAt(a, (SubCell)i, checkIfBlocker)) return (SubCell)i; return SubCell.Invalid; }
public bool CanEnterCell(World world, Actor self, CPos cell, SubCell subCell = SubCell.FullCell, Actor ignoreActor = null, BlockedByActor check = BlockedByActor.All) { // IPositionable*Info*.CanEnterCell is only ever used for things like exiting production facilities, // all places relevant for husks check IPositionable.CanEnterCell instead, so we can safely set this to true. return(true); }
public bool CanEnterCell(World world, Actor self, CPos cell, SubCell subCell = SubCell.FullCell, Actor ignoreActor = null, BlockedByActor check = BlockedByActor.All) { // Since crates don't share cells and GetAvailableSubCell only returns SubCell.Full or SubCell.Invalid, we ignore the subCell parameter return(GetAvailableSubCell(world, cell, ignoreActor, check) != SubCell.Invalid); }
// NOTE: can not check aircraft public bool AnyUnitsAt(CPos a, SubCell sub, Func<Actor, bool> withCondition) { if (!map.Contains(a)) return false; var always = sub == SubCell.FullCell || sub == SubCell.Any; for (var i = influence[a]; i != null; i = i.Next) if (always || i.SubCell == sub || i.SubCell == SubCell.FullCell) if (withCondition(i.Actor)) return true; return false; }
// The standard constructor for most purposes public Map(string path) { Path = path; Container = GlobalFileSystem.OpenPackage(path, null, int.MaxValue); AssertExists("map.yaml"); AssertExists("map.bin"); var yaml = new MiniYaml(null, MiniYaml.FromStream(Container.GetContent("map.yaml"), path)); FieldLoader.Load(this, yaml); // Support for formats 1-3 dropped 2011-02-11. // Use release-20110207 to convert older maps to format 4 // Use release-20110511 to convert older maps to format 5 // Use release-20141029 to convert older maps to format 6 if (MapFormat < 6) { throw new InvalidDataException("Map format {0} is not supported.\n File: {1}".F(MapFormat, path)); } var nd = yaml.ToDictionary(); // Format 6 -> 7 combined the Selectable and UseAsShellmap flags into the Class enum if (MapFormat < 7) { MiniYaml useAsShellmap; if (nd.TryGetValue("UseAsShellmap", out useAsShellmap) && bool.Parse(useAsShellmap.Value)) { Visibility = MapVisibility.Shellmap; } else if (Type == "Mission" || Type == "Campaign") { Visibility = MapVisibility.MissionSelector; } } // Load players foreach (var my in nd["Players"].ToDictionary().Values) { var player = new PlayerReference(my); Players.Add(player.Name, player); } Actors = Exts.Lazy(() => { var ret = new Dictionary <string, ActorReference>(); foreach (var kv in nd["Actors"].ToDictionary()) { ret.Add(kv.Key, new ActorReference(kv.Value.Value, kv.Value.ToDictionary())); } return(ret); }); // Smudges Smudges = Exts.Lazy(() => { var ret = new List <SmudgeReference>(); foreach (var name in nd["Smudges"].ToDictionary().Keys) { var vals = name.Split(' '); var loc = vals[1].Split(','); ret.Add(new SmudgeReference(vals[0], new int2( Exts.ParseIntegerInvariant(loc[0]), Exts.ParseIntegerInvariant(loc[1])), Exts.ParseIntegerInvariant(vals[2]))); } return(ret); }); RuleDefinitions = MiniYaml.NodesOrEmpty(yaml, "Rules"); SequenceDefinitions = MiniYaml.NodesOrEmpty(yaml, "Sequences"); VoxelSequenceDefinitions = MiniYaml.NodesOrEmpty(yaml, "VoxelSequences"); WeaponDefinitions = MiniYaml.NodesOrEmpty(yaml, "Weapons"); VoiceDefinitions = MiniYaml.NodesOrEmpty(yaml, "Voices"); NotificationDefinitions = MiniYaml.NodesOrEmpty(yaml, "Notifications"); TranslationDefinitions = MiniYaml.NodesOrEmpty(yaml, "Translations"); MapTiles = Exts.Lazy(() => LoadMapTiles()); MapResources = Exts.Lazy(() => LoadResourceTiles()); MapHeight = Exts.Lazy(() => LoadMapHeight()); TileShape = Game.ModData.Manifest.TileShape; SubCellOffsets = Game.ModData.Manifest.SubCellOffsets; LastSubCell = (SubCell)(SubCellOffsets.Length - 1); DefaultSubCell = (SubCell)Game.ModData.Manifest.SubCellDefaultIndex; if (Container.Exists("map.png")) { using (var dataStream = Container.GetContent("map.png")) CustomPreview = new Bitmap(dataStream); } PostInit(); // The Uid is calculated from the data on-disk, so // format changes must be flushed to disk. // TODO: this isn't very nice if (MapFormat < 7) { Save(path); } Uid = ComputeHash(); }
public SubCell GetValidSubCell(SubCell preferred) { return(SubCell.Invalid); }
public SubCell GetAvailableSubCell(CPos a, SubCell preferredSubCell = SubCell.Any, Actor ignoreActor = null, bool checkTransientActors = true) { // Does not use any subcell return(SubCell.Invalid); }
public IReadOnlyDictionary <CPos, SubCell> OccupiedCells(ActorInfo info, CPos location, SubCell subCell = SubCell.Any) { var occupied = new Dictionary <CPos, SubCell>() { { location, SubCell.FullCell } }; return(new ReadOnlyDictionary <CPos, SubCell>(occupied)); }
public Activity MoveIntoWorld(Actor self, CPos cell, SubCell subCell = SubCell.Any) { return(null); }
public bool IsLeavingCell(CPos location, SubCell subCell = SubCell.Any) { return(false); }
public IReadOnlyDictionary <CPos, SubCell> OccupiedCells(ActorInfo info, CPos topLeft, SubCell subCell = SubCell.Any) { var occupied = UnpathableTiles(topLeft) .ToDictionary(c => c, c => SubCell.FullCell); return(new ReadOnlyDictionary <CPos, SubCell>(occupied)); }
public IReadOnlyDictionary <CPos, SubCell> OccupiedCells(ActorInfo info, CPos location, SubCell subCell = SubCell.Any) { return(new Dictionary <CPos, SubCell>()); }
public Activity MoveIntoWorld(Actor self, CPos cell, SubCell subCell = SubCell.Any) { return(new HeliFly(self, Target.FromCell(self.World, cell, subCell))); }
// Support upgrading format 5 maps to a more // recent version by defining upgradeForMod. public Map(string path, string upgradeForMod) { Path = path; Container = GlobalFileSystem.OpenPackage(path, null, int.MaxValue); AssertExists("map.yaml"); AssertExists("map.bin"); var yaml = new MiniYaml(null, MiniYaml.FromStream(Container.GetContent("map.yaml"))); FieldLoader.Load(this, yaml); // Support for formats 1-3 dropped 2011-02-11. // Use release-20110207 to convert older maps to format 4 // Use release-20110511 to convert older maps to format 5 if (MapFormat < 5) throw new InvalidDataException("Map format {0} is not supported.\n File: {1}".F(MapFormat, path)); // Format 5 -> 6 enforces the use of RequiresMod if (MapFormat == 5) { if (upgradeForMod == null) throw new InvalidDataException("Map format {0} is not supported, but can be upgraded.\n File: {1}".F(MapFormat, path)); Console.WriteLine("Upgrading {0} from Format 5 to Format 6", path); // TODO: This isn't very nice, but there is no other consistent way // of finding the mod early during the engine initialization. RequiresMod = upgradeForMod; } var nd = yaml.ToDictionary(); // Load players foreach (var my in nd["Players"].ToDictionary().Values) { var player = new PlayerReference(my); Players.Add(player.Name, player); } Actors = Exts.Lazy(() => { var ret = new Dictionary<string, ActorReference>(); foreach (var kv in nd["Actors"].ToDictionary()) ret.Add(kv.Key, new ActorReference(kv.Value.Value, kv.Value.ToDictionary())); return ret; }); // Smudges Smudges = Exts.Lazy(() => { var ret = new List<SmudgeReference>(); foreach (var name in nd["Smudges"].ToDictionary().Keys) { var vals = name.Split(' '); var loc = vals[1].Split(','); ret.Add(new SmudgeReference(vals[0], new int2( Exts.ParseIntegerInvariant(loc[0]), Exts.ParseIntegerInvariant(loc[1])), Exts.ParseIntegerInvariant(vals[2]))); } return ret; }); RuleDefinitions = MiniYaml.NodesOrEmpty(yaml, "Rules"); SequenceDefinitions = MiniYaml.NodesOrEmpty(yaml, "Sequences"); VoxelSequenceDefinitions = MiniYaml.NodesOrEmpty(yaml, "VoxelSequences"); WeaponDefinitions = MiniYaml.NodesOrEmpty(yaml, "Weapons"); VoiceDefinitions = MiniYaml.NodesOrEmpty(yaml, "Voices"); NotificationDefinitions = MiniYaml.NodesOrEmpty(yaml, "Notifications"); TranslationDefinitions = MiniYaml.NodesOrEmpty(yaml, "Translations"); MapTiles = Exts.Lazy(() => LoadMapTiles()); MapResources = Exts.Lazy(() => LoadResourceTiles()); TileShape = Game.modData.Manifest.TileShape; SubCellOffsets = Game.modData.Manifest.SubCellOffsets; LastSubCell = (SubCell)(SubCellOffsets.Length - 1); DefaultSubCell = (SubCell)Game.modData.Manifest.SubCellDefaultIndex; // The Uid is calculated from the data on-disk, so // format changes must be flushed to disk. // TODO: this isn't very nice if (MapFormat < 6) Save(path); Uid = ComputeHash(); if (Container.Exists("map.png")) using (var dataStream = Container.GetContent("map.png")) CustomPreview = new Bitmap(dataStream); PostInit(); }
public bool CanEnterCell(World world, Actor self, CPos cell, SubCell subCell = SubCell.FullCell, Actor ignoreActor = null, BlockedByActor check = BlockedByActor.All) { // SBMs may not land. return(false); }
public SubCell GetAvailableSubCell(CPos a, SubCell preferredSubCell = SubCell.Any, Actor ignoreActor = null, BlockedByActor check = BlockedByActor.All) { // Does not use any subcell return(SubCell.Invalid); }
// The standard constructor for most purposes public Map(string path) { Path = path; Container = GlobalFileSystem.OpenPackage(path, null, int.MaxValue); AssertExists("map.yaml"); AssertExists("map.bin"); var yaml = new MiniYaml(null, MiniYaml.FromStream(Container.GetContent("map.yaml"), path)); FieldLoader.Load(this, yaml); // Support for formats 1-3 dropped 2011-02-11. // Use release-20110207 to convert older maps to format 4 // Use release-20110511 to convert older maps to format 5 // Use release-20141029 to convert older maps to format 6 if (MapFormat < 6) throw new InvalidDataException("Map format {0} is not supported.\n File: {1}".F(MapFormat, path)); var nd = yaml.ToDictionary(); // Format 6 -> 7 combined the Selectable and UseAsShellmap flags into the Class enum if (MapFormat < 7) { MiniYaml useAsShellmap; if (nd.TryGetValue("UseAsShellmap", out useAsShellmap) && bool.Parse(useAsShellmap.Value)) Visibility = MapVisibility.Shellmap; else if (Type == "Mission" || Type == "Campaign") Visibility = MapVisibility.MissionSelector; } SpawnPoints = Exts.Lazy(() => { var spawns = new List<CPos>(); foreach (var kv in ActorDefinitions.Where(d => d.Value.Value == "mpspawn")) { var s = new ActorReference(kv.Value.Value, kv.Value.ToDictionary()); spawns.Add(s.InitDict.Get<LocationInit>().Value(null)); } return spawns.ToArray(); }); RuleDefinitions = MiniYaml.NodesOrEmpty(yaml, "Rules"); SequenceDefinitions = MiniYaml.NodesOrEmpty(yaml, "Sequences"); VoxelSequenceDefinitions = MiniYaml.NodesOrEmpty(yaml, "VoxelSequences"); WeaponDefinitions = MiniYaml.NodesOrEmpty(yaml, "Weapons"); VoiceDefinitions = MiniYaml.NodesOrEmpty(yaml, "Voices"); NotificationDefinitions = MiniYaml.NodesOrEmpty(yaml, "Notifications"); TranslationDefinitions = MiniYaml.NodesOrEmpty(yaml, "Translations"); PlayerDefinitions = MiniYaml.NodesOrEmpty(yaml, "Players"); ActorDefinitions = MiniYaml.NodesOrEmpty(yaml, "Actors"); SmudgeDefinitions = MiniYaml.NodesOrEmpty(yaml, "Smudges"); MapTiles = Exts.Lazy(LoadMapTiles); MapResources = Exts.Lazy(LoadResourceTiles); MapHeight = Exts.Lazy(LoadMapHeight); TileShape = Game.ModData.Manifest.TileShape; SubCellOffsets = Game.ModData.Manifest.SubCellOffsets; LastSubCell = (SubCell)(SubCellOffsets.Length - 1); DefaultSubCell = (SubCell)Game.ModData.Manifest.SubCellDefaultIndex; if (Container.Exists("map.png")) using (var dataStream = Container.GetContent("map.png")) CustomPreview = new Bitmap(dataStream); PostInit(); // The Uid is calculated from the data on-disk, so // format changes must be flushed to disk. // TODO: this isn't very nice if (MapFormat < 7) Save(path); Uid = ComputeHash(); }
public void SetLocation(int2 from, SubCell fromSub, int2 to, SubCell toSub) { if (fromCell == from && toCell == to) return; RemoveInfluence(); __fromCell = from; __toCell = to; fromSubCell = fromSub; toSubCell = toSub; AddInfluence(); }
public WVec OffsetOfSubCell(SubCell subCell) { if (subCell == SubCell.Invalid || subCell == SubCell.Any) return WVec.Zero; return SubCellOffsets[(int)subCell]; }
public MapGrid(MiniYaml yaml) { FieldLoader.Load(this, yaml); // The default subcell index defaults to the middle entry var defaultSubCellIndex = (byte)DefaultSubCell; if (defaultSubCellIndex == byte.MaxValue) DefaultSubCell = (SubCell)(SubCellOffsets.Length / 2); else if (defaultSubCellIndex < (SubCellOffsets.Length > 1 ? 1 : 0) || defaultSubCellIndex >= SubCellOffsets.Length) throw new InvalidDataException("Subcell default index must be a valid index into the offset triples and must be greater than 0 for mods with subcells"); var leftDelta = Type == MapGridType.RectangularIsometric ? new WVec(-512, 0, 0) : new WVec(-512, -512, 0); var topDelta = Type == MapGridType.RectangularIsometric ? new WVec(0, -512, 0) : new WVec(512, -512, 0); var rightDelta = Type == MapGridType.RectangularIsometric ? new WVec(512, 0, 0) : new WVec(512, 512, 0); var bottomDelta = Type == MapGridType.RectangularIsometric ? new WVec(0, 512, 0) : new WVec(-512, 512, 0); CellCorners = cellCornerHalfHeights.Select(ramp => new WVec[] { leftDelta + new WVec(0, 0, 512 * ramp[0]), topDelta + new WVec(0, 0, 512 * ramp[1]), rightDelta + new WVec(0, 0, 512 * ramp[2]), bottomDelta + new WVec(0, 0, 512 * ramp[3]) }).ToArray(); TilesByDistance = CreateTilesByDistance(); }
public IrregularCell GetSubRect(SubCell sub) { switch (sub) { case SubCell.LeftBottom: return new IrregularCell(LeftBottom, BottomSide, Center.ToVector(), LeftSide); case SubCell.LeftTop: return new IrregularCell(LeftSide, Center.ToVector(), TopSide, LeftTop); case SubCell.RightBottom: return new IrregularCell(BottomSide, RightBottom, RightSide, Center.ToVector()); case SubCell.RightTop: default: return new IrregularCell(Center.ToVector(), RightSide, RightTop, TopSide); } }
void ITickRender.TickRender(WorldRenderer wr, Actor self) { if (wr.World.Type != WorldType.Editor) { return; } if (Type == EditorCursorType.TerrainTemplate || Type == EditorCursorType.Resource) { var cell = wr.Viewport.ViewToWorld(Viewport.LastMousePos); if (terrainOrResourceCell != cell || terrainOrResourceDirty) { terrainOrResourceCell = cell; terrainOrResourceDirty = false; terrainOrResourcePreview.Clear(); var pos = world.Map.CenterOfCell(cell); if (Type == EditorCursorType.TerrainTemplate) { var i = 0; for (var y = 0; y < TerrainTemplate.Size.Y; y++) { for (var x = 0; x < TerrainTemplate.Size.X; x++) { var tile = new TerrainTile(TerrainTemplate.Id, (byte)i++); var tileInfo = world.Map.Rules.TileSet.GetTileInfo(tile); // Empty tile if (tileInfo == null) { continue; } var sprite = wr.Theater.TileSprite(tile, 0); var offset = world.Map.Offset(new CVec(x, y), tileInfo.Height); var palette = wr.Palette(TerrainTemplate.Palette ?? TileSet.TerrainPaletteInternalName); terrainOrResourcePreview.Add(new SpriteRenderable(sprite, pos, offset, 0, palette, 1, false)); } } } else { var variant = Resource.Sequences.FirstOrDefault(); var sequence = wr.World.Map.Rules.Sequences.GetSequence("resources", variant); var sprite = sequence.GetSprite(Resource.MaxDensity - 1); var palette = wr.Palette(Resource.Palette); terrainOrResourcePreview.Add(new SpriteRenderable(sprite, pos, WVec.Zero, 0, palette, 1, false)); } } } else if (Type == EditorCursorType.Actor) { // Offset mouse position by the center offset (in world pixels) var worldPx = wr.Viewport.ViewToWorldPx(Viewport.LastMousePos) - wr.ScreenPxOffset(actorCenterOffset); var cell = wr.Viewport.ViewToWorld(wr.Viewport.WorldToViewPx(worldPx)); var subCell = actorSharesCell ? editorLayer.FreeSubCellAt(cell) : SubCell.Invalid; var updated = false; if (actorLocation != cell) { actorLocation = cell; Actor.ReplaceInit(new LocationInit(cell)); updated = true; } if (actorSubCell != subCell) { actorSubCell = subCell; if (Actor.RemoveInits <SubCellInit>() > 0) { updated = true; } var subcell = world.Map.Tiles.Contains(cell) ? editorLayer.FreeSubCellAt(cell) : SubCell.Invalid; if (subcell != SubCell.Invalid) { Actor.AddInit(new SubCellInit(subcell)); updated = true; } } if (updated) { Actor = new EditorActorPreview(wr, null, Actor.Export(), Actor.Owner); } } }
public MapGrid(MiniYaml yaml) { FieldLoader.Load(this, yaml); // The default subcell index defaults to the middle entry var defaultSubCellIndex = (byte)DefaultSubCell; if (defaultSubCellIndex == byte.MaxValue) { DefaultSubCell = (SubCell)(SubCellOffsets.Length / 2); } else { var minSubCellOffset = SubCellOffsets.Length > 1 ? 1 : 0; if (defaultSubCellIndex < minSubCellOffset || defaultSubCellIndex >= SubCellOffsets.Length) { throw new InvalidDataException("Subcell default index must be a valid index into the offset triples and must be greater than 0 for mods with subcells"); } } // Rotation axes and amounts for the different slope types var southEast = new WVec(724, 724, 0); var southWest = new WVec(-724, 724, 0); var south = new WVec(0, 1024, 0); var east = new WVec(1024, 0, 0); var forward = new WAngle(64); var backward = -forward; var halfForward = new WAngle(48); var halfBackward = -halfForward; // Slope types are hardcoded following the convention from the TS and RA2 map format Ramps = new[] { // Flat new CellRamp(Type, WRot.None), // Two adjacent corners raised by half a cell new CellRamp(Type, new WRot(southEast, backward), tr: RampCornerHeight.Half, br: RampCornerHeight.Half), new CellRamp(Type, new WRot(southWest, backward), br: RampCornerHeight.Half, bl: RampCornerHeight.Half), new CellRamp(Type, new WRot(southEast, forward), tl: RampCornerHeight.Half, bl: RampCornerHeight.Half), new CellRamp(Type, new WRot(southWest, forward), tl: RampCornerHeight.Half, tr: RampCornerHeight.Half), // One corner raised by half a cell new CellRamp(Type, new WRot(south, halfBackward), br: RampCornerHeight.Half, split: RampSplit.X), new CellRamp(Type, new WRot(east, halfForward), bl: RampCornerHeight.Half, split: RampSplit.Y), new CellRamp(Type, new WRot(south, halfForward), tl: RampCornerHeight.Half, split: RampSplit.X), new CellRamp(Type, new WRot(east, halfBackward), tr: RampCornerHeight.Half, split: RampSplit.Y), // Three corners raised by half a cell new CellRamp(Type, new WRot(south, halfBackward), tr: RampCornerHeight.Half, br: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.X), new CellRamp(Type, new WRot(east, halfForward), tl: RampCornerHeight.Half, br: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.Y), new CellRamp(Type, new WRot(south, halfForward), tl: RampCornerHeight.Half, tr: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.X), new CellRamp(Type, new WRot(east, halfBackward), tl: RampCornerHeight.Half, tr: RampCornerHeight.Half, br: RampCornerHeight.Half, split: RampSplit.Y), // Full tile sloped (mid corners raised by half cell, far corner by full cell) new CellRamp(Type, new WRot(south, backward), tr: RampCornerHeight.Half, br: RampCornerHeight.Full, bl: RampCornerHeight.Half), new CellRamp(Type, new WRot(east, forward), tl: RampCornerHeight.Half, br: RampCornerHeight.Half, bl: RampCornerHeight.Full), new CellRamp(Type, new WRot(south, forward), tl: RampCornerHeight.Full, tr: RampCornerHeight.Half, bl: RampCornerHeight.Half), new CellRamp(Type, new WRot(east, backward), tl: RampCornerHeight.Half, tr: RampCornerHeight.Full, br: RampCornerHeight.Half), // Two opposite corners raised by half a cell new CellRamp(Type, WRot.None, tr: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.Y), new CellRamp(Type, WRot.None, tl: RampCornerHeight.Half, br: RampCornerHeight.Half, split: RampSplit.Y), new CellRamp(Type, WRot.None, tr: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.X), new CellRamp(Type, WRot.None, tl: RampCornerHeight.Half, br: RampCornerHeight.Half, split: RampSplit.X), }; TilesByDistance = CreateTilesByDistance(); }
public WPos CenterOfSubCell(CPos cell, SubCell subCell) { var index = (int)subCell; if (index >= 0 && index <= SubCellOffsets.Length) return CenterOfCell(cell) + SubCellOffsets[index]; return CenterOfCell(cell); }
public IReadOnlyDictionary <CPos, SubCell> OccupiedCells(ActorInfo info, CPos topLeft, SubCell subCell = SubCell.Any) { return(OccupiedTiles(topLeft) .ToDictionary(c => c, c => SubCell.FullCell)); }
public ValuesInCell GetSubCell(SubCell subCell) { switch (subCell) { case SubCell.LeftBottom: return LeftBottomCell; case SubCell.LeftTop: return LeftTopCell; case SubCell.RightBottom: return RightBottomCell; case SubCell.RightTop: default: return RightTopCell; } }
public SubCell GetAvailableSubCell(CPos a, SubCell preferredSubCell = SubCell.Any, Actor ignoreActor = null, bool checkTransientActors = true) { return(Info.GetAvailableSubCell(self.World, self, a, preferredSubCell, ignoreActor, checkTransientActors ? CellConditions.All : CellConditions.None)); }
// NOTE: can not check aircraft public bool AnyUnitsAt(CPos a, SubCell sub, bool checkTransient = true) { if (!map.Contains(a)) return false; var always = sub == SubCell.FullCell || sub == SubCell.Any; for (var i = influence[a]; i != null; i = i.Next) { if (always || i.SubCell == sub || i.SubCell == SubCell.FullCell) { if (checkTransient) return true; var pos = i.Actor.TraitOrDefault<IPositionable>(); if (pos == null || !pos.IsLeavingCell(a, i.SubCell)) return true; } } return false; }
/// <summary> /// /// </summary> /// <param name="location"></param> /// <param name="subCell"></param> /// <returns></returns> public bool IsLeavingCell(CPos location, SubCell subCell = SubCell.Any) { return(ToCell != location && fromCell == location && (subCell == SubCell.Any || FromSubCell == subCell || subCell == SubCell.FullCell || FromSubCell == SubCell.FullCell)); }
public SubCell FreeSubCell(CPos a, SubCell preferredSubCell = SubCell.Any, bool checkTransient = true) { if (preferredSubCell > SubCell.Any && !AnyUnitsAt(a, preferredSubCell, checkTransient)) return preferredSubCell; if (!AnyUnitsAt(a)) return map.DefaultSubCell; for (var i = (int)SubCell.First; i < map.SubCellOffsets.Length; i++) if (i != (int)preferredSubCell && !AnyUnitsAt(a, (SubCell)i, checkTransient)) return (SubCell)i; return SubCell.Invalid; }
// Sets only the location (Location) void SetLocation(Actor self, CPos cell, SubCell subCell = SubCell.Any) { self.World.ActorMap.RemoveInfluence(self, this); Location = cell; self.World.ActorMap.AddInfluence(self, this); }
private static SubCell GetAdjacentEdge(SubCell sub, Edge edge) { SubCell res = SubCell.LeftBottom; switch (sub) { case SubCell.LeftBottom: res = edge == Edge.Top ? SubCell.LeftTop : SubCell.RightBottom; break; case SubCell.LeftTop: res = edge == Edge.Bottom ? SubCell.LeftBottom : SubCell.RightTop; break; case SubCell.RightBottom: res = edge == Edge.Top ? SubCell.RightTop : SubCell.LeftBottom; break; case SubCell.RightTop: default: res = edge == Edge.Bottom ? SubCell.RightBottom : SubCell.LeftTop; break; } return res; }
public bool IsLeavingCell(CPos location, SubCell subCell = SubCell.Any) { return(self.Location == location && ticks + 1 == info.Lifetime * 25); }