Empieza refactoring masivo
This commit is contained in:
parent
feac5f79bd
commit
e6ae93208d
12 changed files with 140 additions and 78 deletions
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ public class AttackScenario {
|
||||||
private static final int PROBABILITY_DENOMINATOR_D6 = 6;
|
private static final int PROBABILITY_DENOMINATOR_D6 = 6;
|
||||||
|
|
||||||
// Attack attributes
|
// Attack attributes
|
||||||
|
private UnitAttackModifiers unitAttackModifiers;
|
||||||
private List<Tuple2<Weapon, Integer>> weapons;
|
private List<Tuple2<Weapon, Integer>> weapons;
|
||||||
private boolean unmodifiableHit;
|
private boolean unmodifiableHit;
|
||||||
private boolean attackerWasStationary;
|
private boolean attackerWasStationary;
|
||||||
|
@ -95,6 +96,9 @@ public class AttackScenario {
|
||||||
Weapon weapon = weaponData._1;
|
Weapon weapon = weaponData._1;
|
||||||
int amountOfWeapons = weaponData._2;
|
int amountOfWeapons = weaponData._2;
|
||||||
|
|
||||||
|
weapon.generateAttackResolution(defensiveProfile, )
|
||||||
|
var weaponAttackResolution = new WeaponAttackResolution();
|
||||||
|
|
||||||
// Attack funnel
|
// Attack funnel
|
||||||
var passingHits = calculatePassingHits(weapon, amountOfWeapons);
|
var passingHits = calculatePassingHits(weapon, amountOfWeapons);
|
||||||
var passingWounds = calculatePassingWounds(weapon, amountOfWeapons, passingHits);
|
var passingWounds = calculatePassingWounds(weapon, amountOfWeapons, passingHits);
|
||||||
|
|
|
@ -1,41 +1,25 @@
|
||||||
package ninja.thefirearchmage.games.fourtykcalculator;
|
package ninja.thefirearchmage.games.fourtykcalculator;
|
||||||
|
|
||||||
import lombok.Getter;
|
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.ArrayList;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static java.lang.String.format;
|
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public class AttackScenarioResolution {
|
public class AttackScenarioResolution {
|
||||||
private Map<String, Map<WeaponStat, Object>> weaponStats;
|
private List<WeaponAttackResolution> weaponAttacksStats;
|
||||||
@Setter
|
|
||||||
private double totalInflictedDamage;
|
private double totalInflictedDamage;
|
||||||
@Setter
|
|
||||||
private double totalPreventedDamage;
|
private double totalPreventedDamage;
|
||||||
|
|
||||||
public AttackScenarioResolution() {
|
public AttackScenarioResolution() {
|
||||||
this.weaponStats = new HashMap<>();
|
this.weaponAttacksStats = new ArrayList<>();
|
||||||
totalInflictedDamage = 0;
|
totalInflictedDamage = 0;
|
||||||
totalPreventedDamage = 0;
|
totalPreventedDamage = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setWeaponStat(Weapon weapon, WeaponStat statName, Object statValue) {
|
public void addWeaponAttackResolution(WeaponAttackResolution resolution) {
|
||||||
if(!statValue.getClass().isAssignableFrom(statName.getStatClass())) {
|
this.weaponAttacksStats.add(resolution);
|
||||||
throw new IllegalArgumentException(
|
totalInflictedDamage += resolution.getTotalDamage();
|
||||||
format("The stat %s's value does not have the expected class %s, but instead has %s class",
|
totalPreventedDamage += resolution.getSaveResolution().getPreventedWounds();
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
package ninja.thefirearchmage.games.fourtykcalculator;
|
||||||
|
|
||||||
|
public enum DamageReductionType {
|
||||||
|
REDUCE_BY_X,
|
||||||
|
REDUCE_TO_X;
|
||||||
|
}
|
|
@ -1,8 +1,10 @@
|
||||||
package ninja.thefirearchmage.games.fourtykcalculator;
|
package ninja.thefirearchmage.games.fourtykcalculator;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import ninja.thefirearchmage.games.fourtykcalculator.utils.datastructures.Tuple3;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
@ -13,7 +15,7 @@ public class DefensiveProfile {
|
||||||
private final int wounds;
|
private final int wounds;
|
||||||
private final int bodies;
|
private final int bodies;
|
||||||
private Optional<InvulnerableSave> invulnerableSave;
|
private Optional<InvulnerableSave> invulnerableSave;
|
||||||
private Optional<FeelNoPainEffect> feelNoPain;
|
private List<FeelNoPainEffect> feelNoPainEffects;
|
||||||
private Set<UnitTypeKeyword> unitTypeKeywords;
|
private Set<UnitTypeKeyword> unitTypeKeywords;
|
||||||
private boolean isInCover;
|
private boolean isInCover;
|
||||||
private boolean isVisible;
|
private boolean isVisible;
|
||||||
|
@ -21,6 +23,9 @@ public class DefensiveProfile {
|
||||||
private boolean hasRerollFailedNormalSaves;
|
private boolean hasRerollFailedNormalSaves;
|
||||||
private boolean hasRerollAnyNormalSaves;
|
private boolean hasRerollAnyNormalSaves;
|
||||||
private boolean fishFor6NormalSaves;
|
private boolean fishFor6NormalSaves;
|
||||||
|
private boolean hasStealth;
|
||||||
|
private Tuple3<Boolean, DamageReductionType, Integer> damageReductionModifier;
|
||||||
|
private Tuple3<Boolean, WeaponRangeType, Integer> apReduction;
|
||||||
|
|
||||||
public DefensiveProfile(int toughness, int wounds, int bodies, int normalSave) {
|
public DefensiveProfile(int toughness, int wounds, int bodies, int normalSave) {
|
||||||
this(toughness, wounds, bodies, normalSave, Optional.empty(), Optional.empty(),
|
this(toughness, wounds, bodies, normalSave, Optional.empty(), Optional.empty(),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package ninja.thefirearchmage.games.fourtykcalculator;
|
package ninja.thefirearchmage.games.fourtykcalculator;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.*;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
@ -9,30 +9,28 @@ import java.util.stream.IntStream;
|
||||||
/**
|
/**
|
||||||
* Assumes a D6
|
* Assumes a D6
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Getter @AllArgsConstructor
|
||||||
public class Reroll {
|
public class Reroll {
|
||||||
|
private RerollType type;
|
||||||
private final Set<Integer> rerollableValues;
|
private final Set<Integer> rerollableValues;
|
||||||
|
private Set<UnitTypeKeyword> validTargetTypes;
|
||||||
|
|
||||||
private Reroll(Set<Integer> rerollableValues) {
|
public static Reroll rerollOnOnes(Set<UnitTypeKeyword> validTargetTypes) {
|
||||||
this.rerollableValues = rerollableValues;
|
return new Reroll(RerollType.ON_ONES, Set.of(1), validTargetTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Reroll rerollOnOnes() {
|
public static Reroll rerollFailures(int targetHit, Set<UnitTypeKeyword> validTargetTypes) {
|
||||||
return new Reroll(Set.of(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Reroll rerollFailures(int targetHit) {
|
|
||||||
var rerollableValues = IntStream.range(1, targetHit).boxed()
|
var rerollableValues = IntStream.range(1, targetHit).boxed()
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
return new Reroll(rerollableValues);
|
return new Reroll(RerollType.ON_FAILURE, rerollableValues, validTargetTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Reroll rerollOnNot(Set<Integer> allowedValues) {
|
public static Reroll rerollOnNot(Set<Integer> allowedValues, Set<UnitTypeKeyword> validTargetTypes) {
|
||||||
var rerollableValues = IntStream.range(1,7).filter(i-> !allowedValues.contains(i))
|
var rerollableValues = IntStream.range(1,7).filter(i-> !allowedValues.contains(i))
|
||||||
.boxed().collect(Collectors.toSet());
|
.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() {
|
public double getProbabilityNumerator() {
|
||||||
return (double) rerollableValues.size() / 6;
|
return (double) rerollableValues.size() / 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean appliesTo(UnitTypeKeyword target) {
|
||||||
|
return this.validTargetTypes.contains(target);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package ninja.thefirearchmage.games.fourtykcalculator;
|
||||||
|
|
||||||
|
public enum RerollType {
|
||||||
|
ON_ONES,
|
||||||
|
ON_FAILURE,
|
||||||
|
ANY;
|
||||||
|
}
|
|
@ -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<Boolean, WeaponRangeType, Integer> sustainedHitsModifier;
|
||||||
|
private Tuple3<Boolean, WeaponRangeType, Integer> lethalHitsModifier;
|
||||||
|
private boolean lanceModifier;
|
||||||
|
private List<Reroll> rerollsModifier;
|
||||||
|
private Tuple3<Boolean, WeaponRangeType, Integer> apModifier;
|
||||||
|
private Tuple2<Boolean, WeaponRangeType> woundModifier;
|
||||||
|
private Tuple2<Boolean, WeaponRangeType> hitModifier;
|
||||||
|
private Tuple2<Boolean, WeaponRangeType> devastatingWoundsModifier;
|
||||||
|
private Tuple2<Boolean, WeaponRangeType> hazardousModifier;
|
||||||
|
private Tuple2<Boolean, WeaponRangeType> heavyModifier;
|
||||||
|
private Tuple3<Boolean, WeaponRangeType, Integer> strengthModifier;
|
||||||
|
private Tuple3<Boolean, WeaponRangeType, Integer> attacksModifier;
|
||||||
|
private boolean unmodifiableHitRoll;
|
||||||
|
|
||||||
|
private boolean unitHasRemainedStationaryModifier;
|
||||||
|
private boolean unitCharged;
|
||||||
|
private boolean targetInRapidFireRange;
|
||||||
|
private boolean isPsychic;
|
||||||
|
private boolean canRerollHazardous;
|
||||||
|
}
|
|
@ -1,8 +1,11 @@
|
||||||
package ninja.thefirearchmage.games.fourtykcalculator;
|
package ninja.thefirearchmage.games.fourtykcalculator;
|
||||||
|
|
||||||
public enum UnitTypeKeyword {
|
public enum UnitTypeKeyword {
|
||||||
|
ALL,
|
||||||
INFANTRY,
|
INFANTRY,
|
||||||
VEHICLE,
|
VEHICLE,
|
||||||
PSYCHIC,
|
PSYCHIC,
|
||||||
|
CHAOS,
|
||||||
|
CHARACTER,
|
||||||
MONSTER;
|
MONSTER;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,7 @@ package ninja.thefirearchmage.games.fourtykcalculator;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.*;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class Weapon {
|
public class Weapon {
|
||||||
@Getter
|
@Getter
|
||||||
|
@ -27,7 +24,7 @@ public class Weapon {
|
||||||
private Optional<Integer> sustainedHits;
|
private Optional<Integer> sustainedHits;
|
||||||
private boolean hasDevastatingWounds;
|
private boolean hasDevastatingWounds;
|
||||||
private boolean hasHazardous;
|
private boolean hasHazardous;
|
||||||
private Map<UnitTypeKeyword, Integer> antiStats;
|
private List<AntiUnitEffect> antiUnitEffects;
|
||||||
private boolean hasHeavy;
|
private boolean hasHeavy;
|
||||||
private boolean hasBlast;
|
private boolean hasBlast;
|
||||||
private boolean hasLance;
|
private boolean hasLance;
|
||||||
|
@ -37,50 +34,39 @@ public class Weapon {
|
||||||
private Optional<Integer> melta;
|
private Optional<Integer> melta;
|
||||||
@Getter
|
@Getter
|
||||||
private Optional<Integer> rapidFire;
|
private Optional<Integer> rapidFire;
|
||||||
|
private boolean isPsychic;
|
||||||
|
|
||||||
public Weapon() {
|
public Weapon(String name, int strength, int ap, int attacks, int hitValue, WeaponDamage damage,
|
||||||
this.antiStats = new HashMap<>();
|
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.melta = Optional.empty();
|
||||||
this.rapidFire = Optional.empty();
|
this.rapidFire = Optional.empty();
|
||||||
this.sustainedHits = 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() {
|
public WeaponAttackResolution generateAttackResolution(DefensiveProfile target, UnitAttackModifiers unitModifiers) {
|
||||||
return hasLethalHits;
|
var weaponAttackResolution = new WeaponAttackResolution();
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasDevastatingWounds() {
|
|
||||||
return hasDevastatingWounds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasHazardous() {
|
|
||||||
return hasHazardous;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isHeavy() {
|
return weaponAttackResolution;
|
||||||
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<Integer> getAnti(UnitTypeKeyword unitType) {
|
|
||||||
return Optional.ofNullable(antiStats.get(unitType));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,11 +1,24 @@
|
||||||
package ninja.thefirearchmage.games.fourtykcalculator;
|
package ninja.thefirearchmage.games.fourtykcalculator;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@AllArgsConstructor @NoArgsConstructor
|
||||||
|
@Getter @Setter
|
||||||
public class WeaponAttackResolution {
|
public class WeaponAttackResolution {
|
||||||
private double normalWounds;
|
private double normalWounds;
|
||||||
|
private double normalWoundsFromLethalHits;
|
||||||
|
private double normalWoundsFromRerolls;
|
||||||
|
private double normalWoundsWithoutRerolls;
|
||||||
private double mortalWounds;
|
private double mortalWounds;
|
||||||
private double hits;
|
private double hits;
|
||||||
private double woundsFromLethalHits;
|
private double hitsFromSustainedHits;
|
||||||
private double woundsFromRerolls;
|
|
||||||
private double hitsFromRerolls;
|
private double hitsFromRerolls;
|
||||||
|
private double hitsWithoutRerolls;
|
||||||
|
private double normalDamage;
|
||||||
|
private double mortalDamage;
|
||||||
|
private double totalDamage;
|
||||||
|
private SaveResolution saveResolution;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package ninja.thefirearchmage.games.fourtykcalculator;
|
package ninja.thefirearchmage.games.fourtykcalculator;
|
||||||
|
|
||||||
public enum WeaponRangeType {
|
public enum WeaponRangeType {
|
||||||
|
ALL,
|
||||||
MELEE,
|
MELEE,
|
||||||
RANGED;
|
RANGED;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue