Skip to content

Commit

Permalink
Fix Logic wrt. ammo AC modifier and 10mm SMG!
Browse files Browse the repository at this point in the history
  • Loading branch information
Janiczek committed Oct 5, 2024
1 parent f5410b4 commit 66cac50
Show file tree
Hide file tree
Showing 4 changed files with 335 additions and 115 deletions.
6 changes: 3 additions & 3 deletions src/Data/Item.elm
Original file line number Diff line number Diff line change
Expand Up @@ -5844,7 +5844,7 @@ unaimedRange kind =
0

Smg10mm ->
0
25

Jhp10mm ->
0
Expand Down Expand Up @@ -6070,7 +6070,7 @@ aimedRange kind =
0

Smg10mm ->
0
25

Jhp10mm ->
0
Expand Down Expand Up @@ -6522,7 +6522,7 @@ burstRange kind =
0

Smg10mm ->
0
20

Jhp10mm ->
0
Expand Down
176 changes: 91 additions & 85 deletions src/Logic.elm
Original file line number Diff line number Diff line change
Expand Up @@ -483,94 +483,99 @@ rangedChanceToHit r =
0

Just equippedWeapon ->
case neededSkill r.attackStyle (Item.types equippedWeapon) of
Nothing ->
-- Wanted to attack in an `attackStyle` the weapon can't do
0

Just weaponSkill ->
let
weaponSkill_ : Int
weaponSkill_ =
Skill.get r.attackerSpecial r.attackerAddedSkillPercentages weaponSkill

distancePenalty_ : Int
distancePenalty_ =
-- This already contains the Weapon Long Range perk calculations.
distancePenalty
{ distanceHexes = r.distanceHexes
, equippedWeapon = r.equippedWeapon
, perception = r.attackerSpecial.perception
}

ammoArmorClassModifier : Int
ammoArmorClassModifier =
r.equippedAmmo
|> Maybe.map Item.ammoArmorClassModifier
|> Maybe.withDefault 0

lightingPenalty_ : Int
lightingPenalty_ =
lightingPenalty
{ isItDark = False
, hasNightVisionPerk = Perk.rank Perk.NightVision r.attackerPerks > 0
, distanceHexes = r.distanceHexes
}

shotPenalty : Int
shotPenalty =
case r.attackStyle of
Throw ->
0

ShootSingleUnaimed ->
0

ShootSingleAimed aim ->
aimedShotChanceToHitPenalty aim

ShootBurst ->
burstShotChanceToHitPenalty

-- These can't happen in ranged:
UnarmedUnaimed ->
0

UnarmedAimed _ ->
0
if r.distanceHexes > Item.range r.attackStyle equippedWeapon then
-- Wanted to attack at a range the weapon can't do
0

MeleeUnaimed ->
0
else
case neededSkill r.attackStyle (Item.types equippedWeapon) of
Nothing ->
-- Wanted to attack in an `attackStyle` the weapon can't do
0

Just weaponSkill ->
let
weaponSkill_ : Int
weaponSkill_ =
Skill.get r.attackerSpecial r.attackerAddedSkillPercentages weaponSkill

distancePenalty_ : Int
distancePenalty_ =
-- This already contains the Weapon Long Range perk calculations.
distancePenalty
{ distanceHexes = r.distanceHexes
, equippedWeapon = r.equippedWeapon
, perception = r.attackerSpecial.perception
}

ammoArmorClassModifier : Int
ammoArmorClassModifier =
r.equippedAmmo
|> Maybe.map Item.ammoArmorClassModifier
|> Maybe.withDefault 0

lightingPenalty_ : Int
lightingPenalty_ =
lightingPenalty
{ isItDark = False
, hasNightVisionPerk = Perk.rank Perk.NightVision r.attackerPerks > 0
, distanceHexes = r.distanceHexes
}

shotPenalty : Int
shotPenalty =
case r.attackStyle of
Throw ->
0

ShootSingleUnaimed ->
0

ShootSingleAimed aim ->
aimedShotChanceToHitPenalty aim

ShootBurst ->
burstShotChanceToHitPenalty

-- These can't happen in ranged:
UnarmedUnaimed ->
0

UnarmedAimed _ ->
0

MeleeUnaimed ->
0

