public virtual List <Shoot> GenerateShoots(Model soldier, ShootingParams shootingParams) { List <Shoot> shoots = new List <Shoot>(); var weapons = soldier.SelectShootingWeapons(); if (weapons.Any()) { foreach (var cweapon in weapons) { var weapon = shootingParams.SuperchargeWeapons ? cweapon.SuperchargeProfile ?? cweapon : cweapon; int count = weapon.GenerateShoots(shootingParams.Distance); shoots.Add(new Shoot() { Attacker = soldier, Weapon = weapon, Attacks = count, }); } } return(shoots); }
public virtual ShootingPhaseStats ShootingPhase(Unit target, ShootingParams shootingParams) { ShootingPhaseStats stats = new ShootingPhaseStats(); var shoots = GenerateShoots(shootingParams); if (shoots.Any()) { var impacts = ResolveShoots(shoots, shootingParams); if (impacts.Any()) { stats.Shoots += impacts.Count; stats.Hits += impacts.Count(i => i.Impacted); var targetSoldiers = target.Soldiers.Where(s => s.W > 0); ResolveWounds(impacts, targetSoldiers, shootingParams, out int wounds, out int generatedDamage, out int damage); stats.Wounds += wounds; stats.GeneratedDamage += generatedDamage; stats.Damage += damage; ResolveBearerWounds(impacts); } } return(stats); }
public override Impact ImpactTest(Model attacker, ShootingParams shootingParams) { return(new Impact() { Attacker = attacker, Weapon = this, Impacted = true, }); }
public override int CalcDamage(Wound wound, Model target, ShootingParams shootingParams) { if (Range / 2 >= shootingParams.Distance) { return(CalcDamage(shootingParams, Damage * 2, DamageDice, DiceModes.Max)); } return(base.CalcDamage(wound, target, shootingParams)); }
public override Impact ImpactTest(Model attacker, ShootingParams shootingParams) { var impact = base.ImpactTest(attacker, shootingParams); if (impact.CriticalFail) { impact.BearerDamage = BearerDamageTypes.Slain; } return(impact); }
public override Impact ImpactTest(Model attacker, ShootingParams shootingParams) { if (Range / 2 >= shootingParams.Distance) { //+1 to impact at short range var nShootingParams = shootingParams.Clone(); nShootingParams.ShootingModifiers++; return(base.ImpactTest(attacker, nShootingParams)); } return(base.ImpactTest(attacker, shootingParams)); }
public virtual Impact ImpactTest(Model attacker, ShootingParams shootingParams) { bool impacted = CalcImpacts(attacker.BS, shootingParams, out int roll, out int rollModified); return(new Impact() { Attacker = attacker, Weapon = this, Impacted = impacted, Roll = roll, RollModified = rollModified, }); }
public virtual List <Impact> ResolveShoots(List <Shoot> shoots, ShootingParams shootingParams) { List <Impact> impacts = new List <Impact>(); foreach (var shoot in shoots) { for (int i = 0; i < shoot.Attacks; i++) { impacts.Add(shoot.Weapon.ImpactTest(shoot.Attacker, shootingParams)); } } return(impacts); }
public virtual List <Wound> WoundTest(int t, ShootingParams shootingParams) { List <Wound> wounds = new List <Wound>(); int s = CalcStrength(); bool wounded = CalcWounds(s, t, shootingParams, out int roll, out int rollModified); if (wounded) { Wound w = new Wound(this, rollModified, false); wounds.Add(w); } return(wounds); }
public virtual List <Shoot> GenerateShoots(ShootingParams shootingParams) { List <Shoot> shoots = new List <Shoot>(); var attackingSoldiers = this.Soldiers.Where(s => s.W > 0); if (attackingSoldiers.Any()) { foreach (var soldier in attackingSoldiers) { shoots.AddRange(GenerateShoots(soldier, shootingParams)); } } return(shoots); }
public static bool CalcWounds(int s, int t, ShootingParams shootingParams, out int roll, out int rollModified) { int toWoundRoll = CalcToWoundRoll(s, t); roll = Dice.D6(); rollModified = roll + shootingParams.WoundingModifiers; if (shootingParams.WoundingRerolls == 0 && roll != 1 && rollModified >= toWoundRoll) { return(true); } bool reroll = false; if (roll == 1 && shootingParams.WoundingRerollUnmodifiedOnes) { reroll = true; } else if (roll < toWoundRoll && shootingParams.WoundingRerollUnmodifiedFails) { reroll = true; } else if (rollModified == 1 && shootingParams.WoundingRerollOnes) { reroll = true; } else if (rollModified < toWoundRoll && shootingParams.WoundingRerollFails) { reroll = true; } if (reroll && shootingParams.WoundingRerolls > 0) { roll = Dice.D6(); rollModified = roll + shootingParams.WoundingModifiers; shootingParams.WoundingRerolls--; } if (roll != 1 && rollModified >= toWoundRoll) { return(true); } return(false); }
public static bool CalcImpacts(int bs, ShootingParams shootingParams, out int roll, out int rollModified) { roll = Dice.D6(); rollModified = roll + shootingParams.ShootingModifiers; if (shootingParams.ShootingRerolls == 0) { return(roll != 1 && rollModified >= bs); } bool reroll = false; if (roll == 1 && shootingParams.ShootingRerollUnmodifiedOnes) { //Reroll ones before modifiers reroll = true; } else if (roll < bs && shootingParams.ShootingRerollUnmodifiedFails) { //Reroll fails before modifiers reroll = true; } else if (rollModified == 1 && shootingParams.ShootingRerollOnes) { //Reroll ones after modifiers reroll = true; } else if (rollModified < bs && shootingParams.ShootingRerollFails) { //Reroll fails after modifiers reroll = true; } if (reroll && shootingParams.ShootingRerolls > 0) { roll = Dice.D6(); rollModified = roll + shootingParams.ShootingModifiers; shootingParams.ShootingRerolls--; } return(roll != 1 && rollModified >= bs); }
public virtual int CalcDamage(Wound wound, Model target, ShootingParams shootingParams) { return(CalcDamage(shootingParams, Damage, DamageDice, DiceModes.Sum)); }
private bool ReadShootingParams(out ShootingParams shootingParams) { StringBuilder sb = new StringBuilder(); string distanceError = "Write a valid distance number (positive integer)"; string toHitModifiersError = "Write a valid to hit modifiers number (integer)"; string toHitRerollsError = "Write a valid to hit number of rerolls (integer, -1 for all rolls)"; string toWoundModifiersError = "Write a valid to wound modifiers number (integer)"; string toWoundRerollsError = "Write a valid to wound number of rerolls (integer, -1 for all rolls)"; string toDamageThresholdError = "Write a valid damage reroll threshold number (positive integer from 0 to 100)"; string toDamageRerollsError = "Write a valid damage number of rerolls (integer, -1 for all rolls)"; if (!int.TryParse(txtSPDistance.Text, out int distance)) { sb.AppendLine(distanceError); } else if (distance <= 0) { sb.AppendLine(distanceError); } if (!int.TryParse(txtSPToHitModifiers.Text, out int shootingModifiers)) { sb.AppendLine(toHitModifiersError); } if (!int.TryParse(txtSPToHitRerolls.Text, out int shootingRerolls)) { sb.AppendLine(toHitRerollsError); } else if (shootingRerolls < -1) { sb.AppendLine(toHitRerollsError); } if (!int.TryParse(txtSPToWoundModifiers.Text, out int woundingModifiers)) { sb.AppendLine(toWoundModifiersError); } if (!int.TryParse(txtSPToWoundRerolls.Text, out int woundingRerolls)) { sb.AppendLine(toWoundRerollsError); } else if (woundingRerolls < -1) { sb.AppendLine(toWoundRerollsError); } if (!int.TryParse(txtSPToDamageThreshold.Text, out int damageThreshold)) { sb.AppendLine(toDamageThresholdError); } else if (damageThreshold < 0 || damageThreshold > 100) { sb.AppendLine(toDamageThresholdError); } if (!int.TryParse(txtSPToDamageRerolls.Text, out int damageRerolls)) { sb.AppendLine(toDamageRerollsError); } else if (damageRerolls < -1) { sb.AppendLine(toDamageRerollsError); } if (sb.Length > 0) { MessageBox.Show(sb.ToString()); shootingParams = null; return(false); } shootingParams = new ShootingParams() { Distance = distance, SuperchargeWeapons = cbSPSupercharge.Checked, OverKill = cbSPOverkill.Checked, ShootingModifiers = shootingModifiers, ShootingRerollUnmodifiedOnes = cbSPToHitRerollUOnes.Checked, ShootingRerollUnmodifiedFails = cbSPToHitRerollUFails.Checked, ShootingRerollOnes = cbSPToHitRerollOnes.Checked, ShootingRerollFails = cbSPToHitRerollFails.Checked, ShootingRerolls = shootingRerolls, WoundingModifiers = woundingModifiers, WoundingRerollUnmodifiedOnes = cbSPToWoundRerollUOnes.Checked, WoundingRerollUnmodifiedFails = cbSPToWoundRerollUFails.Checked, WoundingRerollOnes = cbSPToWoundRerollOnes.Checked, WoundingRerollFails = cbSPToWoundRerollFails.Checked, WoundingRerolls = woundingRerolls, DamageReroll = cbSPToDamageReroll.Checked, PercentToRerollDamage = damageThreshold, DamageRerolls = damageRerolls, }; return(true); }
private void Test(UnitGen unitGenerator, ShootingParams shootingParams, int testCount) { TestResults.Text = "Testing..."; StringBuilder tests = new StringBuilder(); string attackerName = null; string defenderName = null; int attackerWounds = 0; int defenderWounds = 0; int totalModels = 0; int totalModelsKilled = 0; int totalShoots = 0; int totalHits = 0; int totalWounds = 0; int totalDamage = 0; int totalGeneratedDamage = 0; int totalKills = 0; int maxDamage = 0; for (int i = 0; i < testCount; i++) { //Set units var attacker = unitGenerator.GetAttacker(); var defender = unitGenerator.GetDefender(); //Get model names attackerName = attacker.Name; defenderName = defender.Name; //Get model wounds counters attackerWounds = attacker.Soldiers.Sum(s => s.W); defenderWounds = defender.Soldiers.Sum(s => s.W); //Do the shooting phase var stats = attacker.ShootingPhase(defender, shootingParams); //Refresh model wounds counters var attackerRemainWounds = attacker.Soldiers.Sum(s => s.W); var defenderRemainWounds = defender.Soldiers.Sum(s => s.W); var modelsKilledString = " "; if (defenderRemainWounds == 0) { totalKills++; modelsKilledString = "*"; } //Models killed versus total models in unit var modelsKilled = defender.Soldiers.Count(s => s.W <= 0); var models = defender.Soldiers.Count; totalModels += models; totalModelsKilled += modelsKilled; totalShoots += stats.Shoots; totalHits += stats.Hits; totalWounds += stats.Wounds; totalGeneratedDamage += stats.GeneratedDamage; totalDamage += stats.Damage; if (stats.GeneratedDamage > maxDamage) { maxDamage = stats.GeneratedDamage; } tests.AppendLine($"{stats.Shoots:000} shoots : {stats.HitPercentage:000.00}% of hits | {stats.Hits:000} hits : {stats.WoundPercentage:000.00}% of wounds | {stats.GeneratedDamage:00}/{defenderWounds:00} Damage done | {modelsKilledString} {modelsKilled:00}/{models:00} Kills | Attacker lost {attackerWounds - attackerRemainWounds:00} wounds"); } TestResults.Text = $"{testCount} tests - {attackerName} vs {defenderName}\r\n\r\n" + $"Medimum stats by test\r\n\r\n" + $"{totalShoots / (float)testCount:000} shoots : {totalHits / (float)totalShoots * 100f:000.00}% of hits | {totalHits / (float)testCount:000} hits : {totalWounds / (float)totalHits * 100f:000.00}% of wounds | {totalDamage / testCount:00}/{defenderWounds:00} Damage done | {totalKills / (float)testCount * 100:00.0}% Kills | {maxDamage:00} Maximum wounds done\r\n\r\n" + $"All tests detail\r\n\r\n" + $"{tests}"; }
public virtual void ResolveWounds(List <Impact> impacts, IEnumerable <Model> targets, ShootingParams shootingParams, out int generatedWounds, out int generatedDamage, out int damageDone) { generatedWounds = 0; generatedDamage = 0; damageDone = 0; //Resolve wounds foreach (var impact in impacts.Where(i => i.Impacted)) { //Get max T var maxT = targets .Where(s => s.W > 0) .OrderByDescending(s => s.T) .Select(s => s.T) .FirstOrDefault(); if (maxT == 0) { //No targets break; } var wounds = impact.Weapon.WoundTest(maxT, shootingParams); if (wounds.Any()) { generatedWounds += wounds.Count; //Find target model var activeTarget = targets .Where(s => s.W > 0) .OrderBy(s => s.W) .FirstOrDefault(); foreach (var wound in wounds) { //Not saved int damage = impact.Weapon.CalcDamage(wound, activeTarget, shootingParams); generatedDamage += damage; //Save roll var saved = activeTarget.SaveTest(wound); if (!saved) { damageDone += damage; //Set damage to target activeTarget.Damage(damage); } } } } }