Skip to content

Commit

Permalink
Indirect fire and concealment
Browse files Browse the repository at this point in the history
- Fix  concealed enemy spotting
- Implement indirect fire
  • Loading branch information
layagyasz committed Aug 25, 2017
1 parent 33a4d32 commit 1e0a64d
Show file tree
Hide file tree
Showing 15 changed files with 211 additions and 170 deletions.
11 changes: 5 additions & 6 deletions Controller/Match/Subcontroller/AttackController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,11 @@ public override void HandleUnitLeftClick(Unit Unit)
Highlight(
Unit.GetFieldOfSight(AttackMethod.NORMAL_FIRE).Select(
i => new Tuple<Tile, Color>(
i.Final,
HIGHLIGHT_COLORS[
Math.Min(
i.Range * HIGHLIGHT_COLORS.Length
/ (Unit.Configuration.GetRange(AttackMethod.NORMAL_FIRE) + 1),
HIGHLIGHT_COLORS.Length - 1)])));
i.Item1.Final,
GetRangeColor(
i.Item2 ? HIGHLIGHT_COLORS : DIM_HIGHLIGHT_COLORS,
i.Item1.Range,
Unit.Configuration.GetRange(AttackMethod.NORMAL_FIRE)))));
}
else if (Unit.Army != _Army)
{
Expand Down
6 changes: 6 additions & 0 deletions Controller/Match/Subcontroller/BaseAttackController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

using Cardamom.Utilities;

using SFML.Graphics;
using SFML.Window;

namespace PanzerBlitz
Expand Down Expand Up @@ -56,6 +57,11 @@ protected void StartAttack(AttackOrder Attack)
_AttackPane.OnExecute += ExecuteAttack;
}

protected Color GetRangeColor(Color[] ColorSet, int Range, int MaxRange)
{
return ColorSet[Math.Min(Range * HIGHLIGHT_COLORS.Length / (MaxRange + 1), ColorSet.Length - 1)];
}

