public void TurnCalculatorReturnsTurnsFairly() { var player = new Creature(7, "Player"); var monster = new Creature(5, "Monster"); // Manually worked this out var expected = new Creature[] { player, monster, player, monster, player, player, monster }; var actual = new List <Creature>(); var turnCalculator = new TurnCalculator(new Creature[] { player, monster }); while (actual.Count < expected.Length) { actual.Add(turnCalculator.NextTurn() as Creature); } for (int i = 0; i < expected.Length; i++) { var e = expected[i]; var a = actual[i]; Assert.That(a, Is.EqualTo(e), $"On turn {i + 1}, expected {e.Name} but got {a.Name}"); } }
private void MovePlayerBy(Point amount, KeyboardInfo info = null) { var stopwatch = new System.Diagnostics.Stopwatch(); stopwatch.Start(); var currentFieldOfView = new RogueSharp.FieldOfView(this.currentMap.GetIMap()); var playerView = this.objects.Single(g => g.Name == "Player"); var fovTiles = currentFieldOfView.ComputeFov(playerView.Position.X, playerView.Position.Y, Config.Instance.Get <int>("PlayerLightRadius"), true); this.MarkCurrentFovAsDiscovered(fovTiles); foreach (var obj in this.objects) { obj.IsVisible = false; } // Undo monster vision tile effect foreach (var monsterView in this.objects.Where(o => o.Data is Monster)) { var data = monsterView.Data as Monster; var monsterFov = currentFieldOfView.ComputeFov(data.X, data.Y, data.VisionSize, true); // If we can see them, they're discovered foreach (var cell in monsterFov) { if (this.discoveredTiles.ContainsKey($"{cell.X}, {cell.Y}")) { this[cell.X, cell.Y].ApplyEffect(DiscoveredEffect); } else { this[cell.X, cell.Y].ApplyEffect(HiddenEffect); } } } // Get the position the player will be at Point newPosition = playerView.Position + amount; // Is there a block there? if (currentMap.GetObjectsAt(newPosition.X, newPosition.Y).Any(e => e is Pushable)) { // Is there an empty space behind it? var behindBlock = newPosition + amount; if (currentMap.IsWalkable(behindBlock.X, behindBlock.Y)) { // Push it (update data) var obj = currentMap.GetObjectsAt(newPosition.X, newPosition.Y).Single(e => e is Pushable); obj.Move(behindBlock.X, behindBlock.Y); // Push it (move view object) this.objects.Single(g => g.Data == obj).Move(behindBlock.X, behindBlock.Y); this.CheckIfBlockPuzzleIsComplete(); } } // Check to see if the position is within the map and walkable if (new Rectangle(0, 0, Width, Height).Contains(newPosition) && currentMap.IsWalkable(newPosition.X, newPosition.Y)) { // Pull a block along if specified if (info.KeysDown.Contains(AsciiKey.Get(Keys.Space))) { // Are you holding space? Did you move in a direction (eg. left) and there's a push block on the other side (eg. right)? // If so, and if the target block position is walkable, pull it along. But this only works if you're pulling // in the direction you're moving (eg. standing on top and pulling a block upward), NOT standing beside a block // and pulling it upward or standing above a block and pulling to the left/right var currentBlockPos = playerView.Position + new Point(amount.X * -1, amount.Y * -1); if (currentMap.GetObjectsAt(currentBlockPos.X, currentBlockPos.Y).Any(e => e is Pullable)) { // Given constraints above, target position is current player position var obj = currentMap.GetObjectsAt(currentBlockPos.X, currentBlockPos.Y).Single(e => e is Pullable); obj.Move(playerView.Position.X, playerView.Position.Y); this.objects.Single(g => g.Data == obj).Move(playerView.Position.X, playerView.Position.Y); this.CheckIfBlockPuzzleIsComplete(); } } // Move the player playerView.Position += amount; playerView.Data.Move(playerView.Position.X, playerView.Position.Y); CenterViewToPlayer(); } var key = this.currentMap.GetObjectsAt(playerView.Position.X, playerView.Position.Y).Where(s => s is Key).SingleOrDefault(); if (key != null) { // Not sure why two keys are spawned here var keys = this.objects.Where(s => s.Data == key).ToList(); foreach (var k in keys) { this.objects.Remove(k); this.currentMap.Remove(key); } (this.objects.Single(o => o.Data is Player).Data as Player).Keys += 1; this.showMessageCallback("Got a key."); } // Door there, and we have a key? Unlock it! if ((playerView.Data as Player).Keys > 0 && this.currentMap.GetObjectsAt(newPosition.X, newPosition.Y).Any(o => o is LockedDoor)) { var doorData = this.currentMap.GetObjectsAt(newPosition.X, newPosition.Y).Where(o => o is LockedDoor).ToList(); var doors = this.objects.Where(o => doorData.Contains(o.Data)).ToList(); // Why, why WHY are there multiple copies? foreach (var door in doors) { this.objects.Remove(door); this.currentMap.Remove(door.Data); } showMessageCallback("You unlock the door."); (playerView.Data as Player).Keys -= 1; } fovTiles = currentFieldOfView.ComputeFov(playerView.Position.X, playerView.Position.Y, Config.Instance.Get <int>("PlayerLightRadius"), true); this.MarkCurrentFovAsVisible(fovTiles); // Monsters turn. Also, draw their field-of-view. Keep queuing turns until it's the player's turn again. var monstersToMove = new List <IHasAgility>(); IHasAgility next = null; while (next != playerView.Data) { if (next != null) { monstersToMove.Add(next); } next = turnCalculator.NextTurn(); } while (monstersToMove.Any()) { var m = monstersToMove[0]; monstersToMove.RemoveAt(0); var monsterView = this.objects.Single(o => o.Data is Monster && o.Data == m); var data = monsterView.Data as Monster; var hurtPlayer = data.MoveWithAi(playerView.Data as Player); if (hurtPlayer) { if ((playerView.Data as Player).IsDead) { showMessageCallback("The monster hits you! You die ..."); this.gameOver = true; } else { showMessageCallback("Monster hits you!"); } } else { monsterView.Position = new Point(data.X, data.Y); bool sawPlayer = false; var monsterFov = currentFieldOfView.ComputeFov(data.X, data.Y, data.VisionSize, true); foreach (var cell in monsterFov) { if (fovTiles.Any(f => f.X == data.X && f.Y == data.Y)) { this[cell.X, cell.Y].ApplyEffect(MonsterVisionEffect); if (playerView.Position.X == cell.X && playerView.Position.Y == cell.Y) { data.HuntPlayer(); monsterView.RenderCells.First().ActualForeground = new Color(255, 0, 0); sawPlayer = true; } } else { if (this.discoveredTiles.ContainsKey($"{data.X}, {data.Y}")) { this[cell.X, cell.Y].ApplyEffect(DiscoveredEffect); } else { this[cell.X, cell.Y].ApplyEffect(HiddenEffect); } } } if (!sawPlayer && monsterView.RenderCells.First().ActualForeground.R == 255) { monsterView.RenderCells.First().ActualForeground = new Color(data.Colour.R, data.Colour.G, data.Colour.B); } } } stopwatch.Stop(); var elapsed = stopwatch.Elapsed.TotalSeconds; Console.WriteLine($"Moving took {elapsed}s"); }