Skip to content

Commit 6885613

Browse files
committed
Add support for "Taunt" and "Fend" skills
- Support for both skills added during the follow up phase of a block - Split logic for BB2020 and BB2025 with regard to push chain and follow up. I am not sure this is needed, but doing it now to be safe. - Added UI Action Wheels and Game Status messages for using both Taunt and Fend - For now, we first ask for Fend, then Taunt. This is done with the assumption that no players will have both skills (or rarely). Since they are mutually exclusive, a better UX would probably be to ask to choose between them, but it would require more UI work than what is worth to implement at this stage.
1 parent cc66524 commit 6885613

File tree

29 files changed

+1191
-146
lines changed

29 files changed

+1191
-146
lines changed

docs/bb2025/rules-faq-bb2025.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,13 +178,27 @@ While this skill is optional, it only affects how many dice are rolled and not
178178
the final outcome. This combined with the fact that there are no scenario where
179179
you might want to _not_ use Guard, we always apply it.
180180

181+
### Page 129 - Juggernaut
182+
Juggernaut cancels Fend, Stand Firm and Wrestle for both the blocked player
183+
and all other opponents in the push chain.
184+
185+
This interpretation comes from the Juggernaut skill description: "..opposition
186+
players cannot use..." (note plural and specifically mentioning opposition).
187+
188+
In particular, this means that in a Chain Push scenario, a player with
189+
Juggernaut will cancel Stand Firm for all opponent players, but not players on
190+
their own team.
191+
181192
### Page 130 - Kick
182193
Jervis uses the NAF interpretation, which means that you are allowed to roll the
183194
deviate die (D6) and then choose to treat the roll as either a D6 or D3.
184195

185196
In the rulebook, D3's are defined as Rolling a D6, taking half the value and
186197
round up.
187198

199+
### Page 132 - Multiple Block
200+
- Taunt: Multiple Block prevents follow-ups, so Taunt has no effect.
201+
188202
### Page 132 - No Ball
189203
Technically, you are allowed to user other skills like Extra Arms, but since
190204
it would be pointless, Jervis just ignore these skills.

docs/bb2025/todo-skills-bb2025.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,10 @@ a test class in `modules/jervis-engine/src/commonTest/kotlin/dk/ilios/jervis/bb2
8282
- [ ] Defender chooses bloc
8383
- [ ] Dauntless
8484
- [ ] Fend
85-
- [ ] Prevents Frenzy from working
85+
- [x] Prevent follow up
86+
- [ ] Does not work against Ball & Chain
87+
- [ ] Does not work against Juggernaut
88+
- [x] Prevent follow up from Frenzy
8689
- [ ] Frenzy*
8790
- [ ] Must take two blocks if player if able
8891
- [ ] Works during Block and Blitz
@@ -112,6 +115,10 @@ a test class in `modules/jervis-engine/src/commonTest/kotlin/dk/ilios/jervis/bb2
112115
- [x] Only use Tackle if other player has Dodge
113116
- [x] Defender does not count as having dodge during a Block
114117
- [ ] Taunt
118+
- [x] Pushed Back
119+
- [x] Stumble with Dodge
120+
- [x] Cannot use Taunt if Fend is selected
121+
- [ ] Does not work against Rooted
115122
- [ ] Wrestle
116123