void ExecuteAttack(object sender, EventArgs e)
{
if (_Match.ExecuteOrder(_AttackBuilder))
Expand Down
10 changes: 9 additions & 1 deletion Controller/Match/Subcontroller/BaseController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,15 @@ namespace PanzerBlitz
{
public abstract class BaseController : Subcontroller
{
public static readonly Color[] HIGHLIGHT_COLORS = new Color[]
public static readonly Color[] HIGHLIGHT_COLORS =
{
new Color(0, 255, 0, 120),
new Color(255, 255, 0, 120),
new Color(255, 128, 0, 120),
new Color(255, 0, 0, 120)
};

public static readonly Color[] DIM_HIGHLIGHT_COLORS =
{
new Color(0, 255, 0, 120),
new Color(255, 255, 0, 120),
Expand Down
11 changes: 5 additions & 6 deletions Controller/Match/Subcontroller/CloseAssaultController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,11 @@ void SetCloseAssaultHighlight(Unit Unit)
{
IEnumerable<Tuple<Tile, Color>> attackRange = Unit.GetFieldOfSight(AttackMethod.CLOSE_ASSAULT).Select(
i => new Tuple<Tile, Color>(
i.Final,
HIGHLIGHT_COLORS[
Math.Min(
i.Range * HIGHLIGHT_COLORS.Length
/ (Unit.Configuration.GetRange(AttackMethod.CLOSE_ASSAULT) + 1),
HIGHLIGHT_COLORS.Length - 1)]));
i.Item1.Final,
GetRangeColor(
HIGHLIGHT_COLORS,
i.Item1.Range,
Unit.Configuration.GetRange(AttackMethod.CLOSE_ASSAULT))));

IEnumerable<Tuple<Tile, Color>> moveRange = Unit.GetFieldOfMovement(true).Select(
i => new Tuple<Tile, Color>(
Expand Down
36 changes: 34 additions & 2 deletions Model/Army.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,22 +72,54 @@ public bool CanSeeUnit(Unit Unit)
|| _OverrideVisibleUnits.Contains(Unit);
}

public bool CanSeeTile(Tile Tile, bool OverrideConcealment = false)
{
if (Tile == null) return false;
if (Tile.Configuration.Concealing && !OverrideConcealment)
return CanSpotTile(Tile);
foreach (Unit u in Units)
{
LineOfSight s = u.GetLineOfSight(Tile);
if (s != null && s.Validate() == NoLineOfSightReason.NONE) return true;
}
return false;
}

public bool CanIndirectFireAtTile(Tile Tile)
{
if (!CanSpotTile(Tile)) return false;
foreach (Unit u in Units.Where(i => i.Configuration.CanSpotIndirectFire))
{
LineOfSight s = u.GetLineOfSight(Tile);
if (s != null && s.Validate() == NoLineOfSightReason.NONE) return true;
}
return false;
}

public bool CanSpotTile(Tile Tile)
{
if (Tile == null) return false;
if (!Tile.Configuration.Concealing) return true;
return Units.Any(i => i.Position != null && (i.Position == Tile || i.Position.Neighbors().Contains(Tile)));
}

public void SetUnitVisibility(Unit Unit, bool Visible)
{
if (Visible) _OverrideVisibleUnits.Add(Unit);
if (Unit.Army == this || Unit.Position == null || !Unit.Position.Configuration.Concealing) return;

if (Unit.Position != null && CanSeeTile(Unit.Position, true))
{
if (Visible) _OverrideVisibleUnits.Add(Unit);
else _OverrideVisibleUnits.Remove(Unit);
}
else _OverrideVisibleUnits.Remove(Unit);
}

public void UpdateUnitVisibility(Unit Unit, Tile MovedFrom, Tile MovedTo)
{
if (Unit.Army == this || !MovedTo.Configuration.Concealing) return;
SetUnitVisibility(Unit, CanSpotTile(MovedFrom) /* Should also check line of sights */);
if (CanSeeTile(MovedFrom)) _OverrideVisibleUnits.Add(Unit);
else _OverrideVisibleUnits.Remove(Unit);
}

public void UpdateUnitVisibility(Unit Unit, Tile MovedTo)
Expand Down
6 changes: 5 additions & 1 deletion Model/Match.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ public Match(Scenario Scenario)
.Concat(Armies.Select(i => new TurnInfo(i, TurnComponent.RESET)))
.Concat(Enumerable.Repeat(StandardTurnOrder(Armies), Scenario.Turns)
.SelectMany(i => i)).GetEnumerator();
foreach (Unit u in Armies.SelectMany(i => i.Units)) u.OnMove += UpdateUnitVisibilityFromMove;
foreach (Unit u in Armies.SelectMany(i => i.Units))
{
u.OnMove += UpdateUnitVisibilityFromMove;
u.OnFire += UpdateUnitVisibilityFromFire;
}
}

public Dictionary<Army, ObjectiveSuccessLevel> GetArmyObjectiveSuccessLevels()
Expand Down
69 changes: 65 additions & 4 deletions Model/Orders/Attack/AttackFactorCalculation.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,78 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace PanzerBlitz
{
public class AttackFactorCalculation
{
public readonly int Attack;
public readonly List<AttackFactorCalculationFactor> Factors;
public int Attack { get; private set; }
public List<AttackFactorCalculationFactor> Factors { get; private set; }

public AttackFactorCalculation(int Attack, List<AttackFactorCalculationFactor> Factors)
public AttackFactorCalculation(int Attack, IEnumerable<AttackFactorCalculationFactor> Factors)
{
this.Attack = Attack;
this.Factors = Factors;
this.Factors = Factors.ToList();
}

public AttackFactorCalculation(Unit Unit, AttackMethod AttackMethod, bool EnemyArmored, LineOfSight LineOfSight)
{
if (AttackMethod == AttackMethod.NORMAL_FIRE) GetNormalAttack(Unit, EnemyArmored, LineOfSight);
else
{
if (Unit.CanAttack(AttackMethod, EnemyArmored, LineOfSight) != NoSingleAttackReason.NONE)
{
Attack = 0;
Factors = new List<AttackFactorCalculationFactor> { AttackFactorCalculationFactor.CANNOT_ATTACK };
}
Attack = Unit.Configuration.Attack;
Factors = new List<AttackFactorCalculationFactor>();
}
}

void GetNormalAttack(Unit Unit, bool EnemyArmored, LineOfSight LineOfSight)
{
if (Unit.CanAttack(AttackMethod.NORMAL_FIRE, EnemyArmored, LineOfSight) != NoSingleAttackReason.NONE)
{
Attack = 0;
Factors = new List<AttackFactorCalculationFactor> { AttackFactorCalculationFactor.CANNOT_ATTACK };
return;
}

Factors = new List<AttackFactorCalculationFactor>();
Attack = Unit.Configuration.Attack;
if (LineOfSight.Initial.Elevation < LineOfSight.Final.Elevation)
{
Attack /= 2;
Factors.Add(AttackFactorCalculationFactor.ELEVATION);
}

if (EnemyArmored)
{
int HalfRange = Unit.Configuration.Range / 2;
if (Unit.Configuration.WeaponClass == WeaponClass.HIGH_EXPLOSIVE
|| Unit.Configuration.WeaponClass == WeaponClass.MORTAR)
{
if (LineOfSight.Range > HalfRange)
{
Attack /= 2;
Factors.Add(AttackFactorCalculationFactor.ARMOR_RANGE);
}
}
else if (Unit.Configuration.WeaponClass == WeaponClass.ANTI_ARMOR)
{
if (LineOfSight.Range <= HalfRange)
{
Attack *= 2;
Factors.Add(AttackFactorCalculationFactor.ANTI_ARMOR_RANGE);
}
}
}
else if (Unit.Configuration.WeaponClass == WeaponClass.ANTI_ARMOR && !EnemyArmored)
{
Attack /= 2;
Factors.Add(AttackFactorCalculationFactor.NOT_ARMORED);
}
}
}
}
9 changes: 4 additions & 5 deletions Model/Orders/Attack/AttackOrder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace PanzerBlitz
{
public class AttackOrder : Order
{
private static readonly CombatResult[,] COMBAT_RESULTS =
static readonly CombatResult[,] COMBAT_RESULTS =
{
{
CombatResult.DISRUPT,
Expand Down Expand Up @@ -234,7 +234,7 @@ public virtual NoAttackReason Validate()
return NoAttackReason.NONE;
}

private bool MustAttackAllUnits()
bool MustAttackAllUnits()
{
return AttackMethod != AttackMethod.NORMAL_FIRE
|| AttackAt.Configuration.MustAttackAllUnits
Expand All @@ -256,15 +256,14 @@ public virtual bool Execute(Random Random)
Random.Next(2, 7) + c.DieModifier];
foreach (Unit u in c.Defenders) u.HandleCombatResult(_Results[i]);
}
if (AttackMethod != AttackMethod.MINEFIELD) AttackAt.FireAt();
return true;
}

// Map 4, 3, 2, 1, 2, 3, 4 => 0 -> 6
private int OddsIndex(int Odds, bool OddsAgainst)
int OddsIndex(int Odds, bool OddsAgainst)
{
if (OddsAgainst) return 4 - Odds;
else return 2 + Odds;
return 2 + Odds;
}
}
}
6 changes: 4 additions & 2 deletions Model/Orders/Attack/LineOfSight.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Linq;

using Cardamom.Graphing;
using Cardamom.Planar;

namespace PanzerBlitz
{
Expand Down Expand Up @@ -45,12 +46,13 @@ public int Range
public LineOfSight(Tile From, Tile To)
{
// We calculate lines of sight to and from to make sure sighting is symmetrical.
Segment s = new Segment(From.Center, To.Center);
Tile[] losA = new Path<Tile>(
From,
To,
i => true,
(i, j) => 1,
(i, j) => i.HeuristicDistanceTo(j),
(i, j) => s.DistanceSquared(j.Center),
i => i.Neighbors(),
(i, j) => i == j).Nodes.ToArray();
Edge[] crossedEdgesA = new Edge[losA.Length - 1];
Expand All @@ -65,7 +67,7 @@ public LineOfSight(Tile From, Tile To)
From,
i => true,
(i, j) => 1,
(i, j) => i.HeuristicDistanceTo(j),
(i, j) => s.DistanceSquared(j.Center),
i => i.Neighbors(),
(i, j) => i == j).Nodes.Reverse().ToArray();
Edge[] crossedEdgesB = new Edge[losB.Length - 1];
Expand Down
3 changes: 1 addition & 2 deletions Model/Orders/Attack/NormalSingleAttackOrder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,14 @@ public override void SetTreatStackAsArmored(bool TreatStackAsArmored)
public override AttackFactorCalculation GetAttack()
{
if (Validate() == NoSingleAttackReason.NONE)
return _Attacker.Configuration.GetAttack(AttackMethod, _TreatStackAsArmored, LineOfSight);
return new AttackFactorCalculation(_Attacker, AttackMethod, _TreatStackAsArmored, LineOfSight);
return new AttackFactorCalculation(
0, new List<AttackFactorCalculationFactor>() { AttackFactorCalculationFactor.CANNOT_ATTACK });
}

public override NoSingleAttackReason Validate()
{
if (_Defender == null) return NoSingleAttackReason.ILLEGAL;
if (LineOfSight.Validate() != NoLineOfSightReason.NONE) return NoSingleAttackReason.NO_LOS;
NoSingleAttackReason r = _Attacker.CanAttack(AttackMethod, _TreatStackAsArmored, LineOfSight);
if (r != NoSingleAttackReason.NONE) return r;

Expand Down
2 changes: 1 addition & 1 deletion Model/Orders/Attack/OverrunSingleAttackOrder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public override void SetTreatStackAsArmored(bool TreatStackAsArmored)
public override AttackFactorCalculation GetAttack()
{
if (Validate() == NoSingleAttackReason.NONE)
return Attacker.Configuration.GetAttack(AttackMethod.OVERRUN, _TreatStackAsArmored, null);
return new AttackFactorCalculation(Attacker, AttackMethod.OVERRUN, _TreatStackAsArmored, null);
else return new AttackFactorCalculation(
0, new List<AttackFactorCalculationFactor>() { AttackFactorCalculationFactor.CANNOT_ATTACK });
}
Expand Down
Loading

0 comments on commit 1e0a64d

Please sign in to comment.