@firestone-hs/simulate-bgs-battle 1.1.722 → 1.1.724

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.
Files changed (58) hide show
  1. package/dist/cards/card.interface.d.ts +4 -0
  2. package/dist/cards/card.interface.js +4 -2
  3. package/dist/cards/card.interface.js.map +1 -1
  4. package/dist/cards/impl/_card-mappings.js +2 -0
  5. package/dist/cards/impl/_card-mappings.js.map +1 -1
  6. package/dist/cards/impl/hero-power/lightning-invocation.js +4 -1
  7. package/dist/cards/impl/hero-power/lightning-invocation.js.map +1 -1
  8. package/dist/cards/impl/minion/sneeds-new-shredder.js +1 -1
  9. package/dist/cards/impl/minion/sneeds-new-shredder.js.map +1 -1
  10. package/dist/cards/impl/minion/timewarped-embalmer.d.ts +2 -2
  11. package/dist/cards/impl/minion/timewarped-embalmer.js +5 -1
  12. package/dist/cards/impl/minion/timewarped-embalmer.js.map +1 -1
  13. package/dist/cards/impl/minion/timewarped-pagle.d.ts +2 -0
  14. package/dist/cards/impl/minion/timewarped-pagle.js +15 -0
  15. package/dist/cards/impl/minion/timewarped-pagle.js.map +1 -0
  16. package/dist/input-clone.d.ts +1 -0
  17. package/dist/input-clone.js +13 -1
  18. package/dist/input-clone.js.map +1 -1
  19. package/dist/mechanics/rally.js +14 -12
  20. package/dist/mechanics/rally.js.map +1 -1
  21. package/dist/mechanics/tavern-spell-repeat.js +3 -4
  22. package/dist/mechanics/tavern-spell-repeat.js.map +1 -1
  23. package/dist/simulate-bgs-battle.d.ts +1 -1
  24. package/dist/simulate-bgs-battle.js +80 -77
  25. package/dist/simulate-bgs-battle.js.map +1 -1
  26. package/dist/simulation/add-minion-to-board.d.ts +1 -1
  27. package/dist/simulation/add-minion-to-board.js +5 -2
  28. package/dist/simulation/add-minion-to-board.js.map +1 -1
  29. package/dist/simulation/attack.js +3 -3
  30. package/dist/simulation/attack.js.map +1 -1
  31. package/dist/simulation/avenge.js +2 -2
  32. package/dist/simulation/avenge.js.map +1 -1
  33. package/dist/simulation/deathrattle-effects.js +48 -14
  34. package/dist/simulation/deathrattle-effects.js.map +1 -1
  35. package/dist/simulation/deathrattle-spawns.js +19 -12
  36. package/dist/simulation/deathrattle-spawns.js.map +1 -1
  37. package/dist/simulation/deathrattle-utils.d.ts +1 -0
  38. package/dist/simulation/deathrattle-utils.js +45 -5
  39. package/dist/simulation/deathrattle-utils.js.map +1 -1
  40. package/dist/simulation/on-attack.js +13 -14
  41. package/dist/simulation/on-attack.js.map +1 -1
  42. package/dist/simulation/reborn.js +11 -0
  43. package/dist/simulation/reborn.js.map +1 -1
  44. package/dist/simulation/simulator.d.ts +1 -0
  45. package/dist/simulation/simulator.js +39 -74
  46. package/dist/simulation/simulator.js.map +1 -1
  47. package/dist/simulation/spectator/resolve-trinket-hero-power.js +11 -2
  48. package/dist/simulation/spectator/resolve-trinket-hero-power.js.map +1 -1
  49. package/dist/simulation/spectator/spectator.d.ts +4 -0
  50. package/dist/simulation/spectator/spectator.js +39 -19
  51. package/dist/simulation/spectator/spectator.js.map +1 -1
  52. package/dist/simulation/stats.js +95 -66
  53. package/dist/simulation/stats.js.map +1 -1
  54. package/dist/simulation/summon-when-space.js +24 -21
  55. package/dist/simulation/summon-when-space.js.map +1 -1
  56. package/dist/utils.js +89 -48
  57. package/dist/utils.js.map +1 -1
  58. package/package.json +3 -2
@@ -9,7 +9,10 @@ exports.LightningInvocation = {
9
9
  for (const heroPower of input.playerEntity.heroPowers) {
10
10
  if (exports.LightningInvocation.cardIds.includes(heroPower.cardId) && heroPower.used) {
11
11
  (0, deathrattle_effects_1.applyLightningInvocationEnchantment)(input.playerBoard, input.playerEntity, input.playerEntity, input.opponentBoard, input.opponentEntity, input.gameState);
12
- return true;
12
+ return {
13
+ hasTriggered: true,
14
+ shouldRecomputeCurrentAttacker: true,
15
+ };
13
16
  }
14
17
  }
15
18
  },
