private Move(TurnPlan owner, RelativeDirection direction, TileVector destination, int energyCost) { Owner = owner; Direction = direction; Destination = destination; EnergyCost = energyCost; }
/// <summary> /// Construct a Move which represents a unit staying completely stationary. /// </summary> /// <param name="owner">the TurnPlan which created this move</param> /// <returns></returns> public static Move Halt(TurnPlan owner) { return(new Move(owner, RelativeDirection.Forward, owner.Unit.Position, 0)); }
/// <summary> /// Construct a Move which represents a unit turning in place. /// </summary> /// <param name="owner">the TurnPlan which created this move</param> /// <param name="direction">the RelativeDirection for it to turn to</param> /// <returns></returns> public static Move Turn(TurnPlan owner, RelativeDirection direction) { var cost = direction == RelativeDirection.Forward ? 0 : 1; return(new Move(owner, direction, owner.Unit.Position, cost)); }
/// <summary> /// Constructe a Move which represents a turn followed by a single tile step. /// </summary> /// <param name="owner">the TurnPlan which created this move</param> /// <param name="direction">the RelativeDirection for it to turn and move in</param> /// <returns></returns> public static Move Step(TurnPlan owner, RelativeDirection direction) { var destination = owner.Unit.Position + owner.Unit.Turn(direction); return(new Move(owner, direction, destination, 2)); }
/// <summary> /// Simulate the game for a single turn. /// </summary> public void DoTurn() { var activeUnits = new Dictionary <Unit, TurnPlan>(); foreach (var unit in _units) { unit.Reset(); activeUnits.Add(unit, unit.GetMovementPlan(_world)); } #if DEBUG DebugLogFrame(); #endif while (activeUnits.Count > 0) // main loop: iterate through each simulation frame { // STEP 1: COMBAT DETERMINATION var pendingCombat = new HashSet <Combat>(); // stops duplication of the same combat var unitsInCombat = new HashSet <Unit>(); foreach (var unit in activeUnits.Keys) { foreach (var adj in unit.Position.Adjacent().Select(pos => _world[pos])) // for each adjacent Hex { if (adj == null) { continue; } var neighbour = adj.Occupant; if (neighbour != null && neighbour.Owner != unit.Owner) { pendingCombat.Add(new Combat(unit, neighbour)); unitsInCombat.Add(unit); unitsInCombat.Add(neighbour); } } } // STEP 2: COMBAT RESOLUTION var pendingCombatSorted = new List <Combat>(pendingCombat); pendingCombatSorted.Sort(Combat.PriorityComparer); // sort by priortiy foreach (var combat in pendingCombatSorted) { var unit1 = combat.Unit1; var unit2 = combat.Unit2; if (!unit1.IsDead() && !unit2.IsDead()) { combat.Apply(); FinaliseCombat(unit1, unit2, activeUnits); // lambda/local-function would be FinaliseCombat(unit2, unit1, activeUnits); // ideal here! outdated version of C# } } // STEP 3: MOVE PRE-PROCESSING var moveDestinations = new Dictionary <TileVector, List <MoveResolver> >(); // where units want to move to var moveOrigins = new Dictionary <TileVector, MoveResolver>(); // where units are moving from foreach (var unit in _units) // collect all units' moves into the Dictionaries, using plans { TurnPlan plan = null; if (activeUnits.ContainsKey(unit)) // only active units can make moves - bind a plan { plan = activeUnits[unit]; if (!plan.IsActive()) // unit can no longer make moves, so remove from active units { activeUnits.Remove(unit); plan = null; // unbind plan for this frame } } var canMove = plan != null && !unitsInCombat.Contains(plan.Unit); var move = canMove? plan.GetNextMove() : null; var mayVacate = canMove && move.IsStep(); // if move is a step, moves here are conditional upon it. otherwise, deny outright var resolver = mayVacate ? MoveResolver.Of(move) : MoveResolver.Deny(); moveOrigins.Add(unit.Position, resolver); if (mayVacate) // add resolver to the move's intended destination for processing later { var hex = _world[move.Destination]; if (hex != null && !hex.Impassable) // if target is valid { List <MoveResolver> movesToDestination; if (moveDestinations.ContainsKey(move.Destination)) { movesToDestination = moveDestinations[move.Destination]; } else // initialise the list if it doesn't exist yet { movesToDestination = new List <MoveResolver>(6); moveDestinations.Add(move.Destination, movesToDestination); } movesToDestination.Add(resolver); } else { resolver.Resolve(false); // reject if move is illegal } } } // STEP 4: MOVE CONFLICT RESOLUTION foreach (var entry in moveDestinations) // check all future mech positions, finalise moves { var destination = entry.Key; var moves = entry.Value; var best = 0; for (var i = 1; i < moves.Count; i++) // reject all moves here except the best one { if (Move.PriorityComparer.Compare(moves[i].Move, moves[best].Move) > 0) { moves[best].Resolve(false); best = i; } else // not the best move: reject { moves[i].Resolve(false); } } var dependency = moveOrigins.ContainsKey(destination) ? moveOrigins[destination] : null; if (dependency != null) { moves[best].SetParent(dependency); } else { moves[best].Resolve(true); } } #if DEBUG DebugLogFrame(); #endif } RoundNumber++; }
public TurnPlan GetMovementPlan(World world) { LastMove = _currentMove; _currentMove = _moveMethod(this, world); return(_currentMove); }