@firestone-hs/simulate-bgs-battle 1.1.759 → 1.1.761
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/board-entity.d.ts +1 -0
- package/dist/board-entity.js.map +1 -1
- package/dist/cards/cards-data.d.ts +1 -0
- package/dist/cards/cards-data.js +5 -0
- package/dist/cards/cards-data.js.map +1 -1
- package/dist/cards/impl/bg-spell/brood-of-nozdormu.js +1 -1
- package/dist/cards/impl/bg-spell/brood-of-nozdormu.js.map +1 -1
- package/dist/cards/impl/enchantments/deaths-embrace.js +1 -3
- package/dist/cards/impl/enchantments/deaths-embrace.js.map +1 -1
- package/dist/cards/impl/enchantments/doppelganger.js +1 -1
- package/dist/cards/impl/enchantments/doppelganger.js.map +1 -1
- package/dist/cards/impl/enchantments/spell-siphon.d.ts +2 -2
- package/dist/cards/impl/enchantments/spell-siphon.js +46 -5
- package/dist/cards/impl/enchantments/spell-siphon.js.map +1 -1
- package/dist/cards/impl/minion/bramble-tunneler.js +1 -1
- package/dist/cards/impl/minion/bramble-tunneler.js.map +1 -1
- package/dist/cards/impl/minion/lurking-leviathan.js +2 -1
- package/dist/cards/impl/minion/lurking-leviathan.js.map +1 -1
- package/dist/cards/impl/minion/relentless-deflector.js +3 -0
- package/dist/cards/impl/minion/relentless-deflector.js.map +1 -1
- package/dist/cards/impl/minion/sinrunner-blanchy.js +1 -0
- package/dist/cards/impl/minion/sinrunner-blanchy.js.map +1 -1
- package/dist/cards/impl/minion/tasty-lobster.js +3 -1
- package/dist/cards/impl/minion/tasty-lobster.js.map +1 -1
- package/dist/cards/impl/minion/underrot-spawn.js +1 -1
- package/dist/cards/impl/minion/underrot-spawn.js.map +1 -1
- package/dist/cards/impl/minion/wildfire-elemental.js +2 -1
- package/dist/cards/impl/minion/wildfire-elemental.js.map +1 -1
- package/dist/cards/impl/trinket/hammer-of-twilight.js +10 -3
- package/dist/cards/impl/trinket/hammer-of-twilight.js.map +1 -1
- package/dist/input-sanitation.js +1 -0
- package/dist/input-sanitation.js.map +1 -1
- package/dist/simulation/death-effects.js +3 -0
- package/dist/simulation/death-effects.js.map +1 -1
- package/dist/simulation/deathrattle-orchestration.js +2 -1
- package/dist/simulation/deathrattle-orchestration.js.map +1 -1
- package/dist/simulation/minion-death.js +5 -1
- package/dist/simulation/minion-death.js.map +1 -1
- package/dist/simulation/simulator.js +5 -11
- package/dist/simulation/simulator.js.map +1 -1
- package/dist/simulation/start-of-combat/soc-action-processor.d.ts +1 -1
- package/dist/simulation/start-of-combat/soc-action-processor.js +10 -8
- package/dist/simulation/start-of-combat/soc-action-processor.js.map +1 -1
- package/dist/simulation/start-of-combat/soc-minion.js +3 -12
- package/dist/simulation/start-of-combat/soc-minion.js.map +1 -1
- package/dist/simulation/summon-when-space.js +1 -0
- package/dist/simulation/summon-when-space.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"minion-death.js","sourceRoot":"","sources":["../../src/simulation/minion-death.ts"],"names":[],"mappings":"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","sourcesContent":["import { BgsPlayerEntity } from '../bgs-player-entity';\r\nimport { BoardEntity } from '../board-entity';\r\nimport { applyRunicEmpowermentOnFriendlyDeath } from '../cards/impl/hero-power/runic-empowerment';\r\nimport { CardIds } from '../services/card-ids';\r\nimport { pickMultipleRandomAlive } from '../services/utils';\r\nimport { getEffectiveTribesForEntity } from '../utils';\r\nimport { updateAvengeCounters } from './avenge';\r\nimport { addCardsInHand } from './cards-in-hand';\r\nimport { handleWheneverMinionsKillEffect } from './deathrattle-effects';\r\nimport { FullGameState } from './internal-game-state';\r\nimport { onQuestProgressUpdated } from './quest';\r\nimport { removeMinionFromBoard } from './remove-minion-from-board';\r\nimport { modifyStats } from './stats';\r\n\r\nexport const makeMinionsDie = (\r\n\tboard: BoardEntity[],\r\n\tboardHero: BgsPlayerEntity,\r\n\totherBoard: BoardEntity[],\r\n\totherBoardHero: BgsPlayerEntity,\r\n\tgameState: FullGameState,\r\n): [number[], number[], BoardEntity[]] => {\r\n\t// Because entities spawn to the left, so the right index is unchanged\r\n\tconst deadMinionIndexesFromLeft: number[] = [];\r\n\tconst deadMinionIndexesFromRight: number[] = [];\r\n\tconst deadEntities: BoardEntity[] = [];\r\n\tconst initialBoardLength = board.length;\r\n\r\n\thandleWheneverMinionsKillEffect(board, boardHero, otherBoard, otherBoardHero, gameState);\r\n\tfor (let i = 0; i < board.length; i++) {\r\n\t\tif (board[i].health <= 0 || board[i].definitelyDead) {\r\n\t\t\tdeadMinionIndexesFromLeft.push(i);\r\n\t\t\tdeadMinionIndexesFromRight.push(initialBoardLength - (i + 1));\r\n\t\t\tdeadEntities.push(board[i]);\r\n\t\t\t// console.log(\r\n\t\t\t// \t'\\tflagging dead minion 0',\r\n\t\t\t// \tstringifySimpleCard(board[i], allCards),\r\n\t\t\t// \tstringifySimple(board, allCards),\r\n\t\t\t// \tinitialBoardLength,\r\n\t\t\t// \ti,\r\n\t\t\t// \tdeadMinionIndexesFromRight,\r\n\t\t\t// );\r\n\t\t}\r\n\t}\r\n\r\n\t// These will always be processed from left to right afterwards\r\n\t// We compute the indexes as they will be once the new board is effective. For a\r\n\t// board of length N, having an indexFromRight at N means it will spawn at the very left\r\n\t// of the board (first minion)\r\n\tlet indexesFromRightAfterDeath = [];\r\n\tfor (let i = deadMinionIndexesFromRight.length - 1; i >= 0; i--) {\r\n\t\tconst newIndex = deadMinionIndexesFromRight[i] - indexesFromRightAfterDeath.length;\r\n\t\tindexesFromRightAfterDeath.push(newIndex);\r\n\t}\r\n\tindexesFromRightAfterDeath = indexesFromRightAfterDeath.reverse();\r\n\r\n\tfor (let i = 0; i < board.length; i++) {\r\n\t\tif (board[i].health <= 0 || board[i].definitelyDead) {\r\n\t\t\t// console.log('\\tflagging dead minion', stringifySimpleCard(board[i], allCards), deadMinionIndexesFromRight);\r\n\t\t\tremoveMinionFromBoard(board, boardHero, i, gameState);\r\n\t\t\t// We modify the original array, so we need to update teh current index accordingly\r\n\t\t\ti--;\r\n\t\t}\r\n\t}\r\n\r\n\t// console.debug('dead entities', stringifySimple(deadEntities, allCards));\r\n\t// Update the avenge counters as soon as minions die. If we wait until the \"avenge\" phase, we might\r\n\t// update the counters for entities that have been spawned after the death of the original entity\r\n\t// ISSUE: deaths are actually processed one by one. Once a minion dies, its DR triggers, then the next, etc.\r\n\t// This means that if you have a minion that summons another one, it can progress and complete and quest\r\n\t// and the next minion dying could count towards that quest progress\r\n\t// See http://replays.firestoneapp.com/?reviewId=0ce4db9c-3269-4704-b662-8a8c31f5afe1&turn=16&action=27\r\n\tfor (const deadEntity of deadEntities) {\r\n\t\tconst indexFromRight = indexesFromRightAfterDeath[deadEntities.indexOf(deadEntity)];\r\n\t\tupdateAvengeCounters(board, boardHero);\r\n\t\tonMinionDeadHeroPower(board, boardHero, deadEntity, gameState);\r\n\t\tonMinionDeadHeroPower(otherBoard, otherBoardHero, deadEntity, gameState);\r\n\t\tonMinionDeadQuest(deadEntity, indexFromRight, board, boardHero, otherBoard, otherBoardHero, gameState);\r\n\t\tonMinionDeadQuest(deadEntity, indexFromRight, otherBoard, otherBoardHero, board, boardHero, gameState);\r\n\t}\r\n\r\n\treturn [deadMinionIndexesFromLeft, indexesFromRightAfterDeath, deadEntities];\r\n};\r\n\r\nexport const onMinionDeadHeroPower = (\r\n\tboard: BoardEntity[],\r\n\tboardHero: BgsPlayerEntity,\r\n\tdeadEntity: BoardEntity,\r\n\tgameState: FullGameState,\r\n) => {\r\n\tfor (const heroPower of boardHero.heroPowers) {\r\n\t\tif (\r\n\t\t\theroPower.cardId === CardIds.IllTakeThat &&\r\n\t\t\theroPower.used &&\r\n\t\t\theroPower.info2 <= 0 &&\r\n\t\t\tdeadEntity.friendly !== boardHero.friendly\r\n\t\t) {\r\n\t\t\taddCardsInHand(boardHero, board, [deadEntity.cardId], gameState);\r\n\t\t\theroPower.info2 = 1;\r\n\t\t}\r\n\t\tapplyRunicEmpowermentOnFriendlyDeath(heroPower, boardHero, deadEntity);\r\n\t}\r\n};\r\n\r\nexport const onMinionDeadQuest = (\r\n\tdeadEntity: BoardEntity,\r\n\tindexFromRight: number,\r\n\tboard: BoardEntity[],\r\n\tboardHero: BgsPlayerEntity,\r\n\totherBoard: BoardEntity[],\r\n\totherBoardHero: BgsPlayerEntity,\r\n\tgameState: FullGameState,\r\n) => {\r\n\tconst quests = boardHero.questEntities ?? [];\r\n\tfor (const quest of quests) {\r\n\t\tswitch (quest.CardId) {\r\n\t\t\tcase CardIds.ReenactTheMurder:\r\n\t\t\t\tif (deadEntity.friendly === boardHero.friendly) {\r\n\t\t\t\t\tonQuestProgressUpdated(boardHero, quest, board, gameState);\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\t\t\tcase CardIds.RoundUpTheSuspects:\r\n\t\t\t\tif (deadEntity.friendly !== boardHero.friendly) {\r\n\t\t\t\t\tonQuestProgressUpdated(boardHero, quest, board, gameState);\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\r\n\tfor (const trinket of boardHero.trinkets) {\r\n\t\tswitch (trinket.cardId) {\r\n\t\t\tcase CardIds.AllianceKeychain_BG30_MagicItem_433:\r\n\t\t\tcase CardIds.AllianceKeychain_AllianceKeychainToken_BG30_MagicItem_433t:\r\n\t\t\t\tif (trinket.scriptDataNum1 > 0 && deadEntity.friendly === boardHero.friendly) {\r\n\t\t\t\t\tconst quantity = trinket.cardId === CardIds.AllianceKeychain_BG30_MagicItem_433 ? 1 : 2;\r\n\t\t\t\t\tconst targets = pickMultipleRandomAlive(board, quantity);\r\n\t\t\t\t\tfor (const target of targets) {\r\n\t\t\t\t\t\tmodifyStats(\r\n\t\t\t\t\t\t\ttarget,\r\n\t\t\t\t\t\t\tboardHero,\r\n\t\t\t\t\t\t\tdeadEntity.maxAttack,\r\n\t\t\t\t\t\t\tdeadEntity.maxHealth,\r\n\t\t\t\t\t\t\tboard,\r\n\t\t\t\t\t\t\tboardHero,\r\n\t\t\t\t\t\t\tgameState,\r\n\t\t\t\t\t\t);\r\n\t\t\t\t\t}\r\n\t\t\t\t\ttrinket.scriptDataNum1--;\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\t\t\tcase CardIds.TheEyeOfDalaran_BG30_MagicItem_981:\r\n\t\t\t\tif (\r\n\t\t\t\t\tdeadEntity.friendly === boardHero.friendly &&\r\n\t\t\t\t\tgetEffectiveTribesForEntity(deadEntity, boardHero, gameState.anomalies, gameState.allCards)\r\n\t\t\t\t\t\t.length === 0\r\n\t\t\t\t) {\r\n\t\t\t\t\taddCardsInHand(boardHero, board, [null], gameState);\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n};\r\n"]}
|
|
1
|
+
{"version":3,"file":"minion-death.js","sourceRoot":"","sources":["../../src/simulation/minion-death.ts"],"names":[],"mappings":"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","sourcesContent":["import { BgsPlayerEntity } from '../bgs-player-entity';\r\nimport { BoardEntity } from '../board-entity';\r\nimport { applyRunicEmpowermentOnFriendlyDeath } from '../cards/impl/hero-power/runic-empowerment';\r\nimport { CardIds } from '../services/card-ids';\r\nimport { pickMultipleRandomAlive } from '../services/utils';\r\nimport { getEffectiveTribesForEntity } from '../utils';\r\nimport { updateAvengeCounters } from './avenge';\r\nimport { addCardsInHand } from './cards-in-hand';\r\nimport { handleWheneverMinionsKillEffect } from './deathrattle-effects';\r\nimport { FullGameState } from './internal-game-state';\r\nimport { onQuestProgressUpdated } from './quest';\r\nimport { removeMinionFromBoard } from './remove-minion-from-board';\r\nimport { modifyStats } from './stats';\r\n\r\nexport const makeMinionsDie = (\r\n\tboard: BoardEntity[],\r\n\tboardHero: BgsPlayerEntity,\r\n\totherBoard: BoardEntity[],\r\n\totherBoardHero: BgsPlayerEntity,\r\n\tgameState: FullGameState,\r\n): [number[], number[], BoardEntity[]] => {\r\n\t// Because entities spawn to the left, so the right index is unchanged\r\n\tconst deadMinionIndexesFromLeft: number[] = [];\r\n\tconst deadMinionIndexesFromRight: number[] = [];\r\n\tconst deadEntities: BoardEntity[] = [];\r\n\tconst initialBoardLength = board.length;\r\n\r\n\thandleWheneverMinionsKillEffect(board, boardHero, otherBoard, otherBoardHero, gameState);\r\n\tfor (let i = 0; i < board.length; i++) {\r\n\t\tif (board[i].health <= 0 || board[i].definitelyDead) {\r\n\t\t\tdeadMinionIndexesFromLeft.push(i);\r\n\t\t\tdeadMinionIndexesFromRight.push(initialBoardLength - (i + 1));\r\n\t\t\tdeadEntities.push(board[i]);\r\n\t\t\t// console.log(\r\n\t\t\t// \t'\\tflagging dead minion 0',\r\n\t\t\t// \tstringifySimpleCard(board[i], allCards),\r\n\t\t\t// \tstringifySimple(board, allCards),\r\n\t\t\t// \tinitialBoardLength,\r\n\t\t\t// \ti,\r\n\t\t\t// \tdeadMinionIndexesFromRight,\r\n\t\t\t// );\r\n\t\t}\r\n\t}\r\n\r\n\t// These will always be processed from left to right afterwards\r\n\t// We compute the indexes as they will be once the new board is effective. For a\r\n\t// board of length N, having an indexFromRight at N means it will spawn at the very left\r\n\t// of the board (first minion)\r\n\tlet indexesFromRightAfterDeath = [];\r\n\tfor (let i = deadMinionIndexesFromRight.length - 1; i >= 0; i--) {\r\n\t\tconst newIndex = deadMinionIndexesFromRight[i] - indexesFromRightAfterDeath.length;\r\n\t\tindexesFromRightAfterDeath.push(newIndex);\r\n\t}\r\n\tindexesFromRightAfterDeath = indexesFromRightAfterDeath.reverse();\r\n\r\n\tfor (let i = 0; i < board.length; i++) {\r\n\t\tif (board[i].health <= 0 || board[i].definitelyDead) {\r\n\t\t\t// console.log('\\tflagging dead minion', stringifySimpleCard(board[i], allCards), deadMinionIndexesFromRight);\r\n\t\t\tremoveMinionFromBoard(board, boardHero, i, gameState);\r\n\t\t\t// We modify the original array, so we need to update teh current index accordingly\r\n\t\t\ti--;\r\n\t\t}\r\n\t}\r\n\r\n\t// console.debug('dead entities', stringifySimple(deadEntities, allCards));\r\n\t// Update the avenge counters as soon as minions die. If we wait until the \"avenge\" phase, we might\r\n\t// update the counters for entities that have been spawned after the death of the original entity\r\n\t// ISSUE: deaths are actually processed one by one. Once a minion dies, its DR triggers, then the next, etc.\r\n\t// This means that if you have a minion that summons another one, it can progress and complete and quest\r\n\t// and the next minion dying could count towards that quest progress\r\n\t// See http://replays.firestoneapp.com/?reviewId=0ce4db9c-3269-4704-b662-8a8c31f5afe1&turn=16&action=27\r\n\tfor (const deadEntity of deadEntities) {\r\n\t\tconst indexFromRight = indexesFromRightAfterDeath[deadEntities.indexOf(deadEntity)];\r\n\t\tupdateAvengeCounters(board, boardHero);\r\n\t\tonMinionDeadHeroPower(board, boardHero, deadEntity, gameState);\r\n\t\tonMinionDeadHeroPower(otherBoard, otherBoardHero, deadEntity, gameState);\r\n\t\tonMinionDeadQuest(deadEntity, indexFromRight, board, boardHero, otherBoard, otherBoardHero, gameState);\r\n\t\tonMinionDeadQuest(deadEntity, indexFromRight, otherBoard, otherBoardHero, board, boardHero, gameState);\r\n\t}\r\n\r\n\treturn [\r\n\t\tdeadMinionIndexesFromLeft,\r\n\t\tindexesFromRightAfterDeath,\r\n\t\t// 2026-09-28: https://replays.firestoneapp.com/?reviewId=6508654f-9f46-47ba-a1e5-6b73566c4d46&turn=9&action=2&debug=true\r\n\t\t// Not always left to right apparently\r\n\t\t// If so, this is an issue, because when we get the opponent's board, we get the entity IDs orderer from\r\n\t\t// left to right, so we lose that information\r\n\t\t// deadEntities.sort((a, b) => a.entityId - b.entityId),\r\n\t\tdeadEntities,\r\n\t];\r\n};\r\n\r\nexport const onMinionDeadHeroPower = (\r\n\tboard: BoardEntity[],\r\n\tboardHero: BgsPlayerEntity,\r\n\tdeadEntity: BoardEntity,\r\n\tgameState: FullGameState,\r\n) => {\r\n\tfor (const heroPower of boardHero.heroPowers) {\r\n\t\tif (\r\n\t\t\theroPower.cardId === CardIds.IllTakeThat &&\r\n\t\t\theroPower.used &&\r\n\t\t\theroPower.info2 <= 0 &&\r\n\t\t\tdeadEntity.friendly !== boardHero.friendly\r\n\t\t) {\r\n\t\t\taddCardsInHand(boardHero, board, [deadEntity.cardId], gameState);\r\n\t\t\theroPower.info2 = 1;\r\n\t\t}\r\n\t\tapplyRunicEmpowermentOnFriendlyDeath(heroPower, boardHero, deadEntity);\r\n\t}\r\n};\r\n\r\nexport const onMinionDeadQuest = (\r\n\tdeadEntity: BoardEntity,\r\n\tindexFromRight: number,\r\n\tboard: BoardEntity[],\r\n\tboardHero: BgsPlayerEntity,\r\n\totherBoard: BoardEntity[],\r\n\totherBoardHero: BgsPlayerEntity,\r\n\tgameState: FullGameState,\r\n) => {\r\n\tconst quests = boardHero.questEntities ?? [];\r\n\tfor (const quest of quests) {\r\n\t\tswitch (quest.CardId) {\r\n\t\t\tcase CardIds.ReenactTheMurder:\r\n\t\t\t\tif (deadEntity.friendly === boardHero.friendly) {\r\n\t\t\t\t\tonQuestProgressUpdated(boardHero, quest, board, gameState);\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\t\t\tcase CardIds.RoundUpTheSuspects:\r\n\t\t\t\tif (deadEntity.friendly !== boardHero.friendly) {\r\n\t\t\t\t\tonQuestProgressUpdated(boardHero, quest, board, gameState);\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\r\n\tfor (const trinket of boardHero.trinkets) {\r\n\t\tswitch (trinket.cardId) {\r\n\t\t\tcase CardIds.AllianceKeychain_BG30_MagicItem_433:\r\n\t\t\tcase CardIds.AllianceKeychain_AllianceKeychainToken_BG30_MagicItem_433t:\r\n\t\t\t\tif (trinket.scriptDataNum1 > 0 && deadEntity.friendly === boardHero.friendly) {\r\n\t\t\t\t\tconst quantity = trinket.cardId === CardIds.AllianceKeychain_BG30_MagicItem_433 ? 1 : 2;\r\n\t\t\t\t\tconst targets = pickMultipleRandomAlive(board, quantity);\r\n\t\t\t\t\tfor (const target of targets) {\r\n\t\t\t\t\t\tmodifyStats(\r\n\t\t\t\t\t\t\ttarget,\r\n\t\t\t\t\t\t\tboardHero,\r\n\t\t\t\t\t\t\tdeadEntity.maxAttack,\r\n\t\t\t\t\t\t\tdeadEntity.maxHealth,\r\n\t\t\t\t\t\t\tboard,\r\n\t\t\t\t\t\t\tboardHero,\r\n\t\t\t\t\t\t\tgameState,\r\n\t\t\t\t\t\t);\r\n\t\t\t\t\t}\r\n\t\t\t\t\ttrinket.scriptDataNum1--;\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\t\t\tcase CardIds.TheEyeOfDalaran_BG30_MagicItem_981:\r\n\t\t\t\tif (\r\n\t\t\t\t\tdeadEntity.friendly === boardHero.friendly &&\r\n\t\t\t\t\tgetEffectiveTribesForEntity(deadEntity, boardHero, gameState.anomalies, gameState.allCards)\r\n\t\t\t\t\t\t.length === 0\r\n\t\t\t\t) {\r\n\t\t\t\t\taddCardsInHand(boardHero, board, [null], gameState);\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n};\r\n"]}
|
|
@@ -6,12 +6,12 @@ const debug_state_1 = require("../debug-state");
|
|
|
6
6
|
const utils_1 = require("../utils");
|
|
7
7
|
const attack_1 = require("./attack");
|
|
8
8
|
const auras_1 = require("./auras");
|
|
9
|
+
const combat_profile_1 = require("./combat-profile");
|
|
9
10
|
const damage_cap_1 = require("./damage-cap");
|
|
10
11
|
const pending_effects_1 = require("./pending-effects");
|
|
11
12
|
const secrets_1 = require("./secrets");
|
|
12
13
|
const start_of_combat_1 = require("./start-of-combat/start-of-combat");
|
|
13
14
|
const summon_when_space_1 = require("./summon-when-space");
|
|
14
|
-
const combat_profile_1 = require("./combat-profile");
|
|
15
15
|
class Simulator {
|
|
16
16
|
constructor(gameState) {
|
|
17
17
|
this.gameState = gameState;
|
|
@@ -134,9 +134,7 @@ class Simulator {
|
|
|
134
134
|
additionalCards: e.additionalCards ? [...e.additionalCards] : e.additionalCards,
|
|
135
135
|
});
|
|
136
136
|
});
|
|
137
|
-
const snapshotSecrets = (secrets) => (secrets !== null && secrets !== void 0 ? secrets : [])
|
|
138
|
-
.filter((s) => !s.triggered)
|
|
139
|
-
.map((s) => ({ ...s }));
|
|
137
|
+
const snapshotSecrets = (secrets) => (secrets !== null && secrets !== void 0 ? secrets : []).filter((s) => !s.triggered).map((s) => ({ ...s }));
|
|
140
138
|
const snapshotTrinkets = (trinkets) => (trinkets !== null && trinkets !== void 0 ? trinkets : []).map((t) => ({ ...t }));
|
|
141
139
|
const snapshotHeroPowers = (hps) => (hps !== null && hps !== void 0 ? hps : []).map((hp) => ({ ...hp }));
|
|
142
140
|
const packed = {
|
|
@@ -183,9 +181,7 @@ class Simulator {
|
|
|
183
181
|
const losingHero = playerEntity !== null && playerEntity !== void 0 ? playerEntity : playerState.player;
|
|
184
182
|
let damage = this.capDamage(this.buildBoardTotalDamage(opponentBoard, (_j = (_h = this.gameState.gameState.opponent) === null || _h === void 0 ? void 0 : _h.teammate) === null || _j === void 0 ? void 0 : _j.board) +
|
|
185
183
|
((_k = winningHero === null || winningHero === void 0 ? void 0 : winningHero.tavernTier) !== null && _k !== void 0 ? _k : 1));
|
|
186
|
-
if (this.gameState.applyIceBlock &&
|
|
187
|
-
damage > 0 &&
|
|
188
|
-
(0, secrets_1.tryPreventCombatDamageWithIceBlock)(losingHero)) {
|
|
184
|
+
if (this.gameState.applyIceBlock && damage > 0 && (0, secrets_1.tryPreventCombatDamageWithIceBlock)(losingHero)) {
|
|
189
185
|
damage = 0;
|
|
190
186
|
}
|
|
191
187
|
this.gameState.spectator.registerOpponentAttack(playerBoard, opponentBoard, damage);
|
|
@@ -200,9 +196,7 @@ class Simulator {
|
|
|
200
196
|
const losingHero = opponentEntity !== null && opponentEntity !== void 0 ? opponentEntity : opponentState.player;
|
|
201
197
|
let damage = this.capDamage(this.buildBoardTotalDamage(playerBoard, (_m = (_l = this.gameState.gameState.player) === null || _l === void 0 ? void 0 : _l.teammate) === null || _m === void 0 ? void 0 : _m.board) +
|
|
202
198
|
((_o = winningHero === null || winningHero === void 0 ? void 0 : winningHero.tavernTier) !== null && _o !== void 0 ? _o : 1));
|
|
203
|
-
if (this.gameState.applyIceBlock &&
|
|
204
|
-
damage > 0 &&
|
|
205
|
-
(0, secrets_1.tryPreventCombatDamageWithIceBlock)(losingHero)) {
|
|
199
|
+
if (this.gameState.applyIceBlock && damage > 0 && (0, secrets_1.tryPreventCombatDamageWithIceBlock)(losingHero)) {
|
|
206
200
|
damage = 0;
|
|
207
201
|
}
|
|
208
202
|
this.gameState.spectator.registerPlayerAttack(playerBoard, opponentBoard, damage);
|
|
@@ -257,7 +251,6 @@ class Simulator {
|
|
|
257
251
|
opponentBoard.length > 0 &&
|
|
258
252
|
playerEntity.hpLeft > 0 &&
|
|
259
253
|
opponentEntity.hpLeft > 0) {
|
|
260
|
-
(0, summon_when_space_1.handleSummonsWhenSpace)(playerBoard, playerEntity, opponentBoard, opponentEntity, this.gameState);
|
|
261
254
|
(0, auras_1.clearStealthIfNeeded)(playerBoard, playerEntity, opponentBoard, opponentEntity, this.gameState);
|
|
262
255
|
if (playerBoard.some((entity) => entity.attackImmediately)) {
|
|
263
256
|
this.currentSpeedAttacker = 0;
|
|
@@ -295,6 +288,7 @@ class Simulator {
|
|
|
295
288
|
this.hasShowShortCircuitWarning = true;
|
|
296
289
|
break;
|
|
297
290
|
}
|
|
291
|
+
(0, summon_when_space_1.handleSummonsWhenSpace)(playerBoard, playerEntity, opponentBoard, opponentEntity, this.gameState);
|
|
298
292
|
}
|
|
299
293
|
if (profile) {
|
|
300
294
|
(0, combat_profile_1.combatProfileAdd)('socMs', socMs);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"simulator.js","sourceRoot":"","sources":["../../src/simulation/simulator.ts"],"names":[],"mappings":"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","sourcesContent":["import { getEffectiveTechLevel } from '@firestone-hs/reference-data';\r\nimport { BgsPlayerEntity } from '../bgs-player-entity';\r\nimport { BoardEntity } from '../board-entity';\r\nimport { debugState } from '../debug-state';\r\nimport { SingleSimulationResult } from '../single-simulation-result';\r\nimport { stringifySimple, stringifySimpleCard } from '../utils';\r\nimport { simulateAttack } from './attack';\r\nimport { clearStealthIfNeeded } from './auras';\r\nimport { applyCombatDamageCap } from './damage-cap';\r\nimport { FullGameState, PlayerState } from './internal-game-state';\r\nimport { resolvePendingEffects } from './pending-effects';\r\nimport { tryPreventCombatDamageWithIceBlock } from './secrets';\r\nimport { handleStartOfCombat } from './start-of-combat/start-of-combat';\r\nimport { handleSummonsWhenSpace } from './summon-when-space';\r\nimport { combatProfileAdd, combatProfileEnabled } from './combat-profile';\r\n\r\n// New simulator should be instantiated for each match\r\nexport class Simulator {\r\n\tprivate currentAttacker: number;\r\n\tprivate currentSpeedAttacker = -1;\r\n\r\n\tprivate hasShowShortCircuitWarning = false;\r\n\r\n\t// It should come already initialized\r\n\tconstructor(private readonly gameState: FullGameState) {}\r\n\r\n\t// Here we suppose that the BoardEntity only contain at most the enchantments that are linked\r\n\t// to auras (so we probably should hand-filter that, since there are actually few auras)\r\n\tpublic simulateSingleBattle(playerState: PlayerState, opponentState: PlayerState): SingleSimulationResult {\r\n\t\tlet playerBoard: BoardEntity[] = playerState.board;\r\n\t\tlet playerEntity: BgsPlayerEntity = playerState.player;\r\n\t\tlet opponentBoard: BoardEntity[] = opponentState.board;\r\n\t\tlet opponentEntity: BgsPlayerEntity = opponentState.player;\r\n\r\n\t\tplayerEntity.globalInfo = playerEntity.globalInfo ?? {};\r\n\t\topponentEntity.globalInfo = opponentEntity.globalInfo ?? {};\r\n\t\tplayerEntity.globalInfo.RallyMinionAttacksThisCombat = 0;\r\n\t\topponentEntity.globalInfo.RallyMinionAttacksThisCombat = 0;\r\n\t\tplayerEntity.globalInfo.FreeRefreshesGainedThisCombat = 0;\r\n\t\topponentEntity.globalInfo.FreeRefreshesGainedThisCombat = 0;\r\n\r\n\t\tlet maxEntityId = -Infinity;\r\n\t\tfor (const group of [playerBoard, opponentBoard, playerEntity.hand, opponentEntity.hand]) {\r\n\t\t\tfor (const entity of group ?? []) {\r\n\t\t\t\tif (entity.entityId > maxEntityId) {\r\n\t\t\t\t\tmaxEntityId = entity.entityId;\r\n\t\t\t\t}\r\n\t\t\t\tfor (const enchantment of entity.enchantments ?? []) {\r\n\t\t\t\t\tif (enchantment.originEntityId > maxEntityId) {\r\n\t\t\t\t\t\tmaxEntityId = enchantment.originEntityId;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\tthis.gameState.sharedState.currentEntityId = maxEntityId + 1;\r\n\r\n\t\twhile (\r\n\t\t\t!playerEntity.startOfCombatDone ||\r\n\t\t\t!opponentEntity.startOfCombatDone ||\r\n\t\t\t(playerBoard?.length > 0 && opponentBoard?.length > 0)\r\n\t\t) {\r\n\t\t\t// Diagnostic only: detect runaway enchantment growth. The full entity id arrays are\r\n\t\t\t// only built in debug mode, since this allocates a lot on every battle pass\r\n\t\t\tif (debugState?.active) {\r\n\t\t\t\tthis.logRunawayEntityDiagnostics(playerBoard, playerEntity, opponentBoard, opponentEntity);\r\n\t\t\t}\r\n\t\t\tthis.simulateSingleBattlePass(playerBoard, playerEntity, opponentBoard, opponentEntity);\r\n\t\t\tif (this.hasShowShortCircuitWarning) {\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\t// The only case where there can only 0-attack minions on a board is when both boards\r\n\t\t\t// are that way (otherwise one side would kill the other)\r\n\t\t\tconst areBothBoards0Attack =\r\n\t\t\t\tplayerState.board.length > 0 &&\r\n\t\t\t\tplayerState.board.every((entity) => entity.attack === 0) &&\r\n\t\t\t\topponentState.board.length > 0 &&\r\n\t\t\t\topponentState.board.every((entity) => entity.attack === 0);\r\n\t\t\tif (areBothBoards0Attack) {\r\n\t\t\t\tplayerState.board = [];\r\n\t\t\t\topponentState.board = [];\r\n\t\t\t}\r\n\r\n\t\t\tconst isPlayerDefeated = playerState.board.length === 0 || playerState.player.hpLeft <= 0;\r\n\t\t\tconst isOpponentDefeated = opponentState.board.length === 0 || opponentState.player.hpLeft <= 0;\r\n\t\t\tif (isPlayerDefeated) {\r\n\t\t\t\tplayerBoard = playerState.teammate?.board;\r\n\t\t\t\tplayerState.board = [];\r\n\t\t\t\tplayerEntity = playerState.teammate?.player;\r\n\t\t\t} else {\r\n\t\t\t\tplayerBoard = playerState.board;\r\n\t\t\t\tplayerEntity = playerState.player;\r\n\t\t\t}\r\n\t\t\tif (isOpponentDefeated) {\r\n\t\t\t\topponentBoard = opponentState.teammate?.board;\r\n\t\t\t\topponentState.board = [];\r\n\t\t\t\topponentEntity = opponentState.teammate?.player;\r\n\t\t\t} else {\r\n\t\t\t\topponentBoard = opponentState.board;\r\n\t\t\t\topponentEntity = opponentState.player;\r\n\t\t\t}\r\n\t\t\t// So that gameState.player always refers to the active player.\r\n\t\t\t// Only swap when a real Duos teammate takes over — assigning an empty\r\n\t\t\t// teammate shell on solo defeat made isDuos true and used the flat 15\r\n\t\t\t// damage cap instead of the turn-scaled solo cap (35dd5300 t4: 13→10).\r\n\t\t\tif (isPlayerDefeated && playerEntity) {\r\n\t\t\t\t// Reset deaths\r\n\t\t\t\tthis.gameState.sharedState.deaths = this.gameState.sharedState.deaths.filter(\r\n\t\t\t\t\t(e) => e.friendly !== this.gameState.gameState.player.player.friendly,\r\n\t\t\t\t);\r\n\t\t\t\tthis.gameState.gameState.player.teammate = {\r\n\t\t\t\t\tboard: this.gameState.gameState.player.board,\r\n\t\t\t\t\tplayer: this.gameState.gameState.player.player,\r\n\t\t\t\t};\r\n\t\t\t\tthis.gameState.gameState.player.player = playerEntity;\r\n\t\t\t\tthis.gameState.gameState.player.board = playerBoard;\r\n\r\n\t\t\t\t// const initialPlayer = this.gameState.gameState.playerInitial;\r\n\t\t\t\t// const initialPlayerTeammate = initialPlayer.teammate;\r\n\t\t\t}\r\n\t\t\tif (isOpponentDefeated && opponentEntity) {\r\n\t\t\t\t// Reset deaths\r\n\t\t\t\tthis.gameState.sharedState.deaths = this.gameState.sharedState.deaths.filter(\r\n\t\t\t\t\t(e) => e.friendly !== this.gameState.gameState.opponent.player.friendly,\r\n\t\t\t\t);\r\n\t\t\t\tthis.gameState.gameState.opponent.teammate = {\r\n\t\t\t\t\tboard: this.gameState.gameState.opponent.board,\r\n\t\t\t\t\tplayer: this.gameState.gameState.opponent.player,\r\n\t\t\t\t};\r\n\t\t\t\tthis.gameState.gameState.opponent.player = opponentEntity;\r\n\t\t\t\tthis.gameState.gameState.opponent.board = opponentBoard;\r\n\t\t\t}\r\n\r\n\t\t\tif (!playerEntity || !opponentEntity) {\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tconst snapshotBoard = (board: BoardEntity[] | null | undefined): BoardEntity[] =>\r\n\t\t\t(board ?? []).map((e) => ({\r\n\t\t\t\t...e,\r\n\t\t\t\tenchantments: [...(e.enchantments ?? [])],\r\n\t\t\t\tpendingAttackBuffs: [...(e.pendingAttackBuffs ?? [])],\r\n\t\t\t\tadditionalCards: e.additionalCards ? [...e.additionalCards] : e.additionalCards,\r\n\t\t\t}));\r\n\r\n\t\tconst snapshotQuests = (hero: BgsPlayerEntity | null | undefined) => ({\r\n\t\t\tquestEntities: (hero?.questEntities ?? []).map((q) => ({ ...q })),\r\n\t\t\tquestRewards: [...(hero?.questRewards ?? [])],\r\n\t\t\tquestRewardEntities: (hero?.questRewardEntities ?? []).map((r) => ({ ...r })),\r\n\t\t});\r\n\r\n\t\tconst withQuestWriteback = (\r\n\t\t\tbase: Omit<\r\n\t\t\t\tSingleSimulationResult,\r\n\t\t\t\t| 'playerQuestEntities'\r\n\t\t\t\t| 'playerQuestRewards'\r\n\t\t\t\t| 'playerQuestRewardEntities'\r\n\t\t\t\t| 'opponentQuestEntities'\r\n\t\t\t\t| 'opponentQuestRewards'\r\n\t\t\t\t| 'opponentQuestRewardEntities'\r\n\t\t\t\t| 'playerRallyAttacksThisCombat'\r\n\t\t\t\t| 'opponentRallyAttacksThisCombat'\r\n\t\t\t\t| 'playerFreeRefreshesGainedThisCombat'\r\n\t\t\t\t| 'opponentFreeRefreshesGainedThisCombat'\r\n\t\t\t\t| 'playerFodderRefreshes'\r\n\t\t\t\t| 'opponentFodderRefreshes'\r\n\t\t\t\t| 'playerGlobalInfo'\r\n\t\t\t\t| 'opponentGlobalInfo'\r\n\t\t\t\t| 'playerHand'\r\n\t\t\t\t| 'opponentHand'\r\n\t\t\t\t| 'playerSecrets'\r\n\t\t\t\t| 'opponentSecrets'\r\n\t\t\t\t| 'playerTrinkets'\r\n\t\t\t\t| 'opponentTrinkets'\r\n\t\t\t\t| 'playerHeroPowers'\r\n\t\t\t\t| 'opponentHeroPowers'\r\n\t\t\t>,\r\n\t\t): SingleSimulationResult => {\r\n\t\t\tconst t0 = combatProfileEnabled() ? performance.now() : 0;\r\n\t\t\tconst playerQ = snapshotQuests(playerEntity);\r\n\t\t\tconst opponentQ = snapshotQuests(opponentEntity);\r\n\t\t\tconst snapshotHand = (hand: typeof playerEntity.hand) =>\r\n\t\t\t\t(hand ?? []).map((e) => ({\r\n\t\t\t\t\t...e,\r\n\t\t\t\t\tenchantments: [...(e.enchantments ?? [])],\r\n\t\t\t\t\tadditionalCards: e.additionalCards ? [...e.additionalCards] : e.additionalCards,\r\n\t\t\t\t}));\r\n\t\t\tconst snapshotSecrets = (secrets: typeof playerEntity.secrets) =>\r\n\t\t\t\t(secrets ?? [])\r\n\t\t\t\t\t.filter((s) => !s.triggered)\r\n\t\t\t\t\t.map((s) => ({ ...s }));\r\n\t\t\tconst snapshotTrinkets = (trinkets: typeof playerEntity.trinkets) =>\r\n\t\t\t\t(trinkets ?? []).map((t) => ({ ...t }));\r\n\t\t\tconst snapshotHeroPowers = (hps: typeof playerEntity.heroPowers) =>\r\n\t\t\t\t(hps ?? []).map((hp) => ({ ...hp }));\r\n\t\t\tconst packed: SingleSimulationResult = {\r\n\t\t\t\t...base,\r\n\t\t\t\tplayerQuestEntities: playerQ.questEntities,\r\n\t\t\t\tplayerQuestRewards: playerQ.questRewards,\r\n\t\t\t\tplayerQuestRewardEntities: playerQ.questRewardEntities,\r\n\t\t\t\topponentQuestEntities: opponentQ.questEntities,\r\n\t\t\t\topponentQuestRewards: opponentQ.questRewards,\r\n\t\t\t\topponentQuestRewardEntities: opponentQ.questRewardEntities,\r\n\t\t\t\tplayerRallyAttacksThisCombat: playerEntity?.globalInfo?.RallyMinionAttacksThisCombat ?? 0,\r\n\t\t\t\topponentRallyAttacksThisCombat: opponentEntity?.globalInfo?.RallyMinionAttacksThisCombat ?? 0,\r\n\t\t\t\tplayerFreeRefreshesGainedThisCombat:\r\n\t\t\t\t\tplayerEntity?.globalInfo?.FreeRefreshesGainedThisCombat ?? 0,\r\n\t\t\t\topponentFreeRefreshesGainedThisCombat:\r\n\t\t\t\t\topponentEntity?.globalInfo?.FreeRefreshesGainedThisCombat ?? 0,\r\n\t\t\t\tplayerFodderRefreshes: playerEntity?.globalInfo?.fodderRefreshes ?? 0,\r\n\t\t\t\topponentFodderRefreshes: opponentEntity?.globalInfo?.fodderRefreshes ?? 0,\r\n\t\t\t\tplayerGlobalInfo: { ...(playerEntity?.globalInfo ?? {}) },\r\n\t\t\t\topponentGlobalInfo: { ...(opponentEntity?.globalInfo ?? {}) },\r\n\t\t\t\tplayerHand: snapshotHand(playerEntity?.hand),\r\n\t\t\t\topponentHand: snapshotHand(opponentEntity?.hand),\r\n\t\t\t\tplayerSecrets: snapshotSecrets(playerEntity?.secrets),\r\n\t\t\t\topponentSecrets: snapshotSecrets(opponentEntity?.secrets),\r\n\t\t\t\tplayerTrinkets: snapshotTrinkets(playerEntity?.trinkets),\r\n\t\t\t\topponentTrinkets: snapshotTrinkets(opponentEntity?.trinkets),\r\n\t\t\t\tplayerHeroPowers: snapshotHeroPowers(playerEntity?.heroPowers),\r\n\t\t\t\topponentHeroPowers: snapshotHeroPowers(opponentEntity?.heroPowers),\r\n\t\t\t};\r\n\t\t\tif (t0) {\r\n\t\t\t\tcombatProfileAdd('writebackMs', performance.now() - t0);\r\n\t\t\t}\r\n\t\t\treturn packed;\r\n\t\t};\r\n\r\n\t\tif (\r\n\t\t\t(!playerBoard?.length && !opponentBoard?.length) ||\r\n\t\t\t// E.g. when both players have a 0-attack minion\r\n\t\t\t(playerBoard?.length > 0 && opponentBoard?.length > 0)\r\n\t\t) {\r\n\t\t\treturn withQuestWriteback({\r\n\t\t\t\tresult: 'tied',\r\n\t\t\t\tdamageDealt: 0,\r\n\t\t\t\tplayerBoard: snapshotBoard(playerBoard),\r\n\t\t\t\topponentBoard: snapshotBoard(opponentBoard),\r\n\t\t\t});\r\n\t\t}\r\n\t\tif (!playerBoard?.length || playerEntity?.hpLeft <= 0) {\r\n\t\t\tconst winningHero = opponentEntity ?? opponentState.player;\r\n\t\t\tconst losingHero = playerEntity ?? playerState.player;\r\n\t\t\tlet damage = this.capDamage(\r\n\t\t\t\tthis.buildBoardTotalDamage(opponentBoard, this.gameState.gameState.opponent?.teammate?.board) +\r\n\t\t\t\t\t(winningHero?.tavernTier ?? 1),\r\n\t\t\t);\r\n\t\t\t// Full-game only: Ice Block prevents face damage (opt-in via applyIceBlock).\r\n\t\t\tif (\r\n\t\t\t\tthis.gameState.applyIceBlock &&\r\n\t\t\t\tdamage > 0 &&\r\n\t\t\t\ttryPreventCombatDamageWithIceBlock(losingHero)\r\n\t\t\t) {\r\n\t\t\t\tdamage = 0;\r\n\t\t\t}\r\n\t\t\tthis.gameState.spectator.registerOpponentAttack(playerBoard, opponentBoard, damage);\r\n\t\t\treturn withQuestWriteback({\r\n\t\t\t\tresult: 'lost',\r\n\t\t\t\tdamageDealt: damage,\r\n\t\t\t\tplayerBoard: snapshotBoard(playerBoard),\r\n\t\t\t\topponentBoard: snapshotBoard(opponentBoard),\r\n\t\t\t});\r\n\t\t}\r\n\r\n\t\tconst winningHero = playerEntity ?? playerState.player;\r\n\t\tconst losingHero = opponentEntity ?? opponentState.player;\r\n\t\tlet damage = this.capDamage(\r\n\t\t\tthis.buildBoardTotalDamage(playerBoard, this.gameState.gameState.player?.teammate?.board) +\r\n\t\t\t\t(winningHero?.tavernTier ?? 1),\r\n\t\t);\r\n\t\tif (\r\n\t\t\tthis.gameState.applyIceBlock &&\r\n\t\t\tdamage > 0 &&\r\n\t\t\ttryPreventCombatDamageWithIceBlock(losingHero)\r\n\t\t) {\r\n\t\t\tdamage = 0;\r\n\t\t}\r\n\t\tthis.gameState.spectator.registerPlayerAttack(playerBoard, opponentBoard, damage);\r\n\t\treturn withQuestWriteback({\r\n\t\t\tresult: 'won',\r\n\t\t\tdamageDealt: damage,\r\n\t\t\tplayerBoard: snapshotBoard(playerBoard),\r\n\t\t\topponentBoard: snapshotBoard(opponentBoard),\r\n\t\t});\r\n\t}\r\n\r\n\tprivate logRunawayEntityDiagnostics(\r\n\t\tplayerBoard: BoardEntity[],\r\n\t\tplayerEntity: BgsPlayerEntity,\r\n\t\topponentBoard: BoardEntity[],\r\n\t\topponentEntity: BgsPlayerEntity,\r\n\t): void {\r\n\t\tconst allEntityIds: number[] = [];\r\n\t\tfor (const group of [playerBoard, opponentBoard, playerEntity.hand, opponentEntity.hand]) {\r\n\t\t\tfor (const entity of group ?? []) {\r\n\t\t\t\tconst result = [entity.entityId, ...(entity.enchantments?.map((e) => e.originEntityId) ?? [])];\r\n\t\t\t\tif (result.length > 200) {\r\n\t\t\t\t\tconsole.log(\r\n\t\t\t\t\t\t'too many entities for entity',\r\n\t\t\t\t\t\tresult.length,\r\n\t\t\t\t\t\tstringifySimpleCard(entity, this.gameState.allCards),\r\n\t\t\t\t\t);\r\n\t\t\t\t}\r\n\t\t\t\tallEntityIds.push(...result);\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (allEntityIds.length > 10000) {\r\n\t\t\tconsole.log('allEntityIds2', allEntityIds, new Error().stack);\r\n\t\t}\r\n\t}\r\n\r\n\tprivate simulateSingleBattlePass(\r\n\t\tplayerBoard: BoardEntity[],\r\n\t\tplayerEntity: BgsPlayerEntity,\r\n\t\topponentBoard: BoardEntity[],\r\n\t\topponentEntity: BgsPlayerEntity,\r\n\t) {\r\n\t\tif (debugState?.active) {\r\n\t\t\tdebugState.onBattleStart();\r\n\t\t}\r\n\t\t// Start of combat happens only once, so we need to flag whether it has already happened for a\r\n\t\t// given player\r\n\t\tthis.gameState.spectator.registerStartOfCombat(playerBoard, opponentBoard, playerEntity, opponentEntity);\r\n\r\n\t\t// Who attacks first is decided by the game before the hero power comes into effect. However, the full board (with the generated minion)\r\n\t\t// is sent tothe simulator\r\n\t\t// But in fact, the first player decision takes into account that additional minion. See\r\n\t\t// https://replays.firestoneapp.com/?reviewId=ddbbbe93-464b-4400-8e8d-4abca8680a2e\r\n\t\tconst effectivePlayerBoardLength = playerBoard.length;\r\n\t\tconst effectiveOpponentBoardLength = opponentBoard.length;\r\n\t\tthis.currentAttacker =\r\n\t\t\teffectivePlayerBoardLength > effectiveOpponentBoardLength\r\n\t\t\t\t? 0\r\n\t\t\t\t: effectiveOpponentBoardLength > effectivePlayerBoardLength\r\n\t\t\t\t? 1\r\n\t\t\t\t: Math.round(Math.random());\r\n\t\tconst profile = combatProfileEnabled();\r\n\t\tconst socT0 = profile ? performance.now() : 0;\r\n\t\tconst suggestedNewCurrentAttacker = handleStartOfCombat(\r\n\t\t\tplayerEntity,\r\n\t\t\tplayerBoard,\r\n\t\t\topponentEntity,\r\n\t\t\topponentBoard,\r\n\t\t\tthis.currentAttacker,\r\n\t\t\tthis.gameState,\r\n\t\t);\r\n\t\tthis.currentAttacker = suggestedNewCurrentAttacker;\r\n\t\tconst socMs = profile ? performance.now() - socT0 : 0;\r\n\t\tlet counter = 0;\r\n\t\tif (debugState?.active) {\r\n\t\t\tthis.currentAttacker = debugState.forcedCurrentAttacker ?? this.currentAttacker;\r\n\t\t}\r\n\t\tconst atkT0 = profile ? performance.now() : 0;\r\n\t\twhile (\r\n\t\t\tplayerBoard.length > 0 &&\r\n\t\t\topponentBoard.length > 0 &&\r\n\t\t\tplayerEntity.hpLeft > 0 &&\r\n\t\t\topponentEntity.hpLeft > 0\r\n\t\t) {\r\n\t\t\thandleSummonsWhenSpace(playerBoard, playerEntity, opponentBoard, opponentEntity, this.gameState);\r\n\t\t\tclearStealthIfNeeded(playerBoard, playerEntity, opponentBoard, opponentEntity, this.gameState);\r\n\t\t\t// console.log('this.currentSpeedAttacker', this.currentAttacker);\r\n\t\t\t// If there are \"attack immediately\" minions, we keep the same player\r\n\t\t\t// We put it here so that it can kick in after the start of combat effects. However here we don't want\r\n\t\t\t// to change who attacks first, so we repeat that block again after all the attacks have been resolved\r\n\t\t\t// FIXME: This is not strictly correct - if there are multiple attack immediately\r\n\t\t\t// minions that spawn on both player sides it might get a bit more complex\r\n\t\t\t// but overall it works\r\n\t\t\t// Also, this doesn't work when there are several deathrattle competing\r\n\t\t\t// to know who triggers first. See the second test case of the scallywag.test.ts\r\n\t\t\t// that is not handled properly today (the attack should in some cases happen before\r\n\t\t\t// the other deathrattle procs)\r\n\t\t\tif (playerBoard.some((entity) => entity.attackImmediately)) {\r\n\t\t\t\tthis.currentSpeedAttacker = 0;\r\n\t\t\t} else if (opponentBoard.some((entity) => entity.attackImmediately)) {\r\n\t\t\t\tthis.currentSpeedAttacker = 1;\r\n\t\t\t} else {\r\n\t\t\t\tthis.currentSpeedAttacker = -1;\r\n\t\t\t}\r\n\t\t\tif (\r\n\t\t\t\tplayerBoard.filter((e) => e.attack > 0).length === 0 &&\r\n\t\t\t\topponentBoard.filter((e) => e.attack > 0).length === 0\r\n\t\t\t) {\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\t// console.log('this.currentSpeedAttacker 2', this.currentAttacker, this.currentSpeedAttacker);\r\n\t\t\tif (this.currentSpeedAttacker === 0 || (this.currentSpeedAttacker === -1 && this.currentAttacker === 0)) {\r\n\t\t\t\tsimulateAttack(playerBoard, playerEntity, opponentBoard, opponentEntity, this.gameState);\r\n\t\t\t} else {\r\n\t\t\t\tsimulateAttack(opponentBoard, opponentEntity, playerBoard, playerEntity, this.gameState);\r\n\t\t\t}\r\n\r\n\t\t\tresolvePendingEffects(playerBoard, playerEntity, opponentBoard, opponentEntity, this.gameState);\r\n\t\t\t// Update the attacker indices in case there were some deaths\r\n\t\t\tif (playerBoard.some((entity) => entity.attackImmediately)) {\r\n\t\t\t\tthis.currentSpeedAttacker = 0;\r\n\t\t\t} else if (opponentBoard.some((entity) => entity.attackImmediately)) {\r\n\t\t\t\tthis.currentSpeedAttacker = 1;\r\n\t\t\t} else {\r\n\t\t\t\tthis.currentSpeedAttacker = -1;\r\n\t\t\t\tthis.currentAttacker = (this.currentAttacker + 1) % 2;\r\n\t\t\t}\r\n\t\t\tcounter++;\r\n\t\t\tif (counter > 400 && !this.hasShowShortCircuitWarning) {\r\n\t\t\t\tconsole.warn(\r\n\t\t\t\t\t'short-circuiting simulation, too many iterations',\r\n\t\t\t\t\tcounter,\r\n\t\t\t\t\t'\\n',\r\n\t\t\t\t\tstringifySimple(playerBoard, this.gameState.allCards),\r\n\t\t\t\t\t'\\n',\r\n\t\t\t\t\tstringifySimple(opponentBoard, this.gameState.allCards),\r\n\t\t\t\t);\r\n\t\t\t\tthis.hasShowShortCircuitWarning = true;\r\n\t\t\t\tbreak;\r\n\t\t\t\t// return null;\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (profile) {\r\n\t\t\tcombatProfileAdd('socMs', socMs);\r\n\t\t\tcombatProfileAdd('atkMs', performance.now() - atkT0);\r\n\t\t}\r\n\t}\r\n\r\n\tprivate capDamage(damage: number): number {\r\n\t\tif (!this.gameState.applyDamageCap) {\r\n\t\t\treturn damage;\r\n\t\t}\r\n\t\treturn applyCombatDamageCap(\r\n\t\t\tdamage,\r\n\t\t\tthis.gameState.currentTurn,\r\n\t\t\tthis.gameState.numberOfPlayersAlive,\r\n\t\t\t!!this.gameState.isDuos,\r\n\t\t);\r\n\t}\r\n\r\n\tprivate buildBoardTotalDamage(playerBoard: readonly BoardEntity[], teammateBoard?: BoardEntity[]): number {\r\n\t\tconst damageFromPlayerBoard = playerBoard\r\n\t\t\t.map((entity) =>\r\n\t\t\t\tgetEffectiveTechLevel(this.gameState.allCards.getCard(entity.cardId), this.gameState.allCards),\r\n\t\t\t)\r\n\t\t\t.reduce((a, b) => a + b, 0);\r\n\t\tconst numberOfTeamateMinionsToSummnon = 7 - playerBoard.length;\r\n\t\tconst damageFromTeammateBoard =\r\n\t\t\tteammateBoard\r\n\t\t\t\t?.slice(0, numberOfTeamateMinionsToSummnon)\r\n\t\t\t\t.map((entity) =>\r\n\t\t\t\t\tgetEffectiveTechLevel(this.gameState.allCards.getCard(entity.cardId), this.gameState.allCards),\r\n\t\t\t\t)\r\n\t\t\t\t.reduce((a, b) => a + b, 0) ?? 0;\r\n\t\treturn damageFromPlayerBoard + damageFromTeammateBoard;\r\n\t}\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"file":"simulator.js","sourceRoot":"","sources":["../../src/simulation/simulator.ts"],"names":[],"mappings":"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","sourcesContent":["import { getEffectiveTechLevel } from '@firestone-hs/reference-data';\r\nimport { BgsPlayerEntity } from '../bgs-player-entity';\r\nimport { BoardEntity } from '../board-entity';\r\nimport { debugState } from '../debug-state';\r\nimport { SingleSimulationResult } from '../single-simulation-result';\r\nimport { stringifySimple, stringifySimpleCard } from '../utils';\r\nimport { simulateAttack } from './attack';\r\nimport { clearStealthIfNeeded } from './auras';\r\nimport { combatProfileAdd, combatProfileEnabled } from './combat-profile';\r\nimport { applyCombatDamageCap } from './damage-cap';\r\nimport { FullGameState, PlayerState } from './internal-game-state';\r\nimport { resolvePendingEffects } from './pending-effects';\r\nimport { tryPreventCombatDamageWithIceBlock } from './secrets';\r\nimport { handleStartOfCombat } from './start-of-combat/start-of-combat';\r\nimport { handleSummonsWhenSpace } from './summon-when-space';\r\n\r\n// New simulator should be instantiated for each match\r\nexport class Simulator {\r\n\tprivate currentAttacker: number;\r\n\tprivate currentSpeedAttacker = -1;\r\n\r\n\tprivate hasShowShortCircuitWarning = false;\r\n\r\n\t// It should come already initialized\r\n\tconstructor(private readonly gameState: FullGameState) {}\r\n\r\n\t// Here we suppose that the BoardEntity only contain at most the enchantments that are linked\r\n\t// to auras (so we probably should hand-filter that, since there are actually few auras)\r\n\tpublic simulateSingleBattle(playerState: PlayerState, opponentState: PlayerState): SingleSimulationResult {\r\n\t\tlet playerBoard: BoardEntity[] = playerState.board;\r\n\t\tlet playerEntity: BgsPlayerEntity = playerState.player;\r\n\t\tlet opponentBoard: BoardEntity[] = opponentState.board;\r\n\t\tlet opponentEntity: BgsPlayerEntity = opponentState.player;\r\n\r\n\t\tplayerEntity.globalInfo = playerEntity.globalInfo ?? {};\r\n\t\topponentEntity.globalInfo = opponentEntity.globalInfo ?? {};\r\n\t\tplayerEntity.globalInfo.RallyMinionAttacksThisCombat = 0;\r\n\t\topponentEntity.globalInfo.RallyMinionAttacksThisCombat = 0;\r\n\t\tplayerEntity.globalInfo.FreeRefreshesGainedThisCombat = 0;\r\n\t\topponentEntity.globalInfo.FreeRefreshesGainedThisCombat = 0;\r\n\r\n\t\tlet maxEntityId = -Infinity;\r\n\t\tfor (const group of [playerBoard, opponentBoard, playerEntity.hand, opponentEntity.hand]) {\r\n\t\t\tfor (const entity of group ?? []) {\r\n\t\t\t\tif (entity.entityId > maxEntityId) {\r\n\t\t\t\t\tmaxEntityId = entity.entityId;\r\n\t\t\t\t}\r\n\t\t\t\tfor (const enchantment of entity.enchantments ?? []) {\r\n\t\t\t\t\tif (enchantment.originEntityId > maxEntityId) {\r\n\t\t\t\t\t\tmaxEntityId = enchantment.originEntityId;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\tthis.gameState.sharedState.currentEntityId = maxEntityId + 1;\r\n\r\n\t\twhile (\r\n\t\t\t!playerEntity.startOfCombatDone ||\r\n\t\t\t!opponentEntity.startOfCombatDone ||\r\n\t\t\t(playerBoard?.length > 0 && opponentBoard?.length > 0)\r\n\t\t) {\r\n\t\t\t// Diagnostic only: detect runaway enchantment growth. The full entity id arrays are\r\n\t\t\t// only built in debug mode, since this allocates a lot on every battle pass\r\n\t\t\tif (debugState?.active) {\r\n\t\t\t\tthis.logRunawayEntityDiagnostics(playerBoard, playerEntity, opponentBoard, opponentEntity);\r\n\t\t\t}\r\n\t\t\tthis.simulateSingleBattlePass(playerBoard, playerEntity, opponentBoard, opponentEntity);\r\n\t\t\tif (this.hasShowShortCircuitWarning) {\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\t// The only case where there can only 0-attack minions on a board is when both boards\r\n\t\t\t// are that way (otherwise one side would kill the other)\r\n\t\t\tconst areBothBoards0Attack =\r\n\t\t\t\tplayerState.board.length > 0 &&\r\n\t\t\t\tplayerState.board.every((entity) => entity.attack === 0) &&\r\n\t\t\t\topponentState.board.length > 0 &&\r\n\t\t\t\topponentState.board.every((entity) => entity.attack === 0);\r\n\t\t\tif (areBothBoards0Attack) {\r\n\t\t\t\tplayerState.board = [];\r\n\t\t\t\topponentState.board = [];\r\n\t\t\t}\r\n\r\n\t\t\tconst isPlayerDefeated = playerState.board.length === 0 || playerState.player.hpLeft <= 0;\r\n\t\t\tconst isOpponentDefeated = opponentState.board.length === 0 || opponentState.player.hpLeft <= 0;\r\n\t\t\tif (isPlayerDefeated) {\r\n\t\t\t\tplayerBoard = playerState.teammate?.board;\r\n\t\t\t\tplayerState.board = [];\r\n\t\t\t\tplayerEntity = playerState.teammate?.player;\r\n\t\t\t} else {\r\n\t\t\t\tplayerBoard = playerState.board;\r\n\t\t\t\tplayerEntity = playerState.player;\r\n\t\t\t}\r\n\t\t\tif (isOpponentDefeated) {\r\n\t\t\t\topponentBoard = opponentState.teammate?.board;\r\n\t\t\t\topponentState.board = [];\r\n\t\t\t\topponentEntity = opponentState.teammate?.player;\r\n\t\t\t} else {\r\n\t\t\t\topponentBoard = opponentState.board;\r\n\t\t\t\topponentEntity = opponentState.player;\r\n\t\t\t}\r\n\t\t\t// So that gameState.player always refers to the active player.\r\n\t\t\t// Only swap when a real Duos teammate takes over — assigning an empty\r\n\t\t\t// teammate shell on solo defeat made isDuos true and used the flat 15\r\n\t\t\t// damage cap instead of the turn-scaled solo cap (35dd5300 t4: 13→10).\r\n\t\t\tif (isPlayerDefeated && playerEntity) {\r\n\t\t\t\t// Reset deaths\r\n\t\t\t\tthis.gameState.sharedState.deaths = this.gameState.sharedState.deaths.filter(\r\n\t\t\t\t\t(e) => e.friendly !== this.gameState.gameState.player.player.friendly,\r\n\t\t\t\t);\r\n\t\t\t\tthis.gameState.gameState.player.teammate = {\r\n\t\t\t\t\tboard: this.gameState.gameState.player.board,\r\n\t\t\t\t\tplayer: this.gameState.gameState.player.player,\r\n\t\t\t\t};\r\n\t\t\t\tthis.gameState.gameState.player.player = playerEntity;\r\n\t\t\t\tthis.gameState.gameState.player.board = playerBoard;\r\n\r\n\t\t\t\t// const initialPlayer = this.gameState.gameState.playerInitial;\r\n\t\t\t\t// const initialPlayerTeammate = initialPlayer.teammate;\r\n\t\t\t}\r\n\t\t\tif (isOpponentDefeated && opponentEntity) {\r\n\t\t\t\t// Reset deaths\r\n\t\t\t\tthis.gameState.sharedState.deaths = this.gameState.sharedState.deaths.filter(\r\n\t\t\t\t\t(e) => e.friendly !== this.gameState.gameState.opponent.player.friendly,\r\n\t\t\t\t);\r\n\t\t\t\tthis.gameState.gameState.opponent.teammate = {\r\n\t\t\t\t\tboard: this.gameState.gameState.opponent.board,\r\n\t\t\t\t\tplayer: this.gameState.gameState.opponent.player,\r\n\t\t\t\t};\r\n\t\t\t\tthis.gameState.gameState.opponent.player = opponentEntity;\r\n\t\t\t\tthis.gameState.gameState.opponent.board = opponentBoard;\r\n\t\t\t}\r\n\r\n\t\t\tif (!playerEntity || !opponentEntity) {\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tconst snapshotBoard = (board: BoardEntity[] | null | undefined): BoardEntity[] =>\r\n\t\t\t(board ?? []).map((e) => ({\r\n\t\t\t\t...e,\r\n\t\t\t\tenchantments: [...(e.enchantments ?? [])],\r\n\t\t\t\tpendingAttackBuffs: [...(e.pendingAttackBuffs ?? [])],\r\n\t\t\t\tadditionalCards: e.additionalCards ? [...e.additionalCards] : e.additionalCards,\r\n\t\t\t}));\r\n\r\n\t\tconst snapshotQuests = (hero: BgsPlayerEntity | null | undefined) => ({\r\n\t\t\tquestEntities: (hero?.questEntities ?? []).map((q) => ({ ...q })),\r\n\t\t\tquestRewards: [...(hero?.questRewards ?? [])],\r\n\t\t\tquestRewardEntities: (hero?.questRewardEntities ?? []).map((r) => ({ ...r })),\r\n\t\t});\r\n\r\n\t\tconst withQuestWriteback = (\r\n\t\t\tbase: Omit<\r\n\t\t\t\tSingleSimulationResult,\r\n\t\t\t\t| 'playerQuestEntities'\r\n\t\t\t\t| 'playerQuestRewards'\r\n\t\t\t\t| 'playerQuestRewardEntities'\r\n\t\t\t\t| 'opponentQuestEntities'\r\n\t\t\t\t| 'opponentQuestRewards'\r\n\t\t\t\t| 'opponentQuestRewardEntities'\r\n\t\t\t\t| 'playerRallyAttacksThisCombat'\r\n\t\t\t\t| 'opponentRallyAttacksThisCombat'\r\n\t\t\t\t| 'playerFreeRefreshesGainedThisCombat'\r\n\t\t\t\t| 'opponentFreeRefreshesGainedThisCombat'\r\n\t\t\t\t| 'playerFodderRefreshes'\r\n\t\t\t\t| 'opponentFodderRefreshes'\r\n\t\t\t\t| 'playerGlobalInfo'\r\n\t\t\t\t| 'opponentGlobalInfo'\r\n\t\t\t\t| 'playerHand'\r\n\t\t\t\t| 'opponentHand'\r\n\t\t\t\t| 'playerSecrets'\r\n\t\t\t\t| 'opponentSecrets'\r\n\t\t\t\t| 'playerTrinkets'\r\n\t\t\t\t| 'opponentTrinkets'\r\n\t\t\t\t| 'playerHeroPowers'\r\n\t\t\t\t| 'opponentHeroPowers'\r\n\t\t\t>,\r\n\t\t): SingleSimulationResult => {\r\n\t\t\tconst t0 = combatProfileEnabled() ? performance.now() : 0;\r\n\t\t\tconst playerQ = snapshotQuests(playerEntity);\r\n\t\t\tconst opponentQ = snapshotQuests(opponentEntity);\r\n\t\t\tconst snapshotHand = (hand: typeof playerEntity.hand) =>\r\n\t\t\t\t(hand ?? []).map((e) => ({\r\n\t\t\t\t\t...e,\r\n\t\t\t\t\tenchantments: [...(e.enchantments ?? [])],\r\n\t\t\t\t\tadditionalCards: e.additionalCards ? [...e.additionalCards] : e.additionalCards,\r\n\t\t\t\t}));\r\n\t\t\tconst snapshotSecrets = (secrets: typeof playerEntity.secrets) =>\r\n\t\t\t\t(secrets ?? []).filter((s) => !s.triggered).map((s) => ({ ...s }));\r\n\t\t\tconst snapshotTrinkets = (trinkets: typeof playerEntity.trinkets) =>\r\n\t\t\t\t(trinkets ?? []).map((t) => ({ ...t }));\r\n\t\t\tconst snapshotHeroPowers = (hps: typeof playerEntity.heroPowers) => (hps ?? []).map((hp) => ({ ...hp }));\r\n\t\t\tconst packed: SingleSimulationResult = {\r\n\t\t\t\t...base,\r\n\t\t\t\tplayerQuestEntities: playerQ.questEntities,\r\n\t\t\t\tplayerQuestRewards: playerQ.questRewards,\r\n\t\t\t\tplayerQuestRewardEntities: playerQ.questRewardEntities,\r\n\t\t\t\topponentQuestEntities: opponentQ.questEntities,\r\n\t\t\t\topponentQuestRewards: opponentQ.questRewards,\r\n\t\t\t\topponentQuestRewardEntities: opponentQ.questRewardEntities,\r\n\t\t\t\tplayerRallyAttacksThisCombat: playerEntity?.globalInfo?.RallyMinionAttacksThisCombat ?? 0,\r\n\t\t\t\topponentRallyAttacksThisCombat: opponentEntity?.globalInfo?.RallyMinionAttacksThisCombat ?? 0,\r\n\t\t\t\tplayerFreeRefreshesGainedThisCombat: playerEntity?.globalInfo?.FreeRefreshesGainedThisCombat ?? 0,\r\n\t\t\t\topponentFreeRefreshesGainedThisCombat: opponentEntity?.globalInfo?.FreeRefreshesGainedThisCombat ?? 0,\r\n\t\t\t\tplayerFodderRefreshes: playerEntity?.globalInfo?.fodderRefreshes ?? 0,\r\n\t\t\t\topponentFodderRefreshes: opponentEntity?.globalInfo?.fodderRefreshes ?? 0,\r\n\t\t\t\tplayerGlobalInfo: { ...(playerEntity?.globalInfo ?? {}) },\r\n\t\t\t\topponentGlobalInfo: { ...(opponentEntity?.globalInfo ?? {}) },\r\n\t\t\t\tplayerHand: snapshotHand(playerEntity?.hand),\r\n\t\t\t\topponentHand: snapshotHand(opponentEntity?.hand),\r\n\t\t\t\tplayerSecrets: snapshotSecrets(playerEntity?.secrets),\r\n\t\t\t\topponentSecrets: snapshotSecrets(opponentEntity?.secrets),\r\n\t\t\t\tplayerTrinkets: snapshotTrinkets(playerEntity?.trinkets),\r\n\t\t\t\topponentTrinkets: snapshotTrinkets(opponentEntity?.trinkets),\r\n\t\t\t\tplayerHeroPowers: snapshotHeroPowers(playerEntity?.heroPowers),\r\n\t\t\t\topponentHeroPowers: snapshotHeroPowers(opponentEntity?.heroPowers),\r\n\t\t\t};\r\n\t\t\tif (t0) {\r\n\t\t\t\tcombatProfileAdd('writebackMs', performance.now() - t0);\r\n\t\t\t}\r\n\t\t\treturn packed;\r\n\t\t};\r\n\r\n\t\tif (\r\n\t\t\t(!playerBoard?.length && !opponentBoard?.length) ||\r\n\t\t\t// E.g. when both players have a 0-attack minion\r\n\t\t\t(playerBoard?.length > 0 && opponentBoard?.length > 0)\r\n\t\t) {\r\n\t\t\treturn withQuestWriteback({\r\n\t\t\t\tresult: 'tied',\r\n\t\t\t\tdamageDealt: 0,\r\n\t\t\t\tplayerBoard: snapshotBoard(playerBoard),\r\n\t\t\t\topponentBoard: snapshotBoard(opponentBoard),\r\n\t\t\t});\r\n\t\t}\r\n\t\tif (!playerBoard?.length || playerEntity?.hpLeft <= 0) {\r\n\t\t\tconst winningHero = opponentEntity ?? opponentState.player;\r\n\t\t\tconst losingHero = playerEntity ?? playerState.player;\r\n\t\t\tlet damage = this.capDamage(\r\n\t\t\t\tthis.buildBoardTotalDamage(opponentBoard, this.gameState.gameState.opponent?.teammate?.board) +\r\n\t\t\t\t\t(winningHero?.tavernTier ?? 1),\r\n\t\t\t);\r\n\t\t\t// Full-game only: Ice Block prevents face damage (opt-in via applyIceBlock).\r\n\t\t\tif (this.gameState.applyIceBlock && damage > 0 && tryPreventCombatDamageWithIceBlock(losingHero)) {\r\n\t\t\t\tdamage = 0;\r\n\t\t\t}\r\n\t\t\tthis.gameState.spectator.registerOpponentAttack(playerBoard, opponentBoard, damage);\r\n\t\t\treturn withQuestWriteback({\r\n\t\t\t\tresult: 'lost',\r\n\t\t\t\tdamageDealt: damage,\r\n\t\t\t\tplayerBoard: snapshotBoard(playerBoard),\r\n\t\t\t\topponentBoard: snapshotBoard(opponentBoard),\r\n\t\t\t});\r\n\t\t}\r\n\r\n\t\tconst winningHero = playerEntity ?? playerState.player;\r\n\t\tconst losingHero = opponentEntity ?? opponentState.player;\r\n\t\tlet damage = this.capDamage(\r\n\t\t\tthis.buildBoardTotalDamage(playerBoard, this.gameState.gameState.player?.teammate?.board) +\r\n\t\t\t\t(winningHero?.tavernTier ?? 1),\r\n\t\t);\r\n\t\tif (this.gameState.applyIceBlock && damage > 0 && tryPreventCombatDamageWithIceBlock(losingHero)) {\r\n\t\t\tdamage = 0;\r\n\t\t}\r\n\t\tthis.gameState.spectator.registerPlayerAttack(playerBoard, opponentBoard, damage);\r\n\t\treturn withQuestWriteback({\r\n\t\t\tresult: 'won',\r\n\t\t\tdamageDealt: damage,\r\n\t\t\tplayerBoard: snapshotBoard(playerBoard),\r\n\t\t\topponentBoard: snapshotBoard(opponentBoard),\r\n\t\t});\r\n\t}\r\n\r\n\tprivate logRunawayEntityDiagnostics(\r\n\t\tplayerBoard: BoardEntity[],\r\n\t\tplayerEntity: BgsPlayerEntity,\r\n\t\topponentBoard: BoardEntity[],\r\n\t\topponentEntity: BgsPlayerEntity,\r\n\t): void {\r\n\t\tconst allEntityIds: number[] = [];\r\n\t\tfor (const group of [playerBoard, opponentBoard, playerEntity.hand, opponentEntity.hand]) {\r\n\t\t\tfor (const entity of group ?? []) {\r\n\t\t\t\tconst result = [entity.entityId, ...(entity.enchantments?.map((e) => e.originEntityId) ?? [])];\r\n\t\t\t\tif (result.length > 200) {\r\n\t\t\t\t\tconsole.log(\r\n\t\t\t\t\t\t'too many entities for entity',\r\n\t\t\t\t\t\tresult.length,\r\n\t\t\t\t\t\tstringifySimpleCard(entity, this.gameState.allCards),\r\n\t\t\t\t\t);\r\n\t\t\t\t}\r\n\t\t\t\tallEntityIds.push(...result);\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (allEntityIds.length > 10000) {\r\n\t\t\tconsole.log('allEntityIds2', allEntityIds, new Error().stack);\r\n\t\t}\r\n\t}\r\n\r\n\tprivate simulateSingleBattlePass(\r\n\t\tplayerBoard: BoardEntity[],\r\n\t\tplayerEntity: BgsPlayerEntity,\r\n\t\topponentBoard: BoardEntity[],\r\n\t\topponentEntity: BgsPlayerEntity,\r\n\t) {\r\n\t\tif (debugState?.active) {\r\n\t\t\tdebugState.onBattleStart();\r\n\t\t}\r\n\t\t// Start of combat happens only once, so we need to flag whether it has already happened for a\r\n\t\t// given player\r\n\t\tthis.gameState.spectator.registerStartOfCombat(playerBoard, opponentBoard, playerEntity, opponentEntity);\r\n\r\n\t\t// Who attacks first is decided by the game before the hero power comes into effect. However, the full board (with the generated minion)\r\n\t\t// is sent tothe simulator\r\n\t\t// But in fact, the first player decision takes into account that additional minion. See\r\n\t\t// https://replays.firestoneapp.com/?reviewId=ddbbbe93-464b-4400-8e8d-4abca8680a2e\r\n\t\tconst effectivePlayerBoardLength = playerBoard.length;\r\n\t\tconst effectiveOpponentBoardLength = opponentBoard.length;\r\n\t\tthis.currentAttacker =\r\n\t\t\teffectivePlayerBoardLength > effectiveOpponentBoardLength\r\n\t\t\t\t? 0\r\n\t\t\t\t: effectiveOpponentBoardLength > effectivePlayerBoardLength\r\n\t\t\t\t? 1\r\n\t\t\t\t: Math.round(Math.random());\r\n\t\tconst profile = combatProfileEnabled();\r\n\t\tconst socT0 = profile ? performance.now() : 0;\r\n\t\tconst suggestedNewCurrentAttacker = handleStartOfCombat(\r\n\t\t\tplayerEntity,\r\n\t\t\tplayerBoard,\r\n\t\t\topponentEntity,\r\n\t\t\topponentBoard,\r\n\t\t\tthis.currentAttacker,\r\n\t\t\tthis.gameState,\r\n\t\t);\r\n\t\tthis.currentAttacker = suggestedNewCurrentAttacker;\r\n\t\tconst socMs = profile ? performance.now() - socT0 : 0;\r\n\t\tlet counter = 0;\r\n\t\tif (debugState?.active) {\r\n\t\t\tthis.currentAttacker = debugState.forcedCurrentAttacker ?? this.currentAttacker;\r\n\t\t}\r\n\t\tconst atkT0 = profile ? performance.now() : 0;\r\n\t\twhile (\r\n\t\t\tplayerBoard.length > 0 &&\r\n\t\t\topponentBoard.length > 0 &&\r\n\t\t\tplayerEntity.hpLeft > 0 &&\r\n\t\t\topponentEntity.hpLeft > 0\r\n\t\t) {\r\n\t\t\tclearStealthIfNeeded(playerBoard, playerEntity, opponentBoard, opponentEntity, this.gameState);\r\n\t\t\t// console.log('this.currentSpeedAttacker', this.currentAttacker);\r\n\t\t\t// If there are \"attack immediately\" minions, we keep the same player\r\n\t\t\t// We put it here so that it can kick in after the start of combat effects. However here we don't want\r\n\t\t\t// to change who attacks first, so we repeat that block again after all the attacks have been resolved\r\n\t\t\t// FIXME: This is not strictly correct - if there are multiple attack immediately\r\n\t\t\t// minions that spawn on both player sides it might get a bit more complex\r\n\t\t\t// but overall it works\r\n\t\t\t// Also, this doesn't work when there are several deathrattle competing\r\n\t\t\t// to know who triggers first. See the second test case of the scallywag.test.ts\r\n\t\t\t// that is not handled properly today (the attack should in some cases happen before\r\n\t\t\t// the other deathrattle procs)\r\n\t\t\tif (playerBoard.some((entity) => entity.attackImmediately)) {\r\n\t\t\t\tthis.currentSpeedAttacker = 0;\r\n\t\t\t} else if (opponentBoard.some((entity) => entity.attackImmediately)) {\r\n\t\t\t\tthis.currentSpeedAttacker = 1;\r\n\t\t\t} else {\r\n\t\t\t\tthis.currentSpeedAttacker = -1;\r\n\t\t\t}\r\n\t\t\tif (\r\n\t\t\t\tplayerBoard.filter((e) => e.attack > 0).length === 0 &&\r\n\t\t\t\topponentBoard.filter((e) => e.attack > 0).length === 0\r\n\t\t\t) {\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\t// console.log('this.currentSpeedAttacker 2', this.currentAttacker, this.currentSpeedAttacker);\r\n\t\t\tif (this.currentSpeedAttacker === 0 || (this.currentSpeedAttacker === -1 && this.currentAttacker === 0)) {\r\n\t\t\t\tsimulateAttack(playerBoard, playerEntity, opponentBoard, opponentEntity, this.gameState);\r\n\t\t\t} else {\r\n\t\t\t\tsimulateAttack(opponentBoard, opponentEntity, playerBoard, playerEntity, this.gameState);\r\n\t\t\t}\r\n\r\n\t\t\tresolvePendingEffects(playerBoard, playerEntity, opponentBoard, opponentEntity, this.gameState);\r\n\t\t\t// Update the attacker indices in case there were some deaths\r\n\t\t\tif (playerBoard.some((entity) => entity.attackImmediately)) {\r\n\t\t\t\tthis.currentSpeedAttacker = 0;\r\n\t\t\t} else if (opponentBoard.some((entity) => entity.attackImmediately)) {\r\n\t\t\t\tthis.currentSpeedAttacker = 1;\r\n\t\t\t} else {\r\n\t\t\t\tthis.currentSpeedAttacker = -1;\r\n\t\t\t\tthis.currentAttacker = (this.currentAttacker + 1) % 2;\r\n\t\t\t}\r\n\t\t\tcounter++;\r\n\t\t\tif (counter > 400 && !this.hasShowShortCircuitWarning) {\r\n\t\t\t\tconsole.warn(\r\n\t\t\t\t\t'short-circuiting simulation, too many iterations',\r\n\t\t\t\t\tcounter,\r\n\t\t\t\t\t'\\n',\r\n\t\t\t\t\tstringifySimple(playerBoard, this.gameState.allCards),\r\n\t\t\t\t\t'\\n',\r\n\t\t\t\t\tstringifySimple(opponentBoard, this.gameState.allCards),\r\n\t\t\t\t);\r\n\t\t\t\tthis.hasShowShortCircuitWarning = true;\r\n\t\t\t\tbreak;\r\n\t\t\t\t// return null;\r\n\t\t\t}\r\n\t\t\thandleSummonsWhenSpace(playerBoard, playerEntity, opponentBoard, opponentEntity, this.gameState);\r\n\t\t}\r\n\t\tif (profile) {\r\n\t\t\tcombatProfileAdd('socMs', socMs);\r\n\t\t\tcombatProfileAdd('atkMs', performance.now() - atkT0);\r\n\t\t}\r\n\t}\r\n\r\n\tprivate capDamage(damage: number): number {\r\n\t\tif (!this.gameState.applyDamageCap) {\r\n\t\t\treturn damage;\r\n\t\t}\r\n\t\treturn applyCombatDamageCap(\r\n\t\t\tdamage,\r\n\t\t\tthis.gameState.currentTurn,\r\n\t\t\tthis.gameState.numberOfPlayersAlive,\r\n\t\t\t!!this.gameState.isDuos,\r\n\t\t);\r\n\t}\r\n\r\n\tprivate buildBoardTotalDamage(playerBoard: readonly BoardEntity[], teammateBoard?: BoardEntity[]): number {\r\n\t\tconst damageFromPlayerBoard = playerBoard\r\n\t\t\t.map((entity) =>\r\n\t\t\t\tgetEffectiveTechLevel(this.gameState.allCards.getCard(entity.cardId), this.gameState.allCards),\r\n\t\t\t)\r\n\t\t\t.reduce((a, b) => a + b, 0);\r\n\t\tconst numberOfTeamateMinionsToSummnon = 7 - playerBoard.length;\r\n\t\tconst damageFromTeammateBoard =\r\n\t\t\tteammateBoard\r\n\t\t\t\t?.slice(0, numberOfTeamateMinionsToSummnon)\r\n\t\t\t\t.map((entity) =>\r\n\t\t\t\t\tgetEffectiveTechLevel(this.gameState.allCards.getCard(entity.cardId), this.gameState.allCards),\r\n\t\t\t\t)\r\n\t\t\t\t.reduce((a, b) => a + b, 0) ?? 0;\r\n\t\treturn damageFromPlayerBoard + damageFromTeammateBoard;\r\n\t}\r\n}\r\n"]}
|
|
@@ -3,4 +3,4 @@ import { BoardEntity } from '../../board-entity';
|
|
|
3
3
|
import { BoardSecret } from '../../board-secret';
|
|
4
4
|
import { StartOfCombatTiming } from '../../cards/card.interface';
|
|
5
5
|
import { SoCInput } from './start-of-combat-input';
|
|
6
|
-
export declare const performStartOfCombatAction: (cardId: string, entity: BoardEntity | BoardTrinket | BgsPlayerEntity | BoardSecret, input: SoCInput, processDeaths: boolean, timing?: StartOfCombatTiming) => boolean;
|
|
6
|
+
export declare const performStartOfCombatAction: (cardId: string, entity: BoardEntity | BoardTrinket | BgsPlayerEntity | BoardSecret, input: SoCInput, processDeaths: boolean, timing?: StartOfCombatTiming, doSummonsWhenSpace?: boolean) => boolean;
|
|
@@ -61,7 +61,7 @@ const training_certificate_1 = require("../../cards/impl/trinket/training-certif
|
|
|
61
61
|
const valorous_medaillion_1 = require("../../cards/impl/trinket/valorous-medaillion");
|
|
62
62
|
const attack_1 = require("../attack");
|
|
63
63
|
const summon_when_space_1 = require("../summon-when-space");
|
|
64
|
-
const performStartOfCombatAction = (cardId, entity, input, processDeaths, timing) => {
|
|
64
|
+
const performStartOfCombatAction = (cardId, entity, input, processDeaths, timing, doSummonsWhenSpace = true) => {
|
|
65
65
|
let hasTriggered = false;
|
|
66
66
|
const promoPortraitCount = getPromoPortraitCount(input.playerEntity);
|
|
67
67
|
for (let i = promoPortraitCount; i >= 0; i--) {
|
|
@@ -76,13 +76,15 @@ const performStartOfCombatAction = (cardId, entity, input, processDeaths, timing
|
|
|
76
76
|
if (!!hasTriggered) {
|
|
77
77
|
if (processDeaths) {
|
|
78
78
|
(0, attack_1.processMinionDeath)(input.playerBoard, input.playerEntity, input.opponentBoard, input.opponentEntity, input.gameState, true);
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
hasTriggered.shouldRecomputeCurrentAttacker
|
|
79
|
+
if (doSummonsWhenSpace) {
|
|
80
|
+
const shouldRecomputeCurrentAttacker = (0, summon_when_space_1.handleSummonsWhenSpace)(input.playerBoard, input.playerEntity, input.opponentBoard, input.opponentEntity, input.gameState);
|
|
81
|
+
if (typeof hasTriggered === 'boolean') {
|
|
82
|
+
hasTriggered = hasTriggered || shouldRecomputeCurrentAttacker;
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
hasTriggered.shouldRecomputeCurrentAttacker =
|
|
86
|
+
hasTriggered.shouldRecomputeCurrentAttacker || shouldRecomputeCurrentAttacker;
|
|
87
|
+
}
|
|
86
88
|
}
|
|
87
89
|
}
|
|
88
90
|
if (typeof hasTriggered !== 'boolean' && hasTriggered.shouldRecomputeCurrentAttacker) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"soc-action-processor.js","sourceRoot":"","sources":["../../../src/simulation/start-of-combat/soc-action-processor.ts"],"names":[],"mappings":"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","sourcesContent":["import { BgsPlayerEntity, BoardTrinket } from '../../bgs-player-entity';\r\nimport { BoardEntity } from '../../board-entity';\r\nimport { BoardSecret } from '../../board-secret';\r\nimport { hasStartOfCombat, StartOfCombatCard, StartOfCombatTiming } from '../../cards/card.interface';\r\nimport { cardMappings } from '../../cards/impl/_card-mappings';\r\nimport { AnomalousTwin } from '../../cards/impl/anomaly/anomalous-twin';\r\nimport { BlessedOrBlighted } from '../../cards/impl/anomaly/blessed-or-blighted';\r\nimport { BoonOfBeetles } from '../../cards/impl/bg-spell/boon-of-beetles';\r\nimport { ToxicTumbleweed } from '../../cards/impl/bg-spell/toxic-tumbleweed';\r\nimport { UpperHand } from '../../cards/impl/bg-spell/upper-hand';\r\nimport { AimHigh } from '../../cards/impl/hero-power/aim-high';\r\nimport { AimLeft } from '../../cards/impl/hero-power/aim-left';\r\nimport { AimLow } from '../../cards/impl/hero-power/aim-low';\r\nimport { AimRight } from '../../cards/impl/hero-power/aim-right';\r\nimport { AllWillBurn } from '../../cards/impl/hero-power/all-will-burn';\r\nimport { AvatarOfNzoth } from '../../cards/impl/hero-power/avatar-of-nzoth';\r\nimport { DieInsects } from '../../cards/impl/hero-power/die-insects';\r\nimport { EarthInvocation } from '../../cards/impl/hero-power/earth-invocation';\r\nimport { EmbraceYourRage } from '../../cards/impl/hero-power/embrace-your-rage';\r\nimport { FireInvocation } from '../../cards/impl/hero-power/fire-invocation';\r\nimport { FireTheCannons } from '../../cards/impl/hero-power/fire-the-cannons';\r\nimport { LightningInvocation } from '../../cards/impl/hero-power/lightning-invocation';\r\nimport { MurlocKing } from '../../cards/impl/hero-power/murloc-king';\r\nimport { NefariousFire } from '../../cards/impl/hero-power/nefarious-fire';\r\nimport { RapidReanimation } from '../../cards/impl/hero-power/rapid-reanimation';\r\nimport { RebornRites } from '../../cards/impl/hero-power/reborn-rites';\r\nimport { Tentacular } from '../../cards/impl/hero-power/tentacular';\r\nimport { WaterInvocation } from '../../cards/impl/hero-power/water-invocation';\r\nimport { WaxWarband } from '../../cards/impl/hero-power/wax-warband';\r\nimport { AmberGuardian } from '../../cards/impl/minion/amber-guardian';\r\nimport { AudaciousAnchor } from '../../cards/impl/minion/audacious-anchor';\r\nimport { CarbonicCopy } from '../../cards/impl/minion/carbonic-copy';\r\nimport { CorruptedMyrmidon } from '../../cards/impl/minion/corrupted-myrmidon';\r\nimport { ElderTaggawag } from '../../cards/impl/minion/elder-taggawag';\r\nimport { HawkstriderHerald } from '../../cards/impl/minion/hawkstrider-herald';\r\nimport { MantidQueen } from '../../cards/impl/minion/mantid-queen';\r\nimport { Sandy } from '../../cards/impl/minion/sandy';\r\nimport { SunScreener } from '../../cards/impl/minion/sun-screener';\r\nimport { TheUninvitedGuest } from '../../cards/impl/minion/the-uninvited-guest';\r\nimport { ThousandthPaperDrake } from '../../cards/impl/minion/thousandth-paper-drake';\r\nimport { YulonFortuneGranter } from '../../cards/impl/minion/yulon-fortune-granter';\r\nimport { EvilTwin } from '../../cards/impl/quest-reward/evil-twin';\r\nimport { StaffOfOrigination } from '../../cards/impl/quest-reward/staff-of-origination';\r\nimport { StolenGold } from '../../cards/impl/quest-reward/stolen-gold';\r\nimport { FleetingVigor } from '../../cards/impl/spell/fleeting-vigor';\r\nimport { BronzeTimepiece } from '../../cards/impl/trinket/bronze-timepiece';\r\nimport { EmeraldDreamcatcher } from '../../cards/impl/trinket/emerald-dreamcatcher';\r\nimport { EternalPortrait } from '../../cards/impl/trinket/eternal-portrait';\r\nimport { FishySticker } from '../../cards/impl/trinket/fishy-sticker';\r\nimport { HoggyBank } from '../../cards/impl/trinket/hoggy-bank';\r\nimport { HollyMallet } from '../../cards/impl/trinket/holly-mallet';\r\nimport { IronforgeAnvil } from '../../cards/impl/trinket/ironforge-anvil';\r\nimport { JarredFrostling } from '../../cards/impl/trinket/jarred-frostling';\r\nimport { KarazhanChessSet } from '../../cards/impl/trinket/karazhan-chess-set';\r\nimport { RivendarePortrait } from '../../cards/impl/trinket/rivendare-portrait';\r\nimport { RustyTrident } from '../../cards/impl/trinket/rusty-trident';\r\nimport { ShipInABottle } from '../../cards/impl/trinket/ship-in-a-bottle';\r\nimport { SummoningSphere } from '../../cards/impl/trinket/summoning-sphere';\r\nimport { TinyfinOnesie } from '../../cards/impl/trinket/tinyfin-onesie';\r\nimport { TrainingCertificate } from '../../cards/impl/trinket/training-certificate';\r\nimport { ValorousMedallion } from '../../cards/impl/trinket/valorous-medaillion';\r\nimport { CardIds } from '../../services/card-ids';\r\nimport { processMinionDeath } from '../attack';\r\nimport { handleSummonsWhenSpace } from '../summon-when-space';\r\nimport { SoCInput } from './start-of-combat-input';\r\n\r\nexport const performStartOfCombatAction = (\r\n\tcardId: string,\r\n\tentity: BoardEntity | BoardTrinket | BgsPlayerEntity | BoardSecret,\r\n\tinput: SoCInput,\r\n\tprocessDeaths: boolean,\r\n\ttiming?: StartOfCombatTiming,\r\n): boolean => {\r\n\tlet hasTriggered:\r\n\t\t| boolean\r\n\t\t| {\r\n\t\t\t\thasTriggered: boolean;\r\n\t\t\t\tshouldRecomputeCurrentAttacker: boolean;\r\n\t\t } = false;\r\n\tconst promoPortraitCount = getPromoPortraitCount(input.playerEntity);\r\n\tfor (let i = promoPortraitCount; i >= 0; i--) {\r\n\t\tconst card = getStartOfCombatAction(cardId);\r\n\t\tif (!!timing && card?.startOfCombatTiming !== timing) {\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\tconst action = card?.startOfCombat;\r\n\t\tif (!!action) {\r\n\t\t\thasTriggered = action(entity, input);\r\n\t\t\t// Always proc it (for promo portrait), and use the hasTriggered for finer control\r\n\t\t\tonStartOfCombatTriggered(i, cardId, input.playerEntity);\r\n\t\t\tif (!!hasTriggered) {\r\n\t\t\t\tif (processDeaths) {\r\n\t\t\t\t\tprocessMinionDeath(\r\n\t\t\t\t\t\tinput.playerBoard,\r\n\t\t\t\t\t\tinput.playerEntity,\r\n\t\t\t\t\t\tinput.opponentBoard,\r\n\t\t\t\t\t\tinput.opponentEntity,\r\n\t\t\t\t\t\tinput.gameState,\r\n\t\t\t\t\t\t// See https://replays.firestoneapp.com/?reviewId=2eacbbc2-7dfa-487b-951d-0fa6d31d175e&turn=25&action=1\r\n\t\t\t\t\t\t// It looks like it doesn't look for the \"summons when space\" between Stitched Salvager's SoC and\r\n\t\t\t\t\t\t// Hawkstrider Herald SoC\r\n\t\t\t\t\t\ttrue,\r\n\t\t\t\t\t);\r\n\t\t\t\t\t// https://replays.firestoneapp.com/?reviewId=6293597f-28fa-472e-ba8c-35f6b7deef58&turn=31&action=6\r\n\t\t\t\t\t// The first RapidReanimation triggers, resummons the target, then the second RapidReanimation triggers\r\n\t\t\t\t\t// So we need to process the resummon as soon as the minion dies, and before processing the second RapidReanimation\r\n\t\t\t\t\tconst shouldRecomputeCurrentAttacker = handleSummonsWhenSpace(\r\n\t\t\t\t\t\tinput.playerBoard,\r\n\t\t\t\t\t\tinput.playerEntity,\r\n\t\t\t\t\t\tinput.opponentBoard,\r\n\t\t\t\t\t\tinput.opponentEntity,\r\n\t\t\t\t\t\tinput.gameState,\r\n\t\t\t\t\t);\r\n\t\t\t\t\tif (typeof hasTriggered === 'boolean') {\r\n\t\t\t\t\t\thasTriggered = hasTriggered || shouldRecomputeCurrentAttacker;\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\thasTriggered.shouldRecomputeCurrentAttacker =\r\n\t\t\t\t\t\t\thasTriggered.shouldRecomputeCurrentAttacker || shouldRecomputeCurrentAttacker;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tif (typeof hasTriggered !== 'boolean' && hasTriggered.shouldRecomputeCurrentAttacker) {\r\n\t\t\t\t\tinput.currentAttacker =\r\n\t\t\t\t\t\tinput.playerBoard.length > input.opponentBoard.length\r\n\t\t\t\t\t\t\t? input.playerIsFriendly\r\n\t\t\t\t\t\t\t\t? 0\r\n\t\t\t\t\t\t\t\t: 1\r\n\t\t\t\t\t\t\t: input.opponentBoard.length > input.playerBoard.length\r\n\t\t\t\t\t\t\t? input.playerIsFriendly\r\n\t\t\t\t\t\t\t\t? 1\r\n\t\t\t\t\t\t\t\t: 0\r\n\t\t\t\t\t\t\t: Math.round(Math.random());\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\treturn !!hasTriggered;\r\n};\r\n\r\n// TODO: load all cards on start, then do some programmatic mapping\r\nconst startOfCombatActionById = new Map<string, StartOfCombatCard | null>();\r\n\r\nconst getStartOfCombatAction = (cardId: string): StartOfCombatCard => {\r\n\tconst hit = startOfCombatActionById.get(cardId);\r\n\tif (hit !== undefined) {\r\n\t\treturn hit;\r\n\t}\r\n\tconst resolved = lookupStartOfCombatAction(cardId);\r\n\tstartOfCombatActionById.set(cardId, resolved);\r\n\treturn resolved;\r\n};\r\n\r\nconst lookupStartOfCombatAction = (cardId: string): StartOfCombatCard => {\r\n\tswitch (cardId) {\r\n\t\t// Quest rewards\r\n\t\tcase CardIds.EvilTwin:\r\n\t\t\treturn EvilTwin;\r\n\t\tcase CardIds.StaffOfOrigination_BG24_Reward_312:\r\n\t\t\treturn StaffOfOrigination;\r\n\t\tcase CardIds.StolenGold:\r\n\t\t\treturn StolenGold;\r\n\r\n\t\t// Trinkets\r\n\t\tcase CardIds.HolyMallet_BG30_MagicItem_902:\r\n\t\t\treturn HollyMallet;\r\n\t\tcase CardIds.TrainingCertificate_BG30_MagicItem_962:\r\n\t\t\treturn TrainingCertificate;\r\n\t\tcase CardIds.ValorousMedallion_BG30_MagicItem_970:\r\n\t\tcase CardIds.ValorousMedallion_ValorousMedallionToken_BG30_MagicItem_970t:\r\n\t\t\treturn ValorousMedallion;\r\n\t\tcase CardIds.EmeraldDreamcatcher_BG30_MagicItem_542:\r\n\t\t\treturn EmeraldDreamcatcher;\r\n\t\tcase CardIds.JarredFrostling_BG30_MagicItem_952:\r\n\t\t\treturn JarredFrostling;\r\n\t\tcase CardIds.RustyTrident_BG30_MagicItem_917:\r\n\t\t\treturn RustyTrident;\r\n\t\tcase CardIds.HoggyBank_BG30_MagicItem_411:\r\n\t\t\treturn HoggyBank;\r\n\t\tcase CardIds.ShipInABottle_BG30_MagicItem_407:\r\n\t\t\treturn ShipInABottle;\r\n\t\tcase CardIds.EternalPortrait_BG30_MagicItem_301:\r\n\t\t\treturn EternalPortrait;\r\n\t\tcase CardIds.RivendarePortrait_BG30_MagicItem_310:\r\n\t\t\treturn RivendarePortrait;\r\n\t\tcase CardIds.TinyfinOnesie_BG30_MagicItem_441:\r\n\t\t\treturn TinyfinOnesie;\r\n\t\tcase CardIds.BronzeTimepiece_BG30_MagicItem_995:\r\n\t\t\treturn BronzeTimepiece;\r\n\t\tcase CardIds.IronforgeAnvil_BG30_MagicItem_403:\r\n\t\t\treturn IronforgeAnvil;\r\n\t\tcase CardIds.KarazhanChessSet_BG30_MagicItem_972:\r\n\t\t\treturn KarazhanChessSet;\r\n\t\t// case CardIds.FishySticker_BG30_MagicItem_821:\r\n\t\tcase CardIds.FishySticker_FishyStickerToken_BG30_MagicItem_821t2:\r\n\t\t\treturn FishySticker;\r\n\t\tcase CardIds.SummoningSphere_BGDUO_MagicItem_003:\r\n\t\t\treturn SummoningSphere;\r\n\r\n\t\t// Hero powers\r\n\t\tcase CardIds.EarthInvocationToken:\r\n\t\t\treturn EarthInvocation;\r\n\t\tcase CardIds.WaterInvocationToken:\r\n\t\t\treturn WaterInvocation;\r\n\t\tcase CardIds.FireInvocationToken:\r\n\t\t\treturn FireInvocation;\r\n\t\tcase CardIds.LightningInvocationToken:\r\n\t\t\treturn LightningInvocation;\r\n\t\tcase CardIds.AllWillBurn:\r\n\t\t\treturn AllWillBurn;\r\n\t\tcase CardIds.AvatarOfNzoth:\r\n\t\t\treturn AvatarOfNzoth;\r\n\t\tcase CardIds.EmbraceYourRage:\r\n\t\t\treturn EmbraceYourRage;\r\n\t\tcase CardIds.Ozumat_Tentacular:\r\n\t\t\treturn Tentacular;\r\n\t\tcase CardIds.RebornRites:\r\n\t\t\treturn RebornRites;\r\n\t\tcase CardIds.TeronGorefiend_RapidReanimation:\r\n\t\t\treturn RapidReanimation;\r\n\t\tcase CardIds.WaxWarband:\r\n\t\t\treturn WaxWarband;\r\n\t\tcase CardIds.DieInsects_TB_BaconShop_HP_019:\r\n\t\t\treturn DieInsects;\r\n\t\tcase CardIds.FireTheCannons:\r\n\t\t\treturn FireTheCannons;\r\n\t\tcase CardIds.NefariousFire:\r\n\t\t\treturn NefariousFire;\r\n\t\tcase CardIds.MurlocKing:\r\n\t\t\treturn MurlocKing;\r\n\t\tcase CardIds.AimLeftToken:\r\n\t\t\treturn AimLeft;\r\n\t\tcase CardIds.AimRightToken:\r\n\t\t\treturn AimRight;\r\n\t\tcase CardIds.AimLowToken:\r\n\t\t\treturn AimLow;\r\n\t\tcase CardIds.AimHighToken:\r\n\t\t\treturn AimHigh;\r\n\r\n\t\t// Anomalies\r\n\t\tcase CardIds.BlessedOrBlighted_BG27_Anomaly_726:\r\n\t\t\treturn BlessedOrBlighted;\r\n\t\tcase CardIds.AnomalousTwin_BG27_Anomaly_560:\r\n\t\t\treturn AnomalousTwin;\r\n\t\tcase CardIds.BoonOfBeetles_BG28_603:\r\n\t\t\treturn BoonOfBeetles;\r\n\r\n\t\t// Spells\r\n\t\tcase CardIds.ToxicTumbleweed_BG28_641:\r\n\t\t\treturn ToxicTumbleweed;\r\n\t\tcase CardIds.UpperHand_BG28_573:\r\n\t\t\treturn UpperHand;\r\n\t\tcase CardIds.FleetingVigor_BG28_519:\r\n\t\t\treturn FleetingVigor;\r\n\r\n\t\t// Minions\r\n\t\tcase CardIds.AmberGuardian_BG24_500:\r\n\t\tcase CardIds.AmberGuardian_BG24_500_G:\r\n\t\t\treturn AmberGuardian;\r\n\t\t// case CardIds.SanctumRester_BG26_356:\r\n\t\t// case CardIds.SanctumRester_BG26_356_G:\r\n\t\t// \treturn SanctumRester;\r\n\t\tcase CardIds.CorruptedMyrmidon_BG23_012:\r\n\t\tcase CardIds.CorruptedMyrmidon_BG23_012_G:\r\n\t\t\treturn CorruptedMyrmidon;\r\n\t\tcase CardIds.MantidQueen_BG22_402:\r\n\t\tcase CardIds.MantidQueen_BG22_402_G:\r\n\t\t\treturn MantidQueen;\r\n\t\tcase CardIds.CarbonicCopy_BG27_503:\r\n\t\tcase CardIds.CarbonicCopy_BG27_503_G:\r\n\t\t\treturn CarbonicCopy;\r\n\t\tcase CardIds.HawkstriderHerald_BG27_079:\r\n\t\tcase CardIds.HawkstriderHerald_BG27_079_G:\r\n\t\t\treturn HawkstriderHerald;\r\n\t\tcase CardIds.AudaciousAnchor_BG28_904:\r\n\t\tcase CardIds.AudaciousAnchor_BG28_904_G:\r\n\t\t\treturn AudaciousAnchor;\r\n\t\tcase CardIds.ThousandthPaperDrake_BG29_810:\r\n\t\tcase CardIds.ThousandthPaperDrake_BG29_810_G:\r\n\t\t\treturn ThousandthPaperDrake;\r\n\t\tcase CardIds.YulonFortuneGranter_BG29_811:\r\n\t\tcase CardIds.YulonFortuneGranter_BG29_811_G:\r\n\t\t\treturn YulonFortuneGranter;\r\n\t\t// case CardIds.HoardingHatespawn_BG29_872:\r\n\t\t// case CardIds.HoardingHatespawn_BG29_872_G:\r\n\t\t// \treturn HoardingHatespawn;\r\n\t\tcase CardIds.TheUninvitedGuest_BG29_875:\r\n\t\tcase CardIds.TheUninvitedGuest_BG29_875_G:\r\n\t\t\treturn TheUninvitedGuest;\r\n\t\tcase CardIds.Sandy_BGDUO_125:\r\n\t\tcase CardIds.Sandy_BGDUO_125_G:\r\n\t\t\treturn Sandy;\r\n\t\tcase CardIds.ElderTaggawag_TB_BaconShop_HERO_14_Buddy:\r\n\t\tcase CardIds.ElderTaggawag_TB_BaconShop_HERO_14_Buddy_G:\r\n\t\t\treturn ElderTaggawag;\r\n\t\tcase CardIds.SunScreener_BG30_101:\r\n\t\tcase CardIds.SunScreener_BG30_101_G:\r\n\t\t\treturn SunScreener;\r\n\r\n\t\tdefault:\r\n\t\t\tconst candidate = cardMappings[cardId];\r\n\t\t\tif (hasStartOfCombat(candidate)) {\r\n\t\t\t\treturn candidate;\r\n\t\t\t}\r\n\t\t\treturn null;\r\n\t}\r\n};\r\n\r\nconst onStartOfCombatTriggered = (iteration: number, triggeredCardId: string, playerEntity: BgsPlayerEntity) => {\r\n\t// Some procs are iso-functional, and don't update the promo portrait\r\n\tconst promoPortraits = playerEntity.trinkets.filter(\r\n\t\t(t) => t.cardId === CardIds.PromoPortrait_BG30_MagicItem_918 && t.scriptDataNum1 > 0,\r\n\t);\r\n\tif (promoPortraits.length === 0 || iteration === 0 || iteration > promoPortraits.length) {\r\n\t\treturn;\r\n\t}\r\n\r\n\tswitch (triggeredCardId) {\r\n\t\tcase CardIds.StolenGold:\r\n\t\tcase CardIds.HolyMallet_BG30_MagicItem_902:\r\n\t\tcase CardIds.EmeraldDreamcatcher_BG30_MagicItem_542:\r\n\t\tcase CardIds.EternalPortrait_BG30_MagicItem_301:\r\n\t\tcase CardIds.BronzeTimepiece_BG30_MagicItem_995:\r\n\t\tcase CardIds.SwattingInsects:\r\n\t\tcase CardIds.RebornRites:\r\n\t\tcase CardIds.BoonOfBeetles_BG28_603:\r\n\t\t\treturn;\r\n\t}\r\n\r\n\tconst promoPortrait = promoPortraits[iteration - 1];\r\n\tpromoPortrait.scriptDataNum1--;\r\n};\r\n\r\nconst getPromoPortraitCount = (playerEntity: BgsPlayerEntity) => {\r\n\treturn playerEntity.trinkets.filter(\r\n\t\t(t) => t.cardId === CardIds.PromoPortrait_BG30_MagicItem_918 && t.scriptDataNum1 > 0,\r\n\t).length;\r\n};\r\n"]}
|
|
1
|
+
{"version":3,"file":"soc-action-processor.js","sourceRoot":"","sources":["../../../src/simulation/start-of-combat/soc-action-processor.ts"],"names":[],"mappings":";;;AAGA,+DAAsG;AACtG,oEAA+D;AAC/D,4EAAwE;AACxE,sFAAiF;AACjF,+EAA0E;AAC1E,iFAA6E;AAC7E,qEAAiE;AACjE,mEAA+D;AAC/D,mEAA+D;AAC/D,iEAA6D;AAC7D,qEAAiE;AACjE,6EAAwE;AACxE,iFAA4E;AAC5E,yEAAqE;AACrE,mFAA+E;AAC/E,qFAAgF;AAChF,iFAA6E;AAC7E,mFAA8E;AAC9E,2FAAuF;AACvF,yEAAqE;AACrE,+EAA2E;AAC3E,qFAAiF;AACjF,2EAAuE;AACvE,uEAAoE;AACpE,mFAA+E;AAC/E,yEAAqE;AACrE,2EAAuE;AACvE,+EAA2E;AAC3E,yEAAqE;AACrE,mFAA+E;AAC/E,2EAAuE;AACvE,mFAA+E;AAC/E,uEAAmE;AACnE,yDAAsD;AACtD,uEAAmE;AACnE,qFAAgF;AAChF,2FAAsF;AACtF,yFAAoF;AACpF,uEAAmE;AACnE,6FAAwF;AACxF,2EAAuE;AACvE,0EAAsE;AACtE,gFAA4E;AAC5E,wFAAoF;AACpF,gFAA4E;AAC5E,0EAAsE;AACtE,oEAAgE;AAChE,wEAAoE;AACpE,8EAA0E;AAC1E,gFAA4E;AAC5E,oFAA+E;AAC/E,oFAAgF;AAChF,0EAAsE;AACtE,gFAA0E;AAC1E,gFAA4E;AAC5E,4EAAwE;AACxE,wFAAoF;AACpF,sFAAiF;AAEjF,sCAA+C;AAC/C,4DAA8D;AAGvD,MAAM,0BAA0B,GAAG,CACzC,MAAc,EACd,MAAkE,EAClE,KAAe,EACf,aAAsB,EACtB,MAA4B,EAC5B,kBAAkB,GAAG,IAAI,EACf,EAAE;IACZ,IAAI,YAAY,GAKT,KAAK,CAAC;IACb,MAAM,kBAAkB,GAAG,qBAAqB,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IACrE,KAAK,IAAI,CAAC,GAAG,kBAAkB,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE;QAC7C,MAAM,IAAI,GAAG,sBAAsB,CAAC,MAAM,CAAC,CAAC;QAC5C,IAAI,CAAC,CAAC,MAAM,IAAI,CAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,mBAAmB,MAAK,MAAM,EAAE;YACrD,OAAO,KAAK,CAAC;SACb;QAED,MAAM,MAAM,GAAG,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,aAAa,CAAC;QACnC,IAAI,CAAC,CAAC,MAAM,EAAE;YACb,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;YAErC,wBAAwB,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;YACxD,IAAI,CAAC,CAAC,YAAY,EAAE;gBACnB,IAAI,aAAa,EAAE;oBAClB,IAAA,2BAAkB,EACjB,KAAK,CAAC,WAAW,EACjB,KAAK,CAAC,YAAY,EAClB,KAAK,CAAC,aAAa,EACnB,KAAK,CAAC,cAAc,EACpB,KAAK,CAAC,SAAS,EAIf,IAAI,CACJ,CAAC;oBAKF,IAAI,kBAAkB,EAAE;wBACvB,MAAM,8BAA8B,GAAG,IAAA,0CAAsB,EAC5D,KAAK,CAAC,WAAW,EACjB,KAAK,CAAC,YAAY,EAClB,KAAK,CAAC,aAAa,EACnB,KAAK,CAAC,cAAc,EACpB,KAAK,CAAC,SAAS,CACf,CAAC;wBACF,IAAI,OAAO,YAAY,KAAK,SAAS,EAAE;4BACtC,YAAY,GAAG,YAAY,IAAI,8BAA8B,CAAC;yBAC9D;6BAAM;4BACN,YAAY,CAAC,8BAA8B;gCAC1C,YAAY,CAAC,8BAA8B,IAAI,8BAA8B,CAAC;yBAC/E;qBACD;iBACD;gBACD,IAAI,OAAO,YAAY,KAAK,SAAS,IAAI,YAAY,CAAC,8BAA8B,EAAE;oBACrF,KAAK,CAAC,eAAe;wBACpB,KAAK,CAAC,WAAW,CAAC,MAAM,GAAG,KAAK,CAAC,aAAa,CAAC,MAAM;4BACpD,CAAC,CAAC,KAAK,CAAC,gBAAgB;gCACvB,CAAC,CAAC,CAAC;gCACH,CAAC,CAAC,CAAC;4BACJ,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,MAAM,GAAG,KAAK,CAAC,WAAW,CAAC,MAAM;gCACvD,CAAC,CAAC,KAAK,CAAC,gBAAgB;oCACvB,CAAC,CAAC,CAAC;oCACH,CAAC,CAAC,CAAC;gCACJ,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;iBAC9B;aACD;SACD;KACD;IACD,OAAO,CAAC,CAAC,YAAY,CAAC;AACvB,CAAC,CAAC;AA3EW,QAAA,0BAA0B,8BA2ErC;AAGF,MAAM,uBAAuB,GAAG,IAAI,GAAG,EAAoC,CAAC;AAE5E,MAAM,sBAAsB,GAAG,CAAC,MAAc,EAAqB,EAAE;IACpE,MAAM,GAAG,GAAG,uBAAuB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAChD,IAAI,GAAG,KAAK,SAAS,EAAE;QACtB,OAAO,GAAG,CAAC;KACX;IACD,MAAM,QAAQ,GAAG,yBAAyB,CAAC,MAAM,CAAC,CAAC;IACnD,uBAAuB,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC9C,OAAO,QAAQ,CAAC;AACjB,CAAC,CAAC;AAEF,MAAM,yBAAyB,GAAG,CAAC,MAAc,EAAqB,EAAE;IACvE,QAAQ,MAAM,EAAE;QAEf;YACC,OAAO,oBAAQ,CAAC;QACjB;YACC,OAAO,yCAAkB,CAAC;QAC3B;YACC,OAAO,wBAAU,CAAC;QAGnB;YACC,OAAO,0BAAW,CAAC;QACpB;YACC,OAAO,0CAAmB,CAAC;QAC5B,0BAAkD;QAClD;YACC,OAAO,uCAAiB,CAAC;QAC1B;YACC,OAAO,0CAAmB,CAAC;QAC5B;YACC,OAAO,kCAAe,CAAC;QACxB;YACC,OAAO,4BAAY,CAAC;QACrB;YACC,OAAO,sBAAS,CAAC;QAClB;YACC,OAAO,gCAAa,CAAC;QACtB;YACC,OAAO,kCAAe,CAAC;QACxB;YACC,OAAO,sCAAiB,CAAC;QAC1B;YACC,OAAO,8BAAa,CAAC;QACtB;YACC,OAAO,kCAAe,CAAC;QACxB;YACC,OAAO,gCAAc,CAAC;QACvB;YACC,OAAO,qCAAgB,CAAC;QAEzB;YACC,OAAO,4BAAY,CAAC;QACrB;YACC,OAAO,kCAAe,CAAC;QAGxB;YACC,OAAO,kCAAe,CAAC;QACxB;YACC,OAAO,kCAAe,CAAC;QACxB;YACC,OAAO,gCAAc,CAAC;QACvB;YACC,OAAO,0CAAmB,CAAC;QAC5B;YACC,OAAO,2BAAW,CAAC;QACpB;YACC,OAAO,+BAAa,CAAC;QACtB;YACC,OAAO,mCAAe,CAAC;QACxB;YACC,OAAO,uBAAU,CAAC;QACnB;YACC,OAAO,0BAAW,CAAC;QACpB;YACC,OAAO,oCAAgB,CAAC;QACzB;YACC,OAAO,wBAAU,CAAC;QACnB;YACC,OAAO,wBAAU,CAAC;QACnB;YACC,OAAO,iCAAc,CAAC;QACvB;YACC,OAAO,8BAAa,CAAC;QACtB;YACC,OAAO,wBAAU,CAAC;QACnB;YACC,OAAO,kBAAO,CAAC;QAChB;YACC,OAAO,oBAAQ,CAAC;QACjB;YACC,OAAO,gBAAM,CAAC;QACf;YACC,OAAO,kBAAO,CAAC;QAGhB;YACC,OAAO,uCAAiB,CAAC;QAC1B;YACC,OAAO,8BAAa,CAAC;QACtB;YACC,OAAO,+BAAa,CAAC;QAGtB;YACC,OAAO,kCAAe,CAAC;QACxB;YACC,OAAO,sBAAS,CAAC;QAClB;YACC,OAAO,8BAAa,CAAC;QAGtB,gBAAoC;QACpC;YACC,OAAO,8BAAa,CAAC;QAItB,gBAAwC;QACxC;YACC,OAAO,sCAAiB,CAAC;QAC1B,gBAAkC;QAClC;YACC,OAAO,0BAAW,CAAC;QACpB,gBAAmC;QACnC;YACC,OAAO,4BAAY,CAAC;QACrB,gBAAwC;QACxC;YACC,OAAO,sCAAiB,CAAC;QAC1B,gBAAsC;QACtC;YACC,OAAO,kCAAe,CAAC;QACxB,gBAA2C;QAC3C;YACC,OAAO,6CAAoB,CAAC;QAC7B,gBAA0C;QAC1C;YACC,OAAO,2CAAmB,CAAC;QAI5B,gBAAwC;QACxC;YACC,OAAO,uCAAiB,CAAC;QAC1B,iBAA6B;QAC7B;YACC,OAAO,aAAK,CAAC;QACd,kCAAsD;QACtD;YACC,OAAO,8BAAa,CAAC;QACtB,gBAAkC;QAClC;YACC,OAAO,0BAAW,CAAC;QAEpB;YACC,MAAM,SAAS,GAAG,6BAAY,CAAC,MAAM,CAAC,CAAC;YACvC,IAAI,IAAA,iCAAgB,EAAC,SAAS,CAAC,EAAE;gBAChC,OAAO,SAAS,CAAC;aACjB;YACD,OAAO,IAAI,CAAC;KACb;AACF,CAAC,CAAC;AAEF,MAAM,wBAAwB,GAAG,CAAC,SAAiB,EAAE,eAAuB,EAAE,YAA6B,EAAE,EAAE;IAE9G,MAAM,cAAc,GAAG,YAAY,CAAC,QAAQ,CAAC,MAAM,CAClD,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,yBAA6C,IAAI,CAAC,CAAC,cAAc,GAAG,CAAC,CACpF,CAAC;IACF,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,IAAI,SAAS,KAAK,CAAC,IAAI,SAAS,GAAG,cAAc,CAAC,MAAM,EAAE;QACxF,OAAO;KACP;IAED,QAAQ,eAAe,EAAE;QACxB,uBAAwB;QACxB,0BAA2C;QAC3C,0BAAoD;QACpD,0BAAgD;QAChD,0BAAgD;QAChD,2BAA6B;QAC7B,2BAAyB;QACzB;YACC,OAAO;KACR;IAED,MAAM,aAAa,GAAG,cAAc,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;IACpD,aAAa,CAAC,cAAc,EAAE,CAAC;AAChC,CAAC,CAAC;AAEF,MAAM,qBAAqB,GAAG,CAAC,YAA6B,EAAE,EAAE;IAC/D,OAAO,YAAY,CAAC,QAAQ,CAAC,MAAM,CAClC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,yBAA6C,IAAI,CAAC,CAAC,cAAc,GAAG,CAAC,CACpF,CAAC,MAAM,CAAC;AACV,CAAC,CAAC","sourcesContent":["import { BgsPlayerEntity, BoardTrinket } from '../../bgs-player-entity';\r\nimport { BoardEntity } from '../../board-entity';\r\nimport { BoardSecret } from '../../board-secret';\r\nimport { hasStartOfCombat, StartOfCombatCard, StartOfCombatTiming } from '../../cards/card.interface';\r\nimport { cardMappings } from '../../cards/impl/_card-mappings';\r\nimport { AnomalousTwin } from '../../cards/impl/anomaly/anomalous-twin';\r\nimport { BlessedOrBlighted } from '../../cards/impl/anomaly/blessed-or-blighted';\r\nimport { BoonOfBeetles } from '../../cards/impl/bg-spell/boon-of-beetles';\r\nimport { ToxicTumbleweed } from '../../cards/impl/bg-spell/toxic-tumbleweed';\r\nimport { UpperHand } from '../../cards/impl/bg-spell/upper-hand';\r\nimport { AimHigh } from '../../cards/impl/hero-power/aim-high';\r\nimport { AimLeft } from '../../cards/impl/hero-power/aim-left';\r\nimport { AimLow } from '../../cards/impl/hero-power/aim-low';\r\nimport { AimRight } from '../../cards/impl/hero-power/aim-right';\r\nimport { AllWillBurn } from '../../cards/impl/hero-power/all-will-burn';\r\nimport { AvatarOfNzoth } from '../../cards/impl/hero-power/avatar-of-nzoth';\r\nimport { DieInsects } from '../../cards/impl/hero-power/die-insects';\r\nimport { EarthInvocation } from '../../cards/impl/hero-power/earth-invocation';\r\nimport { EmbraceYourRage } from '../../cards/impl/hero-power/embrace-your-rage';\r\nimport { FireInvocation } from '../../cards/impl/hero-power/fire-invocation';\r\nimport { FireTheCannons } from '../../cards/impl/hero-power/fire-the-cannons';\r\nimport { LightningInvocation } from '../../cards/impl/hero-power/lightning-invocation';\r\nimport { MurlocKing } from '../../cards/impl/hero-power/murloc-king';\r\nimport { NefariousFire } from '../../cards/impl/hero-power/nefarious-fire';\r\nimport { RapidReanimation } from '../../cards/impl/hero-power/rapid-reanimation';\r\nimport { RebornRites } from '../../cards/impl/hero-power/reborn-rites';\r\nimport { Tentacular } from '../../cards/impl/hero-power/tentacular';\r\nimport { WaterInvocation } from '../../cards/impl/hero-power/water-invocation';\r\nimport { WaxWarband } from '../../cards/impl/hero-power/wax-warband';\r\nimport { AmberGuardian } from '../../cards/impl/minion/amber-guardian';\r\nimport { AudaciousAnchor } from '../../cards/impl/minion/audacious-anchor';\r\nimport { CarbonicCopy } from '../../cards/impl/minion/carbonic-copy';\r\nimport { CorruptedMyrmidon } from '../../cards/impl/minion/corrupted-myrmidon';\r\nimport { ElderTaggawag } from '../../cards/impl/minion/elder-taggawag';\r\nimport { HawkstriderHerald } from '../../cards/impl/minion/hawkstrider-herald';\r\nimport { MantidQueen } from '../../cards/impl/minion/mantid-queen';\r\nimport { Sandy } from '../../cards/impl/minion/sandy';\r\nimport { SunScreener } from '../../cards/impl/minion/sun-screener';\r\nimport { TheUninvitedGuest } from '../../cards/impl/minion/the-uninvited-guest';\r\nimport { ThousandthPaperDrake } from '../../cards/impl/minion/thousandth-paper-drake';\r\nimport { YulonFortuneGranter } from '../../cards/impl/minion/yulon-fortune-granter';\r\nimport { EvilTwin } from '../../cards/impl/quest-reward/evil-twin';\r\nimport { StaffOfOrigination } from '../../cards/impl/quest-reward/staff-of-origination';\r\nimport { StolenGold } from '../../cards/impl/quest-reward/stolen-gold';\r\nimport { FleetingVigor } from '../../cards/impl/spell/fleeting-vigor';\r\nimport { BronzeTimepiece } from '../../cards/impl/trinket/bronze-timepiece';\r\nimport { EmeraldDreamcatcher } from '../../cards/impl/trinket/emerald-dreamcatcher';\r\nimport { EternalPortrait } from '../../cards/impl/trinket/eternal-portrait';\r\nimport { FishySticker } from '../../cards/impl/trinket/fishy-sticker';\r\nimport { HoggyBank } from '../../cards/impl/trinket/hoggy-bank';\r\nimport { HollyMallet } from '../../cards/impl/trinket/holly-mallet';\r\nimport { IronforgeAnvil } from '../../cards/impl/trinket/ironforge-anvil';\r\nimport { JarredFrostling } from '../../cards/impl/trinket/jarred-frostling';\r\nimport { KarazhanChessSet } from '../../cards/impl/trinket/karazhan-chess-set';\r\nimport { RivendarePortrait } from '../../cards/impl/trinket/rivendare-portrait';\r\nimport { RustyTrident } from '../../cards/impl/trinket/rusty-trident';\r\nimport { ShipInABottle } from '../../cards/impl/trinket/ship-in-a-bottle';\r\nimport { SummoningSphere } from '../../cards/impl/trinket/summoning-sphere';\r\nimport { TinyfinOnesie } from '../../cards/impl/trinket/tinyfin-onesie';\r\nimport { TrainingCertificate } from '../../cards/impl/trinket/training-certificate';\r\nimport { ValorousMedallion } from '../../cards/impl/trinket/valorous-medaillion';\r\nimport { CardIds } from '../../services/card-ids';\r\nimport { processMinionDeath } from '../attack';\r\nimport { handleSummonsWhenSpace } from '../summon-when-space';\r\nimport { SoCInput } from './start-of-combat-input';\r\n\r\nexport const performStartOfCombatAction = (\r\n\tcardId: string,\r\n\tentity: BoardEntity | BoardTrinket | BgsPlayerEntity | BoardSecret,\r\n\tinput: SoCInput,\r\n\tprocessDeaths: boolean,\r\n\ttiming?: StartOfCombatTiming,\r\n\tdoSummonsWhenSpace = true,\r\n): boolean => {\r\n\tlet hasTriggered:\r\n\t\t| boolean\r\n\t\t| {\r\n\t\t\t\thasTriggered: boolean;\r\n\t\t\t\tshouldRecomputeCurrentAttacker: boolean;\r\n\t\t } = false;\r\n\tconst promoPortraitCount = getPromoPortraitCount(input.playerEntity);\r\n\tfor (let i = promoPortraitCount; i >= 0; i--) {\r\n\t\tconst card = getStartOfCombatAction(cardId);\r\n\t\tif (!!timing && card?.startOfCombatTiming !== timing) {\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\tconst action = card?.startOfCombat;\r\n\t\tif (!!action) {\r\n\t\t\thasTriggered = action(entity, input);\r\n\t\t\t// Always proc it (for promo portrait), and use the hasTriggered for finer control\r\n\t\t\tonStartOfCombatTriggered(i, cardId, input.playerEntity);\r\n\t\t\tif (!!hasTriggered) {\r\n\t\t\t\tif (processDeaths) {\r\n\t\t\t\t\tprocessMinionDeath(\r\n\t\t\t\t\t\tinput.playerBoard,\r\n\t\t\t\t\t\tinput.playerEntity,\r\n\t\t\t\t\t\tinput.opponentBoard,\r\n\t\t\t\t\t\tinput.opponentEntity,\r\n\t\t\t\t\t\tinput.gameState,\r\n\t\t\t\t\t\t// See https://replays.firestoneapp.com/?reviewId=2eacbbc2-7dfa-487b-951d-0fa6d31d175e&turn=25&action=1\r\n\t\t\t\t\t\t// It looks like it doesn't look for the \"summons when space\" between Stitched Salvager's SoC and\r\n\t\t\t\t\t\t// Hawkstrider Herald SoC\r\n\t\t\t\t\t\ttrue,\r\n\t\t\t\t\t);\r\n\t\t\t\t\t// https://replays.firestoneapp.com/?reviewId=6293597f-28fa-472e-ba8c-35f6b7deef58&turn=31&action=6\r\n\t\t\t\t\t// The first RapidReanimation triggers, resummons the target, then the second RapidReanimation triggers\r\n\t\t\t\t\t// So we need to process the resummon as soon as the minion dies, and before processing the\r\n\t\t\t\t\t// second RapidReanimation\r\n\t\t\t\t\tif (doSummonsWhenSpace) {\r\n\t\t\t\t\t\tconst shouldRecomputeCurrentAttacker = handleSummonsWhenSpace(\r\n\t\t\t\t\t\t\tinput.playerBoard,\r\n\t\t\t\t\t\t\tinput.playerEntity,\r\n\t\t\t\t\t\t\tinput.opponentBoard,\r\n\t\t\t\t\t\t\tinput.opponentEntity,\r\n\t\t\t\t\t\t\tinput.gameState,\r\n\t\t\t\t\t\t);\r\n\t\t\t\t\t\tif (typeof hasTriggered === 'boolean') {\r\n\t\t\t\t\t\t\thasTriggered = hasTriggered || shouldRecomputeCurrentAttacker;\r\n\t\t\t\t\t\t} else {\r\n\t\t\t\t\t\t\thasTriggered.shouldRecomputeCurrentAttacker =\r\n\t\t\t\t\t\t\t\thasTriggered.shouldRecomputeCurrentAttacker || shouldRecomputeCurrentAttacker;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tif (typeof hasTriggered !== 'boolean' && hasTriggered.shouldRecomputeCurrentAttacker) {\r\n\t\t\t\t\tinput.currentAttacker =\r\n\t\t\t\t\t\tinput.playerBoard.length > input.opponentBoard.length\r\n\t\t\t\t\t\t\t? input.playerIsFriendly\r\n\t\t\t\t\t\t\t\t? 0\r\n\t\t\t\t\t\t\t\t: 1\r\n\t\t\t\t\t\t\t: input.opponentBoard.length > input.playerBoard.length\r\n\t\t\t\t\t\t\t? input.playerIsFriendly\r\n\t\t\t\t\t\t\t\t? 1\r\n\t\t\t\t\t\t\t\t: 0\r\n\t\t\t\t\t\t\t: Math.round(Math.random());\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\treturn !!hasTriggered;\r\n};\r\n\r\n// TODO: load all cards on start, then do some programmatic mapping\r\nconst startOfCombatActionById = new Map<string, StartOfCombatCard | null>();\r\n\r\nconst getStartOfCombatAction = (cardId: string): StartOfCombatCard => {\r\n\tconst hit = startOfCombatActionById.get(cardId);\r\n\tif (hit !== undefined) {\r\n\t\treturn hit;\r\n\t}\r\n\tconst resolved = lookupStartOfCombatAction(cardId);\r\n\tstartOfCombatActionById.set(cardId, resolved);\r\n\treturn resolved;\r\n};\r\n\r\nconst lookupStartOfCombatAction = (cardId: string): StartOfCombatCard => {\r\n\tswitch (cardId) {\r\n\t\t// Quest rewards\r\n\t\tcase CardIds.EvilTwin:\r\n\t\t\treturn EvilTwin;\r\n\t\tcase CardIds.StaffOfOrigination_BG24_Reward_312:\r\n\t\t\treturn StaffOfOrigination;\r\n\t\tcase CardIds.StolenGold:\r\n\t\t\treturn StolenGold;\r\n\r\n\t\t// Trinkets\r\n\t\tcase CardIds.HolyMallet_BG30_MagicItem_902:\r\n\t\t\treturn HollyMallet;\r\n\t\tcase CardIds.TrainingCertificate_BG30_MagicItem_962:\r\n\t\t\treturn TrainingCertificate;\r\n\t\tcase CardIds.ValorousMedallion_BG30_MagicItem_970:\r\n\t\tcase CardIds.ValorousMedallion_ValorousMedallionToken_BG30_MagicItem_970t:\r\n\t\t\treturn ValorousMedallion;\r\n\t\tcase CardIds.EmeraldDreamcatcher_BG30_MagicItem_542:\r\n\t\t\treturn EmeraldDreamcatcher;\r\n\t\tcase CardIds.JarredFrostling_BG30_MagicItem_952:\r\n\t\t\treturn JarredFrostling;\r\n\t\tcase CardIds.RustyTrident_BG30_MagicItem_917:\r\n\t\t\treturn RustyTrident;\r\n\t\tcase CardIds.HoggyBank_BG30_MagicItem_411:\r\n\t\t\treturn HoggyBank;\r\n\t\tcase CardIds.ShipInABottle_BG30_MagicItem_407:\r\n\t\t\treturn ShipInABottle;\r\n\t\tcase CardIds.EternalPortrait_BG30_MagicItem_301:\r\n\t\t\treturn EternalPortrait;\r\n\t\tcase CardIds.RivendarePortrait_BG30_MagicItem_310:\r\n\t\t\treturn RivendarePortrait;\r\n\t\tcase CardIds.TinyfinOnesie_BG30_MagicItem_441:\r\n\t\t\treturn TinyfinOnesie;\r\n\t\tcase CardIds.BronzeTimepiece_BG30_MagicItem_995:\r\n\t\t\treturn BronzeTimepiece;\r\n\t\tcase CardIds.IronforgeAnvil_BG30_MagicItem_403:\r\n\t\t\treturn IronforgeAnvil;\r\n\t\tcase CardIds.KarazhanChessSet_BG30_MagicItem_972:\r\n\t\t\treturn KarazhanChessSet;\r\n\t\t// case CardIds.FishySticker_BG30_MagicItem_821:\r\n\t\tcase CardIds.FishySticker_FishyStickerToken_BG30_MagicItem_821t2:\r\n\t\t\treturn FishySticker;\r\n\t\tcase CardIds.SummoningSphere_BGDUO_MagicItem_003:\r\n\t\t\treturn SummoningSphere;\r\n\r\n\t\t// Hero powers\r\n\t\tcase CardIds.EarthInvocationToken:\r\n\t\t\treturn EarthInvocation;\r\n\t\tcase CardIds.WaterInvocationToken:\r\n\t\t\treturn WaterInvocation;\r\n\t\tcase CardIds.FireInvocationToken:\r\n\t\t\treturn FireInvocation;\r\n\t\tcase CardIds.LightningInvocationToken:\r\n\t\t\treturn LightningInvocation;\r\n\t\tcase CardIds.AllWillBurn:\r\n\t\t\treturn AllWillBurn;\r\n\t\tcase CardIds.AvatarOfNzoth:\r\n\t\t\treturn AvatarOfNzoth;\r\n\t\tcase CardIds.EmbraceYourRage:\r\n\t\t\treturn EmbraceYourRage;\r\n\t\tcase CardIds.Ozumat_Tentacular:\r\n\t\t\treturn Tentacular;\r\n\t\tcase CardIds.RebornRites:\r\n\t\t\treturn RebornRites;\r\n\t\tcase CardIds.TeronGorefiend_RapidReanimation:\r\n\t\t\treturn RapidReanimation;\r\n\t\tcase CardIds.WaxWarband:\r\n\t\t\treturn WaxWarband;\r\n\t\tcase CardIds.DieInsects_TB_BaconShop_HP_019:\r\n\t\t\treturn DieInsects;\r\n\t\tcase CardIds.FireTheCannons:\r\n\t\t\treturn FireTheCannons;\r\n\t\tcase CardIds.NefariousFire:\r\n\t\t\treturn NefariousFire;\r\n\t\tcase CardIds.MurlocKing:\r\n\t\t\treturn MurlocKing;\r\n\t\tcase CardIds.AimLeftToken:\r\n\t\t\treturn AimLeft;\r\n\t\tcase CardIds.AimRightToken:\r\n\t\t\treturn AimRight;\r\n\t\tcase CardIds.AimLowToken:\r\n\t\t\treturn AimLow;\r\n\t\tcase CardIds.AimHighToken:\r\n\t\t\treturn AimHigh;\r\n\r\n\t\t// Anomalies\r\n\t\tcase CardIds.BlessedOrBlighted_BG27_Anomaly_726:\r\n\t\t\treturn BlessedOrBlighted;\r\n\t\tcase CardIds.AnomalousTwin_BG27_Anomaly_560:\r\n\t\t\treturn AnomalousTwin;\r\n\t\tcase CardIds.BoonOfBeetles_BG28_603:\r\n\t\t\treturn BoonOfBeetles;\r\n\r\n\t\t// Spells\r\n\t\tcase CardIds.ToxicTumbleweed_BG28_641:\r\n\t\t\treturn ToxicTumbleweed;\r\n\t\tcase CardIds.UpperHand_BG28_573:\r\n\t\t\treturn UpperHand;\r\n\t\tcase CardIds.FleetingVigor_BG28_519:\r\n\t\t\treturn FleetingVigor;\r\n\r\n\t\t// Minions\r\n\t\tcase CardIds.AmberGuardian_BG24_500:\r\n\t\tcase CardIds.AmberGuardian_BG24_500_G:\r\n\t\t\treturn AmberGuardian;\r\n\t\t// case CardIds.SanctumRester_BG26_356:\r\n\t\t// case CardIds.SanctumRester_BG26_356_G:\r\n\t\t// \treturn SanctumRester;\r\n\t\tcase CardIds.CorruptedMyrmidon_BG23_012:\r\n\t\tcase CardIds.CorruptedMyrmidon_BG23_012_G:\r\n\t\t\treturn CorruptedMyrmidon;\r\n\t\tcase CardIds.MantidQueen_BG22_402:\r\n\t\tcase CardIds.MantidQueen_BG22_402_G:\r\n\t\t\treturn MantidQueen;\r\n\t\tcase CardIds.CarbonicCopy_BG27_503:\r\n\t\tcase CardIds.CarbonicCopy_BG27_503_G:\r\n\t\t\treturn CarbonicCopy;\r\n\t\tcase CardIds.HawkstriderHerald_BG27_079:\r\n\t\tcase CardIds.HawkstriderHerald_BG27_079_G:\r\n\t\t\treturn HawkstriderHerald;\r\n\t\tcase CardIds.AudaciousAnchor_BG28_904:\r\n\t\tcase CardIds.AudaciousAnchor_BG28_904_G:\r\n\t\t\treturn AudaciousAnchor;\r\n\t\tcase CardIds.ThousandthPaperDrake_BG29_810:\r\n\t\tcase CardIds.ThousandthPaperDrake_BG29_810_G:\r\n\t\t\treturn ThousandthPaperDrake;\r\n\t\tcase CardIds.YulonFortuneGranter_BG29_811:\r\n\t\tcase CardIds.YulonFortuneGranter_BG29_811_G:\r\n\t\t\treturn YulonFortuneGranter;\r\n\t\t// case CardIds.HoardingHatespawn_BG29_872:\r\n\t\t// case CardIds.HoardingHatespawn_BG29_872_G:\r\n\t\t// \treturn HoardingHatespawn;\r\n\t\tcase CardIds.TheUninvitedGuest_BG29_875:\r\n\t\tcase CardIds.TheUninvitedGuest_BG29_875_G:\r\n\t\t\treturn TheUninvitedGuest;\r\n\t\tcase CardIds.Sandy_BGDUO_125:\r\n\t\tcase CardIds.Sandy_BGDUO_125_G:\r\n\t\t\treturn Sandy;\r\n\t\tcase CardIds.ElderTaggawag_TB_BaconShop_HERO_14_Buddy:\r\n\t\tcase CardIds.ElderTaggawag_TB_BaconShop_HERO_14_Buddy_G:\r\n\t\t\treturn ElderTaggawag;\r\n\t\tcase CardIds.SunScreener_BG30_101:\r\n\t\tcase CardIds.SunScreener_BG30_101_G:\r\n\t\t\treturn SunScreener;\r\n\r\n\t\tdefault:\r\n\t\t\tconst candidate = cardMappings[cardId];\r\n\t\t\tif (hasStartOfCombat(candidate)) {\r\n\t\t\t\treturn candidate;\r\n\t\t\t}\r\n\t\t\treturn null;\r\n\t}\r\n};\r\n\r\nconst onStartOfCombatTriggered = (iteration: number, triggeredCardId: string, playerEntity: BgsPlayerEntity) => {\r\n\t// Some procs are iso-functional, and don't update the promo portrait\r\n\tconst promoPortraits = playerEntity.trinkets.filter(\r\n\t\t(t) => t.cardId === CardIds.PromoPortrait_BG30_MagicItem_918 && t.scriptDataNum1 > 0,\r\n\t);\r\n\tif (promoPortraits.length === 0 || iteration === 0 || iteration > promoPortraits.length) {\r\n\t\treturn;\r\n\t}\r\n\r\n\tswitch (triggeredCardId) {\r\n\t\tcase CardIds.StolenGold:\r\n\t\tcase CardIds.HolyMallet_BG30_MagicItem_902:\r\n\t\tcase CardIds.EmeraldDreamcatcher_BG30_MagicItem_542:\r\n\t\tcase CardIds.EternalPortrait_BG30_MagicItem_301:\r\n\t\tcase CardIds.BronzeTimepiece_BG30_MagicItem_995:\r\n\t\tcase CardIds.SwattingInsects:\r\n\t\tcase CardIds.RebornRites:\r\n\t\tcase CardIds.BoonOfBeetles_BG28_603:\r\n\t\t\treturn;\r\n\t}\r\n\r\n\tconst promoPortrait = promoPortraits[iteration - 1];\r\n\tpromoPortrait.scriptDataNum1--;\r\n};\r\n\r\nconst getPromoPortraitCount = (playerEntity: BgsPlayerEntity) => {\r\n\treturn playerEntity.trinkets.filter(\r\n\t\t(t) => t.cardId === CardIds.PromoPortrait_BG30_MagicItem_918 && t.scriptDataNum1 > 0,\r\n\t).length;\r\n};\r\n"]}
|
|
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.performStartOfCombatMinionsForPlayer = exports.handleStartOfCombatMinions = void 0;
|
|
4
4
|
const card_interface_1 = require("../../cards/card.interface");
|
|
5
5
|
const _card_mappings_1 = require("../../cards/impl/_card-mappings");
|
|
6
|
-
const summon_when_space_1 = require("../summon-when-space");
|
|
7
6
|
const soc_action_processor_1 = require("./soc-action-processor");
|
|
8
7
|
const handleStartOfCombatMinions = (playerEntity, playerBoard, opponentEntity, opponentBoard, currentAttacker, playerBoardBefore, opponentBoardBefore, gameState) => {
|
|
9
8
|
let attackerForStart = Math.random() < 0.5 ? 0 : 1;
|
|
@@ -65,15 +64,6 @@ const handleStartOfCombatMinions = (playerEntity, playerBoard, opponentEntity, o
|
|
|
65
64
|
attackerForStart = (attackerForStart + 1) % 2;
|
|
66
65
|
}
|
|
67
66
|
}
|
|
68
|
-
const shouldRecomputeCurrentAttacker = (0, summon_when_space_1.handleSummonsWhenSpace)(playerBoard, playerEntity, opponentBoard, opponentEntity, gameState);
|
|
69
|
-
if (shouldRecomputeCurrentAttacker) {
|
|
70
|
-
currentAttacker =
|
|
71
|
-
playerBoard.length > opponentBoard.length
|
|
72
|
-
? 0
|
|
73
|
-
: opponentBoard.length > playerBoard.length
|
|
74
|
-
? 1
|
|
75
|
-
: Math.round(Math.random());
|
|
76
|
-
}
|
|
77
67
|
return currentAttacker;
|
|
78
68
|
};
|
|
79
69
|
exports.handleStartOfCombatMinions = handleStartOfCombatMinions;
|
|
@@ -87,9 +77,10 @@ const performStartOfCombatMinionsForPlayer = (minion, input) => {
|
|
|
87
77
|
if (input.playerEntity.startOfCombatDone) {
|
|
88
78
|
return false;
|
|
89
79
|
}
|
|
90
|
-
hasProcessed = (0, soc_action_processor_1.performStartOfCombatAction)(minion.cardId, minion, input, true) || hasProcessed;
|
|
80
|
+
hasProcessed = (0, soc_action_processor_1.performStartOfCombatAction)(minion.cardId, minion, input, true, undefined, false) || hasProcessed;
|
|
91
81
|
for (const enchantment of (_b = minion.enchantments) !== null && _b !== void 0 ? _b : []) {
|
|
92
|
-
hasProcessed =
|
|
82
|
+
hasProcessed =
|
|
83
|
+
(0, soc_action_processor_1.performStartOfCombatAction)(enchantment.cardId, minion, input, true, undefined, false) || hasProcessed;
|
|
93
84
|
}
|
|
94
85
|
}
|
|
95
86
|
return hasProcessed;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"soc-minion.js","sourceRoot":"","sources":["../../../src/simulation/start-of-combat/soc-minion.ts"],"names":[],"mappings":";;;AAEA,+DAAsE;AACtE,oEAA+D;AAG/D,
|
|
1
|
+
{"version":3,"file":"soc-minion.js","sourceRoot":"","sources":["../../../src/simulation/start-of-combat/soc-minion.ts"],"names":[],"mappings":"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","sourcesContent":["import { BgsPlayerEntity } from '../../bgs-player-entity';\r\nimport { BoardEntity } from '../../board-entity';\r\nimport { hasStartOfCombatFromHand } from '../../cards/card.interface';\r\nimport { cardMappings } from '../../cards/impl/_card-mappings';\r\nimport { CardIds } from '../../services/card-ids';\r\nimport { FullGameState } from '../internal-game-state';\r\nimport { performStartOfCombatAction } from './soc-action-processor';\r\nimport { SoCInput } from './start-of-combat-input';\r\n\r\nexport const handleStartOfCombatMinions = (\r\n\tplayerEntity: BgsPlayerEntity,\r\n\tplayerBoard: BoardEntity[],\r\n\topponentEntity: BgsPlayerEntity,\r\n\topponentBoard: BoardEntity[],\r\n\tcurrentAttacker: number,\r\n\tplayerBoardBefore: BoardEntity[],\r\n\topponentBoardBefore: BoardEntity[],\r\n\tgameState: FullGameState,\r\n): number => {\r\n\tlet attackerForStart = Math.random() < 0.5 ? 0 : 1;\r\n\tconst playerAttackers = [\r\n\t\t// Hand triggers first\r\n\t\t...playerEntity.hand.filter((e) => hasStartOfCombatFromHand(cardMappings[e.cardId])),\r\n\t\t...playerBoard,\r\n\t];\r\n\tconst opponentAttackers = [\r\n\t\t...opponentEntity.hand.filter((e) => hasStartOfCombatFromHand(cardMappings[e.cardId])),\r\n\t\t...opponentBoard,\r\n\t];\r\n\r\n\t// UPDATE 2025-11-16 We probably want to run the Start of Combat first for one player completely, then for the other\r\n\t// I thought it would alternate, but this apparently isn't the case:\r\n\t// https://replays.firestoneapp.com/?reviewId=af9053ee-9ee6-440a-a09f-b7fb2f4b7a3e&turn=9&action=0\r\n\twhile (playerAttackers.length > 0 || opponentAttackers.length > 0) {\r\n\t\tlet shouldUpdateNextPlayer = false;\r\n\t\tif (attackerForStart === 0 && playerAttackers.length > 0) {\r\n\t\t\tconst attacker = playerAttackers.splice(0, 1)[0];\r\n\t\t\tif (attacker.health <= 0 || attacker.definitelyDead) {\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\tconst input = {\r\n\t\t\t\tplayerBoard: playerBoard,\r\n\t\t\t\tplayerEntity: playerEntity,\r\n\t\t\t\topponentBoard: opponentBoard,\r\n\t\t\t\topponentEntity: opponentEntity,\r\n\t\t\t\tplayerBoardBefore: playerBoardBefore,\r\n\t\t\t\topponentBoardBefore: opponentBoardBefore,\r\n\t\t\t\tplayerIsFriendly: true,\r\n\t\t\t\tcurrentAttacker: currentAttacker,\r\n\t\t\t\tgameState,\r\n\t\t\t};\r\n\t\t\tconst newShouldUpdateNextPlayer = performStartOfCombatMinionsForPlayer(attacker, input);\r\n\t\t\tcurrentAttacker = input.currentAttacker;\r\n\t\t} else if (attackerForStart === 0 && playerAttackers.length === 0) {\r\n\t\t\tshouldUpdateNextPlayer = true;\r\n\t\t} else if (attackerForStart === 1 && opponentAttackers.length > 0) {\r\n\t\t\tconst attacker = opponentAttackers.splice(0, 1)[0];\r\n\t\t\tif (attacker.health <= 0 || attacker.definitelyDead) {\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\tconst input = {\r\n\t\t\t\tplayerBoard: opponentBoard,\r\n\t\t\t\tplayerEntity: opponentEntity,\r\n\t\t\t\topponentBoard: playerBoard,\r\n\t\t\t\topponentEntity: playerEntity,\r\n\t\t\t\tplayerBoardBefore: opponentBoardBefore,\r\n\t\t\t\topponentBoardBefore: playerBoardBefore,\r\n\t\t\t\tplayerIsFriendly: false,\r\n\t\t\t\tcurrentAttacker: currentAttacker,\r\n\t\t\t\tgameState,\r\n\t\t\t};\r\n\t\t\tconst newShouldUpdateNextPlayer = performStartOfCombatMinionsForPlayer(attacker, input);\r\n\t\t\tcurrentAttacker = input.currentAttacker;\r\n\t\t} else if (attackerForStart === 1 && opponentAttackers.length === 0) {\r\n\t\t\tshouldUpdateNextPlayer = true;\r\n\t\t}\r\n\r\n\t\tif (shouldUpdateNextPlayer) {\r\n\t\t\tattackerForStart = (attackerForStart + 1) % 2;\r\n\t\t}\r\n\t}\r\n\t// 2026-09-29: I'm having doubts as to whether the \"summonsWhenSpace\" should be called here.\r\n\t// https://replays.firestoneapp.com/?reviewId=71184008-9884-47d0-89fc-c175c37a3be3&turn=15&action=1\r\n\t// Bomm-in-a-Box triggers, then the player attacks, and only the does Tentacular procs\r\n\t// https://replays.firestoneapp.com/?reviewId=49582580-dae9-4079-b2ff-741bab446da6&turn=27&action=2\r\n\t// On this board, Heroic Broodmother attacks twice as part of the Start of Combat, then the\r\n\t// opponent's turn starts and it attacks immediately before giving Frostwolf Fervor a chance to proc\r\n\t// For now I will comment this out, and we'll see how that goes\r\n\t// const shouldRecomputeCurrentAttacker = handleSummonsWhenSpace(\r\n\t// \tplayerBoard,\r\n\t// \tplayerEntity,\r\n\t// \topponentBoard,\r\n\t// \topponentEntity,\r\n\t// \tgameState,\r\n\t// );\r\n\t// if (shouldRecomputeCurrentAttacker) {\r\n\t// \tcurrentAttacker =\r\n\t// \t\tplayerBoard.length > opponentBoard.length\r\n\t// \t\t\t? 0\r\n\t// \t\t\t: opponentBoard.length > playerBoard.length\r\n\t// \t\t\t? 1\r\n\t// \t\t\t: Math.round(Math.random());\r\n\t// }\r\n\treturn currentAttacker;\r\n};\r\n\r\n// Apparently, the board composition used for start of combat minion effects (like Red Whelp, and I assume it works the\r\n// same way for others like Prized Promo Drake or Mantid Queen) is the one that is used before Illidan's effect is handled.\r\n// Since this also runs before HP start of combat, we probably also use the state as it was before HP were triggered, like\r\n// Tamsin's Phylactery.\r\nexport const performStartOfCombatMinionsForPlayer = (minion: BoardEntity, input: SoCInput): boolean => {\r\n\tconst loops = input.playerEntity.trinkets?.some((t) => t.cardId === CardIds.ValdrakkenWindChimes_BG32_MagicItem_365)\r\n\t\t? 2\r\n\t\t: 1;\r\n\tlet hasProcessed = false;\r\n\tfor (let i = 0; i < loops; i++) {\r\n\t\tif (input.playerEntity.startOfCombatDone) {\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\thasProcessed = performStartOfCombatAction(minion.cardId, minion, input, true, undefined, false) || hasProcessed;\r\n\t\t// Dark Gifts are enchantments on the minion, and the ones with a custom effect implement it via the\r\n\t\t// enchantment card id, with the host minion as the acting entity\r\n\t\tfor (const enchantment of minion.enchantments ?? []) {\r\n\t\t\thasProcessed =\r\n\t\t\t\tperformStartOfCombatAction(enchantment.cardId, minion, input, true, undefined, false) || hasProcessed;\r\n\t\t}\r\n\t}\r\n\treturn hasProcessed;\r\n};\r\n"]}
|
|
@@ -163,6 +163,7 @@ const handleFrostwolfFervorForPlayer = (heroPower, playerBoard, playerEntity, op
|
|
|
163
163
|
const indexFromRight = playerBoard.length - (playerBoard.indexOf(target) + 1);
|
|
164
164
|
const newMinions = (0, deathrattle_spawns_1.spawnEntities)(copy.cardId, 1, playerBoard, playerEntity, opponentBoard, opponentEntity, gameState, playerEntity.friendly, false, false, false, copy);
|
|
165
165
|
const spawns = (0, spawns_1.performEntitySpawns)(newMinions, playerBoard, playerEntity, playerEntity, indexFromRight, opponentBoard, opponentEntity, gameState);
|
|
166
|
+
spawns.forEach((e) => (e.hasAttacked = target.hasAttacked));
|
|
166
167
|
gameState.spectator.registerPowerTarget(playerEntity, spawns[0], playerBoard, playerEntity, opponentEntity);
|
|
167
168
|
return true;
|
|
168
169
|
}
|