コード例 #1
0
ファイル: PlayerState.cs プロジェクト: epicvrvs/PanzerKontrol
 public void AttackUnit(Unit attacker, Unit defender)
 {
     if (!attacker.Deployed)
         throw new GameException("Tried to attack with an undeployed unit");
     if (!defender.Deployed)
         throw new GameException("Tried to attack an undeployed unit");
     if (!attacker.CanPerformAction)
         throw new GameException("This unit cannot perform any more actions this turn");
     Combat outcome;
     if (attacker.IsAirUnit())
     {
         List<Unit> antiAirUnits = Opponent.GetAntiAirUnits(defender);
         outcome = new Combat(attacker, defender, true, antiAirUnits);
     }
     else
     {
         int distance = attacker.Hex.GetDistance(defender.Hex);
         if (distance > attacker.Stats.Range)
             throw new GameException("The target is out of range");
         outcome = new Combat(attacker, defender, true);
     }
     attacker.CanPerformAction = false;
     // Attacking a unit breaks entrenchment
     attacker.BreakEntrenchment();
     attacker.Strength = outcome.AttackerStrength;
     defender.Strength = outcome.DefenderStrength;
     if (!attacker.IsAlive())
         OnUnitDeath(attacker);
     if (!defender.IsAlive())
         Opponent.OnUnitDeath(defender);
 }
コード例 #2
0
 public UnitCombatState(Unit unit, Combat combat)
 {
     Unit = unit;
     UnitStrength = unit.Strength;
     BaseDamage = null;
     CombatEfficiency = combat.GetCombatEfficiency();
     TotalDamageDealt = 0.0;
     DamageDivisor = 1;
 }
コード例 #3
0
ファイル: Map.cs プロジェクト: epicvrvs/PanzerKontrol
 // This calculates a map of positions a unit can move to (keys) and best paths discovered (values)
 public Dictionary<Position, Path> CreateMovementMap(Unit unit)
 {
     var map = new Dictionary<Position, Path>(new PositionComparer());
     Path path = new Path(unit.MovementPoints);
     CreateMovementMap(unit, unit.Hex, path, unit.Owner.Identifier, map);
     // Remove all the hexes occupied by friendly units since those were only permitted for passing through
     foreach (var position in map.Keys)
     {
         Hex hex = GetHex(position);
         if (hex.Unit != null)
             map.Remove(position);
     }
     return map;
 }
コード例 #4
0
ファイル: Combat.cs プロジェクト: epicvrvs/PanzerKontrol
        public Combat(Unit attacker, Unit defender, bool useRandomisedCombatEfficiency, List<Unit> antiAirUnits = null)
        {
            UseRandomisedCombatEfficiency = useRandomisedCombatEfficiency;
            Generator = new NormalDistribution(GameConstants.CombatEfficiencyMean, GameConstants.CombatEfficiencyDeviation);

            Attacker = new UnitCombatState(attacker, this);
            Defender = new UnitCombatState(defender, this);

            if (attacker.IsAirUnit())
            {
                AttackType = AttackType.AirAttack;
                AntiAirUnits = antiAirUnits.Select((Unit x) => new UnitCombatState(x, this)).ToList();
                foreach (var unit in AntiAirUnits)
                    unit.SetDamage(unit.Unit.Type.Stats.AirAttack.Value);
            }
            else if (attacker.IsArtillery())
                AttackType = AttackType.ArtilleryAttack;
            else
                AttackType = AttackType.GroundAttack;

            // Calculate the outcome right away
            Attack();
        }
コード例 #5
0
ファイル: Map.cs プロジェクト: epicvrvs/PanzerKontrol
 void CreateMovementMap(Unit unit, Hex currentHex, Path currentPath, PlayerIdentifier owner, Dictionary<Position, Path> map)
 {
     for(int i = 0; i < HexOffsets.Length; i++)
     {
         RiverEdge riverEdge = currentHex.RiverEdges[i];
         Position offset = HexOffsets[i];
         Position neighbourPosition = currentHex.Position + offset;
         Hex neighbourHex = GetHex(neighbourPosition);
         if (neighbourHex == null)
         {
             // This hex is not part of the map, skip it
             continue;
         }
         if (neighbourHex.Unit != null && neighbourHex.Unit.Owner.Identifier != owner)
         {
             // This hex is already occupied by an enemy unit, skip it
             continue;
         }
         int terrainMovementPoints = neighbourHex.GetTerrainMovementPoints();
         int movementPointsLost;
         if (riverEdge != null && !riverEdge.IsBridge)
         {
             // It's a move across a river without a bridge
             // This is only possible under special circumstances
             // The unit must have its full movement points and it will lose all of them after the crossing
             int maximumMovementPoints = unit.Stats.Movement.Value;
             if (currentPath.MovementPointsLeft < maximumMovementPoints)
             {
                 // The unit had already moved so it can't cross the river
                 continue;
             }
             if (currentPath.MovementPointsLeft < terrainMovementPoints)
             {
                 // This is an extraordinarily rare case but it means that the unit can't cross the river because it couldn't enter the target terrain type, even if the river wasn't there
                 continue;
             }
             movementPointsLost = maximumMovementPoints;
         }
         else
         {
             // It's either a regular move without a river or a move across a bridge
             movementPointsLost = terrainMovementPoints;
         }
         int newMovementPoints = currentPath.MovementPointsLeft - movementPointsLost;
         if (newMovementPoints < 0)
         {
             // The unit doesn't have enough movement points left to enter this hex
             continue;
         }
         Path previousPath;
         if (map.TryGetValue(neighbourPosition, out previousPath))
         {
             // This neighbouring hex was already analysed by a previous recursive call to this function, check if we can even improve on what it calculated
             if (previousPath.MovementPointsLeft <= newMovementPoints)
             {
                 // The solution is inferior or just as good, skip it
                 continue;
             }
         }
         // Create or update the entry in the movement map
         Path newPath = new Path(currentPath, neighbourHex, newMovementPoints);
         map[neighbourPosition] = newPath;
         CreateMovementMap(unit, neighbourHex, newPath, owner, map);
     }
 }
