|
| 1 | +package com.jervisffb.engine.model.context |
| 2 | + |
| 3 | +import com.jervisffb.engine.actions.RerollOptionSelected |
| 4 | +import com.jervisffb.engine.commands.Command |
| 5 | +import com.jervisffb.engine.commands.context.AddContextListItem |
| 6 | +import com.jervisffb.engine.commands.context.SetContextProperty |
| 7 | +import com.jervisffb.engine.fsm.Procedure |
| 8 | +import com.jervisffb.engine.model.Game |
| 9 | +import com.jervisffb.engine.model.Player |
| 10 | +import com.jervisffb.engine.model.TurnOver |
| 11 | +import com.jervisffb.engine.model.locations.FieldCoordinate |
| 12 | +import com.jervisffb.engine.rules.DiceRollType |
| 13 | +import com.jervisffb.engine.rules.bb2020.procedures.actions.block.standard.StandardBlockApplyResult |
| 14 | +import com.jervisffb.engine.rules.bb2020.procedures.actions.block.standard.StandardBlockRerollDice |
| 15 | +import com.jervisffb.engine.rules.bb2020.procedures.actions.block.standard.StandardBlockRollDice |
| 16 | +import com.jervisffb.engine.rules.common.actions.BlockType |
| 17 | +import com.jervisffb.engine.rules.common.actions.BlockType.CHAINSAW |
| 18 | +import com.jervisffb.engine.rules.common.actions.BlockType.MULTIPLE_BLOCK |
| 19 | +import com.jervisffb.engine.rules.common.actions.BlockType.PROJECTILE_VOMIT |
| 20 | +import com.jervisffb.engine.rules.common.actions.BlockType.STAB |
| 21 | +import com.jervisffb.engine.rules.common.actions.BlockType.STANDARD |
| 22 | +import com.jervisffb.engine.rules.common.procedures.tables.injury.RiskingInjuryContext |
| 23 | +/** |
| 24 | + * Context containing state related to doing a Multiple Block. |
| 25 | + * |
| 26 | + * Note, this context has been flattened to make it easier to update, but |
| 27 | + * it exposes an API that makes it possible to access rolls using list |
| 28 | + * indexes. |
| 29 | + */ |
| 30 | +data class BB2025MultipleBlockContext( |
| 31 | + val attacker: Player, |
| 32 | + val defender1: Player? = null, |
| 33 | + val defender2: Player? = null, |
| 34 | + val actionAborted: Boolean = true, |
| 35 | + // Rolls for the two blocks |
| 36 | + val roll1: MultipleBlockDiceRoll? = null, |
| 37 | + val roll2: MultipleBlockDiceRoll? = null, |
| 38 | + // Tracks the index of which defender is currently in focus. If set, it must either be 0 or 1. |
| 39 | + var activeDefender: Int? = null, |
| 40 | + // If the blocks result in a push, all the data related to the push is stored here. |
| 41 | + var defender1PushChain: PushContext? = null, |
| 42 | + var defender2PushChain: PushContext? = null, |
| 43 | + // Tracks the ball for those players where it needs to bounce. Set back to `null` once the ball has bounced |
| 44 | + val defender1BallsHandled: Boolean = false, |
| 45 | + val defender2BallsHandled: Boolean = false, |
| 46 | + val attackerBallHandled: Boolean = false, |
| 47 | + // Set to true, if the player should be Knocked Down, when we come to that phase of Multiple Block |
| 48 | + val attackerKnockedDown: Boolean = false, |
| 49 | + val defender1KnockedDown: Boolean = false, |
| 50 | + val defender2KnockedDown: Boolean = false, |
| 51 | + // Set if any of the players involved received an injury. The attacker might suffer an |
| 52 | + // injury from both blocks |
| 53 | + val attackerInjuryContext: MutableList<RiskingInjuryContext> = mutableListOf(), |
| 54 | + var defender1InjuryContext: RiskingInjuryContext? = null, |
| 55 | + var defender2InjuryContext: RiskingInjuryContext? = null, |
| 56 | + // Set to true, if a turnover happened during the first block. |
| 57 | + var postponeTurnOver: TurnOver? = null, |
| 58 | + // Player starting locations (as they might leave the field due to injuries) |
| 59 | + val attackerLocation: FieldCoordinate = attacker.coordinates, |
| 60 | + val defender1Location: FieldCoordinate? = null, |
| 61 | + val defender2Location: FieldCoordinate? = null, |
| 62 | +): ProcedureContext { |
| 63 | + |
| 64 | + val rolls: List<MultipleBlockDiceRoll> |
| 65 | + get() = listOfNotNull(roll1, roll2) |
| 66 | + |
| 67 | + operator fun get(index: Int): MultipleBlockDiceRoll { |
| 68 | + return when (index) { |
| 69 | + 0 -> roll1!! |
| 70 | + 1 -> roll2!! |
| 71 | + else -> throw IllegalArgumentException("Invalid index: $index") |
| 72 | + } |
| 73 | + } |
| 74 | + |
| 75 | + fun getActiveRerollType(): BlockType { |
| 76 | + return get(activeDefender!!).type |
| 77 | + } |
| 78 | + |
| 79 | + fun updateRollContext(index: Int, updatedRollContext: ProcedureContext): Command { |
| 80 | + return when (index) { |
| 81 | + 0 -> SetContextProperty(MultipleBlockDiceRoll::rollContext, roll1!!, updatedRollContext) |
| 82 | + 1 -> SetContextProperty(MultipleBlockDiceRoll::rollContext, roll2!!, updatedRollContext) |
| 83 | + else -> throw IllegalArgumentException("Invalid roll index: $index") |
| 84 | + } |
| 85 | + } |
| 86 | + |
| 87 | + fun copyAndUpdateHasAcceptedResult(index: Int, hasAcceptedResult: Boolean): BB2025MultipleBlockContext { |
| 88 | + return when (index) { |
| 89 | + 0 -> copy(roll1 = roll1!!.copyAndSetHasAcceptedResult(hasAcceptedResult)) |
| 90 | + 1 -> copy(roll2 = roll2!!.copyAndSetHasAcceptedResult(hasAcceptedResult)) |
| 91 | + else -> throw IllegalArgumentException("Invalid index: $index") |
| 92 | + } |
| 93 | + } |
| 94 | + |
| 95 | + /** |
| 96 | + * Creates a [UseRerollContext] for currently active Multiple Block Action its reroll type |
| 97 | + */ |
| 98 | + fun createRerollContext(state: Game, action: RerollOptionSelected): UseRerollContext { |
| 99 | + return when (getActiveRerollType()) { |
| 100 | + BlockType.BREATHE_FIRE -> TODO() |
| 101 | + CHAINSAW -> TODO() |
| 102 | + MULTIPLE_BLOCK -> TODO() |
| 103 | + PROJECTILE_VOMIT -> TODO() |
| 104 | + STAB -> TODO() |
| 105 | + STANDARD -> UseRerollContext(DiceRollType.BLOCK, action.getRerollSource(state)) |
| 106 | + } |
| 107 | + } |
| 108 | + |
| 109 | + fun getRollDiceProcedure(): Procedure { |
| 110 | + return when (getActiveRerollType()) { |
| 111 | + BlockType.BREATHE_FIRE -> TODO() |
| 112 | + CHAINSAW -> TODO() |
| 113 | + MULTIPLE_BLOCK -> TODO() |
| 114 | + PROJECTILE_VOMIT -> TODO() |
| 115 | + STAB -> TODO() |
| 116 | + STANDARD -> StandardBlockRollDice |
| 117 | + } |
| 118 | + } |
| 119 | + /** |
| 120 | + * Returns the Procedure used to reroll dice for the given block type. |
| 121 | + */ |
| 122 | + fun getRerollDiceProcedure(): Procedure { |
| 123 | + return when (getActiveRerollType()) { |
| 124 | + BlockType.BREATHE_FIRE -> TODO() |
| 125 | + CHAINSAW -> TODO() |
| 126 | + MULTIPLE_BLOCK -> TODO() |
| 127 | + PROJECTILE_VOMIT -> TODO() |
| 128 | + STAB -> TODO() |
| 129 | + STANDARD -> StandardBlockRerollDice |
| 130 | + } |
| 131 | + } |
| 132 | + |
| 133 | + /** |
| 134 | + * Returns the procedure responsible for applying a active block type |
| 135 | + */ |
| 136 | + fun getResolveBlockResultProcedure(): Procedure { |
| 137 | + return when (getActiveRerollType()) { |
| 138 | + BlockType.BREATHE_FIRE -> TODO() |
| 139 | + CHAINSAW -> TODO() |
| 140 | + MULTIPLE_BLOCK -> TODO() |
| 141 | + PROJECTILE_VOMIT -> TODO() |
| 142 | + STAB -> TODO() |
| 143 | + STANDARD -> StandardBlockApplyResult |
| 144 | + } |
| 145 | + } |
| 146 | + |
| 147 | + /** |
| 148 | + * Calling this method will retrieve the roll context for the given |
| 149 | + * block type and replace the active [MultipleBlockDiceRoll.rollContext] |
| 150 | + * with it. |
| 151 | + */ |
| 152 | + fun updateWithLatestBlockTypeContext(state: Game): Command { |
| 153 | + val updatedContext = when (getActiveRerollType()) { |
| 154 | + BlockType.BREATHE_FIRE -> TODO() |
| 155 | + CHAINSAW -> TODO() |
| 156 | + MULTIPLE_BLOCK -> TODO() |
| 157 | + PROJECTILE_VOMIT -> TODO() |
| 158 | + STAB -> TODO() |
| 159 | + STANDARD -> state.getContext<BlockContext>() |
| 160 | + } |
| 161 | + return updateRollContext(activeDefender!!, updatedContext) |
| 162 | + } |
| 163 | + |
| 164 | + /** |
| 165 | + * Remove the provided player from the context. Also unset it from being |
| 166 | + * active if it was set there. |
| 167 | + * |
| 168 | + * Will throw exception if player was not found |
| 169 | + */ |
| 170 | + fun copyAndUnsetDefender(player: Player): ProcedureContext { |
| 171 | + return when (player) { |
| 172 | + defender1 -> copy(defender1 = null, activeDefender = if (activeDefender == 0) null else 0) |
| 173 | + defender2 -> copy(defender2 = null, activeDefender = if (activeDefender == 1) null else 1) |
| 174 | + else -> throw IllegalArgumentException("Invalid defender: $player") |
| 175 | + } |
| 176 | + } |
| 177 | + |
| 178 | + /** |
| 179 | + * Return the commands needed to add an Injury to the injury pool. |
| 180 | + */ |
| 181 | + fun addInjuryReferenceForPlayer(player: Player, injuryContext: RiskingInjuryContext): Command { |
| 182 | + return when (player) { |
| 183 | + attacker -> AddContextListItem(attackerInjuryContext, injuryContext) |
| 184 | + defender1 -> SetContextProperty(BB2025MultipleBlockContext::defender1InjuryContext, this, injuryContext) |
| 185 | + defender2 -> SetContextProperty(BB2025MultipleBlockContext::defender2InjuryContext, this, injuryContext) |
| 186 | + else -> throw IllegalArgumentException("Invalid player: $player") |
| 187 | + } |
| 188 | + } |
| 189 | + |
| 190 | + /** |
| 191 | + * Sets the block type for the current active defender. |
| 192 | + * This also c |
| 193 | + */ |
| 194 | + fun copyAndSetBlockTypeForActiveDefender(type: BlockType): BB2025MultipleBlockContext { |
| 195 | + val defender = when (activeDefender) { |
| 196 | + 0 -> defender1!! |
| 197 | + 1 -> defender2!! |
| 198 | + else -> throw IllegalStateException("Invalid active defender") |
| 199 | + } |
| 200 | + |
| 201 | + val context = when (type) { |
| 202 | + BlockType.BREATHE_FIRE -> TODO() |
| 203 | + CHAINSAW -> TODO() |
| 204 | + MULTIPLE_BLOCK -> TODO() |
| 205 | + PROJECTILE_VOMIT -> TODO() |
| 206 | + STAB -> TODO() |
| 207 | + STANDARD -> BlockContext( |
| 208 | + attacker = attacker, |
| 209 | + defender = defender, |
| 210 | + isUsingMultiBlock = true |
| 211 | + ) |
| 212 | + } |
| 213 | + |
| 214 | + return when (activeDefender) { |
| 215 | + 0 -> copy(roll1 = MultipleBlockDiceRoll(type, context)) |
| 216 | + 1 -> copy(roll2 = MultipleBlockDiceRoll(type, context)) |
| 217 | + else -> throw IllegalArgumentException("Invalid active defender: $activeDefender") |
| 218 | + } |
| 219 | + } |
| 220 | + |
| 221 | + fun getActiveDefender(): Player? { |
| 222 | + return when (activeDefender) { |
| 223 | + 0 -> return defender1!! |
| 224 | + 1 -> return defender2!! |
| 225 | + else -> null |
| 226 | + } |
| 227 | + } |
| 228 | + |
| 229 | + /** |
| 230 | + * Returns the block context for the currently active defender. |
| 231 | + * Note, it is the context stored in _this_ context that is returned, |
| 232 | + * and not the one stored globally. |
| 233 | + * |
| 234 | + * See [updateWithLatestBlockTypeContext] for that. |
| 235 | + */ |
| 236 | + fun getContextForCurrentBlock(): ProcedureContext { |
| 237 | + return when (activeDefender) { |
| 238 | + 0 -> roll1!!.rollContext |
| 239 | + 1 -> roll2!!.rollContext |
| 240 | + else -> throw IllegalArgumentException("Invalid active defender: $activeDefender") |
| 241 | + } |
| 242 | + } |
| 243 | + |
| 244 | + fun copyAndKnockDownActiveDefender(knockedDown: Boolean): BB2025MultipleBlockContext { |
| 245 | + return when (activeDefender) { |
| 246 | + 0 -> this.copy(defender1KnockedDown = knockedDown) |
| 247 | + 1 -> this.copy(defender2KnockedDown = knockedDown) |
| 248 | + else -> throw IllegalArgumentException("Invalid active defender: $activeDefender") |
| 249 | + } |
| 250 | + } |
| 251 | +} |
0 commit comments