117124
## Mutations
@@ -213,7 +220,8 @@ a test class in `modules/jervis-engine/src/commonTest/kotlin/dk/ilios/jervis/bb2
213220
- [ ] Mighty Blow
214221
- [ ] Multiple Block
215222
- [ ] During Multiple Block: Scoring Turnovers will win over End-Turn turnovers.
216-
- [ ] Tackle on both blocks
223+
- [ ] Tackle is available for both blocks
224+
- [ ] Taunt does not work against Multiple Block
217225
- [ ] Stand Firm
218226
- [ ] Strong Arm
219227
- [ ] Thick Skull

docs/bugs.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
This file is just a temporary way to capture any bugs seen that I didn't have time to investigate yet.
44

55
## Known bugs
6+
- Move Counter is not updated on forced follow-up during a blitz, like when using Taunt
7+
- Move counter is wrong when using the first GFI to Blitz and then the 2nd GFI to move in another direction.
68
- Move counter on field squares does not reset when a players action ends immediately (like when using Safe Pass)
79
- Action Wheel animation isn't correct when animating D6 rererolls when automatically selecting the reroll type.
810
It looks like two animations are running over each other or that some positions are being reused.

modules/fumbbl-net/src/commonMain/kotlin/com/jervisffb/fumbbl/net/adapter/impl/InjuryRollMapper.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import com.jervisffb.engine.actions.DirectionSelected
77
import com.jervisffb.engine.actions.SelectDirection
88
import com.jervisffb.engine.model.Game
99
import com.jervisffb.engine.rules.Rules
10-
import com.jervisffb.engine.rules.common.procedures.actions.block.PushStepInitialMoveSequence
10+
import com.jervisffb.engine.rules.bb2025.procedures.actions.block.BB2025PushStepInitialMoveSequence
1111
import com.jervisffb.engine.rules.common.procedures.tables.injury.ArmourRoll
1212
import com.jervisffb.engine.rules.common.procedures.tables.injury.CasualtyRoll
1313
import com.jervisffb.engine.rules.common.procedures.tables.injury.InjuryRoll
@@ -42,10 +42,10 @@ object InjuryRollMapper: CommandActionMapper {
4242
newActions.add(
4343
action = { state: Game, rules: Rules ->
4444
// Any of them will push the player of the field
45-
val action = PushStepInitialMoveSequence.SelectPushDirection.getAvailableActions(state, rules).single() as SelectDirection
45+
val action = BB2025PushStepInitialMoveSequence.SelectPushDirection.getAvailableActions(state, rules).single() as SelectDirection
4646
DirectionSelected(action.directions.random())
4747
},
48-
expectedNode = PushStepInitialMoveSequence.SelectPushDirection
48+
expectedNode = BB2025PushStepInitialMoveSequence.SelectPushDirection
4949
)
5050
}
5151

modules/fumbbl-net/src/commonMain/kotlin/com/jervisffb/fumbbl/net/adapter/impl/block/FollowUpMapper.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package com.jervisffb.fumbbl.net.adapter.impl.blitz
33
import com.jervisffb.engine.actions.Cancel
44
import com.jervisffb.engine.actions.Confirm
55
import com.jervisffb.engine.model.Game
6-
import com.jervisffb.engine.rules.common.procedures.actions.block.PushStepInitialMoveSequence
6+
import com.jervisffb.engine.rules.bb2025.procedures.actions.block.BB2025PushStepInitialMoveSequence
77
import com.jervisffb.fumbbl.net.adapter.CommandActionMapper
88
import com.jervisffb.fumbbl.net.adapter.JervisActionHolder
99
import com.jervisffb.fumbbl.net.adapter.add
@@ -48,9 +48,9 @@ object FollowUpMapper: CommandActionMapper {
4848
(previousPreviousCommand is GameSetDialogParameter) &&
4949
previousPreviousCommand.value?.dialogId == com.jervisffb.fumbbl.net.model.DialogId.FOLLOWUP_CHOICE
5050
) {
51-
newActions.add(Confirm, PushStepInitialMoveSequence.DecideToFollowUp)
51+
newActions.add(Confirm, BB2025PushStepInitialMoveSequence.ChooseToFollowUp)
5252
} else {
53-
newActions.add(Cancel, PushStepInitialMoveSequence.DecideToFollowUp)
53+
newActions.add(Cancel, BB2025PushStepInitialMoveSequence.ChooseToFollowUp)
5454
}
5555
}
5656
}

modules/fumbbl-net/src/commonMain/kotlin/com/jervisffb/fumbbl/net/adapter/impl/block/PushbackMapper.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package com.jervisffb.fumbbl.net.adapter.impl.blitz
22

33
import com.jervisffb.engine.actions.DirectionSelected
44
import com.jervisffb.engine.model.Game
5-
import com.jervisffb.engine.rules.common.procedures.actions.block.PushStepInitialMoveSequence
5+
import com.jervisffb.engine.rules.bb2025.procedures.actions.block.BB2025PushStepInitialMoveSequence
66
import com.jervisffb.fumbbl.net.adapter.CommandActionMapper
77
import com.jervisffb.fumbbl.net.adapter.JervisActionHolder
88
import com.jervisffb.fumbbl.net.adapter.add
@@ -48,7 +48,7 @@ object PushbackMapper: CommandActionMapper {
4848
} as FieldModelRemovePushbackSquare
4949
newActions.add(
5050
DirectionSelected(cmd.value.direction.transformToJervisDirection()),
51-
PushStepInitialMoveSequence.SelectPushDirection
51+
BB2025PushStepInitialMoveSequence.SelectPushDirection
5252
)
5353
}
5454
}

modules/fumbbl-net/src/commonMain/kotlin/com/jervisffb/fumbbl/net/adapter/impl/block/PushbackUsingSideStepMapper.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package com.jervisffb.fumbbl.net.adapter.impl.blitz
33
import com.jervisffb.engine.actions.Confirm
44
import com.jervisffb.engine.actions.FieldSquareSelected
55
import com.jervisffb.engine.model.Game
6-
import com.jervisffb.engine.rules.common.procedures.actions.block.PushStepInitialMoveSequence
6+
import com.jervisffb.engine.rules.bb2025.procedures.actions.block.BB2025PushStepInitialMoveSequence
77
import com.jervisffb.fumbbl.net.adapter.CommandActionMapper
88
import com.jervisffb.fumbbl.net.adapter.JervisActionHolder
99
import com.jervisffb.fumbbl.net.adapter.add
@@ -39,7 +39,7 @@ object PushbackUsingSideStepMapper: CommandActionMapper {
3939
} as FieldModelRemovePushbackSquare
4040
val target = cmd.value.coordinate
4141

42-
newActions.add(Confirm, PushStepInitialMoveSequence.DecideToUseSidestep)
43-
newActions.add(FieldSquareSelected(target.x, target.y), PushStepInitialMoveSequence.SelectPushDirection)
42+
newActions.add(Confirm, BB2025PushStepInitialMoveSequence.DecideToUseSidestep)
43+
newActions.add(FieldSquareSelected(target.x, target.y), BB2025PushStepInitialMoveSequence.SelectPushDirection)
4444
}
4545
}

modules/jervis-engine/src/commonMain/kotlin/com/jervisffb/engine/commands/context/SetContextProperty.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package com.jervisffb.engine.commands.context
22

33
import com.jervisffb.engine.commands.Command
44
import com.jervisffb.engine.model.Game
5-
import com.jervisffb.engine.rules.common.procedures.actions.block.PushContext
65
import kotlin.reflect.KMutableProperty1
76

87
/**
@@ -44,7 +43,6 @@ import kotlin.reflect.KMutableProperty1
4443
* scanning nodes. So it feels like hiding these behind helper functions
4544
* makes it a bit more readable.
4645
*
47-
* @see PushContext
4846
* @see com.jervisffb.engine.model.context.DodgeRollContext
4947
*/
5048
class SetContextProperty<T, V>(

modules/jervis-engine/src/commonMain/kotlin/com/jervisffb/engine/model/context/MultipleBlockContext.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import com.jervisffb.engine.rules.common.actions.BlockType.STAB
2323
import com.jervisffb.engine.rules.common.actions.BlockType.STANDARD
2424
import com.jervisffb.engine.rules.common.procedures.DieRoll
2525
import com.jervisffb.engine.rules.common.procedures.actions.block.BlockContext
26-
import com.jervisffb.engine.rules.common.procedures.actions.block.PushContext
2726
import com.jervisffb.engine.rules.common.procedures.actions.block.standard.StandardBlockApplyResult
2827
import com.jervisffb.engine.rules.common.procedures.actions.block.standard.StandardBlockRerollDice
2928
import com.jervisffb.engine.rules.common.procedures.actions.block.standard.StandardBlockRollDice
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package com.jervisffb.engine.model.context
2+
3+
import com.jervisffb.engine.model.Ball
4+
import com.jervisffb.engine.model.Player
5+
import com.jervisffb.engine.model.hasSkill
6+
import com.jervisffb.engine.model.locations.FieldCoordinate
7+
import com.jervisffb.engine.rules.common.skills.SkillType
8+
9+
data class PushContext(
10+
// Player starting the first push
11+
val firstPusher: Player,
12+
// First player being pushed
13+
val firstPushee: Player,
14+
// Tracks if the attacker is using Juggernaut.
15+
val isAttackerUsingJuggernaut: Boolean,
16+
// firstPushee is knocked down after the pushback has resolved. So if they
17+
// have the ball, it will bounce.
18+
val isDefenderKnockedDown: Boolean,
19+
// Is the push part of a multiple block
20+
val isMultipleBlock: Boolean,
21+
// Chain of pushes. For a single push, this contains one element
22+
// Should only be modified from within the `PushStep` procedure.
23+
val pushChain: MutableList<PushData>,
24+
// If `true`, the `firstPusher` will use Fend, preventing follow-up
25+
var defenderIsUsingFend: Boolean = false,
26+
// If `true`, the `firstPusher` will use Taunt, forcing a follow-up
27+
var defenderIsUsingTaunt: Boolean = false,
28+
// If `true`, the `firstPusher` will follow up after resolving the rest of the chain.
29+
var followsUp: Boolean = false,
30+
// Temporary state tracking the current player being resolved for this push step.
31+
var fullyResolveInProgress: Player? = null,
32+
// Track any balls that must bounce after the push is resolved.
33+
// Either because a player was pushed into it, or because a trapdoor
34+
// swallowed a player with a ball. Balls should be added and resolved in
35+
// order.
36+
val looseBalls: MutableList<Ball> = mutableListOf(),
37+
) : ProcedureContext {
38+
39+
// Tracks if the attacker is using Juggernaut.
40+
val isAttackerUsingFrenzy: Boolean = firstPusher.hasSkill(SkillType.FRENZY)
41+
42+
// Returns last "pusher" in the push chain
43+
fun pusher(): Player {
44+
return pushChain.last().pusher
45+
}
46+
47+
// Returns the last "pushee" in the chain
48+
fun pushee(): Player {
49+
return pushChain.last().pushee
50+
}
51+
52+
data class PushData(
53+
val pusher: Player,
54+
val pushee: Player,
55+
// Where is the pushee being pushed from?
56+
val from: FieldCoordinate,
57+
// Where is the pushee being pushed to?
58+
var to: FieldCoordinate? = null, // If `null` push direction has not been selected yet
59+
val isBlitzing: Boolean = false, // If first pusher is doing a Blitz
60+
val isChainPush: Boolean = false, // True for every push beyond the first
61+
var usedGrab: Boolean = false,
62+
var usedStandFirm: Boolean = false,
63+
var usedSideStep: Boolean = false,
64+
// Set to `true` if we checked the player in this step for scoring
65+
var checkedForScoringAfterTrapdoors: Boolean = false,
66+
)
67+
}

0 commit comments

Comments
 (0)