public static List <EncounterAction> ActionsForUnitReform(EncounterState state, Entity parent, int formationNumber, Unit unit) { var actions = new List <EncounterAction>(); var parentPos = parent.GetComponent <PositionComponent>().EncounterPosition; var playerPos = state.Player.GetComponent <PositionComponent>().EncounterPosition; var targetPosition = AIUtils._DecideFormationPosition(formationNumber, playerPos, unit); if (parentPos != targetPosition) { // TODO: We may want to make pathfinding stateful/cache it or something, to save on turn times var path = Pathfinder.AStarWithNewGrid(parentPos, targetPosition, state); if (path != null) { actions.Add(new MoveAction(parent.EntityId, path[0])); } else { actions.Add(new WaitAction(parent.EntityId)); } } else { actions.Add(new WaitAction(parent.EntityId)); } return(actions); }
public override List <EncounterAction> _DecideNextAction(EncounterState state, Entity parent) { var actions = new List <EncounterAction>(); var parentPos = parent.GetComponent <PositionComponent>().EncounterPosition; var playerPos = state.Player.GetComponent <PositionComponent>().EncounterPosition; // Close distance if further than 5 if (parentPos.DistanceTo(playerPos) >= 5f) { // PERF: We may want to make pathfinding stateful/cache it or something, to save on turn times var path = Pathfinder.AStarWithNewGrid(parentPos, playerPos, state); if (path != null) { actions.Add(new MoveAction(parent.EntityId, path[0])); } } // Fire shotgun pellets=5, spread=5 every 4th turn, else fire single cannon (this is honestly a comical amount of spread though) if (this.CurrentShotgunCooldown == 0) { actions.AddRange(FireProjectileAction.CreateSmallShotgunAction(parent.EntityId, playerPos, numPellets: 5, spread: 5, state.EncounterRand)); this.CurrentShotgunCooldown += this.ShotgunCooldown; } else { actions.Add(FireProjectileAction.CreateSmallCannonAction(parent.EntityId, playerPos)); if (this.CurrentShotgunCooldown > 0) { this.CurrentShotgunCooldown -= 1; } } return(actions); }
private static bool ResolveAutopilotBeginTravel(AutopilotBeginAction action, EncounterState state) { var playerPosition = state.Player.GetComponent <PositionComponent>().EncounterPosition; EncounterZone zone = state.GetZoneById(action.ZoneId); var foundPath = Pathfinder.AStarWithNewGrid(playerPosition, zone.Center, state, 9000); int autopilotTries = 0; while (foundPath == null && autopilotTries < 5) { foundPath = Pathfinder.AStarWithNewGrid(playerPosition, zone.RandomEmptyPosition(state.EncounterRand, state), state, 9000); autopilotTries++; } if (foundPath != null && autopilotTries > 0) { state.LogMessage(String.Format("Autopilot could not find path to center of [b]{0}[/b]; autopiloting to randomly chosen position in [b]{0}[/b].", zone.ZoneName)); state.Player.GetComponent <PlayerComponent>().LayInAutopilotPathForTravel(new EncounterPath(foundPath)); return(true); } else if (foundPath != null) { state.LogMessage(String.Format("Autopiloting to [b]{0}[/b]", zone.ZoneName)); state.Player.GetComponent <PlayerComponent>().LayInAutopilotPathForTravel(new EncounterPath(foundPath)); return(true); } else { state.LogMessage(String.Format("Autopilot failed to plot course to to [b]{0}[/b]", zone.ZoneName), failed: true); return(false); } }
public override List <EncounterAction> _DecideNextAction(EncounterState state, Entity parent) { var actions = new List <EncounterAction>(); var parentPos = parent.GetComponent <PositionComponent>().EncounterPosition; var playerPos = state.Player.GetComponent <PositionComponent>().EncounterPosition; // Always attempt to close distance var path = Pathfinder.AStarWithNewGrid(parentPos, playerPos, state); if (path != null) { actions.Add(new MoveAction(parent.EntityId, path[0])); } // Fire reverser + cannon every 3rd turn, and shotgun + secondary batteries otherwise if (this.CurrentReverserCooldown == 0) { actions.Add(FireProjectileAction.CreateReverserAction(parent.EntityId, playerPos)); actions.Add(FireProjectileAction.CreateSmallCannonAction(parent.EntityId, playerPos)); this.CurrentReverserCooldown += ReverserCooldown; } else { actions.Add(FireProjectileAction.CreateSmallCannonAction(parent.EntityId, playerPos)); actions.Add(FireProjectileAction.CreateSmallCannonAction(parent.EntityId, playerPos)); actions.AddRange(FireProjectileAction.CreateSmallShotgunAction(parent.EntityId, playerPos, numPellets: 2, spread: 3, state.EncounterRand)); if (this.CurrentReverserCooldown > 0) { this.CurrentReverserCooldown -= 1; } } return(actions); }
public override List <EncounterAction> _DecideNextAction(EncounterState state, Entity parent) { var actions = new List <EncounterAction>(); var parentPos = parent.GetComponent <PositionComponent>().EncounterPosition; var playerPos = state.Player.GetComponent <PositionComponent>().EncounterPosition; // Always attempt to close distance var path = Pathfinder.AStarWithNewGrid(parentPos, playerPos, state); if (path != null) { actions.Add(new MoveAction(parent.EntityId, path[0])); } // Fire comically dense & inaccurate shotgun volley every 4 turns, and shotgun otherwise if (this.CurrentVolleyCooldown == 0) { actions.AddRange(FireProjectileAction.CreateSmallShotgunAction(parent.EntityId, playerPos, numPellets: 30, spread: 7, state.EncounterRand)); actions.Add(FireProjectileAction.CreateSmallCannonAction(parent.EntityId, playerPos)); this.CurrentVolleyCooldown += VolleyCooldown; } else { actions.AddRange(FireProjectileAction.CreateSmallShotgunAction(parent.EntityId, playerPos, numPellets: 2, spread: 1, state.EncounterRand)); if (this.CurrentVolleyCooldown > 0) { this.CurrentVolleyCooldown -= 1; } } return(actions); }
public override List <EncounterAction> _DecideNextAction(EncounterState state, Entity parent) { var actions = new List <EncounterAction>(); var parentPos = parent.GetComponent <PositionComponent>().EncounterPosition; var playerPos = state.Player.GetComponent <PositionComponent>().EncounterPosition; var distanceToPlayer = parentPos.DistanceTo(playerPos); // Only attempt to close distance if >=7 squares away if (distanceToPlayer >= 7f) { var path = Pathfinder.AStarWithNewGrid(parentPos, playerPos, state); if (path != null) { actions.Add(new MoveAction(parent.EntityId, path[0])); } } // Always fire cannon actions.Add(FireProjectileAction.CreateSmallCannonAction(parent.EntityId, playerPos)); // Fire railgun on cooldown (every 3rd turn) if (this.CurrentRailgunCooldown == 0) { actions.Add(FireProjectileAction.CreateRailgunAction(parent.EntityId, playerPos)); this.CurrentRailgunCooldown += this.RailgunCooldown; } else if (this.CurrentRailgunCooldown > 0) { this.CurrentRailgunCooldown -= 1; } // If player is close and flak is off cooldown, fire if (distanceToPlayer <= 4 && this.CurrentFlakCooldown == 0) { actions.AddRange(FireProjectileAction.CreateSmallShotgunAction(parent.EntityId, playerPos, numPellets: 30, spread: 5, state.EncounterRand)); this.CurrentFlakCooldown = this.FlakCooldown; } else if (this.CurrentFlakCooldown > 0) { this.CurrentFlakCooldown -= 1; } return(actions); }
public override List <EncounterAction> _DecideNextAction(EncounterState state, Entity parent) { var actions = new List <EncounterAction>(); var parentPos = parent.GetComponent <PositionComponent>().EncounterPosition; var playerPos = state.Player.GetComponent <PositionComponent>().EncounterPosition; // Always close distance var path = Pathfinder.AStarWithNewGrid(parentPos, playerPos, state); if (path != null) { actions.Add(new MoveAction(parent.EntityId, path[0])); } // Always fire actions.Add(FireProjectileAction.CreateSmallGatlingAction(parent.EntityId, playerPos)); actions.Add(FireProjectileAction.CreateSmallGatlingAction(parent.EntityId, playerPos)); actions.Add(FireProjectileAction.CreateSmallGatlingAction(parent.EntityId, playerPos)); return(actions); }
public override List <EncounterAction> _DecideNextAction(EncounterState state, Entity parent) { var actions = new List <EncounterAction>(); var parentPos = parent.GetComponent <PositionComponent>().EncounterPosition; var playerPos = state.Player.GetComponent <PositionComponent>().EncounterPosition; // Carrier never moves // Always launch randomly chosen strike craft - immediately paths one square to get it out of the carrier's square var chosenEntityDef = this.LaunchTable[state.EncounterRand.Next(3)]; var strikeCraft = EntityBuilder.CreateEnemyByEntityDefId(chosenEntityDef, this.ActivationGroupId, state.CurrentTick); actions.Add(new SpawnEntityAction(parent.EntityId, strikeCraft, parentPos, true)); var path = Pathfinder.AStarWithNewGrid(parentPos, playerPos, state); // TODO: If you park adjacent the carrier, spawns will try to path literally into you and be stuck on the carrier square. // Won't crash anything it'll just put some error logs out and be deeply comical. if (path != null) { actions.Add(new MoveAction(strikeCraft.EntityId, path[0])); } // If player is close and flak is off cooldown, fire (comically this can, and will, friendly-fire its fighter wing) // TODO: friendly fire, maybe? It is a bit silly. if (parentPos.DistanceTo(playerPos) <= 4 && this.CurrentFlakCooldown == 0) { actions.AddRange(FireProjectileAction.CreateSmallShotgunAction(parent.EntityId, playerPos, numPellets: 30, spread: 5, state.EncounterRand)); this.CurrentFlakCooldown = this.FlakCooldown; } else if (this.CurrentFlakCooldown > 0) { this.CurrentFlakCooldown -= 1; } return(actions); }
public override List <EncounterAction> _DecideNextAction(EncounterState state, Entity parent) { var actions = new List <EncounterAction>(); var parentPos = parent.GetComponent <PositionComponent>().EncounterPosition; var playerPos = state.Player.GetComponent <PositionComponent>().EncounterPosition; // Close distance if further than 5 if (parentPos.DistanceTo(playerPos) >= 5f) { // TODO: We may want to make pathfinding stateful/cache it or something, to save on turn times var path = Pathfinder.AStarWithNewGrid(parentPos, playerPos, state); if (path != null) { actions.Add(new MoveAction(parent.EntityId, path[0])); } } // Always fire shotgun (spread=2, pellets=3) var fire = FireProjectileAction.CreateSmallShotgunAction(parent.EntityId, playerPos, numPellets: 3, spread: 2, state.EncounterRand); actions.AddRange(fire); return(actions); }
/** * A zone is considered "explored" when: * 1: All storables have been found * 1a: If your inventory is not full, all storable items have been picked up * 2: All non-storable features have been seen * * Actual % of FoW revealed is not important. This is kinda cheaty, because it implies the autopilot knows intel for the * area, but also, who cares? It's fine. Likewise it doesn't factor into account having destroyed the enemies because if * there is an encounter, you'll definitely run into it! */ private static bool ResolveAutopilotContinueExplore(EncounterState state) { var player = state.Player; var playerComponent = player.GetComponent <PlayerComponent>(); var playerPos = player.GetComponent <PositionComponent>().EncounterPosition; var path = playerComponent.AutopilotPath; var zone = state.GetZoneById(playerComponent.AutopilotZoneId); var nextUngottenStorable = zone.ReadoutItems.Concat(zone.ReadoutFeatures) .FirstOrDefault(i => state.GetEntityById(i.EntityId) != null && state.GetEntityById(i.EntityId).GetComponent <StorableComponent>() != null); // We rely on the fact that there's no ungettable features other than stairs, and that there's only one stair, to make this // work. If there were multiple ungettable features you'd want to put them into a list and autopilot between them so you // could cycle the 'x' button to find the one you want. var stairs = zone.ReadoutFeatures.FirstOrDefault(r => state.GetEntityById(r.EntityId).GetComponent <StairsComponent>() != null && state.GetEntityById(r.EntityId).GetComponent <PositionComponent>().EncounterPosition != playerPos ); if (PlayerSeesEnemies(state)) { ResolveAction(new AutopilotEndAction(player.EntityId, AutopilotEndReason.ENEMY_DETECTED), state); return(false); } // If you are already on a path, progress on the path else if (path != null) { if (path.AtEnd) { playerComponent.ClearAutopilotPath(); return(false); } else { Rulebook.ResolveAction(new MoveAction(player.EntityId, path.Step()), state); return(true); } } // If you're on top of a storable item, get it else if (state.EntitiesAtPosition(playerPos.X, playerPos.Y).Any(e => e.GetComponent <StorableComponent>() != null)) { var nextStorable = state.EntitiesAtPosition(playerPos.X, playerPos.Y).First(e => e.GetComponent <StorableComponent>() != null); if (ResolveAction(new GetItemAction(player.EntityId), state)) { return(true); } else { ResolveAction(new AutopilotEndAction(player.EntityId, AutopilotEndReason.INVENTORY_FULL), state); return(false); } } // If there are any storable items, move towards them else if (nextUngottenStorable != null) { var nextPos = state.GetEntityById(nextUngottenStorable.EntityId).GetComponent <PositionComponent>().EncounterPosition; var foundPath = Pathfinder.AStarWithNewGrid(playerPos, nextPos, state, 900); if (foundPath == null) { ResolveAction(new AutopilotEndAction(player.EntityId, AutopilotEndReason.NO_PATH), state); return(false); } else { foundPath.Add(nextPos); playerComponent.LayInAutopilotPathForExploration(new EncounterPath(foundPath)); return(true); } } // If this is the stairs zone, go to the stairs else if (stairs != null) { var nextPos = state.GetEntityById(stairs.EntityId).GetComponent <PositionComponent>().EncounterPosition; var foundPath = Pathfinder.AStarWithNewGrid(playerPos, nextPos, state, 900); if (foundPath == null) { ResolveAction(new AutopilotEndAction(player.EntityId, AutopilotEndReason.NO_PATH), state); return(false); } else { foundPath.Add(nextPos); playerComponent.LayInAutopilotPathForExploration(new EncounterPath(foundPath)); return(true); } } // Otherwise you're done! else { ResolveAction(new AutopilotEndAction(player.EntityId, AutopilotEndReason.TASK_COMPLETED), state); return(false); } }