Refactor hit calculation, moving on to wound calculation
This commit is contained in:
parent
ec880e119b
commit
96a5e91e0b
6 changed files with 103 additions and 57 deletions
|
@ -95,7 +95,7 @@ public class DefensiveProfile {
|
||||||
resolution.setPreventedNormalWoundsFromSavesWithoutRerolls(savedNormalWounds);
|
resolution.setPreventedNormalWoundsFromSavesWithoutRerolls(savedNormalWounds);
|
||||||
var saveRerolls = calculateBestReroll(saveRollGoal);
|
var saveRerolls = calculateBestReroll(saveRollGoal);
|
||||||
if(saveRerolls.isPresent()) {
|
if(saveRerolls.isPresent()) {
|
||||||
var rerollProbability = saveRerolls.get().getProbabilityNumerator();
|
var rerollProbability = saveRerolls.get().getSuccessProbability();
|
||||||
var savedNormalWoundsFromRerolls = normalWounds*(1 - saveProbabilityNumerator)/6 * rerollProbability/6;
|
var savedNormalWoundsFromRerolls = normalWounds*(1 - saveProbabilityNumerator)/6 * rerollProbability/6;
|
||||||
resolution.setPreventedNormalWoundsFromSavesRerolls(savedNormalWoundsFromRerolls);
|
resolution.setPreventedNormalWoundsFromSavesRerolls(savedNormalWoundsFromRerolls);
|
||||||
savedNormalWounds += savedNormalWoundsFromRerolls;
|
savedNormalWounds += savedNormalWoundsFromRerolls;
|
||||||
|
|
|
@ -9,41 +9,38 @@ import java.util.stream.IntStream;
|
||||||
/**
|
/**
|
||||||
* Assumes a D6
|
* Assumes a D6
|
||||||
*/
|
*/
|
||||||
@Getter @AllArgsConstructor
|
@Getter @Setter @AllArgsConstructor @NoArgsConstructor
|
||||||
public class Reroll {
|
public class Reroll {
|
||||||
private RerollStageType rerollStageType;
|
private RerollStageType rerollStageType;
|
||||||
private RerollType type;
|
private RerollType type;
|
||||||
private final Set<Integer> rerollableValues;
|
private Set<Integer> rerollableValues;
|
||||||
private Set<UnitTypeKeyword> validTargetTypes;
|
private Set<UnitTypeKeyword> validTargetTypes;
|
||||||
|
private Set<WeaponRangeType> validWeaponRanges;
|
||||||
|
|
||||||
public static Reroll rerollOnOnes(RerollStageType rerollStageType, Set<UnitTypeKeyword> validTargetTypes) {
|
public static Reroll rerollOnOnes(RerollStageType rerollStageType, Set<UnitTypeKeyword> validTargetTypes, Set<WeaponRangeType> validWeaponRanges) {
|
||||||
return new Reroll(rerollStageType, RerollType.ON_ONES, Set.of(1), validTargetTypes);
|
return new Reroll(rerollStageType, RerollType.ON_ONES, Set.of(1), validTargetTypes, validWeaponRanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Reroll rerollFailures(RerollStageType rerollStageType, int targetHit,
|
public static Reroll rerollFailures(RerollStageType rerollStageType, int targetHit,
|
||||||
Set<UnitTypeKeyword> validTargetTypes) {
|
Set<UnitTypeKeyword> validTargetTypes, Set<WeaponRangeType> validWeaponRanges) {
|
||||||
var rerollableValues = IntStream.range(1, targetHit).boxed()
|
var rerollableValues = IntStream.range(1, targetHit).boxed()
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
return new Reroll(rerollStageType, RerollType.ON_FAILURE, rerollableValues, validTargetTypes);
|
return new Reroll(rerollStageType, RerollType.ON_FAILURE, rerollableValues, validTargetTypes, validWeaponRanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Reroll rerollOnNot(RerollStageType rerollStageType, Set<Integer> allowedValues,
|
public static Reroll rerollOnNot(RerollStageType rerollStageType, Set<Integer> allowedValues,
|
||||||
Set<UnitTypeKeyword> validTargetTypes) {
|
Set<UnitTypeKeyword> validTargetTypes, Set<WeaponRangeType> validWeaponRanges) {
|
||||||
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(rerollStageType, RerollType.ANY, rerollableValues, validTargetTypes);
|
return new Reroll(rerollStageType, RerollType.ANY, rerollableValues, validTargetTypes, validWeaponRanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* From a D6 returns what proportion of rerollable values this reroll is.
|
* From a D6 returns what proportion of rerollable values this reroll is.
|
||||||
*/
|
*/
|
||||||
public double getProbabilityNumerator() {
|
public double getSuccessProbability() {
|
||||||
return (double) rerollableValues.size() / 6;
|
return (double) rerollableValues.size() / 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean appliesTo(UnitTypeKeyword target) {
|
|
||||||
return this.validTargetTypes.contains(target);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package ninja.thefirearchmage.games.fourtykcalculator;
|
package ninja.thefirearchmage.games.fourtykcalculator;
|
||||||
|
|
||||||
public enum RerollStageType {
|
public enum RerollStageType {
|
||||||
|
ATTACKS,
|
||||||
HIT,
|
HIT,
|
||||||
WOUND,
|
WOUND,
|
||||||
DAMAGE;
|
DAMAGE;
|
||||||
|
|
|
@ -7,6 +7,10 @@ import ninja.thefirearchmage.games.fourtykcalculator.utils.datastructures.Tuple2
|
||||||
import ninja.thefirearchmage.games.fourtykcalculator.utils.datastructures.Tuple3;
|
import ninja.thefirearchmage.games.fourtykcalculator.utils.datastructures.Tuple3;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Models what modifiers a unit that is performing the attacking provides to its weapons.
|
* Models what modifiers a unit that is performing the attacking provides to its weapons.
|
||||||
|
@ -18,9 +22,8 @@ public class UnitAttackModifiers {
|
||||||
private Tuple2<Boolean, WeaponRangeType> lethalHitsModifier;
|
private Tuple2<Boolean, WeaponRangeType> lethalHitsModifier;
|
||||||
private int criticalHitValue = 6;
|
private int criticalHitValue = 6;
|
||||||
private boolean lanceModifier;
|
private boolean lanceModifier;
|
||||||
private List<Reroll> hitRerollsModifier;
|
// These rerolls do not contain actual values, they must be generated from the method getRerollFor
|
||||||
private List<Reroll> woundRerollsModifier;
|
private List<Reroll> rerollsModifier;
|
||||||
private List<Reroll> damageRerollsModifier;
|
|
||||||
private Tuple3<Boolean, WeaponRangeType, Integer> apModifier;
|
private Tuple3<Boolean, WeaponRangeType, Integer> apModifier;
|
||||||
private Tuple2<Boolean, WeaponRangeType> woundBonusModifier;
|
private Tuple2<Boolean, WeaponRangeType> woundBonusModifier;
|
||||||
private Tuple2<Boolean, WeaponRangeType> woundMalusModifier;
|
private Tuple2<Boolean, WeaponRangeType> woundMalusModifier;
|
||||||
|
@ -43,5 +46,29 @@ public class UnitAttackModifiers {
|
||||||
private boolean isPsychic;
|
private boolean isPsychic;
|
||||||
private boolean canRerollHazardous;
|
private boolean canRerollHazardous;
|
||||||
|
|
||||||
public boolean hasReroll
|
public boolean hasRerollFor(RerollStageType stageType, RerollType type,
|
||||||
|
Set<UnitTypeKeyword> targetsType, WeaponRangeType weaponRangeType) {
|
||||||
|
return rerollsModifier.stream()
|
||||||
|
.filter( r -> setContainsAnyOf(r.getValidTargetTypes(), targetsType, UnitTypeKeyword.ALL))
|
||||||
|
.filter( r -> setContainsAnyOf(r.getValidWeaponRanges(), Set.of(weaponRangeType), WeaponRangeType.ALL))
|
||||||
|
.filter(r -> r.getRerollStageType() == stageType)
|
||||||
|
.anyMatch(r -> r.getType() == type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Reroll> getRerollFor(RerollStageType stageType, RerollType type,
|
||||||
|
Set<UnitTypeKeyword> targetsType, WeaponRangeType weaponRangeType, int rollGoal) {
|
||||||
|
if(hasRerollFor(stageType, type, targetsType, weaponRangeType)){
|
||||||
|
return Optional.of(switch(type) {
|
||||||
|
case ON_ONES -> Reroll.rerollOnOnes(stageType, targetsType, Set.of(weaponRangeType));
|
||||||
|
case ON_FAILURE -> Reroll.rerollFailures(stageType, rollGoal, targetsType, Set.of(weaponRangeType));
|
||||||
|
case ANY -> Reroll.rerollOnNot(stageType, IntStream.range(rollGoal,7).boxed().collect(Collectors.toSet()), targetsType, Set.of(weaponRangeType));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> boolean setContainsAnyOf(Set<T> oneSet, Set<T> otherSet, T otherValue) {
|
||||||
|
return oneSet.stream().anyMatch(i -> i.equals(otherValue) || otherSet.contains(i));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,33 +84,35 @@ public class Weapon {
|
||||||
private double calculatePassingHits(DefensiveProfile target, UnitAttackModifiers unitAttackModifiers,
|
private double calculatePassingHits(DefensiveProfile target, UnitAttackModifiers unitAttackModifiers,
|
||||||
int amountOfWeapons, WeaponAttackResolution weaponAttackResolution) {
|
int amountOfWeapons, WeaponAttackResolution weaponAttackResolution) {
|
||||||
int hitRollGoal = calculateHitRollGoal(target, unitAttackModifiers);
|
int hitRollGoal = calculateHitRollGoal(target, unitAttackModifiers);
|
||||||
int hitSuccessProbabilityNumerator = calculateProbabilityNumeratorFrom(hitRollGoal);
|
double hitSuccessProbability = (double) calculateProbabilityNumeratorFrom(hitRollGoal) / 6;
|
||||||
int weaponAttacks = calculateWeaponAttacks(amountOfWeapons, unitAttackModifiers, weaponAttackResolution);
|
double hitFailureProbability = (double) (6 - calculateProbabilityNumeratorFrom(hitRollGoal)) / 6;
|
||||||
|
int weaponAttacks = calculateWeaponAttacks(amountOfWeapons, unitAttackModifiers, target, weaponAttackResolution);
|
||||||
// calculate sustained hits
|
|
||||||
var sustainedHits = calculateSustainedHits(weaponAttacks, unitAttackModifiers, weaponAttackResolution);
|
|
||||||
weaponAttackResolution.setHitsFromSustainedHits(sustainedHits);
|
|
||||||
weaponAttackResolution.setTotalAttacks(weaponAttacks+sustainedHits);
|
|
||||||
|
|
||||||
// Base passing hits without rerolls
|
// Base passing hits without rerolls
|
||||||
double passingHits = (weaponAttacks*amountOfWeapons) * ((double) hitSuccessProbabilityNumerator / PROBABILITY_DENOMINATOR_D6);
|
double passingHits = weaponAttacks * hitSuccessProbability;
|
||||||
results.setWeaponStat(weapon, WeaponStat.HITS_COUNT_WITHOUT_REROLL, passingHits);
|
weaponAttackResolution.setHitsWithoutRerolls(passingHits);
|
||||||
|
|
||||||
// Add hits from rerolls
|
// Add hits from rerolls
|
||||||
var hitRerolls = prepareHitRerolls(hitRollGoal);
|
var hitRerolls = prepareHitRerolls(hitRollGoal, unitAttackModifiers, target);
|
||||||
if(hitRerolls.isPresent()) {
|
if(hitRerolls.isPresent()) {
|
||||||
var rerolls = hitRerolls.get();
|
var rerolls = hitRerolls.get();
|
||||||
var successfulHitFromRerolls = rerolls.getProbabilityNumerator() * ((double) hitSuccessProbabilityNumerator / PROBABILITY_DENOMINATOR_D6);
|
var successfulHitFromRerolls = weaponAttacks * hitFailureProbability * rerolls.getSuccessProbability();
|
||||||
results.setWeaponStat(weapon, WeaponStat.HITS_COUNT_FROM_REROLL, successfulHitFromRerolls);
|
weaponAttackResolution.setHitsFromRerolls(successfulHitFromRerolls);
|
||||||
passingHits += successfulHitFromRerolls;
|
passingHits += successfulHitFromRerolls;
|
||||||
}
|
}
|
||||||
results.setWeaponStat(weapon, WeaponStat.HITS_TOTAL_COUNT, passingHits);
|
|
||||||
|
// calculate sustained hits
|
||||||
|
var sustainedHits = calculateSustainedHits(weaponAttacks, unitAttackModifiers, target);
|
||||||
|
weaponAttackResolution.setHitsFromSustainedHits(sustainedHits);
|
||||||
|
passingHits += sustainedHits;
|
||||||
|
|
||||||
|
weaponAttackResolution.setHits(passingHits);
|
||||||
|
|
||||||
return passingHits;
|
return passingHits;
|
||||||
}
|
}
|
||||||
|
|
||||||
private double calculateSustainedHits(int weaponAttacks, UnitAttackModifiers unitAttackModifiers,
|
private double calculateSustainedHits(int weaponAttacks, UnitAttackModifiers unitAttackModifiers,
|
||||||
WeaponAttackResolution weaponAttackResolution) {
|
DefensiveProfile target) {
|
||||||
var sustainedHitsAmounts = 0d;
|
var sustainedHitsAmounts = 0d;
|
||||||
|
|
||||||
var hasSustainedHits = this.sustainedHits.isPresent() ||
|
var hasSustainedHits = this.sustainedHits.isPresent() ||
|
||||||
|
@ -124,7 +126,9 @@ public class Weapon {
|
||||||
|
|
||||||
double nonSustainedProbability = (double) (6 - (7 - criticalHitValue)) / 6;
|
double nonSustainedProbability = (double) (6 - (7 - criticalHitValue)) / 6;
|
||||||
double sustainedProbability = (double) (7 - criticalHitValue) / 6;
|
double sustainedProbability = (double) (7 - criticalHitValue) / 6;
|
||||||
if(unitAttackModifiers.isFishForCriticalHits() && hasRerollAny(unitAttackModifiers.getHitRerollsModifier())) {
|
var hasHitRerollAny = unitAttackModifiers.hasRerollFor(RerollStageType.HIT, RerollType.ANY,
|
||||||
|
target.getUnitTypeKeywords(), this.rangeType);
|
||||||
|
if(unitAttackModifiers.isFishForCriticalHits() && hasHitRerollAny) {
|
||||||
sustainedProbability += nonSustainedProbability*sustainedProbability;
|
sustainedProbability += nonSustainedProbability*sustainedProbability;
|
||||||
}
|
}
|
||||||
sustainedHitsAmounts = weaponAttacks * sustainedProbability * sustainedHitsValue;
|
sustainedHitsAmounts = weaponAttacks * sustainedProbability * sustainedHitsValue;
|
||||||
|
@ -150,7 +154,7 @@ public class Weapon {
|
||||||
if(woundsRerolls.isPresent()) {
|
if(woundsRerolls.isPresent()) {
|
||||||
var rerolls = woundsRerolls.get();
|
var rerolls = woundsRerolls.get();
|
||||||
var failedWounds = normalHits * ((double) (1 - woundSuccessProbabilityNumerator) / PROBABILITY_DENOMINATOR_D6);
|
var failedWounds = normalHits * ((double) (1 - woundSuccessProbabilityNumerator) / PROBABILITY_DENOMINATOR_D6);
|
||||||
var extraWoundsFromRerolls = failedWounds * rerolls.getProbabilityNumerator() / PROBABILITY_DENOMINATOR_D6;
|
var extraWoundsFromRerolls = failedWounds * rerolls.getSuccessProbability() / PROBABILITY_DENOMINATOR_D6;
|
||||||
results.setWeaponStat(weapon, WeaponStat.WOUNDS_COUNT_FROM_REROLL, extraWoundsFromRerolls);
|
results.setWeaponStat(weapon, WeaponStat.WOUNDS_COUNT_FROM_REROLL, extraWoundsFromRerolls);
|
||||||
passingNormalWounds += extraWoundsFromRerolls;
|
passingNormalWounds += extraWoundsFromRerolls;
|
||||||
}
|
}
|
||||||
|
@ -192,12 +196,25 @@ public class Weapon {
|
||||||
return lethalHitsCount;
|
return lethalHitsCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<Reroll> prepareHitRerolls(int hitRollGoal) {
|
private Optional<Reroll> prepareHitRerolls(int hitRollGoal, UnitAttackModifiers unitAttackModifiers,
|
||||||
if(hasRerollHitsOnes) {
|
DefensiveProfile target) {
|
||||||
return Optional.of(Reroll.rerollOnOnes());
|
var hasAnyHitReroll = unitAttackModifiers.hasRerollFor(RerollStageType.HIT, RerollType.ANY,
|
||||||
|
target.getUnitTypeKeywords(), this.rangeType);
|
||||||
|
var hasOnesHitsReroll = unitAttackModifiers.hasRerollFor(RerollStageType.HIT, RerollType.ON_ONES,
|
||||||
|
target.getUnitTypeKeywords(), this.rangeType);
|
||||||
|
var hasFailedHitsReroll = unitAttackModifiers.hasRerollFor(RerollStageType.HIT, RerollType.ON_FAILURE,
|
||||||
|
target.getUnitTypeKeywords(), this.rangeType);
|
||||||
|
|
||||||
|
// Priorize rerolling failed/any hits over ones
|
||||||
|
if(hasAnyHitReroll || hasFailedHitsReroll) {
|
||||||
|
return hasAnyHitReroll ? unitAttackModifiers.getRerollFor(RerollStageType.HIT, RerollType.ANY,
|
||||||
|
target.getUnitTypeKeywords(), this.rangeType, hitRollGoal) :
|
||||||
|
unitAttackModifiers.getRerollFor(RerollStageType.HIT, RerollType.ON_FAILURE,
|
||||||
|
target.getUnitTypeKeywords(), this.rangeType, hitRollGoal);
|
||||||
}
|
}
|
||||||
if(hasRerollHitsFailures || hasRerollHitsAny) {
|
if(hasOnesHitsReroll) {
|
||||||
return Optional.of(Reroll.rerollFailures(hitRollGoal));
|
return unitAttackModifiers.getRerollFor(RerollStageType.HIT, RerollType.ON_ONES,
|
||||||
|
target.getUnitTypeKeywords(), this.rangeType, hitRollGoal);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
|
@ -215,30 +232,33 @@ public class Weapon {
|
||||||
}
|
}
|
||||||
|
|
||||||
private int calculateWeaponAttacks(int amountOfWeapons, UnitAttackModifiers unitAttackModifiers,
|
private int calculateWeaponAttacks(int amountOfWeapons, UnitAttackModifiers unitAttackModifiers,
|
||||||
WeaponAttackResolution weaponAttackResolution) {
|
DefensiveProfile target, WeaponAttackResolution weaponAttackResolution) {
|
||||||
double weaponAttacks = calculateBasicWeaponAttacks(amountOfWeapons, unitAttackModifiers, weaponAttackResolution);
|
var weaponAttacks = calculateBasicWeaponAttacks(amountOfWeapons, unitAttackModifiers, weaponAttackResolution);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return weaponAttacks;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean hasRerollAny(List<Reroll> rerolls) {
|
|
||||||
return rerolls.stream().anyMatch(r -> r.getType() == RerollType.ANY);
|
|
||||||
}
|
|
||||||
|
|
||||||
private double calculateBasicWeaponAttacks(int amountOfWeapons, UnitAttackModifiers unitAttackModifiers,
|
|
||||||
WeaponAttackResolution weaponAttackResolution) {
|
|
||||||
var weaponAttacks = this.attacks;
|
|
||||||
|
|
||||||
// Rapid fire
|
// Rapid fire
|
||||||
var hasRapidFire = this.getRapidFire().isPresent() || unitAttackModifiers.getRapidFireModifier()._1;
|
var hasRapidFire = this.getRapidFire().isPresent() || unitAttackModifiers.getRapidFireModifier()._1;
|
||||||
if(hasRapidFire && unitAttackModifiers.isTargetInRapidFireRange()) {
|
if(hasRapidFire && unitAttackModifiers.isTargetInRapidFireRange()) {
|
||||||
var higherRapidFireValue = Math.max(this.rapidFire.get(), unitAttackModifiers.getRapidFireModifier()._3);
|
var higherRapidFireValue = Math.max(this.rapidFire.get(), unitAttackModifiers.getRapidFireModifier()._3);
|
||||||
weaponAttacks += higherRapidFireValue;
|
weaponAttacks += higherRapidFireValue*amountOfWeapons;
|
||||||
weaponAttackResolution.setAttacksFromRapidFire(higherRapidFireValue*amountOfWeapons);
|
weaponAttackResolution.setAttacksFromRapidFire(higherRapidFireValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Blast
|
||||||
|
if(this.hasBlast) {
|
||||||
|
var blastBonus = (int)Math.floor((double)target.getBodies() / 5);
|
||||||
|
weaponAttacks += blastBonus*amountOfWeapons;
|
||||||
|
weaponAttackResolution.setAttacksFromBlast(blastBonus);
|
||||||
|
}
|
||||||
|
|
||||||
|
weaponAttackResolution.setTotalAttacks(weaponAttacks);
|
||||||
|
|
||||||
|
return weaponAttacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int calculateBasicWeaponAttacks(int amountOfWeapons, UnitAttackModifiers unitAttackModifiers,
|
||||||
|
WeaponAttackResolution weaponAttackResolution) {
|
||||||
|
var weaponAttacks = this.attacks;
|
||||||
|
|
||||||
// Attacks modifiers
|
// Attacks modifiers
|
||||||
var attackModifier = 0;
|
var attackModifier = 0;
|
||||||
var attackBonus = unitAttackModifiers.getAttacksBonusModifier();
|
var attackBonus = unitAttackModifiers.getAttacksBonusModifier();
|
||||||
|
@ -253,7 +273,6 @@ public class Weapon {
|
||||||
weaponAttacks += attackModifier;
|
weaponAttacks += attackModifier;
|
||||||
|
|
||||||
weaponAttacks *= amountOfWeapons;
|
weaponAttacks *= amountOfWeapons;
|
||||||
weaponAttackResolution.setTotalAttacks(weaponAttacks);
|
|
||||||
|
|
||||||
return weaponAttacks;
|
return weaponAttacks;
|
||||||
}
|
}
|
||||||
|
@ -298,6 +317,7 @@ public class Weapon {
|
||||||
// Because a roll of 1 always fails and a roll of 6 always succeeds, we modify the roll goal to be between
|
// Because a roll of 1 always fails and a roll of 6 always succeeds, we modify the roll goal to be between
|
||||||
// values 2-5
|
// values 2-5
|
||||||
var modifiedRollGoal = Math.max(rollGoal, 2);
|
var modifiedRollGoal = Math.max(rollGoal, 2);
|
||||||
|
modifiedRollGoal = Math.min(modifiedRollGoal, 5);
|
||||||
|
|
||||||
return 7 - modifiedRollGoal;
|
return 7 - modifiedRollGoal;
|
||||||
|
|
||||||
|
|
|
@ -23,5 +23,6 @@ public class WeaponAttackResolution {
|
||||||
private double totalAttacks;
|
private double totalAttacks;
|
||||||
private int attacksFromRapidFire;
|
private int attacksFromRapidFire;
|
||||||
private int attacksFromModifiers;
|
private int attacksFromModifiers;
|
||||||
|
private int attacksFromBlast;
|
||||||
private SaveResolution saveResolution;
|
private SaveResolution saveResolution;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue