diff --git a/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/AntiUnitEffect.java b/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/AntiUnitEffect.java new file mode 100644 index 0000000..6c499e9 --- /dev/null +++ b/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/AntiUnitEffect.java @@ -0,0 +1,15 @@ +package ninja.thefirearchmage.games.fourtykcalculator; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +@AllArgsConstructor @Getter @Setter +public class AntiUnitEffect { + private UnitTypeKeyword unitType; + private int woundRollValue; + + public boolean appliesTo(UnitTypeKeyword unitType) { + return this.unitType == unitType; + } +} diff --git a/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/AttackScenario.java b/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/AttackScenario.java index 31e0f35..10046dc 100644 --- a/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/AttackScenario.java +++ b/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/AttackScenario.java @@ -11,6 +11,7 @@ public class AttackScenario { private static final int PROBABILITY_DENOMINATOR_D6 = 6; // Attack attributes + private UnitAttackModifiers unitAttackModifiers; private List> weapons; private boolean unmodifiableHit; private boolean attackerWasStationary; @@ -95,6 +96,9 @@ public class AttackScenario { Weapon weapon = weaponData._1; int amountOfWeapons = weaponData._2; + weapon.generateAttackResolution(defensiveProfile, ) + var weaponAttackResolution = new WeaponAttackResolution(); + // Attack funnel var passingHits = calculatePassingHits(weapon, amountOfWeapons); var passingWounds = calculatePassingWounds(weapon, amountOfWeapons, passingHits); diff --git a/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/AttackScenarioResolution.java b/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/AttackScenarioResolution.java index 7f8d51f..5a6aa8c 100644 --- a/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/AttackScenarioResolution.java +++ b/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/AttackScenarioResolution.java @@ -1,41 +1,25 @@ package ninja.thefirearchmage.games.fourtykcalculator; import lombok.Getter; -import lombok.Setter; -import ninja.thefirearchmage.games.fourtykcalculator.utils.datastructures.Tuple; -import ninja.thefirearchmage.games.fourtykcalculator.utils.datastructures.Tuple2; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; - -import static java.lang.String.format; @Getter public class AttackScenarioResolution { - private Map> weaponStats; - @Setter + private List weaponAttacksStats; private double totalInflictedDamage; - @Setter private double totalPreventedDamage; public AttackScenarioResolution() { - this.weaponStats = new HashMap<>(); + this.weaponAttacksStats = new ArrayList<>(); totalInflictedDamage = 0; totalPreventedDamage = 0; } - public void setWeaponStat(Weapon weapon, WeaponStat statName, Object statValue) { - if(!statValue.getClass().isAssignableFrom(statName.getStatClass())) { - throw new IllegalArgumentException( - format("The stat %s's value does not have the expected class %s, but instead has %s class", - statName.name(), statName.getStatClass().getName(), statValue.getClass().getName())); - } - - var currentStats = weaponStats.putIfAbsent(weapon.getName(), Map.of(statName, statValue)); - if(currentStats != null) { - currentStats.put(statName, statValue); - } + public void addWeaponAttackResolution(WeaponAttackResolution resolution) { + this.weaponAttacksStats.add(resolution); + totalInflictedDamage += resolution.getTotalDamage(); + totalPreventedDamage += resolution.getSaveResolution().getPreventedWounds(); } } diff --git a/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/DamageReductionType.java b/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/DamageReductionType.java new file mode 100644 index 0000000..e901c55 --- /dev/null +++ b/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/DamageReductionType.java @@ -0,0 +1,6 @@ +package ninja.thefirearchmage.games.fourtykcalculator; + +public enum DamageReductionType { + REDUCE_BY_X, + REDUCE_TO_X; +} diff --git a/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/DefensiveProfile.java b/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/DefensiveProfile.java index b12a413..71d4d14 100644 --- a/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/DefensiveProfile.java +++ b/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/DefensiveProfile.java @@ -1,8 +1,10 @@ package ninja.thefirearchmage.games.fourtykcalculator; import lombok.Getter; +import ninja.thefirearchmage.games.fourtykcalculator.utils.datastructures.Tuple3; import java.util.HashSet; +import java.util.List; import java.util.Optional; import java.util.Set; @@ -13,7 +15,7 @@ public class DefensiveProfile { private final int wounds; private final int bodies; private Optional invulnerableSave; - private Optional feelNoPain; + private List feelNoPainEffects; private Set unitTypeKeywords; private boolean isInCover; private boolean isVisible; @@ -21,6 +23,9 @@ public class DefensiveProfile { private boolean hasRerollFailedNormalSaves; private boolean hasRerollAnyNormalSaves; private boolean fishFor6NormalSaves; + private boolean hasStealth; + private Tuple3 damageReductionModifier; + private Tuple3 apReduction; public DefensiveProfile(int toughness, int wounds, int bodies, int normalSave) { this(toughness, wounds, bodies, normalSave, Optional.empty(), Optional.empty(), diff --git a/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/Reroll.java b/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/Reroll.java index 1789775..a91d85d 100644 --- a/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/Reroll.java +++ b/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/Reroll.java @@ -1,6 +1,6 @@ package ninja.thefirearchmage.games.fourtykcalculator; -import lombok.Getter; +import lombok.*; import java.util.Set; import java.util.stream.Collectors; @@ -9,30 +9,28 @@ import java.util.stream.IntStream; /** * Assumes a D6 */ -@Getter +@Getter @AllArgsConstructor public class Reroll { + private RerollType type; private final Set rerollableValues; + private Set validTargetTypes; - private Reroll(Set rerollableValues) { - this.rerollableValues = rerollableValues; + public static Reroll rerollOnOnes(Set validTargetTypes) { + return new Reroll(RerollType.ON_ONES, Set.of(1), validTargetTypes); } - public static Reroll rerollOnOnes() { - return new Reroll(Set.of(1)); - } - - public static Reroll rerollFailures(int targetHit) { + public static Reroll rerollFailures(int targetHit, Set validTargetTypes) { var rerollableValues = IntStream.range(1, targetHit).boxed() .collect(Collectors.toSet()); - return new Reroll(rerollableValues); + return new Reroll(RerollType.ON_FAILURE, rerollableValues, validTargetTypes); } - public static Reroll rerollOnNot(Set allowedValues) { + public static Reroll rerollOnNot(Set allowedValues, Set validTargetTypes) { var rerollableValues = IntStream.range(1,7).filter(i-> !allowedValues.contains(i)) .boxed().collect(Collectors.toSet()); - return new Reroll(rerollableValues); + return new Reroll(RerollType.ANY, rerollableValues, validTargetTypes); } /** @@ -41,4 +39,8 @@ public class Reroll { public double getProbabilityNumerator() { return (double) rerollableValues.size() / 6; } + + public boolean appliesTo(UnitTypeKeyword target) { + return this.validTargetTypes.contains(target); + } } diff --git a/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/RerollType.java b/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/RerollType.java new file mode 100644 index 0000000..78f1c26 --- /dev/null +++ b/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/RerollType.java @@ -0,0 +1,7 @@ +package ninja.thefirearchmage.games.fourtykcalculator; + +public enum RerollType { + ON_ONES, + ON_FAILURE, + ANY; +} diff --git a/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/UnitAttackModifiers.java b/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/UnitAttackModifiers.java new file mode 100644 index 0000000..d31541d --- /dev/null +++ b/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/UnitAttackModifiers.java @@ -0,0 +1,36 @@ +package ninja.thefirearchmage.games.fourtykcalculator; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import ninja.thefirearchmage.games.fourtykcalculator.utils.datastructures.Tuple2; +import ninja.thefirearchmage.games.fourtykcalculator.utils.datastructures.Tuple3; + +import java.util.List; + +/** + * Models what modifiers a unit that is performing the attacking provides to its weapons. + * For example, a unit of eradicators has reroll on hits, wounds and damage against monsters and vehicles. + */ +@NoArgsConstructor @Getter @Setter +public class UnitAttackModifiers { + private Tuple3 sustainedHitsModifier; + private Tuple3 lethalHitsModifier; + private boolean lanceModifier; + private List rerollsModifier; + private Tuple3 apModifier; + private Tuple2 woundModifier; + private Tuple2 hitModifier; + private Tuple2 devastatingWoundsModifier; + private Tuple2 hazardousModifier; + private Tuple2 heavyModifier; + private Tuple3 strengthModifier; + private Tuple3 attacksModifier; + private boolean unmodifiableHitRoll; + + private boolean unitHasRemainedStationaryModifier; + private boolean unitCharged; + private boolean targetInRapidFireRange; + private boolean isPsychic; + private boolean canRerollHazardous; +} diff --git a/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/UnitTypeKeyword.java b/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/UnitTypeKeyword.java index 2e735f8..325adf2 100644 --- a/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/UnitTypeKeyword.java +++ b/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/UnitTypeKeyword.java @@ -1,8 +1,11 @@ package ninja.thefirearchmage.games.fourtykcalculator; public enum UnitTypeKeyword { + ALL, INFANTRY, VEHICLE, PSYCHIC, + CHAOS, + CHARACTER, MONSTER; } diff --git a/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/Weapon.java b/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/Weapon.java index 9e4a4c0..facc142 100644 --- a/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/Weapon.java +++ b/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/Weapon.java @@ -2,10 +2,7 @@ package ninja.thefirearchmage.games.fourtykcalculator; import lombok.Getter; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.Set; +import java.util.*; public class Weapon { @Getter @@ -27,7 +24,7 @@ public class Weapon { private Optional sustainedHits; private boolean hasDevastatingWounds; private boolean hasHazardous; - private Map antiStats; + private List antiUnitEffects; private boolean hasHeavy; private boolean hasBlast; private boolean hasLance; @@ -37,50 +34,39 @@ public class Weapon { private Optional melta; @Getter private Optional rapidFire; + private boolean isPsychic; - public Weapon() { - this.antiStats = new HashMap<>(); + public Weapon(String name, int strength, int ap, int attacks, int hitValue, WeaponDamage damage, + WeaponRangeType weaponRangeType) { + this.name = name; + this.strength = strength; + this.ap = ap; + this.attacks = attacks; + this.hitValue = hitValue; + this.damage = damage; + this.rangeType = weaponRangeType; + + this.antiUnitEffects = new ArrayList<>(); this.melta = Optional.empty(); this.rapidFire = Optional.empty(); this.sustainedHits = Optional.empty(); + + this.hasDevastatingWounds = false; + this.hasHazardous = false; + this.hasHeavy = false; + this.hasBlast = false; + this.hasLance = false; + this.ignoresCover = false; + this.hasIndirectFire = false; + this.hasLethalHits = false; } - public boolean hasLethalHits() { - return hasLethalHits; - } + public WeaponAttackResolution generateAttackResolution(DefensiveProfile target, UnitAttackModifiers unitModifiers) { + var weaponAttackResolution = new WeaponAttackResolution(); - public boolean hasDevastatingWounds() { - return hasDevastatingWounds; - } - public boolean hasHazardous() { - return hasHazardous; - } - public boolean isHeavy() { - return hasHeavy; - } - - public boolean hasBlast() { - return hasBlast; - } - - public boolean ignoresCover() { - return ignoresCover; - } - - public boolean hasLance() { - return hasLance; - } - public boolean hasIndirectFire() { - return hasIndirectFire; - } - - /** - * Returns whether this weapon has Anti-X where X is the given unit type. - */ - public Optional getAnti(UnitTypeKeyword unitType) { - return Optional.ofNullable(antiStats.get(unitType)); + return weaponAttackResolution; } /** diff --git a/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/WeaponAttackResolution.java b/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/WeaponAttackResolution.java index e746ed5..2dcb141 100644 --- a/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/WeaponAttackResolution.java +++ b/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/WeaponAttackResolution.java @@ -1,11 +1,24 @@ package ninja.thefirearchmage.games.fourtykcalculator; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor @NoArgsConstructor +@Getter @Setter public class WeaponAttackResolution { private double normalWounds; + private double normalWoundsFromLethalHits; + private double normalWoundsFromRerolls; + private double normalWoundsWithoutRerolls; private double mortalWounds; private double hits; - private double woundsFromLethalHits; - private double woundsFromRerolls; + private double hitsFromSustainedHits; private double hitsFromRerolls; - + private double hitsWithoutRerolls; + private double normalDamage; + private double mortalDamage; + private double totalDamage; + private SaveResolution saveResolution; } diff --git a/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/WeaponRangeType.java b/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/WeaponRangeType.java index eacb7c1..433e233 100644 --- a/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/WeaponRangeType.java +++ b/src/main/java/ninja/thefirearchmage/games/fourtykcalculator/WeaponRangeType.java @@ -1,6 +1,7 @@ package ninja.thefirearchmage.games.fourtykcalculator; public enum WeaponRangeType { + ALL, MELEE, RANGED; }