@firestone-hs/simulate-bgs-battle 1.1.136 → 1.1.141

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 (35) hide show
  1. package/dist/bgs-battle-info.d.ts +6 -0
  2. package/dist/bgs-battle-info.js.map +1 -1
  3. package/dist/bgs-battle-options.d.ts +1 -0
  4. package/dist/bgs-battle-options.js.map +1 -1
  5. package/dist/bgs-player-entity.d.ts +2 -0
  6. package/dist/bgs-player-entity.js.map +1 -1
  7. package/dist/board-entity.d.ts +1 -0
  8. package/dist/board-entity.js.map +1 -1
  9. package/dist/cards/cards-data.d.ts +2 -0
  10. package/dist/cards/cards-data.js +21 -4
  11. package/dist/cards/cards-data.js.map +1 -1
  12. package/dist/simulate-bgs-battle.js +12 -6
  13. package/dist/simulate-bgs-battle.js.map +1 -1
  14. package/dist/simulation/attack.d.ts +2 -2
  15. package/dist/simulation/attack.js +38 -24
  16. package/dist/simulation/attack.js.map +1 -1
  17. package/dist/simulation/auras.js +22 -0
  18. package/dist/simulation/auras.js.map +1 -1
  19. package/dist/simulation/deathrattle-effects.d.ts +7 -2
  20. package/dist/simulation/deathrattle-effects.js +176 -17
  21. package/dist/simulation/deathrattle-effects.js.map +1 -1
  22. package/dist/simulation/deathrattle-spawns.js +26 -4
  23. package/dist/simulation/deathrattle-spawns.js.map +1 -1
  24. package/dist/simulation/frenzy.js +1 -1
  25. package/dist/simulation/frenzy.js.map +1 -1
  26. package/dist/simulation/simulator.d.ts +2 -1
  27. package/dist/simulation/simulator.js +2 -2
  28. package/dist/simulation/simulator.js.map +1 -1
  29. package/dist/simulation/start-of-combat.d.ts +3 -2
  30. package/dist/simulation/start-of-combat.js +62 -6
  31. package/dist/simulation/start-of-combat.js.map +1 -1
  32. package/dist/utils.d.ts +3 -1
  33. package/dist/utils.js +81 -22
  34. package/dist/utils.js.map +1 -1
  35. package/package.json +4 -4
@@ -1,8 +1,14 @@
1
+ import { Race } from '@firestone-hs/reference-data';
1
2
  import { BgsBattleOptions } from './bgs-battle-options';
2
3
  import { BgsBoardInfo } from './bgs-board-info';
3
4
  export interface BgsBattleInfo {
4
5
  readonly playerBoard: BgsBoardInfo;
5
6
  readonly opponentBoard: BgsBoardInfo;
6
7
  readonly options: BgsBattleOptions;
8
+ readonly gameState: BgsGameState;
7
9
  readonly heroHasDied?: boolean;
8
10
  }
11
+ export interface BgsGameState {
12
+ readonly currentTurn: number;
13
+ readonly validTribes?: readonly Race[];
14
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"bgs-battle-info.js","sourceRoot":"","sources":["../src/bgs-battle-info.ts"],"names":[],"mappings":"","sourcesContent":["import { BgsBattleOptions } from './bgs-battle-options';\r\nimport { BgsBoardInfo } from './bgs-board-info';\r\n\r\nexport interface BgsBattleInfo {\r\n\treadonly playerBoard: BgsBoardInfo;\r\n\treadonly opponentBoard: BgsBoardInfo;\r\n\treadonly options: BgsBattleOptions;\r\n\treadonly heroHasDied?: boolean;\r\n}\r\n"]}
1
+ {"version":3,"file":"bgs-battle-info.js","sourceRoot":"","sources":["../src/bgs-battle-info.ts"],"names":[],"mappings":"","sourcesContent":["import { Race } from '@firestone-hs/reference-data';\r\nimport { BgsBattleOptions } from './bgs-battle-options';\r\nimport { BgsBoardInfo } from './bgs-board-info';\r\n\r\nexport interface BgsBattleInfo {\r\n\treadonly playerBoard: BgsBoardInfo;\r\n\treadonly opponentBoard: BgsBoardInfo;\r\n\treadonly options: BgsBattleOptions;\r\n\treadonly gameState: BgsGameState;\r\n\treadonly heroHasDied?: boolean;\r\n}\r\n\r\nexport interface BgsGameState {\r\n\treadonly currentTurn: number;\r\n\treadonly validTribes?: readonly Race[];\r\n}\r\n"]}
@@ -3,4 +3,5 @@ export interface BgsBattleOptions {
3
3
  readonly numberOfSimulations: number;
4
4
  readonly maxAcceptableDuration?: number;
5
5
  readonly validTribes?: readonly Race[];
6
+ readonly skipInfoLogs: boolean;
6
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"bgs-battle-options.js","sourceRoot":"","sources":["../src/bgs-battle-options.ts"],"names":[],"mappings":"","sourcesContent":["import { Race } from '@firestone-hs/reference-data';\r\n\r\nexport interface BgsBattleOptions {\r\n\treadonly numberOfSimulations: number;\r\n\treadonly maxAcceptableDuration?: number;\r\n\treadonly validTribes?: readonly Race[];\r\n}\r\n"]}
1
+ {"version":3,"file":"bgs-battle-options.js","sourceRoot":"","sources":["../src/bgs-battle-options.ts"],"names":[],"mappings":"","sourcesContent":["import { Race } from '@firestone-hs/reference-data';\r\n\r\nexport interface BgsBattleOptions {\r\n\treadonly numberOfSimulations: number;\r\n\treadonly maxAcceptableDuration?: number;\r\n\t/** @deprecated */\r\n\treadonly validTribes?: readonly Race[];\r\n\treadonly skipInfoLogs: boolean;\r\n}\r\n"]}
@@ -5,5 +5,7 @@ export interface BgsPlayerEntity {
5
5
  readonly tavernTier: number;
6
6
  readonly heroPowerId: string;
7
7
  readonly heroPowerUsed: boolean;
8
+ readonly heroPowerInfo?: number;
8
9
  cardsInHand?: number;
10
+ deadEyeDamageDone?: number;
9
11
  }