コード例 #6
0
ファイル: PlayerState.cs プロジェクト: epicvrvs/PanzerKontrol
 public void UpgradeUnit(Unit unit, UnitUpgrade unitUpgrade)
 {
     int points = unitUpgrade.Points;
     if (points > ReinforcementPoints)
         throw new GameException("Not enough reinforcement points left to purchase this upgrade");
     unit.AddUpgrade(unitUpgrade);
     _ReinforcementPoints -= points;
 }
コード例 #7
0
ファイル: PlayerState.cs プロジェクト: epicvrvs/PanzerKontrol
 public void ReinforceUnit(Unit unit)
 {
     double maximumReinforcements = unit.IsInfantry() ? GameConstants.InfantryReinforcementsMaximum : GameConstants.MotorisedReinforcementsMaximum;
     double newStrength = Math.Min(unit.Strength + maximumReinforcements, GameConstants.FullUnitStrength);
     if (newStrength == unit.Strength)
         throw new GameException("Reinforcing this unit has no effect");
     int expenses = (int)Math.Round((newStrength - unit.Strength) * unit.Points * GameConstants.ReinforcementCostMitigation);
     if (expenses > ReinforcementPoints)
         throw new GameException("You do not have enough reinforcement points to reinforce this unit");
     if (unit.AttritionDuration > 0)
         throw new GameException("Units that are out of supplies cannot be reinforced");
     if (unit.MovementPoints != unit.Stats.Movement)
         throw new GameException("A unit cannot be reinforced in the current turn once it has moved");
     if (!unit.CanPerformAction)
         throw new GameException("This unit cannot perform any more actions this turn");
     _ReinforcementPoints -= expenses;
     unit.Strength = newStrength;
     // Reinforcing a unit uses up all its movement points and prevents it from performing any more actions in the current turn
     unit.MovementPoints = 0;
     unit.CanPerformAction = false;
 }
コード例 #8
0
ファイル: PlayerState.cs プロジェクト: epicvrvs/PanzerKontrol
 public void OnUnitDeath(Unit unit)
 {
     Units.Remove(unit);
 }
コード例 #9
0
ファイル: PlayerState.cs プロジェクト: epicvrvs/PanzerKontrol
 public void InitialUnitDeployment(Unit unit, Position position)
 {
     if (unit.Deployed)
         throw new GameException("Tried to specify the position of a unit that has already been deployed");
     if (!Map.IsInInitialDeploymentZone(Identifier, position))
         throw new GameException("Tried to deploy units outside the player's deployment zone");
     Hex hex = Map.GetHex(position);
     if (hex.Unit != null)
         throw new GameException("Tried to deploy a unit on a hex that is already occupied");
     unit.MoveToHex(hex);
 }
コード例 #10
0
ファイル: PlayerState.cs プロジェクト: epicvrvs/PanzerKontrol
 public void MoveUnit(Unit unit, Position newPosition, out int movementPointsLeft, out List<Hex> captures)
 {
     if (!unit.Deployed)
         throw new GameException("Tried to move an undeployed unit");
     var movementMap = Map.CreateMovementMap(unit);
     Path pathUsed;
     if (!movementMap.TryGetValue(newPosition, out pathUsed))
         throw new GameException("The unit can't reach the specified hex");
     unit.MovementPoints = pathUsed.MovementPointsLeft;
     Hex hex = Map.GetHex(newPosition);
     unit.MoveToHex(hex);
     captures = CaptureHexes(pathUsed);
     movementPointsLeft = pathUsed.MovementPointsLeft;
 }
コード例 #11
0
ファイル: PlayerState.cs プロジェクト: epicvrvs/PanzerKontrol
 // Retrieve a list of anti-air units capable of protecting the target
 public List<Unit> GetAntiAirUnits(Unit target)
 {
     List<Unit> output = new List<Unit>();
     foreach (var unit in Units)
     {
         if (unit.Deployed && unit.IsAntiAirUnit() && unit.Hex.GetDistance(target.Hex) <= unit.Stats.AntiAirRange.Value)
             output.Add(unit);
     }
     return output;
 }