@@ -1 +1 @@
1
- {"version":3,"file":"lightning-invocation.js","sourceRoot":"","sources":["../../../../src/cards/impl/hero-power/lightning-invocation.ts"],"names":[],"mappings":";;;AAEA,iFAA8F;AAIjF,QAAA,mBAAmB,GAAsB;IACrD,mBAAmB,EAAE,iBAAiB;IACtC,OAAO,EAAE,4CAA+D;IACxE,aAAa,EAAE,CAAC,OAAqB,EAAE,KAAe,EAAE,EAAE;QACzD,KAAK,MAAM,SAAS,IAAI,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE;YACtD,IAAI,2BAAmB,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,SAAS,CAAC,IAAI,EAAE;gBAC7E,IAAA,yDAAmC,EAClC,KAAK,CAAC,WAAW,EACjB,KAAK,CAAC,YAAY,EAClB,KAAK,CAAC,YAAY,EAClB,KAAK,CAAC,aAAa,EACnB,KAAK,CAAC,cAAc,EACpB,KAAK,CAAC,SAAS,CACf,CAAC;gBACF,OAAO,IAAI,CAAC;aACZ;SACD;IACF,CAAC;CACD,CAAC","sourcesContent":["import { CardIds } from '../../../services/card-ids';\nimport { BoardTrinket } from '../../../bgs-player-entity';\r\nimport { applyLightningInvocationEnchantment } from '../../../simulation/deathrattle-effects';\r\nimport { SoCInput } from '../../../simulation/start-of-combat/start-of-combat-input';\r\nimport { StartOfCombatCard } from '../../card.interface';\r\n\r\nexport const LightningInvocation: StartOfCombatCard = {\r\n\tstartOfCombatTiming: 'start-of-combat',\r\n\tcardIds: [CardIds.LightningInvocationToken, CardIds.LightningInvocation],\r\n\tstartOfCombat: (trinket: BoardTrinket, input: SoCInput) => {\r\n\t\tfor (const heroPower of input.playerEntity.heroPowers) {\r\n\t\t\tif (LightningInvocation.cardIds.includes(heroPower.cardId) && heroPower.used) {\r\n\t\t\t\tapplyLightningInvocationEnchantment(\r\n\t\t\t\t\tinput.playerBoard,\r\n\t\t\t\t\tinput.playerEntity,\r\n\t\t\t\t\tinput.playerEntity,\r\n\t\t\t\t\tinput.opponentBoard,\r\n\t\t\t\t\tinput.opponentEntity,\r\n\t\t\t\t\tinput.gameState,\r\n\t\t\t\t);\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\t\t}\r\n\t},\r\n};\r\n"]}
1
+ {"version":3,"file":"lightning-invocation.js","sourceRoot":"","sources":["../../../../src/cards/impl/hero-power/lightning-invocation.ts"],"names":[],"mappings":";;;AAIA,iFAA8F;AAIjF,QAAA,mBAAmB,GAAsB;IACrD,mBAAmB,EAAE,iBAAiB;IACtC,OAAO,EAAE,4CAA+D;IACxE,aAAa,EAAE,CAAC,OAAmE,EAAE,KAAe,EAAE,EAAE;QACvG,KAAK,MAAM,SAAS,IAAI,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE;YACtD,IAAI,2BAAmB,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,SAAS,CAAC,IAAI,EAAE;gBAC7E,IAAA,yDAAmC,EAClC,KAAK,CAAC,WAAW,EACjB,KAAK,CAAC,YAAY,EAClB,KAAK,CAAC,YAAY,EAClB,KAAK,CAAC,aAAa,EACnB,KAAK,CAAC,cAAc,EACpB,KAAK,CAAC,SAAS,CACf,CAAC;gBAGF,OAAO;oBACN,YAAY,EAAE,IAAI;oBAClB,8BAA8B,EAAE,IAAI;iBACpC,CAAC;aACF;SACD;IACF,CAAC;CACD,CAAC","sourcesContent":["import { BgsPlayerEntity, BoardTrinket } from '../../../bgs-player-entity';\r\nimport { BoardEntity } from '../../../board-entity';\r\nimport { BoardSecret } from '../../../board-secret';\r\nimport { CardIds } from '../../../services/card-ids';\r\nimport { applyLightningInvocationEnchantment } from '../../../simulation/deathrattle-effects';\r\nimport { SoCInput } from '../../../simulation/start-of-combat/start-of-combat-input';\r\nimport { StartOfCombatCard } from '../../card.interface';\r\n\r\nexport const LightningInvocation: StartOfCombatCard = {\r\n\tstartOfCombatTiming: 'start-of-combat',\r\n\tcardIds: [CardIds.LightningInvocationToken, CardIds.LightningInvocation],\r\n\tstartOfCombat: (trinket: BoardTrinket | BoardEntity | BgsPlayerEntity | BoardSecret, input: SoCInput) => {\r\n\t\tfor (const heroPower of input.playerEntity.heroPowers) {\r\n\t\t\tif (LightningInvocation.cardIds.includes(heroPower.cardId) && heroPower.used) {\r\n\t\t\t\tapplyLightningInvocationEnchantment(\r\n\t\t\t\t\tinput.playerBoard,\r\n\t\t\t\t\tinput.playerEntity,\r\n\t\t\t\t\tinput.playerEntity,\r\n\t\t\t\t\tinput.opponentBoard,\r\n\t\t\t\t\tinput.opponentEntity,\r\n\t\t\t\t\tinput.gameState,\r\n\t\t\t\t);\r\n\t\t\t\t// See https://replays.firestoneapp.com/?reviewId=09b7ace7-cbe1-4533-bf58-c26d04fa15e9&turn=11&action=8\r\n\t\t\t\t// 2 vs 4 minions, and the 2 side attacks after it becoems 2 vs 2\r\n\t\t\t\treturn {\r\n\t\t\t\t\thasTriggered: true,\r\n\t\t\t\t\tshouldRecomputeCurrentAttacker: true,\r\n\t\t\t\t};\r\n\t\t\t}\r\n\t\t}\r\n\t},\r\n};\r\n"]}
@@ -25,7 +25,7 @@ exports.SneedsNewShredder = {
25
25
  if (spawn) {
26
26
  spawn.locked = true;
27
27
  const copy = { ...spawn };
28
- (0, add_minion_to_board_1.removeAurasFromSelf)(copy, input.boardWithDeadEntity, input.boardWithDeadEntityHero, input.gameState);
28
+ (0, add_minion_to_board_1.removeAurasFromSelf)(copy, input.boardWithDeadEntity, input.boardWithDeadEntityHero, input.gameState, true, true, false);
29
29
  const spawns = (0, deathrattle_spawns_1.spawnEntities)(spawn.cardId, 1, input.boardWithDeadEntity, input.boardWithDeadEntityHero, input.otherBoard, input.otherBoardHero, input.gameState, minion.friendly, false, false, true, copy);
30
30
  for (const s of spawns) {
31
31
  (0, divine_shield_1.updateDivineShield)(s, input.boardWithDeadEntity, input.boardWithDeadEntityHero, input.otherBoardHero, true, input.gameState);
@@ -1 +1 @@
1
- {"version":3,"file":"sneeds-new-shredder.js","sourceRoot":"","sources":["../../../../src/cards/impl/minion/sneeds-new-shredder.ts"],"names":[],"mappings":"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","sourcesContent":["import { BoardEntity } from '../../../board-entity';\r\nimport { updateDivineShield } from '../../../keywords/divine-shield';\r\nimport { CardIds } from '../../../services/card-ids';\r\nimport { pickRandom } from '../../../services/utils';\r\nimport { removeAurasFromSelf } from '../../../simulation/add-minion-to-board';\r\nimport { DeathrattleTriggeredInput } from '../../../simulation/deathrattle-on-trigger';\r\nimport { spawnEntities } from '../../../simulation/deathrattle-spawns';\r\nimport { DeathrattleSpawnCard } from '../../card.interface';\r\n\r\nexport const SneedsNewShredder: DeathrattleSpawnCard = {\r\n\tcardIds: [CardIds.Sneed_SneedsNewShredderToken_BG21_HERO_030t, CardIds.SneedsNewShredder_BG21_HERO_030t_G],\r\n\tdeathrattleSpawn: (minion: BoardEntity, input: DeathrattleTriggeredInput) => {\r\n\t\tconst mult = minion.cardId === CardIds.SneedsNewShredder_BG21_HERO_030t_G ? 2 : 1;\r\n\t\tconst allSpawns = [];\r\n\t\tfor (let i = 0; i < mult; i++) {\r\n\t\t\tconst hand =\r\n\t\t\t\tinput.boardWithDeadEntityHero.hand\r\n\t\t\t\t\t.filter((e) => !!e?.cardId)\r\n\t\t\t\t\t// 2025-12-04: it can summon a minion that is locked by another way, like Flighty scout\r\n\t\t\t\t\t// BUT: if it was summoned by Bassgill, then it won't work\r\n\t\t\t\t\t.filter((e) => !e.locked) ?? [];\r\n\t\t\tconst highestHealth = Math.max(...hand.filter((c) => c.health).map((c) => c.health));\r\n\t\t\tconst highestHealthMinions = highestHealth ? hand.filter((c) => c.health === highestHealth) : null;\r\n\t\t\tconst spawn = !!highestHealthMinions?.length\r\n\t\t\t\t? pickRandom(highestHealthMinions)\r\n\t\t\t\t: hand.filter((c) => c.cardId).length\r\n\t\t\t\t? pickRandom(hand.filter((c) => c.cardId))\r\n\t\t\t\t: null;\r\n\t\t\tif (spawn) {\r\n\t\t\t\t// 2025-12-04: actually doesn't look like it gets locked\r\n\t\t\t\tspawn.locked = true;\r\n\t\t\t\tconst copy = { ...spawn } as BoardEntity;\r\n\t\t\t\tremoveAurasFromSelf(copy, input.boardWithDeadEntity, input.boardWithDeadEntityHero, input.gameState);\r\n\t\t\t\tconst spawns = spawnEntities(\r\n\t\t\t\t\tspawn.cardId,\r\n\t\t\t\t\t1,\r\n\t\t\t\t\tinput.boardWithDeadEntity,\r\n\t\t\t\t\tinput.boardWithDeadEntityHero,\r\n\t\t\t\t\tinput.otherBoard,\r\n\t\t\t\t\tinput.otherBoardHero,\r\n\t\t\t\t\tinput.gameState,\r\n\t\t\t\t\tminion.friendly,\r\n\t\t\t\t\tfalse,\r\n\t\t\t\t\tfalse,\r\n\t\t\t\t\ttrue,\r\n\t\t\t\t\tcopy,\r\n\t\t\t\t);\r\n\t\t\t\tfor (const s of spawns) {\r\n\t\t\t\t\tupdateDivineShield(\r\n\t\t\t\t\t\ts,\r\n\t\t\t\t\t\tinput.boardWithDeadEntity,\r\n\t\t\t\t\t\tinput.boardWithDeadEntityHero,\r\n\t\t\t\t\t\tinput.otherBoardHero,\r\n\t\t\t\t\t\ttrue,\r\n\t\t\t\t\t\tinput.gameState,\r\n\t\t\t\t\t);\r\n\t\t\t\t\ts.onCanceledSummon = () => (spawn.locked = false);\r\n\t\t\t\t\t// s.backRef = spawn;\r\n\t\t\t\t}\r\n\t\t\t\tallSpawns.push(...spawns);\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn allSpawns;\r\n\t},\r\n};\r\n"]}
1
+ {"version":3,"file":"sneeds-new-shredder.js","sourceRoot":"","sources":["../../../../src/cards/impl/minion/sneeds-new-shredder.ts"],"names":[],"mappings":";;;AACA,mEAAqE;AAErE,mDAAqD;AACrD,iFAA8E;AAE9E,+EAAuE;AAG1D,QAAA,iBAAiB,GAAyB;IACtD,OAAO,EAAE,sCAAiG;IAC1G,gBAAgB,EAAE,CAAC,MAAmB,EAAE,KAAgC,EAAE,EAAE;;QAC3E,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,uBAA+C,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAClF,MAAM,SAAS,GAAG,EAAE,CAAC;QACrB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,EAAE;YAC9B,MAAM,IAAI,GACT,MAAA,KAAK,CAAC,uBAAuB,CAAC,IAAI;iBAChC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAA,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,MAAM,CAAA,CAAC;iBAG1B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,mCAAI,EAAE,CAAC;YAClC,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;YACrF,MAAM,oBAAoB,GAAG,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YACnG,MAAM,KAAK,GAAG,CAAC,CAAC,CAAA,oBAAoB,aAApB,oBAAoB,uBAApB,oBAAoB,CAAE,MAAM,CAAA;gBAC3C,CAAC,CAAC,IAAA,kBAAU,EAAC,oBAAoB,CAAC;gBAClC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM;oBACrC,CAAC,CAAC,IAAA,kBAAU,EAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;oBAC1C,CAAC,CAAC,IAAI,CAAC;YACR,IAAI,KAAK,EAAE;gBAEV,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;gBACpB,MAAM,IAAI,GAAG,EAAE,GAAG,KAAK,EAAiB,CAAC;gBACzC,IAAA,yCAAmB,EAClB,IAAI,EACJ,KAAK,CAAC,mBAAmB,EACzB,KAAK,CAAC,uBAAuB,EAC7B,KAAK,CAAC,SAAS,EACf,IAAI,EACJ,IAAI,EACJ,KAAK,CACL,CAAC;gBACF,MAAM,MAAM,GAAG,IAAA,kCAAa,EAC3B,KAAK,CAAC,MAAM,EACZ,CAAC,EACD,KAAK,CAAC,mBAAmB,EACzB,KAAK,CAAC,uBAAuB,EAC7B,KAAK,CAAC,UAAU,EAChB,KAAK,CAAC,cAAc,EACpB,KAAK,CAAC,SAAS,EACf,MAAM,CAAC,QAAQ,EACf,KAAK,EACL,KAAK,EACL,IAAI,EACJ,IAAI,CACJ,CAAC;gBACF,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE;oBACvB,IAAA,kCAAkB,EACjB,CAAC,EACD,KAAK,CAAC,mBAAmB,EACzB,KAAK,CAAC,uBAAuB,EAC7B,KAAK,CAAC,cAAc,EACpB,IAAI,EACJ,KAAK,CAAC,SAAS,CACf,CAAC;oBACF,CAAC,CAAC,gBAAgB,GAAG,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC;iBAElD;gBACD,SAAS,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC;aAC1B;SACD;QACD,OAAO,SAAS,CAAC;IAClB,CAAC;CACD,CAAC","sourcesContent":["import { BoardEntity } from '../../../board-entity';\r\nimport { updateDivineShield } from '../../../keywords/divine-shield';\r\nimport { CardIds } from '../../../services/card-ids';\r\nimport { pickRandom } from '../../../services/utils';\r\nimport { removeAurasFromSelf } from '../../../simulation/add-minion-to-board';\r\nimport { DeathrattleTriggeredInput } from '../../../simulation/deathrattle-on-trigger';\r\nimport { spawnEntities } from '../../../simulation/deathrattle-spawns';\r\nimport { DeathrattleSpawnCard } from '../../card.interface';\r\n\r\nexport const SneedsNewShredder: DeathrattleSpawnCard = {\r\n\tcardIds: [CardIds.Sneed_SneedsNewShredderToken_BG21_HERO_030t, CardIds.SneedsNewShredder_BG21_HERO_030t_G],\r\n\tdeathrattleSpawn: (minion: BoardEntity, input: DeathrattleTriggeredInput) => {\r\n\t\tconst mult = minion.cardId === CardIds.SneedsNewShredder_BG21_HERO_030t_G ? 2 : 1;\r\n\t\tconst allSpawns = [];\r\n\t\tfor (let i = 0; i < mult; i++) {\r\n\t\t\tconst hand =\r\n\t\t\t\tinput.boardWithDeadEntityHero.hand\r\n\t\t\t\t\t.filter((e) => !!e?.cardId)\r\n\t\t\t\t\t// 2025-12-04: it can summon a minion that is locked by another way, like Flighty scout\r\n\t\t\t\t\t// BUT: if it was summoned by Bassgill, then it won't work\r\n\t\t\t\t\t.filter((e) => !e.locked) ?? [];\r\n\t\t\tconst highestHealth = Math.max(...hand.filter((c) => c.health).map((c) => c.health));\r\n\t\t\tconst highestHealthMinions = highestHealth ? hand.filter((c) => c.health === highestHealth) : null;\r\n\t\t\tconst spawn = !!highestHealthMinions?.length\r\n\t\t\t\t? pickRandom(highestHealthMinions)\r\n\t\t\t\t: hand.filter((c) => c.cardId).length\r\n\t\t\t\t? pickRandom(hand.filter((c) => c.cardId))\r\n\t\t\t\t: null;\r\n\t\t\tif (spawn) {\r\n\t\t\t\t// 2025-12-04: actually doesn't look like it gets locked\r\n\t\t\t\tspawn.locked = true;\r\n\t\t\t\tconst copy = { ...spawn } as BoardEntity;\r\n\t\t\t\tremoveAurasFromSelf(\r\n\t\t\t\t\tcopy,\r\n\t\t\t\t\tinput.boardWithDeadEntity,\r\n\t\t\t\t\tinput.boardWithDeadEntityHero,\r\n\t\t\t\t\tinput.gameState,\r\n\t\t\t\t\ttrue,\r\n\t\t\t\t\ttrue,\r\n\t\t\t\t\tfalse,\r\n\t\t\t\t);\r\n\t\t\t\tconst spawns = spawnEntities(\r\n\t\t\t\t\tspawn.cardId,\r\n\t\t\t\t\t1,\r\n\t\t\t\t\tinput.boardWithDeadEntity,\r\n\t\t\t\t\tinput.boardWithDeadEntityHero,\r\n\t\t\t\t\tinput.otherBoard,\r\n\t\t\t\t\tinput.otherBoardHero,\r\n\t\t\t\t\tinput.gameState,\r\n\t\t\t\t\tminion.friendly,\r\n\t\t\t\t\tfalse,\r\n\t\t\t\t\tfalse,\r\n\t\t\t\t\ttrue,\r\n\t\t\t\t\tcopy,\r\n\t\t\t\t);\r\n\t\t\t\tfor (const s of spawns) {\r\n\t\t\t\t\tupdateDivineShield(\r\n\t\t\t\t\t\ts,\r\n\t\t\t\t\t\tinput.boardWithDeadEntity,\r\n\t\t\t\t\t\tinput.boardWithDeadEntityHero,\r\n\t\t\t\t\t\tinput.otherBoardHero,\r\n\t\t\t\t\t\ttrue,\r\n\t\t\t\t\t\tinput.gameState,\r\n\t\t\t\t\t);\r\n\t\t\t\t\ts.onCanceledSummon = () => (spawn.locked = false);\r\n\t\t\t\t\t// s.backRef = spawn;\r\n\t\t\t\t}\r\n\t\t\t\tallSpawns.push(...spawns);\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn allSpawns;\r\n\t},\r\n};\r\n"]}
@@ -1,2 +1,2 @@
1
- import { DefaultChargesCard, OnOtherSpawnedCard } from '../../card.interface';
2
- export declare const TimewarpedEmbalmer: OnOtherSpawnedCard & DefaultChargesCard;
1
+ import { BeforeRebornSelfEffectCard, DefaultChargesCard, OnOtherSpawnedCard } from '../../card.interface';
2
+ export declare const TimewarpedEmbalmer: OnOtherSpawnedCard & DefaultChargesCard & BeforeRebornSelfEffectCard;
@@ -6,11 +6,15 @@ exports.TimewarpedEmbalmer = {
6
6
  cardIds: ["BG34_Giant_332", "BG34_Giant_332_G"],
7
7
  defaultCharges: (entity) => entity.scriptDataNum1,
8
8
  onOtherSpawned: (minion, input) => {
9
- if (minion.abiityChargesLeft > 0) {
9
+ if (minion.abiityChargesLeft > 0 && !input.spawned.reborn) {
10
10
  minion.abiityChargesLeft--;
11
11
  const target = input.spawned;
12
12
  (0, reborn_1.updateReborn)(target, true, input.board, input.hero, input.otherHero, input.gameState);
13
13
  }
14
14
  },
15
+ beforeRebornSelfEffect: (minion, input) => {
16
+ minion.scriptDataNum1 = 2;
17
+ minion.abiityChargesLeft = 2;
18
+ },
15
19
  };
16
20
  //# sourceMappingURL=timewarped-embalmer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"timewarped-embalmer.js","sourceRoot":"","sources":["../../../../src/cards/impl/minion/timewarped-embalmer.ts"],"names":[],"mappings":";;;AACA,qDAAwD;AAK3C,QAAA,kBAAkB,GAA4C;IAC1E,OAAO,EAAE,sCAAwF;IAEjG,cAAc,EAAE,CAAC,MAAmB,EAAE,EAAE,CAAC,MAAM,CAAC,cAAc;IAC9D,cAAc,EAAE,CAAC,MAAmB,EAAE,KAAwB,EAAE,EAAE;QACjE,IAAI,MAAM,CAAC,iBAAiB,GAAG,CAAC,EAAE;YACjC,MAAM,CAAC,iBAAiB,EAAE,CAAC;YAC3B,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC;YAC7B,IAAA,qBAAY,EAAC,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;SACtF;IACF,CAAC;CACD,CAAC","sourcesContent":["import { BoardEntity } from '../../../board-entity';\r\nimport { updateReborn } from '../../../keywords/reborn';\r\nimport { CardIds } from '../../../services/card-ids';\r\nimport { OnOtherSpawnInput } from '../../../simulation/add-minion-to-board';\r\nimport { DefaultChargesCard, OnOtherSpawnedCard } from '../../card.interface';\r\n\r\nexport const TimewarpedEmbalmer: OnOtherSpawnedCard & DefaultChargesCard = {\r\n\tcardIds: [CardIds.TimewarpedEmbalmer_BG34_Giant_332, CardIds.TimewarpedEmbalmer_BG34_Giant_332_G],\r\n\t// It only resets at the start of the shop phase, so we need to rely on the value from the board state\r\n\tdefaultCharges: (entity: BoardEntity) => entity.scriptDataNum1,\r\n\tonOtherSpawned: (minion: BoardEntity, input: OnOtherSpawnInput) => {\r\n\t\tif (minion.abiityChargesLeft > 0) {\r\n\t\t\tminion.abiityChargesLeft--;\r\n\t\t\tconst target = input.spawned;\r\n\t\t\tupdateReborn(target, true, input.board, input.hero, input.otherHero, input.gameState);\r\n\t\t}\r\n\t},\r\n};\r\n"]}
1
+ {"version":3,"file":"timewarped-embalmer.js","sourceRoot":"","sources":["../../../../src/cards/impl/minion/timewarped-embalmer.ts"],"names":[],"mappings":";;;AAEA,qDAAwD;AAM3C,QAAA,kBAAkB,GAAyE;IACvG,OAAO,EAAE,sCAAwF;IAEjG,cAAc,EAAE,CAAC,MAAmB,EAAE,EAAE,CAEvC,MAAM,CAAC,cAAc;IACtB,cAAc,EAAE,CAAC,MAAkC,EAAE,KAAwB,EAAE,EAAE;QAChF,IAAK,MAAsB,CAAC,iBAAiB,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE;YAC1E,MAAsB,CAAC,iBAAiB,EAAE,CAAC;YAC5C,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC;YAC7B,IAAA,qBAAY,EAAC,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;SACtF;IACF,CAAC;IACD,sBAAsB,EAAE,CAAC,MAAkC,EAAE,KAAwB,EAAE,EAAE;QACvF,MAAsB,CAAC,cAAc,GAAG,CAAC,CAAC;QAC1C,MAAsB,CAAC,iBAAiB,GAAG,CAAC,CAAC;IAC/C,CAAC;CACD,CAAC","sourcesContent":["import { BoardTrinket } from '../../../bgs-player-entity';\r\nimport { BoardEntity } from '../../../board-entity';\r\nimport { updateReborn } from '../../../keywords/reborn';\r\nimport { CardIds } from '../../../services/card-ids';\r\nimport { OnOtherSpawnInput } from '../../../simulation/add-minion-to-board';\r\nimport { RebornEffectInput } from '../../../simulation/reborn';\r\nimport { BeforeRebornSelfEffectCard, DefaultChargesCard, OnOtherSpawnedCard } from '../../card.interface';\r\n\r\nexport const TimewarpedEmbalmer: OnOtherSpawnedCard & DefaultChargesCard & BeforeRebornSelfEffectCard = {\r\n\tcardIds: [CardIds.TimewarpedEmbalmer_BG34_Giant_332, CardIds.TimewarpedEmbalmer_BG34_Giant_332_G],\r\n\t// It only resets at the start of the shop phase, so we need to rely on the value from the board state\r\n\tdefaultCharges: (entity: BoardEntity) =>\r\n\t\t// Some charges can be used during the shop phase\r\n\t\tentity.scriptDataNum1,\r\n\tonOtherSpawned: (minion: BoardEntity | BoardTrinket, input: OnOtherSpawnInput) => {\r\n\t\tif ((minion as BoardEntity).abiityChargesLeft > 0 && !input.spawned.reborn) {\r\n\t\t\t(minion as BoardEntity).abiityChargesLeft--;\r\n\t\t\tconst target = input.spawned;\r\n\t\t\tupdateReborn(target, true, input.board, input.hero, input.otherHero, input.gameState);\r\n\t\t}\r\n\t},\r\n\tbeforeRebornSelfEffect: (minion: BoardEntity | BoardTrinket, input: RebornEffectInput) => {\r\n\t\t(minion as BoardEntity).scriptDataNum1 = 2;\r\n\t\t(minion as BoardEntity).abiityChargesLeft = 2;\r\n\t},\r\n};\r\n"]}
@@ -0,0 +1,2 @@
1
+ import { OnMinionKilledCard } from '../../card.interface';
2
+ export declare const TimewarpedPagle: OnMinionKilledCard;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TimewarpedPagle = void 0;
4
+ const cards_in_hand_1 = require("../../../simulation/cards-in-hand");
5
+ exports.TimewarpedPagle = {
6
+ cardIds: ["BG34_Giant_208", "BG34_Giant_208_G"],
7
+ onMinionKilled: (_entity, input) => {
8
+ if (input.killer !== _entity || !input.killerIsAttacking) {
9
+ return { dmgDoneByAttacker: 0, dmgDoneByDefender: 0 };
10
+ }
11
+ (0, cards_in_hand_1.addCardsInHand)(input.attackingHero, input.attackingBoard, ["TB_BaconShop_Triples_01"], input.gameState);
12
+ return { dmgDoneByAttacker: 0, dmgDoneByDefender: 0 };
13
+ },
14
+ };
15
+ //# sourceMappingURL=timewarped-pagle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timewarped-pagle.js","sourceRoot":"","sources":["../../../../src/cards/impl/minion/timewarped-pagle.ts"],"names":[],"mappings":";;;AAGA,qEAAmE;AAGtD,QAAA,eAAe,GAAuB;IAClD,OAAO,EAAE,sCAAkF;IAC3F,cAAc,EAAE,CAAC,OAAoB,EAAE,KAA0B,EAAE,EAAE;QACpE,IAAI,KAAK,CAAC,MAAM,KAAK,OAAO,IAAI,CAAC,KAAK,CAAC,iBAAiB,EAAE;YACzD,OAAO,EAAE,iBAAiB,EAAE,CAAC,EAAE,iBAAiB,EAAE,CAAC,EAAE,CAAC;SACtD;QACD,IAAA,8BAAc,EAAC,KAAK,CAAC,aAAa,EAAE,KAAK,CAAC,cAAc,EAAE,2BAAsB,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;QACnG,OAAO,EAAE,iBAAiB,EAAE,CAAC,EAAE,iBAAiB,EAAE,CAAC,EAAE,CAAC;IACvD,CAAC;CACD,CAAC","sourcesContent":["import { BoardEntity } from '../../../board-entity';\r\nimport { CardIds } from '../../../services/card-ids';\r\nimport { OnMinionKilledInput } from '../../../simulation/attack';\r\nimport { addCardsInHand } from '../../../simulation/cards-in-hand';\r\nimport { OnMinionKilledCard } from '../../card.interface';\r\n\r\nexport const TimewarpedPagle: OnMinionKilledCard = {\r\n\tcardIds: [CardIds.TimewarpedPagle_BG34_Giant_208, CardIds.TimewarpedPagle_BG34_Giant_208_G],\r\n\tonMinionKilled: (_entity: BoardEntity, input: OnMinionKilledInput) => {\r\n\t\tif (input.killer !== _entity || !input.killerIsAttacking) {\r\n\t\t\treturn { dmgDoneByAttacker: 0, dmgDoneByDefender: 0 };\r\n\t\t}\r\n\t\taddCardsInHand(input.attackingHero, input.attackingBoard, [CardIds.TripleReward], input.gameState);\r\n\t\treturn { dmgDoneByAttacker: 0, dmgDoneByDefender: 0 };\r\n\t},\r\n};\r\n"]}
@@ -1,4 +1,5 @@
1
1
  import { BgsBattleInfo } from './bgs-battle-info';
2
2
  import { BgsBoardInfo } from './bgs-board-info';
3
+ export declare const buildTemplateCloner: (template: BgsBattleInfo) => (() => BgsBattleInfo);
3
4
  export declare const cloneInput3: (input: BgsBattleInfo) => BgsBattleInfo;
4
5
  export declare const cloneBoard: (board: BgsBoardInfo) => BgsBoardInfo;
@@ -1,6 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.cloneBoard = exports.cloneInput3 = void 0;
3
+ exports.cloneBoard = exports.cloneInput3 = exports.buildTemplateCloner = void 0;
4
+ const buildTemplateCloner = (template) => {
5
+ const str = JSON.stringify(template);
6
+ if (!str.includes('"__proto__"')) {
7
+ try {
8
+ return new Function(`return ${str};`);
9
+ }
10
+ catch (e) {
11
+ }
12
+ }
13
+ return () => JSON.parse(str);
14
+ };
15
+ exports.buildTemplateCloner = buildTemplateCloner;
4
16
  const cloneInput3 = (input) => {
5
17
  const result = {
6
18
  gameState: {
@@ -1 +1 @@
1
- {"version":3,"file":"input-clone.js","sourceRoot":"","sources":["../src/input-clone.ts"],"names":[],"mappings":"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","sourcesContent":["import { BgsBattleInfo } from './bgs-battle-info';\r\nimport { BgsBoardInfo } from './bgs-board-info';\r\nimport { BoardEntity } from './board-entity';\r\n\r\n// const cloneInput = (input: BgsBattleInfo): BgsBattleInfo => {\r\n// \treturn structuredClone(input);\r\n// };\r\n// const cloneInput2 = (input: string): BgsBattleInfo => {\r\n// \treturn JSON.parse(input);\r\n// };\r\n\r\nexport const cloneInput3 = (input: BgsBattleInfo): BgsBattleInfo => {\r\n\tconst result: BgsBattleInfo = {\r\n\t\tgameState: {\r\n\t\t\tcurrentTurn: input.gameState.currentTurn,\r\n\t\t\tanomalies: input.gameState.anomalies,\r\n\t\t\tvalidTribes: input.gameState.validTribes,\r\n\t\t},\r\n\t\theroHasDied: input.heroHasDied,\r\n\t\tplayerBoard: cloneBoard(input.playerBoard),\r\n\t\topponentBoard: cloneBoard(input.opponentBoard),\r\n\t\tplayerTeammateBoard: cloneBoard(input.playerTeammateBoard),\r\n\t\topponentTeammateBoard: cloneBoard(input.opponentTeammateBoard),\r\n\t\toptions: null,\r\n\t};\r\n\treturn result;\r\n};\r\n\r\nexport const cloneBoard = (board: BgsBoardInfo): BgsBoardInfo => {\r\n\tif (!board) {\r\n\t\treturn null;\r\n\t}\r\n\tconst result: BgsBoardInfo = {\r\n\t\tplayer: {\r\n\t\t\t...board.player,\r\n\t\t\tquestEntities: board.player.questEntities?.map((quest) => ({ ...quest })),\r\n\t\t\tquestRewardEntities: board.player.questRewardEntities?.map((reward) => ({ ...reward })),\r\n\t\t\tquestRewards: board.player.questRewards?.map((reward) => reward),\r\n\t\t\thand: board.player.hand?.map((entity) => cloneEntity(entity)),\r\n\t\t\tsecrets: board.player.secrets?.map((secret) => ({ ...secret })).sort((a, b) => a.entityId - b.entityId),\r\n\t\t\ttrinkets: board.player.trinkets?.map((trinket) => ({ ...trinket, tags: { ...trinket.tags } })),\r\n\t\t\tglobalInfo: { ...board.player.globalInfo },\r\n\t\t\theroPowers: board.player.heroPowers?.map((heroPower) => ({ ...heroPower })),\r\n\t\t},\r\n\t\tboard: board.board.map((entity) => cloneEntity(entity)),\r\n\t};\r\n\treturn result;\r\n};\r\n\r\nconst cloneEntity = (entity: BoardEntity): BoardEntity => {\r\n\tconst result: BoardEntity = {\r\n\t\t...entity,\r\n\t\tenchantments: entity.enchantments.map((enchant) => ({ ...enchant })),\r\n\t\tpendingAttackBuffs: [],\r\n\t\tadditionalCards: entity.additionalCards?.map((card) => card),\r\n\t\trememberedDeathrattles: [],\r\n\t};\r\n\treturn result;\r\n};\r\n"]}
1
+ {"version":3,"file":"input-clone.js","sourceRoot":"","sources":["../src/input-clone.ts"],"names":[],"mappings":"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","sourcesContent":["import { BgsBattleInfo } from './bgs-battle-info';\r\nimport { BgsBoardInfo } from './bgs-board-info';\r\nimport { BoardEntity } from './board-entity';\r\n\r\n// const cloneInput = (input: BgsBattleInfo): BgsBattleInfo => {\r\n// \treturn structuredClone(input);\r\n// };\r\n// const cloneInput2 = (input: string): BgsBattleInfo => {\r\n// \treturn JSON.parse(input);\r\n// };\r\n\r\n/**\r\n * Builds a function that returns a fresh deep copy of the template on every call.\r\n *\r\n * The template is serialized once, and compiled into a function body that rebuilds it as a\r\n * plain object literal. V8 parses that literal a single time, so each subsequent call only\r\n * allocates the objects, which is much faster than re-parsing JSON on every simulation\r\n * iteration (measured ~0.02ms/clone vs ~0.12ms for JSON.parse on a 7v7 board).\r\n */\r\nexport const buildTemplateCloner = (template: BgsBattleInfo): (() => BgsBattleInfo) => {\r\n\tconst str = JSON.stringify(template);\r\n\t// In an object literal (unlike JSON.parse), a \"__proto__\" key would set the prototype\r\n\t// instead of creating an own property. Our input shape never contains that key, but if\r\n\t// it ever does, fall back to the safe path\r\n\tif (!str.includes('\"__proto__\"')) {\r\n\t\ttry {\r\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-implied-eval\r\n\t\t\treturn new Function(`return ${str};`) as () => BgsBattleInfo;\r\n\t\t} catch (e) {\r\n\t\t\t// Code generation from strings can be disallowed (e.g. by a CSP when this runs\r\n\t\t\t// inside the app); JSON.parse is the fallback\r\n\t\t}\r\n\t}\r\n\treturn () => JSON.parse(str);\r\n};\r\n\r\nexport const cloneInput3 = (input: BgsBattleInfo): BgsBattleInfo => {\r\n\tconst result: BgsBattleInfo = {\r\n\t\tgameState: {\r\n\t\t\tcurrentTurn: input.gameState.currentTurn,\r\n\t\t\tanomalies: input.gameState.anomalies,\r\n\t\t\tvalidTribes: input.gameState.validTribes,\r\n\t\t},\r\n\t\theroHasDied: input.heroHasDied,\r\n\t\tplayerBoard: cloneBoard(input.playerBoard),\r\n\t\topponentBoard: cloneBoard(input.opponentBoard),\r\n\t\tplayerTeammateBoard: cloneBoard(input.playerTeammateBoard),\r\n\t\topponentTeammateBoard: cloneBoard(input.opponentTeammateBoard),\r\n\t\toptions: null,\r\n\t};\r\n\treturn result;\r\n};\r\n\r\nexport const cloneBoard = (board: BgsBoardInfo): BgsBoardInfo => {\r\n\tif (!board) {\r\n\t\treturn null;\r\n\t}\r\n\tconst result: BgsBoardInfo = {\r\n\t\tplayer: {\r\n\t\t\t...board.player,\r\n\t\t\tquestEntities: board.player.questEntities?.map((quest) => ({ ...quest })),\r\n\t\t\tquestRewardEntities: board.player.questRewardEntities?.map((reward) => ({ ...reward })),\r\n\t\t\tquestRewards: board.player.questRewards?.map((reward) => reward),\r\n\t\t\thand: board.player.hand?.map((entity) => cloneEntity(entity)),\r\n\t\t\tsecrets: board.player.secrets?.map((secret) => ({ ...secret })).sort((a, b) => a.entityId - b.entityId),\r\n\t\t\ttrinkets: board.player.trinkets?.map((trinket) => ({ ...trinket, tags: { ...trinket.tags } })),\r\n\t\t\tglobalInfo: { ...board.player.globalInfo },\r\n\t\t\theroPowers: board.player.heroPowers?.map((heroPower) => ({ ...heroPower })),\r\n\t\t},\r\n\t\tboard: board.board.map((entity) => cloneEntity(entity)),\r\n\t};\r\n\treturn result;\r\n};\r\n\r\nconst cloneEntity = (entity: BoardEntity): BoardEntity => {\r\n\tconst result: BoardEntity = {\r\n\t\t...entity,\r\n\t\tenchantments: entity.enchantments.map((enchant) => ({ ...enchant })),\r\n\t\tpendingAttackBuffs: [],\r\n\t\tadditionalCards: entity.additionalCards?.map((card) => card),\r\n\t\trememberedDeathrattles: [],\r\n\t};\r\n\treturn result;\r\n};\r\n"]}
@@ -13,18 +13,20 @@ const triggerRally = (attackingBoard, attackingBoardHero, attacker, defendingBoa
13
13
  let damageDoneByAttacker = 0;
14
14
  let damageDoneByDefender = 0;
15
15
  const isAttackerRallying = (0, utils_1.hasEntityMechanic)(attacker, reference_data_1.GameTag.BACON_RALLY, gameState.allCards);
16
- const cardIds = [
17
- ...attackingBoard.map((e) => e.cardId),
18
- ...((_b = (_a = attackingBoardHero.secrets) === null || _a === void 0 ? void 0 : _a.map((e) => e.cardId)) !== null && _b !== void 0 ? _b : []),
19
- ];
20
- const triggerMult = cardIds.some((cardId) => TRIPLE_RALLY_CARD_IDS.includes(cardId))
21
- ? 3
22
- : cardIds.some((cardId) => DOUBLE_RALLY_CARD_IDS.includes(cardId))
23
- ? 2
24
- : 1;
25
- const numberOfRallyingCries = (_d = (_c = attackingBoardHero.questRewardEntities) === null || _c === void 0 ? void 0 : _c.filter((r) => r.cardId === "BG33_Reward_021").length) !== null && _d !== void 0 ? _d : 0;
26
- const multiplier = triggerMult + numberOfRallyingCries;
27
- const rallyLoops = !isAttackerRallying ? 1 : multiplier;
16
+ let rallyLoops = 1;
17
+ if (isAttackerRallying) {
18
+ const cardIds = [
19
+ ...attackingBoard.map((e) => e.cardId),
20
+ ...((_b = (_a = attackingBoardHero.secrets) === null || _a === void 0 ? void 0 : _a.map((e) => e.cardId)) !== null && _b !== void 0 ? _b : []),
21
+ ];
22
+ const triggerMult = cardIds.some((cardId) => TRIPLE_RALLY_CARD_IDS.includes(cardId))
23
+ ? 3
24
+ : cardIds.some((cardId) => DOUBLE_RALLY_CARD_IDS.includes(cardId))
25
+ ? 2
26
+ : 1;
27
+ const numberOfRallyingCries = (_d = (_c = attackingBoardHero.questRewardEntities) === null || _c === void 0 ? void 0 : _c.filter((r) => r.cardId === "BG33_Reward_021").length) !== null && _d !== void 0 ? _d : 0;
28
+ rallyLoops = triggerMult + numberOfRallyingCries;
29
+ }
28
30
  for (let i = 0; i < rallyLoops; i++) {
29
31
  const onAttackImpl = _card_mappings_1.cardMappings[attacker.cardId];
30
32
  if ((0, card_interface_1.hasRally)(onAttackImpl)) {
@@ -1 +1 @@
1
- {"version":3,"file":"rally.js","sourceRoot":"","sources":["../../src/mechanics/rally.ts"],"names":[],"mappings":"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","sourcesContent":["import { GameTag } from '@firestone-hs/reference-data';\r\nimport { BgsPlayerEntity } from '../bgs-player-entity';\r\nimport { BoardEntity } from '../board-entity';\r\nimport { hasRally } from '../cards/card.interface';\r\nimport { cardMappings } from '../cards/impl/_card-mappings';\r\nimport { CardIds } from '../services/card-ids';\r\nimport { fixEnchantments } from '../simulation/enchantments';\r\nimport { FullGameState } from '../simulation/internal-game-state';\r\nimport { hasEntityMechanic } from '../utils';\r\n\r\nconst DOUBLE_RALLY_CARD_IDS: string[] = [CardIds.TimewarpedDeios_BG34_Giant_376];\r\nconst TRIPLE_RALLY_CARD_IDS: string[] = [CardIds.TimewarpedDeios_BG34_Giant_376_G];\r\n\r\nexport const triggerRally = (\r\n\tattackingBoard: BoardEntity[],\r\n\tattackingBoardHero: BgsPlayerEntity,\r\n\tattacker: BoardEntity,\r\n\tdefendingBoard: BoardEntity[],\r\n\tdefendingBoardHero: BgsPlayerEntity,\r\n\tdefendingEntity: BoardEntity,\r\n\tgameState: FullGameState,\r\n): { dmgDoneByAttacker: number; dmgDoneByDefender: number } => {\r\n\tlet damageDoneByAttacker = 0;\r\n\tlet damageDoneByDefender = 0;\r\n\tconst isAttackerRallying = hasEntityMechanic(attacker, GameTag.BACON_RALLY, gameState.allCards);\r\n\r\n\tconst cardIds = [\r\n\t\t...attackingBoard.map((e) => e.cardId),\r\n\t\t...(attackingBoardHero.secrets?.map((e) => e.cardId) ?? []),\r\n\t];\r\n\tconst triggerMult = cardIds.some((cardId) => TRIPLE_RALLY_CARD_IDS.includes(cardId))\r\n\t\t? 3\r\n\t\t: cardIds.some((cardId) => DOUBLE_RALLY_CARD_IDS.includes(cardId))\r\n\t\t? 2\r\n\t\t: 1;\r\n\tconst numberOfRallyingCries =\r\n\t\tattackingBoardHero.questRewardEntities?.filter((r) => r.cardId === CardIds.RallyingCry_BG33_Reward_021)\r\n\t\t\t.length ?? 0;\r\n\tconst multiplier = triggerMult + numberOfRallyingCries;\r\n\tconst rallyLoops = !isAttackerRallying ? 1 : multiplier;\r\n\r\n\tfor (let i = 0; i < rallyLoops; i++) {\r\n\t\tconst onAttackImpl = cardMappings[attacker.cardId];\r\n\t\tif (hasRally(onAttackImpl)) {\r\n\t\t\tconst { dmgDoneByAttacker, dmgDoneByDefender } = onAttackImpl.rally(attacker, {\r\n\t\t\t\tattacker: attacker,\r\n\t\t\t\tattackingHero: attackingBoardHero,\r\n\t\t\t\tattackingBoard: attackingBoard,\r\n\t\t\t\tdefendingEntity: defendingEntity,\r\n\t\t\t\tdefendingHero: defendingBoardHero,\r\n\t\t\t\tdefendingBoard: defendingBoard,\r\n\t\t\t\tgameState,\r\n\t\t\t\tplayerIsFriendly: attackingBoardHero.friendly,\r\n\t\t\t});\r\n\t\t\tdamageDoneByAttacker += dmgDoneByAttacker;\r\n\t\t\tdamageDoneByDefender += dmgDoneByDefender;\r\n\t\t}\r\n\t\tconst enchantments = attacker.enchantments;\r\n\t\tfor (const enchantment of enchantments) {\r\n\t\t\tconst onAttackImpl = cardMappings[enchantment.cardId];\r\n\t\t\tif (hasRally(onAttackImpl)) {\r\n\t\t\t\tlet enchantmentToMinion: BoardEntity = {\r\n\t\t\t\t\t...enchantment,\r\n\t\t\t\t\tentityId: attacker.entityId,\r\n\t\t\t\t\tattack: attacker.attack,\r\n\t\t\t\t\thealth: attacker.health,\r\n\t\t\t\t\tmaxHealth: attacker.maxHealth,\r\n\t\t\t\t\tmaxAttack: attacker.maxAttack,\r\n\t\t\t\t\tabiityChargesLeft: attacker.abiityChargesLeft,\r\n\t\t\t\t};\r\n\t\t\t\tenchantmentToMinion = fixEnchantments(enchantmentToMinion, gameState.allCards);\r\n\t\t\t\tconst { dmgDoneByAttacker, dmgDoneByDefender } = onAttackImpl.rally(enchantmentToMinion, {\r\n\t\t\t\t\tattacker: attacker,\r\n\t\t\t\t\tattackingHero: attackingBoardHero,\r\n\t\t\t\t\tattackingBoard: attackingBoard,\r\n\t\t\t\t\tdefendingEntity: defendingEntity,\r\n\t\t\t\t\tdefendingHero: defendingBoardHero,\r\n\t\t\t\t\tdefendingBoard: defendingBoard,\r\n\t\t\t\t\tgameState,\r\n\t\t\t\t\tplayerIsFriendly: attackingBoardHero.friendly,\r\n\t\t\t\t});\r\n\t\t\t\tdamageDoneByAttacker += dmgDoneByAttacker;\r\n\t\t\t\tdamageDoneByDefender += dmgDoneByDefender;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\treturn { dmgDoneByAttacker: damageDoneByAttacker, dmgDoneByDefender: damageDoneByDefender };\r\n};\r\n"]}
1
+ {"version":3,"file":"rally.js","sourceRoot":"","sources":["../../src/mechanics/rally.ts"],"names":[],"mappings":"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","sourcesContent":["import { GameTag } from '@firestone-hs/reference-data';\r\nimport { BgsPlayerEntity } from '../bgs-player-entity';\r\nimport { BoardEntity } from '../board-entity';\r\nimport { hasRally } from '../cards/card.interface';\r\nimport { cardMappings } from '../cards/impl/_card-mappings';\r\nimport { CardIds } from '../services/card-ids';\r\nimport { fixEnchantments } from '../simulation/enchantments';\r\nimport { FullGameState } from '../simulation/internal-game-state';\r\nimport { hasEntityMechanic } from '../utils';\r\n\r\nconst DOUBLE_RALLY_CARD_IDS: string[] = [CardIds.TimewarpedDeios_BG34_Giant_376];\r\nconst TRIPLE_RALLY_CARD_IDS: string[] = [CardIds.TimewarpedDeios_BG34_Giant_376_G];\r\n\r\nexport const triggerRally = (\r\n\tattackingBoard: BoardEntity[],\r\n\tattackingBoardHero: BgsPlayerEntity,\r\n\tattacker: BoardEntity,\r\n\tdefendingBoard: BoardEntity[],\r\n\tdefendingBoardHero: BgsPlayerEntity,\r\n\tdefendingEntity: BoardEntity,\r\n\tgameState: FullGameState,\r\n): { dmgDoneByAttacker: number; dmgDoneByDefender: number } => {\r\n\tlet damageDoneByAttacker = 0;\r\n\tlet damageDoneByDefender = 0;\r\n\tconst isAttackerRallying = hasEntityMechanic(attacker, GameTag.BACON_RALLY, gameState.allCards);\r\n\r\n\t// Only compute the multiplier when the attacker actually rallies, as this is a hot path\r\n\tlet rallyLoops = 1;\r\n\tif (isAttackerRallying) {\r\n\t\tconst cardIds = [\r\n\t\t\t...attackingBoard.map((e) => e.cardId),\r\n\t\t\t...(attackingBoardHero.secrets?.map((e) => e.cardId) ?? []),\r\n\t\t];\r\n\t\tconst triggerMult = cardIds.some((cardId) => TRIPLE_RALLY_CARD_IDS.includes(cardId))\r\n\t\t\t? 3\r\n\t\t\t: cardIds.some((cardId) => DOUBLE_RALLY_CARD_IDS.includes(cardId))\r\n\t\t\t? 2\r\n\t\t\t: 1;\r\n\t\tconst numberOfRallyingCries =\r\n\t\t\tattackingBoardHero.questRewardEntities?.filter((r) => r.cardId === CardIds.RallyingCry_BG33_Reward_021)\r\n\t\t\t\t.length ?? 0;\r\n\t\trallyLoops = triggerMult + numberOfRallyingCries;\r\n\t}\r\n\r\n\tfor (let i = 0; i < rallyLoops; i++) {\r\n\t\tconst onAttackImpl = cardMappings[attacker.cardId];\r\n\t\tif (hasRally(onAttackImpl)) {\r\n\t\t\tconst { dmgDoneByAttacker, dmgDoneByDefender } = onAttackImpl.rally(attacker, {\r\n\t\t\t\tattacker: attacker,\r\n\t\t\t\tattackingHero: attackingBoardHero,\r\n\t\t\t\tattackingBoard: attackingBoard,\r\n\t\t\t\tdefendingEntity: defendingEntity,\r\n\t\t\t\tdefendingHero: defendingBoardHero,\r\n\t\t\t\tdefendingBoard: defendingBoard,\r\n\t\t\t\tgameState,\r\n\t\t\t\tplayerIsFriendly: attackingBoardHero.friendly,\r\n\t\t\t});\r\n\t\t\tdamageDoneByAttacker += dmgDoneByAttacker;\r\n\t\t\tdamageDoneByDefender += dmgDoneByDefender;\r\n\t\t}\r\n\t\tconst enchantments = attacker.enchantments;\r\n\t\tfor (const enchantment of enchantments) {\r\n\t\t\tconst onAttackImpl = cardMappings[enchantment.cardId];\r\n\t\t\tif (hasRally(onAttackImpl)) {\r\n\t\t\t\tlet enchantmentToMinion: BoardEntity = {\r\n\t\t\t\t\t...enchantment,\r\n\t\t\t\t\tentityId: attacker.entityId,\r\n\t\t\t\t\tattack: attacker.attack,\r\n\t\t\t\t\thealth: attacker.health,\r\n\t\t\t\t\tmaxHealth: attacker.maxHealth,\r\n\t\t\t\t\tmaxAttack: attacker.maxAttack,\r\n\t\t\t\t\tabiityChargesLeft: attacker.abiityChargesLeft,\r\n\t\t\t\t};\r\n\t\t\t\tenchantmentToMinion = fixEnchantments(enchantmentToMinion, gameState.allCards);\r\n\t\t\t\tconst { dmgDoneByAttacker, dmgDoneByDefender } = onAttackImpl.rally(enchantmentToMinion, {\r\n\t\t\t\t\tattacker: attacker,\r\n\t\t\t\t\tattackingHero: attackingBoardHero,\r\n\t\t\t\t\tattackingBoard: attackingBoard,\r\n\t\t\t\t\tdefendingEntity: defendingEntity,\r\n\t\t\t\t\tdefendingHero: defendingBoardHero,\r\n\t\t\t\t\tdefendingBoard: defendingBoard,\r\n\t\t\t\t\tgameState,\r\n\t\t\t\t\tplayerIsFriendly: attackingBoardHero.friendly,\r\n\t\t\t\t});\r\n\t\t\t\tdamageDoneByAttacker += dmgDoneByAttacker;\r\n\t\t\t\tdamageDoneByDefender += dmgDoneByDefender;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\treturn { dmgDoneByAttacker: damageDoneByAttacker, dmgDoneByDefender: damageDoneByDefender };\r\n};\r\n"]}
@@ -26,15 +26,14 @@ const getTavernSpellEffectRepeatCount = (input) => {
26
26
  repeats += proudPrivateerExtra;
27
27
  }
28
28
  const cathedrals = input.hero.trinkets.filter((t) => {
29
- var _a, _b;
29
+ var _a;
30
30
  return t.cardId === "BG30_MagicItem_434" &&
31
- t.scriptDataNum1 > 0 &&
32
- ((_b = (_a = t.tags) === null || _a === void 0 ? void 0 : _a[reference_data_1.GameTag.TRIGGER_VISUAL]) !== null && _b !== void 0 ? _b : 0) > 0;
31
+ !((_a = t.tags) === null || _a === void 0 ? void 0 : _a[reference_data_1.GameTag.TAG_SCRIPT_DATA_NUM_6]);
33
32
  });
34
33
  repeats += cathedrals.length;
35
34
  for (const cathedral of cathedrals) {
36
35
  cathedral.tags = (_b = cathedral.tags) !== null && _b !== void 0 ? _b : {};
37
- cathedral.tags[reference_data_1.GameTag.TRIGGER_VISUAL] = 0;
36
+ cathedral.tags[reference_data_1.GameTag.TAG_SCRIPT_DATA_NUM_6] = 1;
38
37
  }
39
38
  return repeats;
40
39
  };
@@ -1 +1 @@
1
- {"version":3,"file":"tavern-spell-repeat.js","sourceRoot":"","sources":["../../src/mechanics/tavern-spell-repeat.ts"],"names":[],"mappings":";;;AAAA,iEAAuD;AAQhD,MAAM,+BAA+B,GAAG,CAAC,KAAqB,EAAU,EAAE;;IAChF,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,IAAI,EAAE;QAChC,OAAO,CAAC,CAAC;KACT;IACD,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,MAAM,gBAAgB,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,iBAA0C,CAAC,CAAC;IACrG,MAAM,gBAAgB,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,eAAwC,CAAC,CAAC;IACnG,IACC,CAAC,gBAAgB,IAAI,gBAAgB,CAAC;QACtC,KAAK,CAAC,MAAM;QACZ,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,EAC5D;QACD,OAAO,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KACnC;IACD,MAAM,cAAc,GACnB,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,eAAuC,CAAC,CAAC,MAAM;QACjF,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,iBAAyC,CAAC,CAAC,MAAM,CAAC;IACzF,OAAO,IAAI,cAAc,CAAC;IAE1B,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IACtE,MAAM,aAAa,GAAG,MAAA,SAAS,aAAT,SAAS,uBAAT,SAAS,CAAE,SAAS,0CAAE,QAAQ,CAAC,cAAc,CAAC,CAAC;IAErE,IAAI,aAAa,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,IAAI,EAAE;QACjD,MAAM,mBAAmB,GACxB,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,eAAoC,CAAC,CAAC,MAAM;YAC9E,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,iBAAsC,CAAC,CAAC,MAAM,CAAC;QACtF,OAAO,IAAI,mBAAmB,CAAC;KAC/B;IAED,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAC5C,CAAC,CAAC,EAAE,EAAE;;QACL,OAAA,CAAC,CAAC,MAAM,yBAAgD;YAExD,CAAC,CAAC,cAAc,GAAG,CAAC;YAapB,CAAC,MAAA,MAAA,CAAC,CAAC,IAAI,0CAAG,wBAAO,CAAC,cAAc,CAAC,mCAAI,CAAC,CAAC,GAAG,CAAC,CAAA;KAAA,CAC5C,CAAC;IACF,OAAO,IAAI,UAAU,CAAC,MAAM,CAAC;IAC7B,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE;QACnC,SAAS,CAAC,IAAI,GAAG,MAAA,SAAS,CAAC,IAAI,mCAAI,EAAE,CAAC;QACtC,SAAS,CAAC,IAAI,CAAC,wBAAO,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;KAC3C;IAED,OAAO,OAAO,CAAC;AAChB,CAAC,CAAC;AAvDW,QAAA,+BAA+B,mCAuD1C","sourcesContent":["import { GameTag } from '@firestone-hs/reference-data';\r\nimport { CastSpellInput } from '../cards/card.interface';\r\nimport { CardIds } from '../services/card-ids';\r\n\r\n/**\r\n * Balinda Stonehearth: extra spell effect resolutions for spells you cast that target a friendly board minion.\r\n * Maelstrom Emergent: each copy adds +1 extra resolution (golden +2) for tavern spells you cast; stacks additively with Balinda.\r\n */\r\nexport const getTavernSpellEffectRepeatCount = (input: CastSpellInput): number => {\r\n\tif (input.source !== input.hero) {\r\n\t\treturn 1;\r\n\t}\r\n\tlet repeats = 1;\r\n\tconst hasGoldenBalinda = input.board.some((e) => e.cardId === CardIds.BalindaStonehearth_BG35_883_G);\r\n\tconst hasNormalBalinda = input.board.some((e) => e.cardId === CardIds.BalindaStonehearth_BG35_883);\r\n\tif (\r\n\t\t(hasGoldenBalinda || hasNormalBalinda) &&\r\n\t\tinput.target &&\r\n\t\tinput.board.some((e) => e.entityId === input.target.entityId)\r\n\t) {\r\n\t\trepeats = hasGoldenBalinda ? 3 : 2;\r\n\t}\r\n\tconst maelstromExtra =\r\n\t\tinput.board.filter((e) => e.cardId === CardIds.MaelstromEmergent_BG34_922).length +\r\n\t\t2 * input.board.filter((e) => e.cardId === CardIds.MaelstromEmergent_BG34_922_G).length;\r\n\trepeats += maelstromExtra;\r\n\r\n\tconst spellCard = input.gameState.allCards.getCard(input.spellCardId);\r\n\tconst isBountySpell = spellCard?.mechanics?.includes('BACON_BOUNTY');\r\n\t// Proud Privateer only affects bounties *you* (the hero) cast — same as Balinda / onTavernSpellCast.\r\n\tif (isBountySpell && input.source === input.hero) {\r\n\t\tconst proudPrivateerExtra =\r\n\t\t\tinput.board.filter((e) => e.cardId === CardIds.ProudPrivateer_BG33_825).length +\r\n\t\t\t2 * input.board.filter((e) => e.cardId === CardIds.ProudPrivateer_BG33_825_G).length;\r\n\t\trepeats += proudPrivateerExtra;\r\n\t}\r\n\r\n\tconst cathedrals = input.hero.trinkets.filter(\r\n\t\t(t) =>\r\n\t\t\tt.cardId === CardIds.ReplicaCathedral_BG30_MagicItem_434 &&\r\n\t\t\t// Total number of times it can trigger\r\n\t\t\tt.scriptDataNum1 > 0 &&\r\n\t\t\t// Total number of times it can still trigger (recruit + combat)\r\n\t\t\t// 2026-04-25:https://replays.firestoneapp.com/?reviewId=f7d1d5ec-b83d-49f5-9669-b063a3debc79&turn=15&action=1\r\n\t\t\t// It triggered during shop, and doesn't re-trigger during combat\r\n\t\t\t/**\r\n\t\t\t * \t{\r\n\t\t\t\t\t\"cardId\": \"BG30_MagicItem_434\",\r\n\t\t\t\t\t\"entityId\": 306,\r\n\t\t\t\t\t\"scriptDataNum1\": 1,\r\n\t\t\t\t\t\"scriptDataNum2\": 0,\r\n\t\t\t\t\t\"scriptDataNum6\": 1\r\n\t\t\t\t},\r\n\t\t\t */\r\n\t\t\t(t.tags?.[GameTag.TRIGGER_VISUAL] ?? 0) > 0,\r\n\t);\r\n\trepeats += cathedrals.length;\r\n\tfor (const cathedral of cathedrals) {\r\n\t\tcathedral.tags = cathedral.tags ?? {};\r\n\t\tcathedral.tags[GameTag.TRIGGER_VISUAL] = 0;\r\n\t}\r\n\r\n\treturn repeats;\r\n};\r\n"]}
1
+ {"version":3,"file":"tavern-spell-repeat.js","sourceRoot":"","sources":["../../src/mechanics/tavern-spell-repeat.ts"],"names":[],"mappings":";;;AAAA,iEAAuD;AAQhD,MAAM,+BAA+B,GAAG,CAAC,KAAqB,EAAU,EAAE;;IAChF,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,IAAI,EAAE;QAChC,OAAO,CAAC,CAAC;KACT;IACD,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,MAAM,gBAAgB,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,iBAA0C,CAAC,CAAC;IACrG,MAAM,gBAAgB,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,eAAwC,CAAC,CAAC;IACnG,IACC,CAAC,gBAAgB,IAAI,gBAAgB,CAAC;QACtC,KAAK,CAAC,MAAM;QACZ,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,EAC5D;QACD,OAAO,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KACnC;IACD,MAAM,cAAc,GACnB,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,eAAuC,CAAC,CAAC,MAAM;QACjF,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,iBAAyC,CAAC,CAAC,MAAM,CAAC;IACzF,OAAO,IAAI,cAAc,CAAC;IAE1B,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IACtE,MAAM,aAAa,GAAG,MAAA,SAAS,aAAT,SAAS,uBAAT,SAAS,CAAE,SAAS,0CAAE,QAAQ,CAAC,cAAc,CAAC,CAAC;IAErE,IAAI,aAAa,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,IAAI,EAAE;QACjD,MAAM,mBAAmB,GACxB,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,eAAoC,CAAC,CAAC,MAAM;YAC9E,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,iBAAsC,CAAC,CAAC,MAAM,CAAC;QACtF,OAAO,IAAI,mBAAmB,CAAC;KAC/B;IAED,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAC5C,CAAC,CAAC,EAAE,EAAE;;QACL,OAAA,CAAC,CAAC,MAAM,yBAAgD;YAexD,CAAC,CAAA,MAAA,CAAC,CAAC,IAAI,0CAAG,wBAAO,CAAC,qBAAqB,CAAC,CAAA,CAAA;KAAA,CACzC,CAAC;IACF,OAAO,IAAI,UAAU,CAAC,MAAM,CAAC;IAC7B,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE;QACnC,SAAS,CAAC,IAAI,GAAG,MAAA,SAAS,CAAC,IAAI,mCAAI,EAAE,CAAC;QACtC,SAAS,CAAC,IAAI,CAAC,wBAAO,CAAC,qBAAqB,CAAC,GAAG,CAAC,CAAC;KAClD;IAED,OAAO,OAAO,CAAC;AAChB,CAAC,CAAC;AAvDW,QAAA,+BAA+B,mCAuD1C","sourcesContent":["import { GameTag } from '@firestone-hs/reference-data';\r\nimport { CastSpellInput } from '../cards/card.interface';\r\nimport { CardIds } from '../services/card-ids';\r\n\r\n/**\r\n * Balinda Stonehearth: extra spell effect resolutions for spells you cast that target a friendly board minion.\r\n * Maelstrom Emergent: each copy adds +1 extra resolution (golden +2) for tavern spells you cast; stacks additively with Balinda.\r\n */\r\nexport const getTavernSpellEffectRepeatCount = (input: CastSpellInput): number => {\r\n\tif (input.source !== input.hero) {\r\n\t\treturn 1;\r\n\t}\r\n\tlet repeats = 1;\r\n\tconst hasGoldenBalinda = input.board.some((e) => e.cardId === CardIds.BalindaStonehearth_BG35_883_G);\r\n\tconst hasNormalBalinda = input.board.some((e) => e.cardId === CardIds.BalindaStonehearth_BG35_883);\r\n\tif (\r\n\t\t(hasGoldenBalinda || hasNormalBalinda) &&\r\n\t\tinput.target &&\r\n\t\tinput.board.some((e) => e.entityId === input.target.entityId)\r\n\t) {\r\n\t\trepeats = hasGoldenBalinda ? 3 : 2;\r\n\t}\r\n\tconst maelstromExtra =\r\n\t\tinput.board.filter((e) => e.cardId === CardIds.MaelstromEmergent_BG34_922).length +\r\n\t\t2 * input.board.filter((e) => e.cardId === CardIds.MaelstromEmergent_BG34_922_G).length;\r\n\trepeats += maelstromExtra;\r\n\r\n\tconst spellCard = input.gameState.allCards.getCard(input.spellCardId);\r\n\tconst isBountySpell = spellCard?.mechanics?.includes('BACON_BOUNTY');\r\n\t// Proud Privateer only affects bounties *you* (the hero) cast — same as Balinda / onTavernSpellCast.\r\n\tif (isBountySpell && input.source === input.hero) {\r\n\t\tconst proudPrivateerExtra =\r\n\t\t\tinput.board.filter((e) => e.cardId === CardIds.ProudPrivateer_BG33_825).length +\r\n\t\t\t2 * input.board.filter((e) => e.cardId === CardIds.ProudPrivateer_BG33_825_G).length;\r\n\t\trepeats += proudPrivateerExtra;\r\n\t}\r\n\r\n\tconst cathedrals = input.hero.trinkets.filter(\r\n\t\t(t) =>\r\n\t\t\tt.cardId === CardIds.ReplicaCathedral_BG30_MagicItem_434 &&\r\n\t\t\t// 2026-04-25:https://replays.firestoneapp.com/?reviewId=f7d1d5ec-b83d-49f5-9669-b063a3debc79&turn=15&action=1\r\n\t\t\t// It triggered during shop, and doesn't re-trigger during combat\r\n\t\t\t/**\r\n\t\t\t * \t{\r\n\t\t\t\t\t\"cardId\": \"BG30_MagicItem_434\",\r\n\t\t\t\t\t\"entityId\": 306,\r\n\t\t\t\t\t\"scriptDataNum1\": 1,\r\n\t\t\t\t\t\"scriptDataNum2\": 0,\r\n\t\t\t\t\t\"scriptDataNum6\": 1\r\n\t\t\t\t},\r\n\t\t\t */\r\n\t\t\t// 2026-07-22: This is not relevant. I had the case where TRIGGER_VISUAL was 1 but it wouln't re-trigger\r\n\t\t\t// (t.tags?.[GameTag.TRIGGER_VISUAL] ?? 0) > 0,\r\n\t\t\t// Next try is SCRIPT_DATA_NUM_6, assuming 1 meaning it can't trigger\r\n\t\t\t!t.tags?.[GameTag.TAG_SCRIPT_DATA_NUM_6],\r\n\t);\r\n\trepeats += cathedrals.length;\r\n\tfor (const cathedral of cathedrals) {\r\n\t\tcathedral.tags = cathedral.tags ?? {};\r\n\t\tcathedral.tags[GameTag.TAG_SCRIPT_DATA_NUM_6] = 1;\r\n\t}\r\n\r\n\treturn repeats;\r\n};\r\n"]}
@@ -2,8 +2,8 @@ import { AllCardsService } from '@firestone-hs/reference-data';
2
2
  import { BgsBattleInfo } from './bgs-battle-info';
3
3
  import { CardsData } from './cards/cards-data';
4
4
  import { SimulationResult } from './simulation-result';
5
+ export { isValidDeathrattleEnchantment } from './simulation/deathrattle-utils';
5
6
  export declare const assignCards: (cards: AllCardsService) => void;
6
7
  declare const _default: (event: any) => Promise<any>;
7
8
  export default _default;
8
9
  export declare const simulateBattle: (battleInput: BgsBattleInfo, cards: AllCardsService, cardsData: CardsData) => Generator<SimulationResult, SimulationResult, void>;
9
- export declare const isValidDeathrattleEnchantment: (cardId: string) => boolean;
@@ -1,16 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isValidDeathrattleEnchantment = exports.simulateBattle = exports.assignCards = void 0;
3
+ exports.simulateBattle = exports.assignCards = exports.isValidDeathrattleEnchantment = void 0;
4
4
  const reference_data_1 = require("@firestone-hs/reference-data");
5
- const card_interface_1 = require("./cards/card.interface");
6
5
  const cards_data_1 = require("./cards/cards-data");
7
- const _card_mappings_1 = require("./cards/impl/_card-mappings");
8
6
  const debug_state_1 = require("./debug-state");
9
7
  const input_clone_1 = require("./input-clone");
10
8
  const input_sanitation_1 = require("./input-sanitation");
11
9
  const shared_state_1 = require("./simulation/shared-state");
12
10
  const simulator_1 = require("./simulation/simulator");
13
11
  const spectator_1 = require("./simulation/spectator/spectator");
12
+ var deathrattle_utils_1 = require("./simulation/deathrattle-utils");
13
+ Object.defineProperty(exports, "isValidDeathrattleEnchantment", { enumerable: true, get: function () { return deathrattle_utils_1.isValidDeathrattleEnchantment; } });
14
+ const MAX_SAMPLE_RESIMS = 500;
14
15
  let globalCards = new reference_data_1.AllCardsService();
15
16
  const assignCards = (cards) => {
16
17
  globalCards = cards;
@@ -89,45 +90,65 @@ const simulateBattle = function* (battleInput, cards, cardsData) {
89
90
  ...(p.chosenCardId && { chosenCardId: p.chosenCardId }),
90
91
  }));
91
92
  }
93
+ const cloneTemplate = (0, input_clone_1.buildTemplateCloner)((0, input_clone_1.cloneInput3)(inputReady));
94
+ const runSingleBattle = () => {
95
+ const input = cloneTemplate();
96
+ let playerInitialState = null;
97
+ let opponentInitialState = null;
98
+ const buildInitialStates = () => {
99
+ const inputClone = cloneTemplate();
100
+ playerInitialState = {
101
+ player: inputClone.playerBoard.player,
102
+ board: inputClone.playerBoard.board,
103
+ teammate: inputClone.playerTeammateBoard,
104
+ };
105
+ opponentInitialState = {
106
+ player: inputClone.opponentBoard.player,
107
+ board: inputClone.opponentBoard.board,
108
+ teammate: inputClone.opponentTeammateBoard,
109
+ };
110
+ };
111
+ const gameState = {
112
+ allCards: cards,
113
+ cardsData: cardsData,
114
+ spectator: spectator,
115
+ sharedState: new shared_state_1.SharedState(),
116
+ currentTurn: input.gameState.currentTurn,
117
+ validTribes: input.gameState.validTribes,
118
+ anomalies: input.gameState.anomalies,
119
+ gameState: {
120
+ player: {
121
+ player: input.playerBoard.player,
122
+ board: input.playerBoard.board,
123
+ teammate: input.playerTeammateBoard,
124
+ },
125
+ opponent: {
126
+ player: input.opponentBoard.player,
127
+ board: input.opponentBoard.board,
128
+ teammate: input.opponentTeammateBoard,
129
+ },
130
+ get playerInitial() {
131
+ if (!playerInitialState) {
132
+ buildInitialStates();
133
+ }
134
+ return playerInitialState;
135
+ },
136
+ get opponentInitial() {
137
+ if (!opponentInitialState) {
138
+ buildInitialStates();
139
+ }
140
+ return opponentInitialState;
141
+ },
142
+ },
143
+ };
144
+ const simulator = new simulator_1.Simulator(gameState);
145
+ return simulator.simulateSingleBattle(gameState.gameState.player, gameState.gameState.opponent);
146
+ };
92
147
  try {
93
148
  !((_o = battleInput.options) === null || _o === void 0 ? void 0 : _o.skipInfoLogs) && console.time('simulation');
94
149
  const outcomes = {};
95
150
  for (let i = 0; i < numberOfSimulations; i++) {
96
- const input = (0, input_clone_1.cloneInput3)(inputReady);
97
- const inputClone = (0, input_clone_1.cloneInput3)(inputReady);
98
- const gameState = {
99
- allCards: cards,
100
- cardsData: cardsData,
101
- spectator: spectator,
102
- sharedState: new shared_state_1.SharedState(),
103
- currentTurn: input.gameState.currentTurn,
104
- validTribes: input.gameState.validTribes,
105
- anomalies: input.gameState.anomalies,
106
- gameState: {
107
- player: {
108
- player: input.playerBoard.player,
109
- board: input.playerBoard.board,
110
- teammate: input.playerTeammateBoard,
111
- },
112
- opponent: {
113
- player: input.opponentBoard.player,
114
- board: input.opponentBoard.board,
115
- teammate: input.opponentTeammateBoard,
116
- },
117
- playerInitial: {
118
- player: inputClone.playerBoard.player,
119
- board: inputClone.playerBoard.board,
120
- teammate: inputClone.playerTeammateBoard,
121
- },
122
- opponentInitial: {
123
- player: inputClone.opponentBoard.player,
124
- board: inputClone.opponentBoard.board,
125
- teammate: inputClone.opponentTeammateBoard,
126
- },
127
- },
128
- };
129
- const simulator = new simulator_1.Simulator(gameState);
130
- const battleResult = simulator.simulateSingleBattle(gameState.gameState.player, gameState.gameState.opponent);
151
+ const battleResult = runSingleBattle();
131
152
  if (!battleResult) {
132
153
  continue;
133
154
  }
@@ -152,7 +173,6 @@ const simulateBattle = function* (battleInput, cards, cardsData) {
152
173
  else if (battleResult.result === 'tied') {
153
174
  simulationResult.tied++;
154
175
  }
155
- spectator.commitBattleResult(battleResult.result);
156
176
  if (Date.now() - start > maxAcceptableDuration && !hideMaxSimulationDurationWarning) {
157
177
  console.warn('Stopping simulation after', i, 'iterations and ', Date.now() - start, 'ms');
158
178
  break;
@@ -164,6 +184,27 @@ const simulateBattle = function* (battleInput, cards, cardsData) {
164
184
  }
165
185
  updateSimulationResult(simulationResult, inputReady, damageConfidence);
166
186
  !((_q = battleInput.options) === null || _q === void 0 ? void 0 : _q.skipInfoLogs) && console.timeEnd('simulation');
187
+ if (includeOutcomeSamples) {
188
+ const neededOutcomes = [];
189
+ simulationResult.won > 0 && neededOutcomes.push('won');
190
+ simulationResult.lost > 0 && neededOutcomes.push('lost');
191
+ simulationResult.tied > 0 && neededOutcomes.push('tied');
192
+ const missingOutcomes = new Set(neededOutcomes);
193
+ if (missingOutcomes.size > 0) {
194
+ spectator.startRecording();
195
+ for (let i = 0; i < MAX_SAMPLE_RESIMS && missingOutcomes.size > 0; i++) {
196
+ const battleResult = runSingleBattle();
197
+ if (!(battleResult === null || battleResult === void 0 ? void 0 : battleResult.result)) {
198
+ continue;
199
+ }
200
+ spectator.commitBattleResult(battleResult.result);
201
+ if (spectator.hasSampleFor(battleResult.result)) {
202
+ missingOutcomes.delete(battleResult.result);
203
+ }
204
+ }
205
+ spectator.stopRecording();
206
+ }
207
+ }
167
208
  spectator.prune();
168
209
  simulationResult.outcomeSamples = spectator.buildOutcomeSamples(battleInput.gameState);
169
210
  simulationResult.damageWons = [];
@@ -223,42 +264,4 @@ const checkRounding = (roundedValue, initialValue, totalValue) => {
223
264
  }
224
265
  return roundedValue;
225
266
  };
226
- const VALID_DEATHRATTLE_ENCHANTMENTS = [
227
- "BG_BOT_312e",
228
- "TB_BaconUps_032e",
229
- "UNG_999t2e",
230
- "BG21_000e",
231
- "BG21_000_Ge",
232
- "BG21_HERO_030p",
233
- "BG21_HERO_030pe",
234
- "BG22_HERO_001_Buddy_e1",
235
- "BG22_HERO_001_Buddy_e2",
236
- "BG22_HERO_001_Buddy_e4",
237
- "BG22_HERO_001_Buddy_e3",
238
- "BG22_HERO_001p_t1e",
239
- "BG22_HERO_001p_t4_s",
240
- "BG28_603e",
241
- "BG30_MagicItem_917e",
242
- "BG30_MagicItem_411e",
243
- "BG30_MagicItem_952e",
244
- "BG31_HERO_801ptee",
245
- "BG32_172e",
246
- "BG32_172_Ge",
247
- "BG20_HERO_282e2",
248
- ];
249
- const validDeathrattleEnchantmentsFromMapping = [];
250
- const isValidDeathrattleEnchantment = (cardId) => {
251
- if (VALID_DEATHRATTLE_ENCHANTMENTS.includes(cardId)) {
252
- return true;
253
- }
254
- if (validDeathrattleEnchantmentsFromMapping.length === 0) {
255
- for (const cardImpl of Object.values(_card_mappings_1.cardMappings)) {
256
- if ((0, card_interface_1.hasDeathrattleSpawnEnchantment)(cardImpl)) {
257
- validDeathrattleEnchantmentsFromMapping.push(...cardImpl.cardIds);
258
- }
259
- }
260
- }
261
- return validDeathrattleEnchantmentsFromMapping.includes(cardId);
262
- };
263
- exports.isValidDeathrattleEnchantment = isValidDeathrattleEnchantment;
264
267
  //# sourceMappingURL=simulate-bgs-battle.js.map