@@ -1 +1 @@
1
- {"version":3,"file":"bgs-player-entity.js","sourceRoot":"","sources":["../src/bgs-player-entity.ts"],"names":[],"mappings":"","sourcesContent":["export interface BgsPlayerEntity {\r\n\treadonly cardId: string;\r\n\treadonly nonGhostCardId?: string;\r\n\treadonly hpLeft: number;\r\n\treadonly tavernTier: number;\r\n\treadonly heroPowerId: string;\r\n\treadonly heroPowerUsed: boolean;\r\n\tcardsInHand?: number;\r\n}\r\n"]}
1
+ {"version":3,"file":"bgs-player-entity.js","sourceRoot":"","sources":["../src/bgs-player-entity.ts"],"names":[],"mappings":"","sourcesContent":["export interface BgsPlayerEntity {\r\n\treadonly cardId: string;\r\n\treadonly nonGhostCardId?: string;\r\n\treadonly hpLeft: number;\r\n\treadonly tavernTier: number;\r\n\treadonly heroPowerId: string;\r\n\treadonly heroPowerUsed: boolean;\r\n\treadonly heroPowerInfo?: number;\r\n\tcardsInHand?: number;\r\n\r\n\tdeadEyeDamageDone?: number;\r\n}\r\n"]}
@@ -24,6 +24,7 @@ export interface BoardEntity {
24
24
  friendly?: boolean;
25
25
  cantAttack?: boolean;
26
26
  attacksPerformed?: number;
27
+ immuneWhenAttackCharges: number;
27
28
  attackImmediately?: boolean;
28
29
  previousAttack?: number;
29
30
  lastAffectedByEntity?: BoardEntity;
@@ -1 +1 @@
1
- {"version":3,"file":"board-entity.js","sourceRoot":"","sources":["../src/board-entity.ts"],"names":[],"mappings":"","sourcesContent":["export interface BoardEntity {\r\n\tentityId: number;\r\n\tcardId: string;\r\n\tattack: number;\r\n\thealth: number;\r\n\r\n\tmaxHealth?: number;\r\n\tavengeCurrent?: number;\r\n\tavengeDefault?: number;\r\n\tfrenzyApplied?: boolean;\r\n\tdefinitelyDead?: boolean;\r\n\ttaunt?: boolean;\r\n\tdivineShield?: boolean;\r\n\tpoisonous?: boolean;\r\n\treborn?: boolean;\r\n\tcleave?: boolean;\r\n\twindfury?: boolean;\r\n\tmegaWindfury?: boolean;\r\n\tenchantments?: { cardId: string; originEntityId?: number; repeats?: number }[];\r\n\t// We only store the card id, because we want all the attack and other data to be computed at runtime, based on the\r\n\t// current stats of the Fish\r\n\trememberedDeathrattles?: string[];\r\n\r\n\tfriendly?: boolean;\r\n\tcantAttack?: boolean;\r\n\tattacksPerformed?: number;\r\n\tattackImmediately?: boolean;\r\n\t// Used only to handle murkeye aura?\r\n\tpreviousAttack?: number;\r\n\tlastAffectedByEntity?: BoardEntity;\r\n\tattacking?: boolean;\r\n}\r\n"]}
1
+ {"version":3,"file":"board-entity.js","sourceRoot":"","sources":["../src/board-entity.ts"],"names":[],"mappings":"","sourcesContent":["export interface BoardEntity {\r\n\tentityId: number;\r\n\tcardId: string;\r\n\tattack: number;\r\n\thealth: number;\r\n\r\n\tmaxHealth?: number;\r\n\tavengeCurrent?: number;\r\n\tavengeDefault?: number;\r\n\tfrenzyApplied?: boolean;\r\n\tdefinitelyDead?: boolean;\r\n\ttaunt?: boolean;\r\n\tdivineShield?: boolean;\r\n\tpoisonous?: boolean;\r\n\treborn?: boolean;\r\n\tcleave?: boolean;\r\n\twindfury?: boolean;\r\n\tmegaWindfury?: boolean;\r\n\tenchantments?: { cardId: string; originEntityId?: number; repeats?: number }[];\r\n\t// We only store the card id, because we want all the attack and other data to be computed at runtime, based on the\r\n\t// current stats of the Fish\r\n\trememberedDeathrattles?: string[];\r\n\r\n\tfriendly?: boolean;\r\n\tcantAttack?: boolean;\r\n\tattacksPerformed?: number;\r\n\timmuneWhenAttackCharges: number;\r\n\tattackImmediately?: boolean;\r\n\t// Used only to handle murkeye aura?\r\n\tpreviousAttack?: number;\r\n\tlastAffectedByEntity?: BoardEntity;\r\n\tattacking?: boolean;\r\n}\r\n"]}
@@ -5,6 +5,8 @@ export declare class CardsData {
5
5
  validDeathrattles: readonly string[];
6
6
  impMamaSpawns: readonly string[];
7
7
  gentleDjinniSpawns: readonly string[];
8
+ kilrekSpawns: readonly string[];
9
+ brannEpicEggSpawns: readonly string[];
8
10
  pirateSpawns: readonly string[];
9
11
  auraEnchantments: readonly string[][];
10
12
  auraOrigins: readonly string[];
@@ -38,6 +38,15 @@ class CardsData {
38
38
  .filter((card) => card.race === 'ELEMENTAL')
39
39
  .filter((card) => card.id !== "BGS_121")
40
40
  .map((card) => card.id);
41
+ this.kilrekSpawns = pool
42
+ .filter((card) => !this.isGolden(card))
43
+ .filter((card) => card.race === reference_data_1.Race[reference_data_1.Race.DEMON])
44
+ .filter((card) => card.id !== "TB_BaconShop_HERO_37_Buddy")
45
+ .map((card) => card.id);
46
+ this.brannEpicEggSpawns = pool
47
+ .filter((card) => !this.isGolden(card))
48
+ .filter((card) => utils_2.hasMechanic(card, 'BATTLECRY'))
49
+ .map((card) => card.id);
41
50
  this.pirateSpawns = pool
42
51
  .filter((card) => !this.isGolden(card))
43
52
  .filter((card) => card.race === 'PIRATE')
@@ -49,6 +58,8 @@ class CardsData {
49
58
  ["TB_BaconUps_008", "TB_BaconUps_008e"],
50
59
  ["NEW1_027", "NEW1_027e"],
51
60
  ["TB_BaconUps_136", "TB_BaconUps_136e"],
61
+ ["TB_BaconShop_HERO_52_Buddy", "TB_BaconShop_HERO_52_Buddy_e"],
62
+ ["TB_BaconShop_HERO_52_Buddy_G", "TB_BaconShop_HERO_52_Buddy_G_e"],
52
63
  ];
53
64
  this.auraOrigins = this.auraEnchantments.map((pair) => pair[0]);
54
65
  this.startOfCombats = [
@@ -56,6 +67,8 @@ class CardsData {
56
67
  "TB_BaconUps_102",
57
68
  "BG21_014",
58
69
  "BG21_014_G",
70
+ "BG22_HERO_000_Buddy",
71
+ "BG22_HERO_000_Buddy_G",
59
72
  ];
60
73
  }
61
74
  avengeValue(cardId) {
@@ -63,15 +76,19 @@ class CardsData {
63
76
  case "BG21_002":
64
77
  case "BG21_002_G":
65
78
  return 1;
66
- case "BG21_001":
67
- case "BG21_001_G":
79
+ case "BG22_HERO_002_Buddy":
80
+ case "BG22_HERO_002_Buddy_G":
68
81
  case "BG21_023":
69
82
  case "BG21_023_G":
83
+ case "BG21_001":
84
+ case "BG21_001_G":
85
+ case "BG22_HERO_003_Buddy":
86
+ case "BG22_HERO_003_Buddy_G":
70
87
  return 2;
71
- case "BG21_009":
72
- case "BG21_009_G":
73
88
  case "BG21_030":
74
89
  case "BG21_030_G":
90
+ case "BG21_009":
91
+ case "BG21_009_G":
75
92
  return 3;
76
93
  case "BG21_007":
77
94
  case "BG21_007_G":
@@ -1 +1 @@
1
- {"version":3,"file":"cards-data.js","sourceRoot":"","sources":["../../src/cards/cards-data.ts"],"names":[],"mappings":"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","sourcesContent":["import { AllCardsService, CardIds, isBattlegroundsCard, Race, ReferenceCard } from '@firestone-hs/reference-data';\r\nimport { groupByFunction, pickRandom } from '../services/utils';\r\nimport { getRaceEnum, hasMechanic } from '../utils';\r\n\r\nexport class CardsData {\r\n\t// public shredderSpawns: readonly string[];\r\n\tpublic ghastcoilerSpawns: readonly string[];\r\n\tpublic validDeathrattles: readonly string[];\r\n\tpublic impMamaSpawns: readonly string[];\r\n\tpublic gentleDjinniSpawns: readonly string[];\r\n\t// public sneedsSpawns: readonly string[];\r\n\t// public treasureChestSpawns: readonly string[];\r\n\tpublic pirateSpawns: readonly string[];\r\n\r\n\tpublic auraEnchantments: readonly string[][];\r\n\tpublic auraOrigins: readonly string[];\r\n\tpublic startOfCombats: readonly string[];\r\n\r\n\tprivate minionsForTier: { [key: string]: readonly ReferenceCard[] };\r\n\r\n\tconstructor(private readonly allCards: AllCardsService, init = true) {\r\n\t\tif (init) {\r\n\t\t\tthis.inititialize();\r\n\t\t}\r\n\t}\r\n\r\n\tpublic inititialize(validTribes?: readonly Race[]): void {\r\n\t\tconst pool = this.allCards\r\n\t\t\t.getCards()\r\n\t\t\t.filter((card) => isBattlegroundsCard(card))\r\n\t\t\t.filter((card) => !!card.techLevel)\r\n\t\t\t.filter((card) => card.set !== 'Vanilla');\r\n\t\tthis.minionsForTier = groupByFunction((card: ReferenceCard) => card.techLevel)(pool.filter((card) => !this.isGolden(card)));\r\n\t\tthis.ghastcoilerSpawns = pool\r\n\t\t\t.filter((card) => !this.isGolden(card))\r\n\t\t\t.filter((card) => card.id !== 'BGS_008')\r\n\t\t\t.filter((card) => hasMechanic(card, 'DEATHRATTLE'))\r\n\t\t\t// .filter((card) => REMOVED_CARD_IDS.indexOf(card.id) === -1)\r\n\t\t\t.filter((card) => this.isValidTribe(validTribes, card.race))\r\n\t\t\t.map((card) => card.id);\r\n\t\tthis.validDeathrattles = pool\r\n\t\t\t// .filter((card) => !card.id.startsWith('TB_BaconUps')) // Ignore golden\r\n\t\t\t.filter((card) => hasMechanic(card, 'DEATHRATTLE'))\r\n\t\t\t// .filter((card) => REMOVED_CARD_IDS.indexOf(card.id) === -1)\r\n\t\t\t.filter((card) => this.isValidTribe(validTribes, card.race))\r\n\t\t\t.map((card) => card.id);\r\n\t\tthis.impMamaSpawns = pool\r\n\t\t\t.filter((card) => !this.isGolden(card))\r\n\t\t\t.filter((card) => card.race === 'DEMON')\r\n\t\t\t.filter((card) => card.id !== CardIds.ImpMama)\r\n\t\t\t// .filter((card) => REMOVED_CARD_IDS.indexOf(card.id) === -1)\r\n\t\t\t.map((card) => card.id);\r\n\t\tthis.gentleDjinniSpawns = pool\r\n\t\t\t.filter((card) => !this.isGolden(card))\r\n\t\t\t.filter((card) => card.race === 'ELEMENTAL')\r\n\t\t\t.filter((card) => card.id !== CardIds.GentleDjinni)\r\n\t\t\t// .filter((card) => REMOVED_CARD_IDS.indexOf(card.id) === -1)\r\n\t\t\t.map((card) => card.id);\r\n\t\tthis.pirateSpawns = pool\r\n\t\t\t.filter((card) => !this.isGolden(card))\r\n\t\t\t.filter((card) => card.race === 'PIRATE')\r\n\t\t\t// .filter((card) => REMOVED_CARD_IDS.indexOf(card.id) === -1)\r\n\t\t\t.map((card) => card.id);\r\n\t\t// Auras are effects that are permanent (unlike deathrattles or \"whenever\" effects)\r\n\t\t// and that stop once the origin entity leaves play (so it doesn't include buffs)\r\n\t\tthis.auraEnchantments = [\r\n\t\t\t[CardIds.Kathranatir2, CardIds.Kathranatir_GraspOfKathranatirEnchantment1],\r\n\t\t\t[CardIds.KathranatirBattlegrounds, CardIds.Kathranatir_GraspOfKathranatirEnchantment2],\r\n\t\t\t[CardIds.MurlocWarleaderLegacy, CardIds.MurlocWarleader_MrgglaarglLegacyEnchantment],\r\n\t\t\t[CardIds.MurlocWarleaderBattlegrounds, CardIds.MurlocWarleader_MrgglaarglEnchantmentBattlegrounds],\r\n\t\t\t[CardIds.SouthseaCaptainLegacy, CardIds.SouthseaCaptain_YarrrLegacyEnchantment],\r\n\t\t\t[CardIds.SouthseaCaptainBattlegrounds, CardIds.SouthseaCaptain_YarrrEnchantmentBattlegrounds],\r\n\t\t];\r\n\t\tthis.auraOrigins = this.auraEnchantments.map((pair) => pair[0]);\r\n\t\tthis.startOfCombats = [\r\n\t\t\tCardIds.RedWhelp,\r\n\t\t\tCardIds.RedWhelpBattlegrounds,\r\n\t\t\tCardIds.PrizedPromoDrake,\r\n\t\t\tCardIds.PrizedPromoDrakeBattlegrounds,\r\n\t\t];\r\n\t}\r\n\r\n\tpublic avengeValue(cardId: string): number {\r\n\t\tswitch (cardId) {\r\n\t\t\tcase CardIds.BirdBuddy:\r\n\t\t\tcase CardIds.BirdBuddyBattlegrounds:\r\n\t\t\t\treturn 1;\r\n\t\t\tcase CardIds.PalescaleCrocolisk:\r\n\t\t\tcase CardIds.PalescaleCrocoliskBattlegrounds:\r\n\t\t\tcase CardIds.MechanoTank:\r\n\t\t\tcase CardIds.MechanoTankBattlegrounds:\r\n\t\t\t\treturn 2;\r\n\t\t\tcase CardIds.Sisefin:\r\n\t\t\tcase CardIds.SisefinBattlegrounds:\r\n\t\t\tcase CardIds.BuddingGreenthumb:\r\n\t\t\tcase CardIds.BuddingGreenthumbBattlegrounds:\r\n\t\t\t\treturn 3;\r\n\t\t\tcase CardIds.ImpatientDoomsayer:\r\n\t\t\tcase CardIds.ImpatientDoomsayerBattlegrounds:\r\n\t\t\tcase CardIds.WitchwingNestmatron:\r\n\t\t\tcase CardIds.WitchwingNestmatronBattlegrounds:\r\n\t\t\tcase CardIds.TonyTwoTusk:\r\n\t\t\tcase CardIds.TonyTwoTuskBattlegrounds:\r\n\t\t\t\treturn 4;\r\n\t\t}\r\n\t\treturn 0;\r\n\t}\r\n\r\n\tpublic getTavernLevel(cardId: string): number {\r\n\t\treturn this.allCards.getCard(cardId).techLevel;\r\n\t}\r\n\r\n\tpublic getRandomMinionForTavernTier(tavernTier: number): string {\r\n\t\t// Tzvern tier can be undefined for hero-power specific tokens, like the Amalgam, or when\r\n\t\t// for some reason tokens end up in the shop. For now, defaulting to 1 for tavern\r\n\t\t// level seems to work in all cases\r\n\t\treturn pickRandom(this.minionsForTier[tavernTier ?? 1]).id;\r\n\t}\r\n\r\n\tprivate isGolden(card: ReferenceCard): boolean {\r\n\t\treturn !!card.battlegroundsNormalDbfId;\r\n\t}\r\n\r\n\tprivate isValidTribe(validTribes: readonly Race[], race: string): boolean {\r\n\t\tconst raceEnum: Race = getRaceEnum(race);\r\n\t\treturn raceEnum === Race.ALL || !validTribes || validTribes.length === 0 || validTribes.includes(raceEnum);\r\n\t}\r\n}\r\n"]}
1
+ {"version":3,"file":"cards-data.js","sourceRoot":"","sources":["../../src/cards/cards-data.ts"],"names":[],"mappings":"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","sourcesContent":["import { AllCardsService, CardIds, isBattlegroundsCard, Race, ReferenceCard } from '@firestone-hs/reference-data';\r\nimport { groupByFunction, pickRandom } from '../services/utils';\r\nimport { getRaceEnum, hasMechanic } from '../utils';\r\n\r\nexport class CardsData {\r\n\t// public shredderSpawns: readonly string[];\r\n\tpublic ghastcoilerSpawns: readonly string[];\r\n\tpublic validDeathrattles: readonly string[];\r\n\tpublic impMamaSpawns: readonly string[];\r\n\tpublic gentleDjinniSpawns: readonly string[];\r\n\tpublic kilrekSpawns: readonly string[];\r\n\tpublic brannEpicEggSpawns: readonly string[];\r\n\t// public sneedsSpawns: readonly string[];\r\n\t// public treasureChestSpawns: readonly string[];\r\n\tpublic pirateSpawns: readonly string[];\r\n\r\n\tpublic auraEnchantments: readonly string[][];\r\n\tpublic auraOrigins: readonly string[];\r\n\tpublic startOfCombats: readonly string[];\r\n\r\n\tprivate minionsForTier: { [key: string]: readonly ReferenceCard[] };\r\n\r\n\tconstructor(private readonly allCards: AllCardsService, init = true) {\r\n\t\tif (init) {\r\n\t\t\tthis.inititialize();\r\n\t\t}\r\n\t}\r\n\r\n\tpublic inititialize(validTribes?: readonly Race[]): void {\r\n\t\tconst pool = this.allCards\r\n\t\t\t.getCards()\r\n\t\t\t.filter((card) => isBattlegroundsCard(card))\r\n\t\t\t.filter((card) => !!card.techLevel)\r\n\t\t\t.filter((card) => card.set !== 'Vanilla');\r\n\t\tthis.minionsForTier = groupByFunction((card: ReferenceCard) => card.techLevel)(pool.filter((card) => !this.isGolden(card)));\r\n\t\tthis.ghastcoilerSpawns = pool\r\n\t\t\t.filter((card) => !this.isGolden(card))\r\n\t\t\t.filter((card) => card.id !== 'BGS_008')\r\n\t\t\t.filter((card) => hasMechanic(card, 'DEATHRATTLE'))\r\n\t\t\t// .filter((card) => REMOVED_CARD_IDS.indexOf(card.id) === -1)\r\n\t\t\t.filter((card) => this.isValidTribe(validTribes, card.race))\r\n\t\t\t.map((card) => card.id);\r\n\t\tthis.validDeathrattles = pool\r\n\t\t\t// .filter((card) => !card.id.startsWith('TB_BaconUps')) // Ignore golden\r\n\t\t\t.filter((card) => hasMechanic(card, 'DEATHRATTLE'))\r\n\t\t\t// .filter((card) => REMOVED_CARD_IDS.indexOf(card.id) === -1)\r\n\t\t\t.filter((card) => this.isValidTribe(validTribes, card.race))\r\n\t\t\t.map((card) => card.id);\r\n\t\tthis.impMamaSpawns = pool\r\n\t\t\t.filter((card) => !this.isGolden(card))\r\n\t\t\t.filter((card) => card.race === 'DEMON')\r\n\t\t\t.filter((card) => card.id !== CardIds.ImpMama)\r\n\t\t\t// .filter((card) => REMOVED_CARD_IDS.indexOf(card.id) === -1)\r\n\t\t\t.map((card) => card.id);\r\n\t\tthis.gentleDjinniSpawns = pool\r\n\t\t\t.filter((card) => !this.isGolden(card))\r\n\t\t\t.filter((card) => card.race === 'ELEMENTAL')\r\n\t\t\t.filter((card) => card.id !== CardIds.GentleDjinni)\r\n\t\t\t// .filter((card) => REMOVED_CARD_IDS.indexOf(card.id) === -1)\r\n\t\t\t.map((card) => card.id);\r\n\t\tthis.kilrekSpawns = pool\r\n\t\t\t.filter((card) => !this.isGolden(card))\r\n\t\t\t.filter((card) => card.race === Race[Race.DEMON])\r\n\t\t\t.filter((card) => card.id !== CardIds.KilrekBattlegrounds1)\r\n\t\t\t// .filter((card) => REMOVED_CARD_IDS.indexOf(card.id) === -1)\r\n\t\t\t.map((card) => card.id);\r\n\t\tthis.brannEpicEggSpawns = pool\r\n\t\t\t.filter((card) => !this.isGolden(card))\r\n\t\t\t.filter((card) => hasMechanic(card, 'BATTLECRY'))\r\n\t\t\t.map((card) => card.id);\r\n\t\tthis.pirateSpawns = pool\r\n\t\t\t.filter((card) => !this.isGolden(card))\r\n\t\t\t.filter((card) => card.race === 'PIRATE')\r\n\t\t\t// .filter((card) => REMOVED_CARD_IDS.indexOf(card.id) === -1)\r\n\t\t\t.map((card) => card.id);\r\n\t\t// Auras are effects that are permanent (unlike deathrattles or \"whenever\" effects)\r\n\t\t// and that stop once the origin entity leaves play (so it doesn't include buffs)\r\n\t\tthis.auraEnchantments = [\r\n\t\t\t[CardIds.Kathranatir2, CardIds.Kathranatir_GraspOfKathranatirEnchantment1],\r\n\t\t\t[CardIds.KathranatirBattlegrounds, CardIds.Kathranatir_GraspOfKathranatirEnchantment2],\r\n\t\t\t[CardIds.MurlocWarleaderLegacy, CardIds.MurlocWarleader_MrgglaarglLegacyEnchantment],\r\n\t\t\t[CardIds.MurlocWarleaderBattlegrounds, CardIds.MurlocWarleader_MrgglaarglEnchantmentBattlegrounds],\r\n\t\t\t[CardIds.SouthseaCaptainLegacy, CardIds.SouthseaCaptain_YarrrLegacyEnchantment],\r\n\t\t\t[CardIds.SouthseaCaptainBattlegrounds, CardIds.SouthseaCaptain_YarrrEnchantmentBattlegrounds],\r\n\t\t\t// TODO find proper enchantment\r\n\t\t\t[CardIds.LadySinestraBattlegrounds1, CardIds.DraconicBlessingEnchantmentBattlegrounds1],\r\n\t\t\t[CardIds.LadySinestraBattlegrounds2, CardIds.DraconicBlessingEnchantmentBattlegrounds2],\r\n\t\t];\r\n\t\tthis.auraOrigins = this.auraEnchantments.map((pair) => pair[0]);\r\n\t\tthis.startOfCombats = [\r\n\t\t\tCardIds.RedWhelp,\r\n\t\t\tCardIds.RedWhelpBattlegrounds,\r\n\t\t\tCardIds.PrizedPromoDrake,\r\n\t\t\tCardIds.PrizedPromoDrakeBattlegrounds,\r\n\t\t\tCardIds.Crabby1,\r\n\t\t\tCardIds.CrabbyBattlegrounds,\r\n\t\t];\r\n\t}\r\n\r\n\tpublic avengeValue(cardId: string): number {\r\n\t\tswitch (cardId) {\r\n\t\t\tcase CardIds.BirdBuddy:\r\n\t\t\tcase CardIds.BirdBuddyBattlegrounds:\r\n\t\t\t\treturn 1;\r\n\t\t\tcase CardIds.FrostwolfLieutenant:\r\n\t\t\tcase CardIds.FrostwolfLieutenantBattlegrounds:\r\n\t\t\tcase CardIds.MechanoTank:\r\n\t\t\tcase CardIds.MechanoTankBattlegrounds:\r\n\t\t\tcase CardIds.PalescaleCrocolisk:\r\n\t\t\tcase CardIds.PalescaleCrocoliskBattlegrounds:\r\n\t\t\tcase CardIds.StormpikeLieutenant:\r\n\t\t\tcase CardIds.StormpikeLieutenantBattlegrounds:\r\n\t\t\t\treturn 2;\r\n\t\t\tcase CardIds.BuddingGreenthumb:\r\n\t\t\tcase CardIds.BuddingGreenthumbBattlegrounds:\r\n\t\t\tcase CardIds.Sisefin:\r\n\t\t\tcase CardIds.SisefinBattlegrounds:\r\n\t\t\t\treturn 3;\r\n\t\t\tcase CardIds.ImpatientDoomsayer:\r\n\t\t\tcase CardIds.ImpatientDoomsayerBattlegrounds:\r\n\t\t\tcase CardIds.WitchwingNestmatron:\r\n\t\t\tcase CardIds.WitchwingNestmatronBattlegrounds:\r\n\t\t\tcase CardIds.TonyTwoTusk:\r\n\t\t\tcase CardIds.TonyTwoTuskBattlegrounds:\r\n\t\t\t\treturn 4;\r\n\t\t}\r\n\t\treturn 0;\r\n\t}\r\n\r\n\tpublic getTavernLevel(cardId: string): number {\r\n\t\treturn this.allCards.getCard(cardId).techLevel;\r\n\t}\r\n\r\n\tpublic getRandomMinionForTavernTier(tavernTier: number): string {\r\n\t\t// Tzvern tier can be undefined for hero-power specific tokens, like the Amalgam, or when\r\n\t\t// for some reason tokens end up in the shop. For now, defaulting to 1 for tavern\r\n\t\t// level seems to work in all cases\r\n\t\treturn pickRandom(this.minionsForTier[tavernTier ?? 1]).id;\r\n\t}\r\n\r\n\tprivate isGolden(card: ReferenceCard): boolean {\r\n\t\treturn !!card.battlegroundsNormalDbfId;\r\n\t}\r\n\r\n\tprivate isValidTribe(validTribes: readonly Race[], race: string): boolean {\r\n\t\tconst raceEnum: Race = getRaceEnum(race);\r\n\t\treturn raceEnum === Race.ALL || !validTribes || validTribes.length === 0 || validTribes.includes(raceEnum);\r\n\t}\r\n}\r\n"]}
@@ -18,11 +18,11 @@ const spectator_1 = require("./simulation/spectator/spectator");
18
18
  const utils_1 = require("./utils");
19
19
  const cards = new reference_data_1.AllCardsService();
20
20
  exports.default = (event) => __awaiter(void 0, void 0, void 0, function* () {
21
- var _a;
21
+ var _a, _b, _c;
22
22
  const battleInput = JSON.parse(event.body);
23
23
  yield cards.initializeCardsDb('121569');
24
24
  const cardsData = new cards_data_1.CardsData(cards, false);
25
- cardsData.inititialize((_a = battleInput.options) === null || _a === void 0 ? void 0 : _a.validTribes);
25
+ cardsData.inititialize((_b = (_a = battleInput.gameState) === null || _a === void 0 ? void 0 : _a.validTribes) !== null && _b !== void 0 ? _b : (_c = battleInput.options) === null || _c === void 0 ? void 0 : _c.validTribes);
26
26
  const simulationResult = exports.simulateBattle(battleInput, cards, cardsData);
27
27
  const response = {
28
28
  statusCode: 200,
@@ -32,7 +32,7 @@ exports.default = (event) => __awaiter(void 0, void 0, void 0, function* () {
32
32
  return response;
33
33
  });
34
34
  const simulateBattle = (battleInput, cards, cardsData) => {
35
- var _a, _b;
35
+ var _a, _b, _c, _d;
36
36
  const start = Date.now();
37
37
  const maxAcceptableDuration = ((_a = battleInput.options) === null || _a === void 0 ? void 0 : _a.maxAcceptableDuration) || 8000;
38
38
  const numberOfSimulations = ((_b = battleInput.options) === null || _b === void 0 ? void 0 : _b.numberOfSimulations) || 5000;
@@ -72,11 +72,11 @@ const simulateBattle = (battleInput, cards, cardsData) => {
72
72
  };
73
73
  const inputStr = JSON.stringify(inputReady);
74
74
  const spectator = new spectator_1.Spectator(battleInput.playerBoard.player.cardId, battleInput.playerBoard.player.heroPowerId, battleInput.opponentBoard.player.cardId, battleInput.opponentBoard.player.heroPowerId);
75
- console.time('simulation');
75
+ !((_c = battleInput.options) === null || _c === void 0 ? void 0 : _c.skipInfoLogs) && console.time('simulation');
76
76
  for (let i = 0; i < numberOfSimulations; i++) {
77
77
  const simulator = new simulator_1.Simulator(cards, cardsData);
78
78
  const input = JSON.parse(inputStr);
79
- const battleResult = simulator.simulateSingleBattle(input.playerBoard.board, input.playerBoard.player, input.opponentBoard.board, input.opponentBoard.player, spectator);
79
+ const battleResult = simulator.simulateSingleBattle(input.playerBoard.board, input.playerBoard.player, input.opponentBoard.board, input.opponentBoard.player, input.gameState, spectator);
80
80
  if (Date.now() - start > maxAcceptableDuration) {
81
81
  console.warn('Stopping simulation after', i, 'iterations and ', Date.now() - start, 'ms', battleResult);
82
82
  break;
@@ -117,7 +117,7 @@ const simulateBattle = (battleInput, cards, cardsData) => {
117
117
  if (simulationResult.averageDamageLost > 0 && simulationResult.averageDamageLost < opponentInfo.player.tavernTier) {
118
118
  console.warn('average damage lost issue', simulationResult, opponentInfo);
119
119
  }
120
- console.timeEnd('simulation');
120
+ !((_d = battleInput.options) === null || _d === void 0 ? void 0 : _d.skipInfoLogs) && console.timeEnd('simulation');
121
121
  spectator.prune();
122
122
  simulationResult.outcomeSamples = spectator.buildOutcomeSamples();
123
123
  return simulationResult;
@@ -144,6 +144,12 @@ exports.validEnchantments = [
144
144
  "BG21_000_Ge",
145
145
  "BG21_HERO_030p",
146
146
  "BG21_HERO_030pe",
147
+ "BG22_HERO_001_Buddy_e1",
148
+ "BG22_HERO_001_Buddy_e2",
149
+ "BG22_HERO_001_Buddy_e4",
150
+ "BG22_HERO_001_Buddy_e3",
151
+ "BG22_HERO_001p_t1e",
152
+ "BG22_HERO_001p_t4_s",
147
153
  ];
148
154
  const cleanEnchantmentsForEntity = (enchantments, entityIds) => {
149
155
  return enchantments.filter((enchant) => entityIds.indexOf(enchant.originEntityId) !== -1 || exports.validEnchantments.indexOf(enchant.cardId) !== -1);
@@ -1 +1 @@
1
- {"version":3,"file":"simulate-bgs-battle.js","sourceRoot":"","sources":["../src/simulate-bgs-battle.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable @typescript-eslint/no-use-before-define */\r\nimport { AllCardsService, CardIds } from '@firestone-hs/reference-data';\r\nimport { BgsBattleInfo } from './bgs-battle-info';\r\nimport { BoardEntity } from './board-entity';\r\nimport { CardsData } from './cards/cards-data';\r\nimport { SimulationResult } from './simulation-result';\r\nimport { removeAuras, setImplicitData } from './simulation/auras';\r\nimport { Simulator } from './simulation/simulator';\r\nimport { Spectator } from './simulation/spectator/spectator';\r\nimport { addImpliedMechanics } from './utils';\r\n\r\nconst cards = new AllCardsService();\r\n\r\n// This example demonstrates a NodeJS 8.10 async handler[1], however of course you could use\r\n// the more traditional callback-style handler.\r\n// [1]: https://aws.amazon.com/blogs/compute/node-js-8-10-runtime-now-available-in-aws-lambda/\r\nexport default async (event): Promise<any> => {\r\n\tconst battleInput: BgsBattleInfo = JSON.parse(event.body);\r\n\tawait cards.initializeCardsDb('121569');\r\n\tconst cardsData = new CardsData(cards, false);\r\n\tcardsData.inititialize(battleInput.options?.validTribes);\r\n\tconst simulationResult = simulateBattle(battleInput, cards, cardsData);\r\n\r\n\tconst response = {\r\n\t\tstatusCode: 200,\r\n\t\tisBase64Encoded: false,\r\n\t\tbody: JSON.stringify(simulationResult),\r\n\t};\r\n\treturn response;\r\n};\r\n\r\nexport const simulateBattle = (battleInput: BgsBattleInfo, cards: AllCardsService, cardsData: CardsData): SimulationResult => {\r\n\tconst start = Date.now();\r\n\r\n\tconst maxAcceptableDuration = battleInput.options?.maxAcceptableDuration || 8000;\r\n\tconst numberOfSimulations = battleInput.options?.numberOfSimulations || 5000;\r\n\r\n\tconst simulationResult: SimulationResult = {\r\n\t\twonLethal: 0,\r\n\t\twon: 0,\r\n\t\ttied: 0,\r\n\t\tlost: 0,\r\n\t\tlostLethal: 0,\r\n\t\tdamageWon: 0,\r\n\t\tdamageLost: 0,\r\n\t\twonLethalPercent: undefined,\r\n\t\twonPercent: undefined,\r\n\t\ttiedPercent: undefined,\r\n\t\tlostPercent: undefined,\r\n\t\tlostLethalPercent: undefined,\r\n\t\taverageDamageWon: undefined,\r\n\t\taverageDamageLost: undefined,\r\n\t};\r\n\r\n\tconst playerInfo = battleInput.playerBoard;\r\n\tconst opponentInfo = battleInput.opponentBoard;\r\n\r\n\tconst playerBoard = playerInfo.board.map((entity) => ({ ...addImpliedMechanics(entity), friendly: true } as BoardEntity));\r\n\tconst opponentBoard = opponentInfo.board.map((entity) => ({ ...addImpliedMechanics(entity), friendly: false } as BoardEntity));\r\n\tremoveAuras(playerBoard, cardsData);\r\n\tremoveAuras(opponentBoard, cardsData);\r\n\tsetImplicitData(playerBoard, cardsData); // Avenge, maxHealth, etc.\r\n\tsetImplicitData(opponentBoard, cardsData); // Avenge, maxHealth, etc.\r\n\r\n\t// We do this so that we can have mutated objects inside the simulation and still\r\n\t// be able to start from a fresh copy for each simulation\r\n\tconst inputReady: BgsBattleInfo = {\r\n\t\tplayerBoard: {\r\n\t\t\tboard: playerBoard,\r\n\t\t\tplayer: playerInfo.player,\r\n\t\t},\r\n\t\topponentBoard: {\r\n\t\t\tboard: opponentBoard,\r\n\t\t\tplayer: opponentInfo.player,\r\n\t\t},\r\n\t} as BgsBattleInfo;\r\n\tconst inputStr = JSON.stringify(inputReady);\r\n\tconst spectator = new Spectator(\r\n\t\tbattleInput.playerBoard.player.cardId,\r\n\t\tbattleInput.playerBoard.player.heroPowerId,\r\n\t\tbattleInput.opponentBoard.player.cardId,\r\n\t\tbattleInput.opponentBoard.player.heroPowerId,\r\n\t);\r\n\tconsole.time('simulation');\r\n\tfor (let i = 0; i < numberOfSimulations; i++) {\r\n\t\t// global.gc();\r\n\t\t// continue;\r\n\t\tconst simulator = new Simulator(cards, cardsData);\r\n\t\tconst input: BgsBattleInfo = JSON.parse(inputStr);\r\n\t\tconst battleResult = simulator.simulateSingleBattle(\r\n\t\t\tinput.playerBoard.board,\r\n\t\t\tinput.playerBoard.player,\r\n\t\t\tinput.opponentBoard.board,\r\n\t\t\tinput.opponentBoard.player,\r\n\t\t\tspectator,\r\n\t\t);\r\n\t\tif (Date.now() - start > maxAcceptableDuration) {\r\n\t\t\t// Can happen in case of inifinite boards, or a bug. Don't hog the user's computer in that case\r\n\t\t\tconsole.warn('Stopping simulation after', i, 'iterations and ', Date.now() - start, 'ms', battleResult);\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\tif (!battleResult) {\r\n\t\t\tcontinue;\r\n\t\t}\r\n\t\tif (battleResult.result === 'won') {\r\n\t\t\tsimulationResult.won++;\r\n\t\t\tsimulationResult.damageWon += battleResult.damageDealt;\r\n\t\t\tif (battleResult.damageDealt >= battleInput.opponentBoard.player.hpLeft) {\r\n\t\t\t\tsimulationResult.wonLethal++;\r\n\t\t\t}\r\n\t\t} else if (battleResult.result === 'lost') {\r\n\t\t\tsimulationResult.lost++;\r\n\t\t\tsimulationResult.damageLost += battleResult.damageDealt;\r\n\t\t\tif (battleInput.playerBoard.player.hpLeft && battleResult.damageDealt >= battleInput.playerBoard.player.hpLeft) {\r\n\t\t\t\tsimulationResult.lostLethal++;\r\n\t\t\t}\r\n\t\t} else if (battleResult.result === 'tied') {\r\n\t\t\tsimulationResult.tied++;\r\n\t\t}\r\n\t\tspectator.commitBattleResult(battleResult.result);\r\n\t}\r\n\tconst totalMatches = simulationResult.won + simulationResult.tied + simulationResult.lost;\r\n\tsimulationResult.wonPercent = checkRounding(\r\n\t\tMath.round((10 * (100 * simulationResult.won)) / totalMatches) / 10,\r\n\t\tsimulationResult.won,\r\n\t\ttotalMatches,\r\n\t);\r\n\tsimulationResult.lostPercent = checkRounding(\r\n\t\tMath.round((10 * (100 * simulationResult.lost)) / totalMatches) / 10,\r\n\t\tsimulationResult.lost,\r\n\t\ttotalMatches,\r\n\t);\r\n\t// simulationResult.tiedPercent = checkRounding(Math.round((10 * (100 * simulationResult.tied)) / totalMatches) / 10, simulationResult.tied, totalMatches);\r\n\tsimulationResult.tiedPercent = checkRounding(\r\n\t\t100 - simulationResult.lostPercent - simulationResult.wonPercent,\r\n\t\tsimulationResult.tied,\r\n\t\ttotalMatches,\r\n\t);\r\n\r\n\tsimulationResult.wonLethalPercent = Math.round((10 * (100 * simulationResult.wonLethal)) / totalMatches) / 10;\r\n\tsimulationResult.lostLethalPercent = Math.round((10 * (100 * simulationResult.lostLethal)) / totalMatches) / 10;\r\n\tsimulationResult.averageDamageWon = simulationResult.won ? simulationResult.damageWon / simulationResult.won : 0;\r\n\tsimulationResult.averageDamageLost = simulationResult.lost ? simulationResult.damageLost / simulationResult.lost : 0;\r\n\tif (simulationResult.averageDamageWon > 0 && simulationResult.averageDamageWon < playerInfo.player.tavernTier) {\r\n\t\tconsole.warn('average damage won issue', simulationResult, playerInfo);\r\n\t}\r\n\tif (simulationResult.averageDamageLost > 0 && simulationResult.averageDamageLost < opponentInfo.player.tavernTier) {\r\n\t\tconsole.warn('average damage lost issue', simulationResult, opponentInfo);\r\n\t}\r\n\tconsole.timeEnd('simulation');\r\n\tspectator.prune();\r\n\tsimulationResult.outcomeSamples = spectator.buildOutcomeSamples();\r\n\t// spectator.reset();\r\n\treturn simulationResult;\r\n};\r\n\r\nconst checkRounding = (roundedValue: number, initialValue: number, totalValue: number): number => {\r\n\tif (roundedValue === 0 && initialValue !== 0) {\r\n\t\treturn 0.01;\r\n\t}\r\n\tif (roundedValue === 100 && initialValue !== totalValue) {\r\n\t\treturn 99.9;\r\n\t}\r\n\treturn roundedValue;\r\n};\r\n\r\nconst cleanEnchantments = (board: readonly BoardEntity[]): readonly BoardEntity[] => {\r\n\tconst entityIds = board.map((entity) => entity.entityId);\r\n\treturn board.map((entity) => ({\r\n\t\t...entity,\r\n\t\tenchantments: cleanEnchantmentsForEntity(entity.enchantments, entityIds),\r\n\t}));\r\n};\r\n\r\nexport const validEnchantments = [\r\n\tCardIds.ReplicatingMenace_ReplicatingMenaceEnchantment,\r\n\tCardIds.ReplicatingMenace_ReplicatingMenaceEnchantmentBattlegrounds,\r\n\tCardIds.LivingSpores_LivingSporesEnchantment,\r\n\tCardIds.Leapfrogger_LeapfrogginEnchantment1,\r\n\tCardIds.Leapfrogger_LeapfrogginEnchantment2,\r\n\tCardIds.Sneed_SneedsReplicator,\r\n\tCardIds.SneedsReplicator_ReplicateEnchantment,\r\n];\r\n\r\nconst cleanEnchantmentsForEntity = (\r\n\tenchantments: { cardId: string; originEntityId?: number }[],\r\n\tentityIds: readonly number[],\r\n): { cardId: string; originEntityId?: number }[] => {\r\n\treturn enchantments.filter(\r\n\t\t(enchant) => entityIds.indexOf(enchant.originEntityId) !== -1 || validEnchantments.indexOf(enchant.cardId as CardIds) !== -1,\r\n\t);\r\n};\r\n"]}
1
+ {"version":3,"file":"simulate-bgs-battle.js","sourceRoot":"","sources":["../src/simulate-bgs-battle.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable @typescript-eslint/no-use-before-define */\r\nimport { AllCardsService, CardIds } from '@firestone-hs/reference-data';\r\nimport { BgsBattleInfo } from './bgs-battle-info';\r\nimport { BoardEntity } from './board-entity';\r\nimport { CardsData } from './cards/cards-data';\r\nimport { SimulationResult } from './simulation-result';\r\nimport { removeAuras, setImplicitData } from './simulation/auras';\r\nimport { Simulator } from './simulation/simulator';\r\nimport { Spectator } from './simulation/spectator/spectator';\r\nimport { addImpliedMechanics } from './utils';\r\n\r\nconst cards = new AllCardsService();\r\n\r\n// This example demonstrates a NodeJS 8.10 async handler[1], however of course you could use\r\n// the more traditional callback-style handler.\r\n// [1]: https://aws.amazon.com/blogs/compute/node-js-8-10-runtime-now-available-in-aws-lambda/\r\nexport default async (event): Promise<any> => {\r\n\tconst battleInput: BgsBattleInfo = JSON.parse(event.body);\r\n\tawait cards.initializeCardsDb('121569');\r\n\tconst cardsData = new CardsData(cards, false);\r\n\tcardsData.inititialize(battleInput.gameState?.validTribes ?? battleInput.options?.validTribes);\r\n\tconst simulationResult = simulateBattle(battleInput, cards, cardsData);\r\n\r\n\tconst response = {\r\n\t\tstatusCode: 200,\r\n\t\tisBase64Encoded: false,\r\n\t\tbody: JSON.stringify(simulationResult),\r\n\t};\r\n\treturn response;\r\n};\r\n\r\nexport const simulateBattle = (battleInput: BgsBattleInfo, cards: AllCardsService, cardsData: CardsData): SimulationResult => {\r\n\tconst start = Date.now();\r\n\r\n\tconst maxAcceptableDuration = battleInput.options?.maxAcceptableDuration || 8000;\r\n\tconst numberOfSimulations = battleInput.options?.numberOfSimulations || 5000;\r\n\r\n\tconst simulationResult: SimulationResult = {\r\n\t\twonLethal: 0,\r\n\t\twon: 0,\r\n\t\ttied: 0,\r\n\t\tlost: 0,\r\n\t\tlostLethal: 0,\r\n\t\tdamageWon: 0,\r\n\t\tdamageLost: 0,\r\n\t\twonLethalPercent: undefined,\r\n\t\twonPercent: undefined,\r\n\t\ttiedPercent: undefined,\r\n\t\tlostPercent: undefined,\r\n\t\tlostLethalPercent: undefined,\r\n\t\taverageDamageWon: undefined,\r\n\t\taverageDamageLost: undefined,\r\n\t};\r\n\r\n\tconst playerInfo = battleInput.playerBoard;\r\n\tconst opponentInfo = battleInput.opponentBoard;\r\n\r\n\tconst playerBoard = playerInfo.board.map((entity) => ({ ...addImpliedMechanics(entity), friendly: true } as BoardEntity));\r\n\tconst opponentBoard = opponentInfo.board.map((entity) => ({ ...addImpliedMechanics(entity), friendly: false } as BoardEntity));\r\n\tremoveAuras(playerBoard, cardsData);\r\n\tremoveAuras(opponentBoard, cardsData);\r\n\tsetImplicitData(playerBoard, cardsData); // Avenge, maxHealth, etc.\r\n\tsetImplicitData(opponentBoard, cardsData); // Avenge, maxHealth, etc.\r\n\r\n\t// We do this so that we can have mutated objects inside the simulation and still\r\n\t// be able to start from a fresh copy for each simulation\r\n\tconst inputReady: BgsBattleInfo = {\r\n\t\tplayerBoard: {\r\n\t\t\tboard: playerBoard,\r\n\t\t\tplayer: playerInfo.player,\r\n\t\t},\r\n\t\topponentBoard: {\r\n\t\t\tboard: opponentBoard,\r\n\t\t\tplayer: opponentInfo.player,\r\n\t\t},\r\n\t} as BgsBattleInfo;\r\n\tconst inputStr = JSON.stringify(inputReady);\r\n\tconst spectator = new Spectator(\r\n\t\tbattleInput.playerBoard.player.cardId,\r\n\t\tbattleInput.playerBoard.player.heroPowerId,\r\n\t\tbattleInput.opponentBoard.player.cardId,\r\n\t\tbattleInput.opponentBoard.player.heroPowerId,\r\n\t);\r\n\t!battleInput.options?.skipInfoLogs && console.time('simulation');\r\n\tfor (let i = 0; i < numberOfSimulations; i++) {\r\n\t\t// global.gc();\r\n\t\t// continue;\r\n\t\tconst simulator = new Simulator(cards, cardsData);\r\n\t\tconst input: BgsBattleInfo = JSON.parse(inputStr);\r\n\t\tconst battleResult = simulator.simulateSingleBattle(\r\n\t\t\tinput.playerBoard.board,\r\n\t\t\tinput.playerBoard.player,\r\n\t\t\tinput.opponentBoard.board,\r\n\t\t\tinput.opponentBoard.player,\r\n\t\t\tinput.gameState,\r\n\t\t\tspectator,\r\n\t\t);\r\n\t\tif (Date.now() - start > maxAcceptableDuration) {\r\n\t\t\t// Can happen in case of inifinite boards, or a bug. Don't hog the user's computer in that case\r\n\t\t\tconsole.warn('Stopping simulation after', i, 'iterations and ', Date.now() - start, 'ms', battleResult);\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\tif (!battleResult) {\r\n\t\t\tcontinue;\r\n\t\t}\r\n\t\tif (battleResult.result === 'won') {\r\n\t\t\tsimulationResult.won++;\r\n\t\t\tsimulationResult.damageWon += battleResult.damageDealt;\r\n\t\t\tif (battleResult.damageDealt >= battleInput.opponentBoard.player.hpLeft) {\r\n\t\t\t\tsimulationResult.wonLethal++;\r\n\t\t\t}\r\n\t\t} else if (battleResult.result === 'lost') {\r\n\t\t\tsimulationResult.lost++;\r\n\t\t\tsimulationResult.damageLost += battleResult.damageDealt;\r\n\t\t\tif (battleInput.playerBoard.player.hpLeft && battleResult.damageDealt >= battleInput.playerBoard.player.hpLeft) {\r\n\t\t\t\tsimulationResult.lostLethal++;\r\n\t\t\t}\r\n\t\t} else if (battleResult.result === 'tied') {\r\n\t\t\tsimulationResult.tied++;\r\n\t\t}\r\n\t\tspectator.commitBattleResult(battleResult.result);\r\n\t}\r\n\tconst totalMatches = simulationResult.won + simulationResult.tied + simulationResult.lost;\r\n\tsimulationResult.wonPercent = checkRounding(\r\n\t\tMath.round((10 * (100 * simulationResult.won)) / totalMatches) / 10,\r\n\t\tsimulationResult.won,\r\n\t\ttotalMatches,\r\n\t);\r\n\tsimulationResult.lostPercent = checkRounding(\r\n\t\tMath.round((10 * (100 * simulationResult.lost)) / totalMatches) / 10,\r\n\t\tsimulationResult.lost,\r\n\t\ttotalMatches,\r\n\t);\r\n\t// simulationResult.tiedPercent = checkRounding(Math.round((10 * (100 * simulationResult.tied)) / totalMatches) / 10, simulationResult.tied, totalMatches);\r\n\tsimulationResult.tiedPercent = checkRounding(\r\n\t\t100 - simulationResult.lostPercent - simulationResult.wonPercent,\r\n\t\tsimulationResult.tied,\r\n\t\ttotalMatches,\r\n\t);\r\n\r\n\tsimulationResult.wonLethalPercent = Math.round((10 * (100 * simulationResult.wonLethal)) / totalMatches) / 10;\r\n\tsimulationResult.lostLethalPercent = Math.round((10 * (100 * simulationResult.lostLethal)) / totalMatches) / 10;\r\n\tsimulationResult.averageDamageWon = simulationResult.won ? simulationResult.damageWon / simulationResult.won : 0;\r\n\tsimulationResult.averageDamageLost = simulationResult.lost ? simulationResult.damageLost / simulationResult.lost : 0;\r\n\tif (simulationResult.averageDamageWon > 0 && simulationResult.averageDamageWon < playerInfo.player.tavernTier) {\r\n\t\tconsole.warn('average damage won issue', simulationResult, playerInfo);\r\n\t}\r\n\tif (simulationResult.averageDamageLost > 0 && simulationResult.averageDamageLost < opponentInfo.player.tavernTier) {\r\n\t\tconsole.warn('average damage lost issue', simulationResult, opponentInfo);\r\n\t}\r\n\t!battleInput.options?.skipInfoLogs && console.timeEnd('simulation');\r\n\tspectator.prune();\r\n\tsimulationResult.outcomeSamples = spectator.buildOutcomeSamples();\r\n\t// spectator.reset();\r\n\treturn simulationResult;\r\n};\r\n\r\nconst checkRounding = (roundedValue: number, initialValue: number, totalValue: number): number => {\r\n\tif (roundedValue === 0 && initialValue !== 0) {\r\n\t\treturn 0.01;\r\n\t}\r\n\tif (roundedValue === 100 && initialValue !== totalValue) {\r\n\t\treturn 99.9;\r\n\t}\r\n\treturn roundedValue;\r\n};\r\n\r\nconst cleanEnchantments = (board: readonly BoardEntity[]): readonly BoardEntity[] => {\r\n\tconst entityIds = board.map((entity) => entity.entityId);\r\n\treturn board.map((entity) => ({\r\n\t\t...entity,\r\n\t\tenchantments: cleanEnchantmentsForEntity(entity.enchantments, entityIds),\r\n\t}));\r\n};\r\n\r\nexport const validEnchantments = [\r\n\tCardIds.ReplicatingMenace_ReplicatingMenaceEnchantment,\r\n\tCardIds.ReplicatingMenace_ReplicatingMenaceEnchantmentBattlegrounds,\r\n\tCardIds.LivingSpores_LivingSporesEnchantment,\r\n\tCardIds.Leapfrogger_LeapfrogginEnchantment1,\r\n\tCardIds.Leapfrogger_LeapfrogginEnchantment2,\r\n\tCardIds.Sneed_SneedsReplicator,\r\n\tCardIds.SneedsReplicator_ReplicateEnchantment,\r\n\tCardIds.EarthRecollectionEnchantment, // Spirit Raptor\r\n\tCardIds.FireRecollectionEnchantment,\r\n\tCardIds.LightningRecollectionEnchantment,\r\n\tCardIds.WaterRecollectionEnchantment,\r\n\tCardIds.EarthInvocation_ElementEarthEnchantment, // Summon a 1/1\r\n\t// CardIds.FireInvocation_ElementFireEnchantment, // Attack is doubled, probably no use to keep it\r\n\t// CardIds.WaterInvocation_ElementWaterEnchantment, // +3 health and taunt, same\r\n\tCardIds.LightningInvocation, // Deal 1 damage to 5 enemy minions\r\n];\r\n\r\nconst cleanEnchantmentsForEntity = (\r\n\tenchantments: { cardId: string; originEntityId?: number }[],\r\n\tentityIds: readonly number[],\r\n): { cardId: string; originEntityId?: number }[] => {\r\n\treturn enchantments.filter(\r\n\t\t(enchant) => entityIds.indexOf(enchant.originEntityId) !== -1 || validEnchantments.indexOf(enchant.cardId as CardIds) !== -1,\r\n\t);\r\n};\r\n"]}
@@ -7,9 +7,9 @@ import { Spectator } from './spectator/spectator';
7
7
  export declare const simulateAttack: (attackingBoard: BoardEntity[], attackingBoardHero: BgsPlayerEntity, defendingBoard: BoardEntity[], defendingBoardHero: BgsPlayerEntity, lastAttackerEntityId: number, allCards: AllCardsService, spawns: CardsData, sharedState: SharedState, spectator: Spectator, forceAttackingEntityIndex?: number) => number;
8
8
  export declare const getNeighbours: (board: BoardEntity[], entity: BoardEntity, deadEntityIndex?: number) => readonly BoardEntity[];
9
9
  export declare const dealDamageToRandomEnemy: (boardToBeDamaged: BoardEntity[], boardToBeDamagedHero: BgsPlayerEntity, damageSource: BoardEntity, damage: number, boardWithAttackOrigin: BoardEntity[], boardWithAttackOriginHero: BgsPlayerEntity, allCards: AllCardsService, cardsData: CardsData, sharedState: SharedState, spectator: Spectator) => void;
10
- export declare const dealDamageToEnemy: (defendingEntity: BoardEntity, defendingBoard: BoardEntity[], defendingBoardHero: BgsPlayerEntity, damageSource: BoardEntity, damage: number, boardWithAttackOrigin: BoardEntity[], boardWithAttackOriginHero: BgsPlayerEntity, allCards: AllCardsService, cardsData: CardsData, sharedState: SharedState, spectator: Spectator) => void;
10
+ export declare const dealDamageToEnemy: (defendingEntity: BoardEntity, defendingBoard: BoardEntity[], defendingBoardHero: BgsPlayerEntity, damageSource: BoardEntity, damage: number, boardWithAttackOrigin: BoardEntity[], boardWithAttackOriginHero: BgsPlayerEntity, allCards: AllCardsService, cardsData: CardsData, sharedState: SharedState, spectator: Spectator) => number;
11
11
  export declare const getDefendingEntity: (defendingBoard: BoardEntity[], attackingEntity: BoardEntity, ignoreTaunts?: boolean) => BoardEntity;
12
- export declare const bumpEntities: (entity: BoardEntity, bumpInto: BoardEntity, entityBoard: BoardEntity[], entityBoardHero: BgsPlayerEntity, otherBoard: BoardEntity[], otherHero: BgsPlayerEntity, allCards: AllCardsService, cardsData: CardsData, sharedState: SharedState, spectator: Spectator) => void;
12
+ export declare const bumpEntities: (entity: BoardEntity, bumpInto: BoardEntity, entityBoard: BoardEntity[], entityBoardHero: BgsPlayerEntity, otherBoard: BoardEntity[], otherHero: BgsPlayerEntity, allCards: AllCardsService, cardsData: CardsData, sharedState: SharedState, spectator: Spectator) => number;
13
13
  export declare const processMinionDeath: (board1: BoardEntity[], board1Hero: BgsPlayerEntity, board2: BoardEntity[], board2Hero: BgsPlayerEntity, allCards: AllCardsService, cardsData: CardsData, sharedState: SharedState, spectator: Spectator) => void;
14
14
  export declare const applyOnAttackBuffs: (attacker: BoardEntity, attackingBoard: BoardEntity[], allCards: AllCardsService, spectator: Spectator) => void;
15
15
  export declare const applyOnBeingAttackedBuffs: (attackedEntity: BoardEntity, defendingBoard: BoardEntity[], allCards: AllCardsService, spectator: Spectator) => void;
@@ -55,7 +55,7 @@ const simulateAttack = (attackingBoard, attackingBoardHero, defendingBoard, defe
55
55
  exports.simulateAttack = simulateAttack;
56
56
  const applyAfterAttackEffects = (attackingEntity, attackingBoard, attackingBoardHero, allCards, spectator) => {
57
57
  if (attackingEntity.cardId === "BG20_104" || attackingEntity.cardId === "BG20_104_G") {
58
- deathrattle_effects_1.addCardsInHand(attackingBoardHero, 1, attackingBoard, allCards, spectator);
58
+ deathrattle_effects_1.addCardsInHand(attackingBoardHero, 1, attackingBoard, allCards, spectator, "BG20_GEM");
59
59
  }
60
60
  };
61
61
  const performAttack = (attackingEntity, defendingEntity, attackingBoard, attackingBoardHero, defendingBoard, defendingBoardHero, allCards, spawns, sharedState, spectator) => {
@@ -71,7 +71,9 @@ const performAttack = (attackingEntity, defendingEntity, attackingBoard, attacki
71
71
  const defenderAliveBeforeAttack = defendingEntity.health > 0 && !defendingEntity.definitelyDead;
72
72
  if (defenderAliveBeforeAttack) {
73
73
  exports.bumpEntities(attackingEntity, defendingEntity, attackingBoard, attackingBoardHero, defendingBoard, defendingBoardHero, allCards, spawns, sharedState, spectator);
74
- exports.bumpEntities(defendingEntity, attackingEntity, defendingBoard, defendingBoardHero, attackingBoard, attackingBoardHero, allCards, spawns, sharedState, spectator);
74
+ if (attackingEntity.immuneWhenAttackCharges <= 0) {
75
+ exports.bumpEntities(defendingEntity, attackingEntity, defendingBoard, defendingBoardHero, attackingBoard, attackingBoardHero, allCards, spawns, sharedState, spectator);
76
+ }
75
77
  }
76
78
  if (attackingEntity.cleave) {
77
79
  const defenderNeighbours = exports.getNeighbours(defendingBoard, defendingEntity);
@@ -89,6 +91,7 @@ const performAttack = (attackingEntity, defendingEntity, attackingBoard, attacki
89
91
  }
90
92
  attackingEntity.attackImmediately = false;
91
93
  exports.processMinionDeath(attackingBoard, attackingBoardHero, defendingBoard, defendingBoardHero, allCards, spawns, sharedState, spectator);
94
+ attackingEntity.immuneWhenAttackCharges = Math.max(0, attackingEntity.immuneWhenAttackCharges - 1);
92
95
  };
93
96
  const triggerRandomDeathrattle = (sourceEntity, attackingBoard, attackingBoardHero, defendingBoard, defendingBoardHero, allCards, spawns, sharedState, spectator, excludeSource = false) => {
94
97
  const validDeathrattles = attackingBoard
@@ -180,12 +183,13 @@ const dealDamageToRandomEnemy = (boardToBeDamaged, boardToBeDamagedHero, damageS
180
183
  exports.dealDamageToRandomEnemy = dealDamageToRandomEnemy;
181
184
  const dealDamageToEnemy = (defendingEntity, defendingBoard, defendingBoardHero, damageSource, damage, boardWithAttackOrigin, boardWithAttackOriginHero, allCards, cardsData, sharedState, spectator) => {
182
185
  if (!defendingEntity) {
183
- return;
186
+ return 0;
184
187
  }
185
188
  const fakeAttacker = Object.assign(Object.assign({}, (damageSource || {})), { entityId: -1, attack: damage, attacking: true });
186
- exports.bumpEntities(defendingEntity, fakeAttacker, defendingBoard, defendingBoardHero, boardWithAttackOrigin, boardWithAttackOriginHero, allCards, cardsData, sharedState, spectator);
189
+ const actualDamageDone = exports.bumpEntities(defendingEntity, fakeAttacker, defendingBoard, defendingBoardHero, boardWithAttackOrigin, boardWithAttackOriginHero, allCards, cardsData, sharedState, spectator);
187
190
  const defendingEntityIndex = defendingBoard.map((entity) => entity.entityId).indexOf(defendingEntity.entityId);
188
191
  defendingBoard[defendingEntityIndex] = defendingEntity;
192
+ return actualDamageDone;
189
193
  };
190
194
  exports.dealDamageToEnemy = dealDamageToEnemy;
191
195
  const getDefendingEntity = (defendingBoard, attackingEntity, ignoreTaunts = false) => {
@@ -213,7 +217,7 @@ const getDefendingEntity = (defendingBoard, attackingEntity, ignoreTaunts = fals
213
217
  exports.getDefendingEntity = getDefendingEntity;
214
218
  const bumpEntities = (entity, bumpInto, entityBoard, entityBoardHero, otherBoard, otherHero, allCards, cardsData, sharedState, spectator) => {
215
219
  if (bumpInto.attack === 0) {
216
- return;
220
+ return 0;
217
221
  }
218
222
  if (entity.divineShield) {
219
223
  for (let i = 0; i < entityBoard.length; i++) {
@@ -229,13 +233,13 @@ const bumpEntities = (entity, bumpInto, entityBoard, entityBoardHero, otherBoard
229
233
  }
230
234
  else if (entityBoard[i].cardId === "BGS_067") {
231
235
  utils_2.modifyAttack(entityBoard[i], 2, entityBoard, allCards);
232
- utils_2.modifyHealth(entityBoard[i], 2);
236
+ utils_2.modifyHealth(entityBoard[i], 2, entityBoard, allCards);
233
237
  utils_2.afterStatsUpdate(entityBoard[i], entityBoard, allCards);
234
238
  spectator.registerPowerTarget(entityBoard[i], entityBoard[i], entityBoard);
235
239
  }
236
240
  else if (entityBoard[i].cardId === "TB_BaconUps_117") {
237
241
  utils_2.modifyAttack(entityBoard[i], 4, entityBoard, allCards);
238
- utils_2.modifyHealth(entityBoard[i], 4);
242
+ utils_2.modifyHealth(entityBoard[i], 4, entityBoard, allCards);
239
243
  utils_2.afterStatsUpdate(entityBoard[i], entityBoard, allCards);
240
244
  spectator.registerPowerTarget(entityBoard[i], entityBoard[i], entityBoard);
241
245
  }
@@ -244,10 +248,10 @@ const bumpEntities = (entity, bumpInto, entityBoard, entityBoardHero, otherBoard
244
248
  entityBoard[i].divineShield = true;
245
249
  }
246
250
  else if (entityBoard[i].cardId === "BG21_037") {
247
- deathrattle_effects_1.addCardsInHand(entityBoardHero, 1, entityBoard, allCards, spectator);
251
+ deathrattle_effects_1.addCardsInHand(entityBoardHero, 1, entityBoard, allCards, spectator, "BG20_GEM");
248
252
  }
249
253
  else if (entityBoard[i].cardId === "BG21_037_G") {
250
- deathrattle_effects_1.addCardsInHand(entityBoardHero, 2, entityBoard, allCards, spectator);
254
+ deathrattle_effects_1.addCardsInHand(entityBoardHero, 2, entityBoard, allCards, spectator, "BG20_GEM");
251
255
  }
252
256
  if (entityBoard[i].entityId === entity.entityId) {
253
257
  entity.divineShield = false;
@@ -257,15 +261,15 @@ const bumpEntities = (entity, bumpInto, entityBoard, entityBoardHero, otherBoard
257
261
  const greaseBotBattlegrounds = entityBoard.filter((entity) => entity.cardId === "BG21_024_G");
258
262
  greaseBots.forEach((bot) => {
259
263
  utils_2.modifyAttack(entity, 1, entityBoard, allCards);
260
- utils_2.modifyHealth(entity, 1);
264
+ utils_2.modifyHealth(entity, 1, entityBoard, allCards);
261
265
  spectator.registerPowerTarget(bot, entity, entityBoard);
262
266
  });
263
267
  greaseBotBattlegrounds.forEach((bot) => {
264
268
  utils_2.modifyAttack(entity, 2, entityBoard, allCards);
265
- utils_2.modifyHealth(entity, 2);
269
+ utils_2.modifyHealth(entity, 2, entityBoard, allCards);
266
270
  spectator.registerPowerTarget(bot, entity, entityBoard);
267
271
  });
268
- return;
272
+ return 0;
269
273
  }
270
274
  entity.health = entity.health - bumpInto.attack;
271
275
  if (bumpInto.poisonous) {
@@ -284,7 +288,7 @@ const bumpEntities = (entity, bumpInto, entityBoard, entityBoardHero, otherBoard
284
288
  spectator.registerMinionsSpawn(entity, entityBoard, entitySpawns);
285
289
  spawn_effect_1.handleSpawnEffects(entityBoard, entitySpawns, allCards, spectator);
286
290
  }
287
- return;
291
+ return bumpInto.attack;
288
292
  };
289
293
  exports.bumpEntities = bumpEntities;
290
294
  const getWheneverEntitySpawns = (entity, entityBoard, entityBoardHero, otherBoard, otherHero, allCards, cardsData, sharedState, spectator) => {
@@ -343,6 +347,12 @@ const processMinionDeath = (board1, board1Hero, board2, board2Hero, allCards, ca
343
347
  board2
344
348
  .filter((entity) => entity.cardId === "TB_BaconShop_HP_105t" || entity.cardId === "TB_BaconUps_307")
345
349
  .forEach((entity) => deathrattle_effects_1.rememberDeathrattles(entity, deadEntities2, cardsData));
350
+ board1
351
+ .filter((entity) => entity.cardId === "BG20_HERO_282_Buddy" || entity.cardId === "BG20_HERO_282_Buddy_G")
352
+ .forEach((entity) => deathrattle_effects_1.applyMonstrosity(entity, deadEntities1, board1, allCards));
353
+ board2
354
+ .filter((entity) => entity.cardId === "BG20_HERO_282_Buddy" || entity.cardId === "BG20_HERO_282_Buddy_G")
355
+ .forEach((entity) => deathrattle_effects_1.applyMonstrosity(entity, deadEntities2, board2, allCards));
346
356
  };
347
357
  exports.processMinionDeath = processMinionDeath;
348
358
  const handleDeathrattlesForFirstBoard = (firstBoard, firstBoardHero, otherBoard, otherBoardHero, deadMinionIndexes, deadEntities, allCards, cardsData, sharedState, spectator) => {
@@ -385,12 +395,12 @@ const applyOnAttackBuffs = (attacker, attackingBoard, allCards, spectator) => {
385
395
  .filter((e) => e.entityId !== attacker.entityId);
386
396
  ripsnarls.forEach((captain) => {
387
397
  utils_2.modifyAttack(attacker, 2, attackingBoard, allCards);
388
- utils_2.modifyHealth(attacker, 2);
398
+ utils_2.modifyHealth(attacker, 2, attackingBoard, allCards);
389
399
  spectator.registerPowerTarget(captain, attacker, attackingBoard);
390
400
  });
391
401
  ripsnarlsTB.forEach((captain) => {
392
402
  utils_2.modifyAttack(attacker, 4, attackingBoard, allCards);
393
- utils_2.modifyHealth(attacker, 4);
403
+ utils_2.modifyHealth(attacker, 4, attackingBoard, allCards);
394
404
  spectator.registerPowerTarget(captain, attacker, attackingBoard);
395
405
  });
396
406
  }
@@ -400,14 +410,14 @@ const applyOnAttackBuffs = (attacker, attackingBoard, allCards, spectator) => {
400
410
  elizas.forEach((eliza) => {
401
411
  attackingBoard.forEach((entity) => {
402
412
  utils_2.modifyAttack(entity, 2, attackingBoard, allCards);
403
- utils_2.modifyHealth(entity, 1);
413
+ utils_2.modifyHealth(entity, 1, attackingBoard, allCards);
404
414
  spectator.registerPowerTarget(eliza, entity, attackingBoard);
405
415
  });
406
416
  });
407
417
  elizasTB.forEach((eliza) => {
408
418
  attackingBoard.forEach((entity) => {
409
419
  utils_2.modifyAttack(entity, 4, attackingBoard, allCards);
410
- utils_2.modifyHealth(entity, 2);
420
+ utils_2.modifyHealth(entity, 2, attackingBoard, allCards);
411
421
  spectator.registerPowerTarget(eliza, entity, attackingBoard);
412
422
  });
413
423
  });
@@ -420,12 +430,12 @@ const applyOnBeingAttackedBuffs = (attackedEntity, defendingBoard, allCards, spe
420
430
  const goldenChampions = defendingBoard.filter((entity) => entity.cardId === "TB_BaconUps_301");
421
431
  champions.forEach((entity) => {
422
432
  utils_2.modifyAttack(entity, 1, defendingBoard, allCards);
423
- utils_2.modifyHealth(entity, 1);
433
+ utils_2.modifyHealth(entity, 1, defendingBoard, allCards);
424
434
  spectator.registerPowerTarget(entity, entity, defendingBoard);
425
435
  });
426
436
  goldenChampions.forEach((entity) => {
427
437
  utils_2.modifyAttack(entity, 2, defendingBoard, allCards);
428
- utils_2.modifyHealth(entity, 2);
438
+ utils_2.modifyHealth(entity, 2, defendingBoard, allCards);
429
439
  spectator.registerPowerTarget(entity, entity, defendingBoard);
430
440
  });
431
441
  const arms = defendingBoard.filter((entity) => entity.cardId === "BGS_110");
@@ -443,7 +453,7 @@ const applyOnBeingAttackedBuffs = (attackedEntity, defendingBoard, allCards, spe
443
453
  const neighbours = exports.getNeighbours(defendingBoard, attackedEntity);
444
454
  neighbours.forEach((entity) => {
445
455
  utils_2.modifyAttack(entity, 1, defendingBoard, allCards);
446
- utils_2.modifyHealth(entity, 1);
456
+ utils_2.modifyHealth(entity, 1, defendingBoard, allCards);
447
457
  spectator.registerPowerTarget(attackedEntity, entity, defendingBoard);
448
458
  });
449
459
  }
@@ -451,7 +461,7 @@ const applyOnBeingAttackedBuffs = (attackedEntity, defendingBoard, allCards, spe
451
461
  const neighbours = exports.getNeighbours(defendingBoard, attackedEntity);
452
462
  neighbours.forEach((entity) => {
453
463
  utils_2.modifyAttack(entity, 2, defendingBoard, allCards);
454
- utils_2.modifyHealth(entity, 2);
464
+ utils_2.modifyHealth(entity, 2, defendingBoard, allCards);
455
465
  spectator.registerPowerTarget(attackedEntity, entity, defendingBoard);
456
466
  });
457
467
  }
@@ -478,13 +488,13 @@ const handleKillEffects = (boardWithKilledMinion, killerBoard, deadEntity, allCa
478
488
  for (const entity of killerBoard) {
479
489
  if (entity.cardId === "BGS_035") {
480
490
  utils_2.modifyAttack(entity, 2, killerBoard, allCards);
481
- utils_2.modifyHealth(entity, 2);
491
+ utils_2.modifyHealth(entity, 2, killerBoard, allCards);
482
492
  utils_2.afterStatsUpdate(entity, killerBoard, allCards);
483
493
  spectator.registerPowerTarget(entity, entity, killerBoard);
484
494
  }
485
495
  else if (entity.cardId === "TB_BaconUps_105") {
486
496
  utils_2.modifyAttack(entity, 4, killerBoard, allCards);
487
- utils_2.modifyHealth(entity, 4);
497
+ utils_2.modifyHealth(entity, 4, killerBoard, allCards);
488
498
  utils_2.afterStatsUpdate(entity, killerBoard, allCards);
489
499
  spectator.registerPowerTarget(entity, entity, killerBoard);
490
500
  }
@@ -517,8 +527,12 @@ const buildBoardAfterDeathrattleSpawns = (boardWithKilledMinion, boardWithKilled
517
527
  }
518
528
  };
519
529
  const buildBoardAfterRebornSpawns = (boardWithKilledMinion, boardWithKilledMinionHero, deadEntity, deadMinionIndex, opponentBoard, opponentBoardHero, allCards, cardsData, sharedState, spectator) => {
530
+ const otherEntityCardIds = boardWithKilledMinion.filter((e) => e.entityId !== deadEntity.entityId).map((e) => e.cardId);
531
+ const numberOfReborns = 1 +
532
+ 1 * otherEntityCardIds.filter((cardId) => cardId === "TB_BaconShop_HERO_22_Buddy").length +
533
+ 2 * otherEntityCardIds.filter((cardId) => cardId === "TB_BaconShop_HERO_22_Buddy_G").length;
520
534
  const entitiesFromReborn = deadEntity.reborn && deadMinionIndex >= 0
521
- ? deathrattle_spawns_1.spawnEntities(deadEntity.cardId, 1, boardWithKilledMinion, boardWithKilledMinionHero, opponentBoard, opponentBoardHero, allCards, cardsData, sharedState, spectator, deadEntity.friendly, false, true)
535
+ ? deathrattle_spawns_1.spawnEntities(deadEntity.cardId, numberOfReborns, boardWithKilledMinion, boardWithKilledMinionHero, opponentBoard, opponentBoardHero, allCards, cardsData, sharedState, spectator, deadEntity.friendly, false, true)
522
536
  : [];
523
537
  performEntitySpawns(entitiesFromReborn, boardWithKilledMinion, boardWithKilledMinionHero, deadEntity, deadMinionIndex, opponentBoard, opponentBoardHero, allCards, cardsData, sharedState, spectator);
524
538
  };