コード例 #12
0
ファイル: PlayerState.cs プロジェクト: epicvrvs/PanzerKontrol
 public void EntrenchUnit(Unit unit)
 {
     if (!unit.Deployed)
         throw new GameException("Tried to entrench an undeployed unit");
     if (!unit.CanEntrench())
         throw new GameException("This unit cannot entrench");
     unit.Entrench();
 }
コード例 #13
0
ファイル: PlayerState.cs プロジェクト: epicvrvs/PanzerKontrol
 public void DeployUnit(Unit unit, Position position)
 {
     if (unit.Deployed)
         throw new GameException("Tried to deploy a unit that has already been deployed");
     Hex hex = Map.GetHex(position);
     if (hex == null)
         throw new GameException("Encountered an invalid deployment position in a deployment request");
     if (hex.InitialDeploymentZone == null || hex.InitialDeploymentZone.Value != Identifier)
         throw new GameException("Tried to deploy a unit outside the deployment zone");
     if (hex.Owner == null || hex.Owner.Value != Identifier)
         throw new GameException("Tried to deploy a unit in a deployment zone that is currently not controlled by the player");
     if (hex.Unit != null)
         throw new GameException("Tried to deploy a unit on a hex that is already occupied");
     unit.MoveToHex(hex);
 }
コード例 #14
0
        // Converts unit data from messages to actual units for the game state
        // Also generates IDs for units
        // The reinforcement point calculation is also justified as it is only used in multiplayer and is not used in singleplayer games
        void InitialiseArmy(BaseArmy army)
        {
            List<Unit> units = new List<Unit>();
            if (army.Units.Count == 0)
                throw new ServerClientException("You cannot play a game with an empty base army");

            int pointsSpent = 0;
            foreach (var unitConfiguration in army.Units)
            {
                if (unitConfiguration.FactionId != PlayerState.Faction.Id)
                    throw new ServerClientException("Tried to deploy an army with units from another faction");
                Unit unit = new Unit(PlayerState, Game.GetUnitId(), unitConfiguration, Server);
                pointsSpent += unit.Points;
                units.Add(unit);
            }
            int pointsAvailable = Game.GameConfiguration.Points;
            if (pointsSpent > pointsAvailable)
                throw new ServerClientException("You have spent too many points");
            int reinforcementPoints = (int)(GameConstants.ReinforcementPointsPenaltyFactor * (pointsAvailable - pointsSpent) + GameConstants.ReinforcementPointsBaseRatio * pointsAvailable);
            PlayerState.InitialiseArmy(units, reinforcementPoints);
        }
コード例 #15
0
ファイル: PlayerState.cs プロジェクト: epicvrvs/PanzerKontrol
 public void PurchaseUnit(Unit unit)
 {
     if (unit.Points > ReinforcementPoints)
         throw new GameException("Not enough reinforcement points remaining to purchase this unit");
     _ReinforcementPoints -= unit.Points;
     Units.Add(unit);
 }
コード例 #16
0
 void OnPurchaseUnit(ClientToServerMessage message)
 {
     PurchaseUnitRequest request = message.PurchaseUnitRequest;
     if (request == null)
         throw new ServerClientException("Invalid purchase request");
     if (request.Unit.FactionId != PlayerState.Faction.Id)
         throw new ServerClientException("Tried to purchase a unit from another faction");
     Unit unit = new Unit(PlayerState, Game.GetUnitId(), request.Unit, Server);
     PlayerState.PurchaseUnit(unit);
     // Update the unit ID prior to broadcasting the purchase information
     UnitConfiguration unitConfiguration = request.Unit;
     unitConfiguration.UnitId = unit.Id;
     UnitPurchasedBroadcast unitPurchased = new UnitPurchasedBroadcast(new ReinforcementState(PlayerState), request.Unit);
     ServerToClientMessage broadcast = new ServerToClientMessage(unitPurchased);
     BroadcastMessage(broadcast);
 }
コード例 #17
0
ファイル: Unit.cs プロジェクト: epicvrvs/PanzerKontrol
 public double GetDamage(Unit target, bool attacking)
 {
     double hardness = target.Type.Hardness.Value;
     double softness = 1 - hardness;
     if (attacking)
         return Stats.SoftAttack.Value * softness + Stats.HardAttack.Value * hardness;
     else
     {
         int bonus = 0;
         int hexOffsetIndex = Map.GetHexOffsetIndex(target.Hex, Hex);
         RiverEdge riverEdge = Hex.RiverEdges[hexOffsetIndex];
         if (riverEdge != null)
         {
             // Ground attacks across rivers cause defenders to receive a bonus
             bonus = 1;
         }
         return (Stats.SoftDefence.Value + bonus) * softness + (Stats.HardDefence.Value + bonus) * hardness;
     }
 }