MeleeAimed _ ->
0

strengthRequirementPenalty : Int
strengthRequirementPenalty =
strengthRequirementChanceToHitPenalty
{ strength = r.attackerSpecial.strength
, equippedWeapon = equippedWeapon
}

weaponAccuratePerk : Int
weaponAccuratePerk =
if Item.isAccurateWeapon equippedWeapon then
20

MeleeAimed _ ->
else
0

strengthRequirementPenalty : Int
strengthRequirementPenalty =
strengthRequirementChanceToHitPenalty
{ strength = r.attackerSpecial.strength
, equippedWeapon = equippedWeapon
}

weaponAccuratePerk : Int
weaponAccuratePerk =
if Item.isAccurateWeapon equippedWeapon then
20

else
0
in
(weaponSkill_
+ (8 * r.attackerSpecial.perception)
-- weapon long range perk is already factored into the distancePenalty
+ weaponAccuratePerk
- distancePenalty_
- (r.targetArmorClass + ammoArmorClassModifier)
- lightingPenalty_
- strengthRequirementPenalty
- shotPenalty
)
|> clamp 0 95
in
(weaponSkill_
+ (8 * r.attackerSpecial.perception)
-- weapon long range perk is already factored into the distancePenalty
+ weaponAccuratePerk
- distancePenalty_
- ((r.targetArmorClass * (100 - ammoArmorClassModifier)) // 100)
- lightingPenalty_
- strengthRequirementPenalty
- shotPenalty
)
|> clamp 0 95


meleeChanceToHit :
Expand All @@ -592,6 +597,7 @@ meleeChanceToHit r =
|> Maybe.withDefault 1
in
if r.distanceHexes > weaponRange then
-- Wanted to attack at a range the weapon can't do
0

else
Expand Down
156 changes: 142 additions & 14 deletions tests/LogicTest.elm
Original file line number Diff line number Diff line change
@@ -1,29 +1,157 @@
module LogicTest exposing (test)

import Data.Fight.AttackStyle as AttackStyle
import Data.Special as Special
import Data.Fight.AttackStyle as AttackStyle exposing (AttackStyle(..))
import Data.Item as Item exposing (Kind(..))
import Data.Perk exposing (Perk)
import Data.Skill exposing (Skill(..))
import Data.Special as Special exposing (Special)
import Expect
import Fuzz exposing (Fuzzer)
import Logic
import SeqDict
import SeqDict exposing (SeqDict)
import Test exposing (Test)
import TestHelpers


test : Test
test =
Test.describe "Logic"
[ Test.describe "chanceToHit"
[ Test.test "Unarmed + good range" <|
\() ->
[ Test.describe "chanceToHit" <|
[ Test.fuzz chanceToHitArgsFuzzer "0..95" <|
\args ->
Logic.chanceToHit args
|> Expect.all
[ Expect.atLeast 0
, Expect.atMost 95
]
, Test.fuzz2 chanceToHitArgsFuzzer (Fuzz.maybe TestHelpers.unarmedWeaponKindFuzzer) "Unarmed + good range: can hit" <|
\args maybeWeapon ->
Logic.chanceToHit
{ attackerAddedSkillPercentages = SeqDict.empty
, attackerPerks = SeqDict.empty
, attackerSpecial = Special.init
, distanceHexes = 1
, equippedWeapon = Nothing
, equippedAmmo = Nothing
, targetArmorClass = 0
, attackStyle = AttackStyle.UnarmedUnaimed
{ args
| attackStyle = AttackStyle.UnarmedUnaimed
, equippedWeapon = maybeWeapon
, distanceHexes = 1
, targetArmorClass = 0
}
|> Expect.greaterThan 0
, Test.fuzz2 chanceToHitArgsFuzzer (Fuzz.maybe TestHelpers.unarmedWeaponKindFuzzer) "Unarmed outside range: cannot hit" <|
\args maybeWeapon ->
Logic.chanceToHit
{ args
| attackStyle = AttackStyle.UnarmedUnaimed
, equippedWeapon = maybeWeapon
, distanceHexes = args.distanceHexes + 1
}
|> Expect.equal 0
, Test.fuzz2 chanceToHitArgsFuzzer TestHelpers.meleeWeaponKindFuzzer "Melee + good range: can hit" <|
\args weapon ->
Logic.chanceToHit
{ args
| attackStyle = AttackStyle.MeleeUnaimed
, equippedWeapon = Just weapon
, distanceHexes = 1
, targetArmorClass = 0
}
|> Expect.greaterThan 0
, Test.fuzz2 chanceToHitArgsFuzzer TestHelpers.meleeWeaponKindFuzzer "Melee outside range: cannot hit" <|
\args weapon ->
Logic.chanceToHit
{ args
| attackStyle = AttackStyle.MeleeUnaimed
, equippedWeapon = Just weapon
, distanceHexes = args.distanceHexes + 1
}
|> Expect.equal 0
, Test.fuzz2 chanceToHitArgsFuzzer TestHelpers.gunKindFuzzer "Ranged + good range: can hit" <|
\args weapon ->
Logic.chanceToHit
{ args
| attackStyle = AttackStyle.ShootSingleUnaimed
, attackerSpecial = args.attackerSpecial |> Special.set Special.Strength 10
, equippedWeapon = Just weapon
, distanceHexes = 1
, targetArmorClass = 0
}
|> Expect.greaterThan 0
, Test.fuzz2 chanceToHitArgsFuzzer TestHelpers.gunKindFuzzer "Ranged outside range: cannot hit" <|
\args weapon ->
Logic.chanceToHit
{ args
| attackStyle = AttackStyle.ShootSingleUnaimed
, equippedWeapon = Just weapon
, distanceHexes = args.distanceHexes + 80
}
|> Expect.equal 0
]
]


log :
{ attackerAddedSkillPercentages : SeqDict Skill Int
, attackerPerks : SeqDict Perk Int
, attackerSpecial : Special
, distanceHexes : Int
, equippedWeapon : Maybe Item.Kind
, equippedAmmo : Maybe Item.Kind
, targetArmorClass : Int
, attackStyle : AttackStyle
}
->
{ attackerAddedSkillPercentages : SeqDict Skill Int
, attackerPerks : SeqDict Perk Int
, attackerSpecial : Special
, distanceHexes : Int
, equippedWeapon : Maybe Item.Kind
, equippedAmmo : Maybe Item.Kind
, targetArmorClass : Int
, attackStyle : AttackStyle
}
log args =
let
_ =
{ attackerAddedSkillPercentages = SeqDict.toList args.attackerAddedSkillPercentages
, attackerPerks = SeqDict.toList args.attackerPerks
, attackerSpecial = args.attackerSpecial
, distanceHexes = args.distanceHexes
, equippedWeapon = args.equippedWeapon
, equippedAmmo = args.equippedAmmo
, targetArmorClass = args.targetArmorClass
, attackStyle = args.attackStyle
}
|> Debug.log "chanceToHit args"
in
args


chanceToHitArgsFuzzer :
Fuzzer
{ attackerAddedSkillPercentages : SeqDict Skill Int
, attackerPerks : SeqDict Perk Int
, attackerSpecial : Special
, distanceHexes : Int
, equippedWeapon : Maybe Item.Kind
, equippedAmmo : Maybe Item.Kind
, targetArmorClass : Int
, attackStyle : AttackStyle
}
chanceToHitArgsFuzzer =
Fuzz.map8
(\attackerAddedSkillPercentages attackerPerks attackerSpecial distanceHexes equippedWeapon equippedAmmo targetArmorClass attackStyle ->
{ attackerAddedSkillPercentages = attackerAddedSkillPercentages
, attackerPerks = attackerPerks
, attackerSpecial = attackerSpecial
, distanceHexes = distanceHexes
, equippedWeapon = equippedWeapon
, equippedAmmo = equippedAmmo
, targetArmorClass = targetArmorClass
, attackStyle = attackStyle
}
)
TestHelpers.addedSkillPercentagesFuzzer
TestHelpers.perksFuzzer
TestHelpers.specialFuzzer
TestHelpers.distanceFuzzer
TestHelpers.equippedWeaponKindFuzzer
TestHelpers.equippedAmmoKindFuzzer
TestHelpers.armorClassFuzzer
TestHelpers.attackStyleFuzzer
Loading

0 comments on commit 66cac50

Please sign in to comment.