|
|
@ -12,26 +12,22 @@ namespace GWConquest |
|
|
|
get => FormationList.Select((f,i) => f.GetComponent<Formation>().Player).Distinct(); |
|
|
|
} |
|
|
|
|
|
|
|
private float LastTurnTime = 0f; |
|
|
|
private int BattleTurn = 0; |
|
|
|
public IEnumerable<Unit> AllUnits { |
|
|
|
get => FormationList.SelectMany(e => e.GetComponent<Formation>().Units); |
|
|
|
} |
|
|
|
|
|
|
|
private List<UnitAction> UnitActions = new List<UnitAction>(); |
|
|
|
|
|
|
|
[System.Serializable] |
|
|
|
private class UnitAction { |
|
|
|
public Unit Unit; |
|
|
|
public BattleFlank BattleFlank; |
|
|
|
public int WeaponIndex; |
|
|
|
public float ActionTime; |
|
|
|
} |
|
|
|
|
|
|
|
private Dictionary<Player, TargetingPool> TargetingPools = new Dictionary<Player, TargetingPool>(); |
|
|
|
|
|
|
|
private TargetingPool GetTargetingPoolForPlayer(Player player) |
|
|
|
{ |
|
|
|
if(!TargetingPools.ContainsKey(player)) |
|
|
|
{ |
|
|
|
TargetingPools[player] = new TargetingPool(); |
|
|
|
public WeaponStats Weapon { |
|
|
|
get => Unit.Class.WeaponStatsArray[WeaponIndex]; |
|
|
|
} |
|
|
|
return TargetingPools[player]; |
|
|
|
} |
|
|
|
|
|
|
|
public int FlankCount { |
|
|
@ -133,73 +129,26 @@ namespace GWConquest |
|
|
|
base.SimulateOwner(); |
|
|
|
|
|
|
|
var gm = GameManager.Instance; |
|
|
|
var time = Time.time; |
|
|
|
|
|
|
|
if(time > LastTurnTime + gm.BattleTurnLength) |
|
|
|
{ |
|
|
|
LastTurnTime = time; |
|
|
|
BattleTurn++; |
|
|
|
BoltLog.Info("Starting turn {0} on battle {1}", BattleTurn, this); |
|
|
|
|
|
|
|
StartNewTurn(time); |
|
|
|
} |
|
|
|
|
|
|
|
SimulateTurn(); |
|
|
|
} |
|
|
|
|
|
|
|
private void StartNewTurn(float time) |
|
|
|
{ |
|
|
|
var allUnits = FormationList.SelectMany((e, i) => e.GetComponent<Formation>().Units); |
|
|
|
|
|
|
|
var actions = allUnits.Select((u,i) => { |
|
|
|
return new UnitAction() { |
|
|
|
Unit = u, |
|
|
|
ActionTime = Random.value |
|
|
|
}; |
|
|
|
}).ToList(); |
|
|
|
|
|
|
|
actions.Sort((a,b) => (int) Mathf.Sign(a.ActionTime - b.ActionTime)); |
|
|
|
|
|
|
|
foreach(var action in actions) |
|
|
|
{ |
|
|
|
action.ActionTime *= GameManager.Instance.BattleTurnLength; |
|
|
|
action.ActionTime += time; |
|
|
|
} |
|
|
|
|
|
|
|
UnitActions = actions; |
|
|
|
|
|
|
|
var allPlayers = AllPlayers; |
|
|
|
foreach(Player p in allPlayers) |
|
|
|
{ |
|
|
|
var pool = GetTargetingPoolForPlayer(p); |
|
|
|
pool.Clear(); |
|
|
|
|
|
|
|
var attackableUnits = allUnits.Where((u,i) => u.Player != p); |
|
|
|
|
|
|
|
foreach(Unit u in attackableUnits) |
|
|
|
{ |
|
|
|
if(!u.IsDead) |
|
|
|
{ |
|
|
|
pool.Add(u, u.Class.Size); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private void SimulateTurn() |
|
|
|
{ |
|
|
|
var time = Time.time; |
|
|
|
var time = Time.fixedTime; |
|
|
|
bool hasChanges = false; |
|
|
|
while(UnitActions.Count > 0 && UnitActions[0].ActionTime <= time) |
|
|
|
foreach(UnitAction action in UnitActions.ToList()) |
|
|
|
{ |
|
|
|
if(action.ActionTime <= time) |
|
|
|
{ |
|
|
|
SimulateUnitAction(action); |
|
|
|
|
|
|
|
var unit = UnitActions[0].Unit; |
|
|
|
|
|
|
|
SimulateUnitAction(unit); |
|
|
|
|
|
|
|
UnitActions.RemoveAt(0); |
|
|
|
RenewUnitAction(action); |
|
|
|
|
|
|
|
hasChanges = true; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if(hasChanges) |
|
|
@ -221,48 +170,131 @@ namespace GWConquest |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private void SimulateUnitAction(Unit unit) |
|
|
|
private void AddCombatActions(Unit unit) |
|
|
|
{ |
|
|
|
BoltLog.Info("Simulating action for unit {0} on turn {1}", unit, BattleTurn); |
|
|
|
float time = Time.fixedTime; |
|
|
|
if(unit.CurrentFlank != null && !unit.IsDead) |
|
|
|
{ |
|
|
|
var weapons = unit.Class.WeaponStatsArray; |
|
|
|
for(int i = 0; i < weapons.Length; i++) |
|
|
|
{ |
|
|
|
var action = new UnitAction() { |
|
|
|
Unit = unit, |
|
|
|
BattleFlank = unit.CurrentFlank, |
|
|
|
WeaponIndex = i |
|
|
|
}; |
|
|
|
RenewUnitAction(action); |
|
|
|
UnitActions.Add(action); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private void RenewUnitAction(UnitAction action) |
|
|
|
{ |
|
|
|
var attackCount = action.Weapon.AttackCount; |
|
|
|
var gm = GameManager.Instance; |
|
|
|
|
|
|
|
float attackDelay = Random.Range(gm.BattleTurnLength - gm.BattleTurnDeviation, gm.BattleTurnLength + gm.BattleTurnDeviation); |
|
|
|
attackDelay /= attackCount; |
|
|
|
|
|
|
|
var pool = GetTargetingPoolForPlayer(unit.Player); |
|
|
|
action.ActionTime = Time.fixedTime + attackDelay; |
|
|
|
} |
|
|
|
|
|
|
|
if(pool.IsEmpty()) |
|
|
|
return; |
|
|
|
private void RemoveUnitActions(Unit unit) |
|
|
|
{ |
|
|
|
UnitActions.RemoveAll(a => a.Unit == unit); |
|
|
|
} |
|
|
|
|
|
|
|
private void SimulateUnitAction(UnitAction action) |
|
|
|
{ |
|
|
|
var unit = action.Unit; |
|
|
|
var flank = action.BattleFlank; |
|
|
|
BoltLog.Info("Simulating action for unit {0} on flank {1}", unit, flank); |
|
|
|
|
|
|
|
var weapons = unit.Class.WeaponStatsArray; |
|
|
|
var weapon = action.Weapon; |
|
|
|
|
|
|
|
foreach(var weapon in weapons) |
|
|
|
var target = FindTargetForCombat(action); |
|
|
|
|
|
|
|
if(target == null) |
|
|
|
{ |
|
|
|
for(int i = 0; i < weapon.AttackCount; i++) |
|
|
|
{ |
|
|
|
if(pool.IsEmpty()) |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if(!Check(weapon.Accuracy)) |
|
|
|
return; |
|
|
|
|
|
|
|
if(!Check(weapon.Accuracy)) |
|
|
|
continue; |
|
|
|
if(Check(target.Class.Evasion)) |
|
|
|
return; |
|
|
|
|
|
|
|
var target = pool.GetRandomUnit(); |
|
|
|
int damage = weapon.Damage; |
|
|
|
float modifier = GetArmourModifier(target.Class.Armour, weapon.Penetration); |
|
|
|
|
|
|
|
if(Check(target.Class.Evasion)) |
|
|
|
continue; |
|
|
|
damage = Mathf.RoundToInt(damage * modifier); |
|
|
|
|
|
|
|
if(damage <= 0) |
|
|
|
return; |
|
|
|
|
|
|
|
int damage = weapon.Damage; |
|
|
|
float modifier = GetArmourModifier(target.Class.Armour, weapon.Penetration); |
|
|
|
BoltLog.Info("Dealing {0} HP damage to unit {1} from unit {2}", damage, target, unit); |
|
|
|
|
|
|
|
target.TakeDamage(damage); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
private Unit FindTargetForCombat(UnitAction action) |
|
|
|
{ |
|
|
|
IEnumerable<Unit> targets; |
|
|
|
|
|
|
|
if(action.BattleFlank != null) |
|
|
|
{ |
|
|
|
targets = action.BattleFlank.OpposingFlank.Units; |
|
|
|
} |
|
|
|
else { |
|
|
|
throw new System.NotImplementedException(); |
|
|
|
} |
|
|
|
|
|
|
|
damage = Mathf.RoundToInt(damage * modifier); |
|
|
|
targets = targets.Where(u => !u.IsDead); |
|
|
|
|
|
|
|
if(damage <= 0) |
|
|
|
continue; |
|
|
|
if(targets.FirstOrDefault() == null) |
|
|
|
{ |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
BoltLog.Info("Dealing {0} HP damage to unit {1} from unit {2}", damage, target, unit); |
|
|
|
float penetration = action.Unit.Class.WeaponStatsArray[action.WeaponIndex].Penetration; |
|
|
|
|
|
|
|
target.TakeDamage(damage); |
|
|
|
if(target.IsDead){ |
|
|
|
pool.RemoveUnit(target); |
|
|
|
} |
|
|
|
IEnumerable<Unit> penTargets = null;; |
|
|
|
for(float p = penetration; p >= 0; p--) |
|
|
|
{ |
|
|
|
penTargets = targets.Where(u => u.Armour <= p && u.Armour > p - 1); |
|
|
|
if(penTargets.FirstOrDefault() != null) |
|
|
|
{ |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if(penTargets == null || penTargets.FirstOrDefault() == null) |
|
|
|
{ |
|
|
|
penTargets = targets; |
|
|
|
} |
|
|
|
|
|
|
|
var tList = penTargets.ToList(); |
|
|
|
int randomInd = Random.Range(0, tList.Count); |
|
|
|
|
|
|
|
return tList[randomInd]; |
|
|
|
} |
|
|
|
|
|
|
|
public void OnBattleStateChanged(Unit unit) |
|
|
|
{ |
|
|
|
RemoveUnitActions(unit); |
|
|
|
|
|
|
|
if(unit.BattleState == BattleUnitState.OnFlank) |
|
|
|
{ |
|
|
|
AddCombatActions(unit); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public void OnUnitDead(Unit unit) |
|
|
|
{ |
|
|
|
RemoveUnitActions(unit); |
|
|
|
} |
|
|
|
|
|
|
|
public void MoveUnitToFlank(Unit unit, BattleFlank flank, int flankIndex) |
|
|
@ -286,11 +318,19 @@ namespace GWConquest |
|
|
|
float movement = unit.Class.Movement <= 0 ? 1f : unit.Class.Movement; |
|
|
|
unit.SetActionCooldown(GameManager.Instance.MoveToFlankCooldown / movement); |
|
|
|
flank.SetUnit(flankIndex, unit); |
|
|
|
unit.CurrentFlank = flank; |
|
|
|
|
|
|
|
//SetUIDirty(updatePositions: true);
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public void MoveUnitToReserve(Unit unit) |
|
|
|
{ |
|
|
|
unit.BattleState = BattleUnitState.MovingToReserve; |
|
|
|
float movement = unit.Class.Movement <= 0 ? 1f : unit.Class.Movement; |
|
|
|
unit.SetActionCooldown(GameManager.Instance.MoveToReserveCooldown / movement); |
|
|
|
} |
|
|
|
|
|
|
|
private static bool Check(float chance) { |
|
|
|
return Random.Range(0f, 1f) <= chance; |
|
|
|
} |
|
|
|