valtech-components 4.0.611 → 4.0.612

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.
@@ -59,7 +59,7 @@ import fixWebmDuration from 'fix-webm-duration';
59
59
  * Current version of valtech-components.
60
60
  * This is automatically updated during the publish process.
61
61
  */
62
- const VERSION = '4.0.611';
62
+ const VERSION = '4.0.612';
63
63
 
64
64
  // Control de estado de refresco (singleton a nivel de módulo)
65
65
  let isRefreshing = false;
@@ -74959,14 +74959,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
74959
74959
 
74960
74960
  const GENESIS_CATALOG_URL = 'assets/games/christianity/data/01-genesis.cards.json';
74961
74961
  const GENESIS_ICON_URL = 'assets/games/christianity/icons/1-icon.png';
74962
- const TUTORIAL_CHARACTER_LIMIT = 12;
74963
- const TUTORIAL_OFFERING_LIMIT = 6;
74964
- const TUTORIAL_OPENING_HAND_SIZE = 5;
74965
- const TUTORIAL_LEADER_CHOICE_COUNT = 2;
74966
- const TUTORIAL_LEADER_CHOICE_SECONDS = 15;
74967
- const TUTORIAL_SEED = 20260728;
74968
74962
  const SPLASH_MIN_DURATION_MS = 720;
74969
- const CHRISTIANITY_PREVIEW_NEUTRAL_TILT = { rotateX: 0, rotateY: 0, shiftX: 0, shiftY: 0 };
74970
74963
  const STARTER_CARDS = {
74971
74964
  o_simple: {
74972
74965
  id: 'o_simple',
@@ -75173,6 +75166,244 @@ const STARTING_GAME = {
75173
75166
  },
75174
75167
  log: ['Partida iniciada.'],
75175
75168
  };
75169
+ function formatScripture(bible) {
75170
+ if (!bible?.book || !bible.reference)
75171
+ return undefined;
75172
+ return `${bible.book} ${bible.reference}${bible.version ? ` ${bible.version}` : ''}`;
75173
+ }
75174
+ function normalizeCatalogCard(card, catalog) {
75175
+ const editionIcon = card.editionIcon ?? catalog.edition?.icon ?? GENESIS_ICON_URL;
75176
+ const verseText = card.verseText ?? card.bible?.text;
75177
+ const scripture = card.scripture ?? formatScripture(card.bible);
75178
+ const offeringValue = card.type === 'offering'
75179
+ ? (card.offeringValue ?? card.life ?? card.cost ?? 1)
75180
+ : card.offeringValue;
75181
+ const life = card.type === 'leader'
75182
+ ? (card.leaderLife ?? card.life ?? 10)
75183
+ : card.type === 'offering'
75184
+ ? offeringValue
75185
+ : (card.life ?? 0);
75186
+ return {
75187
+ ...card,
75188
+ cost: card.type === 'offering' ? 0 : (card.cost ?? 0),
75189
+ offeringValue,
75190
+ life,
75191
+ leaderLife: card.type === 'leader' ? (card.leaderLife ?? life) : card.leaderLife,
75192
+ attack: card.type === 'character' ? (card.attack ?? 0) : card.attack,
75193
+ abilityName: card.abilityName ?? card.abilities?.[0]?.name,
75194
+ abilityText: card.abilityText ?? card.abilities?.[0]?.text,
75195
+ verseText,
75196
+ scripture,
75197
+ edition: card.edition ?? catalog.edition?.id ?? 'GEN',
75198
+ editionNumber: card.editionNumber ?? catalog.edition?.number,
75199
+ editionIcon,
75200
+ imageUrl: card.imageUrl ?? card.art?.imageUrl,
75201
+ author: card.author ?? card.art?.artistCredit,
75202
+ year: card.year ?? '2026',
75203
+ frontTemplate: card.frontTemplate ?? 'genesis-classic',
75204
+ };
75205
+ }
75206
+
75207
+ const TUTORIAL_CHARACTER_LIMIT = 12;
75208
+ const TUTORIAL_OFFERING_LIMIT = 6;
75209
+ const TUTORIAL_OPENING_HAND_SIZE = 5;
75210
+ const TUTORIAL_LEADER_CHOICE_COUNT = 2;
75211
+ const TUTORIAL_LEADER_CHOICE_SECONDS = 15;
75212
+ const TUTORIAL_SEED = 20260728;
75213
+ function isClassicGalleryCard(card) {
75214
+ return (card.frontTemplate ?? 'genesis-classic') === 'genesis-classic';
75215
+ }
75216
+ function cardSortKey(card, nameResolver = c => c.name) {
75217
+ const collector = card.collectorNumber ?? card.id;
75218
+ return `${card.edition ?? ''}-${collector.padStart(8, '0')}-${nameResolver(card)}`;
75219
+ }
75220
+ function tutorialLeaderCards(cards, nameResolver = c => c.name) {
75221
+ const leaders = cards
75222
+ .filter(card => card.type === 'leader' && isClassicGalleryCard(card))
75223
+ .sort((a, b) => cardSortKey(a, nameResolver).localeCompare(cardSortKey(b, nameResolver), 'es'));
75224
+ if (leaders.length >= 2)
75225
+ return leaders.slice(0, 4);
75226
+ return cards.filter(card => card.type === 'leader').slice(0, 4);
75227
+ }
75228
+ function findCardByName(cards, namePart, nameResolver = c => c.name) {
75229
+ return cards.find(card => nameResolver(card).toLowerCase().includes(namePart.toLowerCase()));
75230
+ }
75231
+ function shuffleCards(cards, seed) {
75232
+ const result = [...cards];
75233
+ let state = seed >>> 0;
75234
+ for (let i = result.length - 1; i > 0; i--) {
75235
+ state = (state * 1664525 + 1013904223) >>> 0;
75236
+ const j = state % (i + 1);
75237
+ [result[i], result[j]] = [result[j], result[i]];
75238
+ }
75239
+ return result;
75240
+ }
75241
+ function ensureOpeningOffering(cards, start, size) {
75242
+ const hand = cards.slice(start, start + size);
75243
+ if (hand.some(card => card.type === 'offering'))
75244
+ return;
75245
+ const offeringIndex = cards.findIndex((card, i) => i >= start + size && card.type === 'offering');
75246
+ if (offeringIndex < 0)
75247
+ return;
75248
+ [cards[start], cards[offeringIndex]] = [cards[offeringIndex], cards[start]];
75249
+ }
75250
+ function prepareTutorialOpeningDeck(cards) {
75251
+ const result = [...cards];
75252
+ ensureOpeningOffering(result, 0, TUTORIAL_OPENING_HAND_SIZE);
75253
+ ensureOpeningOffering(result, TUTORIAL_OPENING_HAND_SIZE, TUTORIAL_OPENING_HAND_SIZE);
75254
+ return result;
75255
+ }
75256
+ function createTutorialPlayer(seat, leader, hand, battleZone, nameResolver = c => c.name) {
75257
+ const base = STARTING_GAME.players[seat];
75258
+ const leaderLife = leader?.leaderLife ?? leader?.life ?? base.leaderMaxHp;
75259
+ return {
75260
+ ...structuredClone(base),
75261
+ leaderCardId: leader?.id,
75262
+ leaderName: leader ? nameResolver(leader) : base.leaderName,
75263
+ leaderHp: leaderLife,
75264
+ leaderMaxHp: leaderLife,
75265
+ offerings: [],
75266
+ offeringPlayedThisTurn: false,
75267
+ hand,
75268
+ handCount: hand ? undefined : 4,
75269
+ battleZone,
75270
+ };
75271
+ }
75272
+ function createTutorialGame(cards, options = {}, context = {}) {
75273
+ const nameResolver = context.nameResolver ?? (c => c.name);
75274
+ const cardById = context.cardById;
75275
+ const leaders = tutorialLeaderCards(cards, nameResolver);
75276
+ const playerLeader = (cardById && options.playerLeaderId ? cardById(options.playerLeaderId) : undefined) ??
75277
+ findCardByName(leaders, 'Adán', nameResolver) ??
75278
+ findCardByName(leaders, 'Adan', nameResolver) ??
75279
+ leaders[0];
75280
+ const opponentLeader = (cardById && options.opponentLeaderId ? cardById(options.opponentLeaderId) : undefined) ??
75281
+ findCardByName(leaders, 'Noé', nameResolver) ??
75282
+ findCardByName(leaders, 'Noe', nameResolver) ??
75283
+ leaders.find(c => c.id !== playerLeader?.id) ??
75284
+ leaders[0];
75285
+ const offerings = cards
75286
+ .filter(c => c.type === 'offering' && isClassicGalleryCard(c))
75287
+ .slice(0, TUTORIAL_OFFERING_LIMIT);
75288
+ const places = cards.filter(c => c.type === 'place');
75289
+ const characters = cards
75290
+ .filter(c => c.type === 'character' &&
75291
+ isClassicGalleryCard(c) &&
75292
+ (c.cost ?? 0) <= 4 &&
75293
+ (c.attack ?? 0) >= 0 &&
75294
+ (c.life ?? 0) > 0)
75295
+ .slice(0, TUTORIAL_CHARACTER_LIMIT);
75296
+ const tutorialDeck = [...offerings, ...characters, ...offerings, ...characters.slice(0, 6)].slice(0, 28);
75297
+ const shuffledDeck = prepareTutorialOpeningDeck(shuffleCards(tutorialDeck, options.seed ?? TUTORIAL_SEED));
75298
+ const playerHand = shuffledDeck.slice(0, TUTORIAL_OPENING_HAND_SIZE).map(c => c.id);
75299
+ const opponentHand = shuffledDeck
75300
+ .slice(TUTORIAL_OPENING_HAND_SIZE, TUTORIAL_OPENING_HAND_SIZE * 2)
75301
+ .map(c => c.id);
75302
+ const sharedDeck = shuffledDeck.slice(TUTORIAL_OPENING_HAND_SIZE * 2).map(c => c.id);
75303
+ const firstPlayer = options.firstPlayer ?? 'P1';
75304
+ const firstPlayerName = firstPlayer === 'P1'
75305
+ ? STARTING_GAME.players.P1.displayName
75306
+ : STARTING_GAME.players.P2.displayName;
75307
+ const messages = context.messages;
75308
+ const log = messages
75309
+ ? [`${messages.turnOf} ${firstPlayerName}.`, messages.tutorialLoaded]
75310
+ : [`Turno de ${firstPlayerName}.`, 'Tutorial cargado.'];
75311
+ return {
75312
+ ...structuredClone(STARTING_GAME),
75313
+ status: 'in_progress',
75314
+ turnOwner: firstPlayer,
75315
+ turnCount: 1,
75316
+ matchScore: { P1: 0, P2: 0 },
75317
+ sharedDeck,
75318
+ placeZone: places[0] ? [places[0].id] : [],
75319
+ historyZone: [],
75320
+ players: {
75321
+ P1: createTutorialPlayer('P1', playerLeader, playerHand, [], nameResolver),
75322
+ P2: createTutorialPlayer('P2', opponentLeader, opponentHand, [], nameResolver),
75323
+ },
75324
+ log,
75325
+ };
75326
+ }
75327
+ function createTutorialSetup(leaders) {
75328
+ const coinWinner = TUTORIAL_SEED % 2 === 0 ? 'P1' : 'P2';
75329
+ const firstPlayer = coinWinner === 'P1' ? 'P2' : 'P1';
75330
+ return {
75331
+ coinWinner,
75332
+ leaderPicker: coinWinner,
75333
+ firstPlayer,
75334
+ availableLeaders: leaders.slice(0, TUTORIAL_LEADER_CHOICE_COUNT).map(c => c.id),
75335
+ };
75336
+ }
75337
+
75338
+ function resolveBotPlays(game, log, nameResolver = c => c.name, cardById = id => ({
75339
+ id,
75340
+ name: id,
75341
+ type: 'character',
75342
+ cost: 0,
75343
+ life: 0,
75344
+ attack: 0,
75345
+ })) {
75346
+ const hand = [...(game.players.P2.hand ?? [])];
75347
+ const offeringId = hand.find(cardId => {
75348
+ const card = cardById(cardId);
75349
+ return card.type === 'offering' && canPlayCard(game, 'P2', card);
75350
+ });
75351
+ if (offeringId) {
75352
+ const card = cardById(offeringId);
75353
+ const result = playCardFromHand(game, 'P2', card, {
75354
+ displayName: nameResolver(card),
75355
+ });
75356
+ if (result.played) {
75357
+ Object.assign(game, result.game);
75358
+ log.push(...result.log);
75359
+ }
75360
+ }
75361
+ const playableCharacters = [...(game.players.P2.hand ?? [])]
75362
+ .map(cardId => cardById(cardId))
75363
+ .filter(card => card.type === 'character' && canPlayCard(game, 'P2', card))
75364
+ .sort((l, r) => (r.attack ?? 0) - (l.attack ?? 0) || (r.life ?? 0) - (l.life ?? 0));
75365
+ const character = playableCharacters[0];
75366
+ if (character) {
75367
+ const result = playCardFromHand(game, 'P2', character, {
75368
+ displayName: nameResolver(character),
75369
+ });
75370
+ if (result.played) {
75371
+ Object.assign(game, result.game);
75372
+ log.push(...result.log);
75373
+ }
75374
+ }
75375
+ }
75376
+ function resolveBotAttack(game, log, impacts, messages) {
75377
+ const bot = game.players.P2;
75378
+ const player = game.players.P1;
75379
+ const attacker = bot.battleZone
75380
+ .filter(card => card.canAttack && card.attack > 0)
75381
+ .sort((l, r) => (l.slot ?? 0) - (r.slot ?? 0))[0];
75382
+ if (!attacker) {
75383
+ log.push(messages.botNoAttackers);
75384
+ return;
75385
+ }
75386
+ const target = player.battleZone
75387
+ .filter(card => card.currentHp > 0)
75388
+ .sort((l, r) => l.currentHp - r.currentHp || (l.slot ?? 0) - (r.slot ?? 0))[0];
75389
+ if (target) {
75390
+ impacts.push({ text: `-${attacker.attack}`, tone: 'damage', seat: 'P1', target: 'battle' });
75391
+ impacts.push({ text: `-${target.attack}`, tone: 'damage', seat: 'P2', target: 'battle' });
75392
+ target.currentHp -= attacker.attack;
75393
+ attacker.currentHp -= target.attack;
75394
+ attacker.canAttack = false;
75395
+ log.push(`${attacker.name} ${messages.combatsAgainst} ${target.name}.`);
75396
+ cleanupDestroyed(player, game);
75397
+ cleanupDestroyed(bot, game);
75398
+ return;
75399
+ }
75400
+ impacts.push({ text: `-${attacker.attack}`, tone: 'damage', seat: 'P1', target: 'leader' });
75401
+ player.leaderHp = Math.max(0, player.leaderHp - attacker.attack);
75402
+ attacker.canAttack = false;
75403
+ log.push(`${attacker.name} ${messages.attacksYourLeader}`);
75404
+ }
75405
+
75406
+ const CHRISTIANITY_PREVIEW_NEUTRAL_TILT = { rotateX: 0, rotateY: 0, shiftX: 0, shiftY: 0 };
75176
75407
  class ChristianityGameComponent {
75177
75408
  isPreviewAbilityPlaceholder(ability) {
75178
75409
  return ability?.title === this.t('christianityNoAbilityTitle') && !ability.canUse;
@@ -75694,99 +75925,20 @@ class ChristianityGameComponent {
75694
75925
  }, remaining);
75695
75926
  }
75696
75927
  normalizeCatalogCard(card, catalog) {
75697
- const editionIcon = card.editionIcon ?? catalog.edition?.icon ?? GENESIS_ICON_URL;
75698
- const verseText = card.verseText ?? card.bible?.text;
75699
- const scripture = card.scripture ?? this.formatScripture(card.bible);
75700
- const offeringValue = card.type === 'offering' ? (card.offeringValue ?? card.life ?? card.cost ?? 1) : card.offeringValue;
75701
- const life = card.type === 'leader'
75702
- ? (card.leaderLife ?? card.life ?? 10)
75703
- : card.type === 'offering'
75704
- ? offeringValue
75705
- : (card.life ?? 0);
75706
- return {
75707
- ...card,
75708
- cost: card.type === 'offering' ? 0 : (card.cost ?? 0),
75709
- offeringValue,
75710
- life,
75711
- leaderLife: card.type === 'leader' ? (card.leaderLife ?? life) : card.leaderLife,
75712
- attack: card.type === 'character' ? (card.attack ?? 0) : card.attack,
75713
- abilityName: card.abilityName ?? card.abilities?.[0]?.name,
75714
- abilityText: card.abilityText ?? card.abilities?.[0]?.text,
75715
- verseText,
75716
- scripture,
75717
- edition: card.edition ?? catalog.edition?.id ?? 'GEN',
75718
- editionNumber: card.editionNumber ?? catalog.edition?.number,
75719
- editionIcon,
75720
- imageUrl: card.imageUrl ?? card.art?.imageUrl,
75721
- author: card.author ?? card.art?.artistCredit,
75722
- year: card.year ?? '2026',
75723
- frontTemplate: card.frontTemplate ?? 'genesis-classic',
75724
- };
75928
+ return normalizeCatalogCard(card, catalog);
75725
75929
  }
75726
75930
  formatScripture(bible) {
75727
- if (!bible?.book || !bible.reference)
75728
- return undefined;
75729
- return `${bible.book} ${bible.reference}${bible.version ? ` ${bible.version}` : ''}`;
75931
+ return formatScripture(bible);
75730
75932
  }
75731
75933
  createTutorialGame(cards, options = {}) {
75732
- const leaders = this.tutorialLeaderCards(cards);
75733
- const playerLeader = this.cardById(options.playerLeaderId) ?? this.findCard(leaders, 'Adán') ?? leaders[0];
75734
- const opponentLeader = this.cardById(options.opponentLeaderId) ??
75735
- this.findCard(leaders, 'Noé') ??
75736
- leaders.find(card => card.id !== playerLeader?.id) ??
75737
- leaders[0];
75738
- const offerings = cards
75739
- .filter(card => card.type === 'offering' && this.isClassicGalleryCard(card))
75740
- .slice(0, TUTORIAL_OFFERING_LIMIT);
75741
- const places = cards.filter(card => card.type === 'place');
75742
- const characters = cards
75743
- .filter(card => card.type === 'character' &&
75744
- this.isClassicGalleryCard(card) &&
75745
- (card.cost ?? 0) <= 4 &&
75746
- (card.attack ?? 0) >= 0 &&
75747
- (card.life ?? 0) > 0)
75748
- .slice(0, TUTORIAL_CHARACTER_LIMIT);
75749
- const tutorialDeck = [...offerings, ...characters, ...offerings, ...characters.slice(0, 6)].slice(0, 28);
75750
- const shuffledDeck = this.prepareTutorialOpeningDeck(this.shuffleCards(tutorialDeck, options.seed ?? TUTORIAL_SEED));
75751
- const playerHand = shuffledDeck.slice(0, TUTORIAL_OPENING_HAND_SIZE).map(card => card.id);
75752
- const opponentHand = shuffledDeck
75753
- .slice(TUTORIAL_OPENING_HAND_SIZE, TUTORIAL_OPENING_HAND_SIZE * 2)
75754
- .map(card => card.id);
75755
- const sharedDeck = shuffledDeck.slice(TUTORIAL_OPENING_HAND_SIZE * 2).map(card => card.id);
75756
- return {
75757
- ...structuredClone(STARTING_GAME),
75758
- status: 'in_progress',
75759
- turnOwner: options.firstPlayer ?? 'P1',
75760
- turnCount: 1,
75761
- matchScore: { P1: 0, P2: 0 },
75762
- sharedDeck,
75763
- placeZone: places[0] ? [places[0].id] : [],
75764
- historyZone: [],
75765
- players: {
75766
- P1: this.createTutorialPlayer('P1', playerLeader, playerHand, []),
75767
- P2: this.createTutorialPlayer('P2', opponentLeader, opponentHand, []),
75768
- },
75769
- log: [
75770
- `${this.t('christianityTurnOf')} ${(options.firstPlayer ?? 'P1') === 'P1' ? STARTING_GAME.players.P1.displayName : STARTING_GAME.players.P2.displayName}.`,
75771
- this.t('christianityTutorialLoaded'),
75772
- ],
75773
- };
75934
+ return createTutorialGame(cards, options, {
75935
+ messages: { turnOf: this.t('christianityTurnOf'), tutorialLoaded: this.t('christianityTutorialLoaded') },
75936
+ nameResolver: c => this.translatedCardText(c).name,
75937
+ cardById: id => this.cardById(id),
75938
+ });
75774
75939
  }
75775
75940
  createTutorialPlayer(seat, leader, hand, battleZone) {
75776
- const base = STARTING_GAME.players[seat];
75777
- const leaderLife = leader?.leaderLife ?? leader?.life ?? base.leaderMaxHp;
75778
- return {
75779
- ...structuredClone(base),
75780
- leaderCardId: leader?.id,
75781
- leaderName: leader ? this.translatedCardText(leader).name : base.leaderName,
75782
- leaderHp: leaderLife,
75783
- leaderMaxHp: leaderLife,
75784
- offerings: [],
75785
- offeringPlayedThisTurn: false,
75786
- hand,
75787
- handCount: hand ? undefined : 4,
75788
- battleZone,
75789
- };
75941
+ return createTutorialPlayer(seat, leader, hand, battleZone, c => this.translatedCardText(c).name);
75790
75942
  }
75791
75943
  createOfferingInstance(card, seat, instanceId) {
75792
75944
  const value = card.offeringValue ?? card.life ?? 1;
@@ -75818,50 +75970,25 @@ class ChristianityGameComponent {
75818
75970
  };
75819
75971
  }
75820
75972
  findCard(cards, namePart) {
75821
- return cards.find(card => this.translatedCardText(card).name.toLowerCase().includes(namePart.toLowerCase()));
75973
+ return findCardByName(cards, namePart, c => this.translatedCardText(c).name);
75822
75974
  }
75823
75975
  cardById(cardId) {
75824
75976
  return cardId ? (this.cardCatalog()[cardId] ?? STARTER_CARDS[cardId]) : undefined;
75825
75977
  }
75826
75978
  tutorialLeaderCards(cards) {
75827
- const leaders = cards
75828
- .filter(card => card.type === 'leader' && this.isClassicGalleryCard(card))
75829
- .sort((left, right) => this.cardSortKey(left).localeCompare(this.cardSortKey(right), 'es'));
75830
- if (leaders.length >= 2)
75831
- return leaders.slice(0, 4);
75832
- return cards.filter(card => card.type === 'leader').slice(0, 4);
75979
+ return tutorialLeaderCards(cards, c => this.translatedCardText(c).name);
75833
75980
  }
75834
75981
  shuffleCards(cards, seed) {
75835
- const result = [...cards];
75836
- let state = seed >>> 0;
75837
- for (let index = result.length - 1; index > 0; index--) {
75838
- state = (state * 1664525 + 1013904223) >>> 0;
75839
- const swapIndex = state % (index + 1);
75840
- [result[index], result[swapIndex]] = [result[swapIndex], result[index]];
75841
- }
75842
- return result;
75982
+ return shuffleCards(cards, seed);
75843
75983
  }
75844
75984
  prepareTutorialOpeningDeck(cards) {
75845
- const result = [...cards];
75846
- this.ensureOpeningOffering(result, 0, TUTORIAL_OPENING_HAND_SIZE);
75847
- this.ensureOpeningOffering(result, TUTORIAL_OPENING_HAND_SIZE, TUTORIAL_OPENING_HAND_SIZE);
75848
- return result;
75849
- }
75850
- ensureOpeningOffering(cards, start, size) {
75851
- const hand = cards.slice(start, start + size);
75852
- if (hand.some(card => card.type === 'offering'))
75853
- return;
75854
- const offeringIndex = cards.findIndex((card, index) => index >= start + size && card.type === 'offering');
75855
- if (offeringIndex < 0)
75856
- return;
75857
- [cards[start], cards[offeringIndex]] = [cards[offeringIndex], cards[start]];
75985
+ return prepareTutorialOpeningDeck(cards);
75858
75986
  }
75859
75987
  cardSortKey(card) {
75860
- const collector = card.collectorNumber ?? card.id;
75861
- return `${card.edition ?? ''}-${collector.padStart(8, '0')}-${this.translatedCardText(card).name}`;
75988
+ return cardSortKey(card, c => this.translatedCardText(c).name);
75862
75989
  }
75863
75990
  isClassicGalleryCard(card) {
75864
- return (card.frontTemplate ?? 'genesis-classic') === 'genesis-classic';
75991
+ return isClassicGalleryCard(card);
75865
75992
  }
75866
75993
  cardDefinition(cardId) {
75867
75994
  return this.cardCatalog()[cardId] ?? STARTER_CARDS[cardId] ?? STARTER_CARDS['p_scout'];
@@ -76689,14 +76816,7 @@ class ChristianityGameComponent {
76689
76816
  }
76690
76817
  createTutorialSetup() {
76691
76818
  const leaders = this.tutorialLeaderCards(this.allCatalogCards()).slice(0, TUTORIAL_LEADER_CHOICE_COUNT);
76692
- const coinWinner = TUTORIAL_SEED % 2 === 0 ? 'P1' : 'P2';
76693
- const firstPlayer = coinWinner === 'P1' ? 'P2' : 'P1';
76694
- return {
76695
- coinWinner,
76696
- leaderPicker: coinWinner,
76697
- firstPlayer,
76698
- availableLeaders: leaders.map(card => card.id),
76699
- };
76819
+ return createTutorialSetup(leaders);
76700
76820
  }
76701
76821
  chooseRivalTutorialLeader() {
76702
76822
  const setup = this.tutorialSetup();
@@ -77116,62 +77236,14 @@ class ChristianityGameComponent {
77116
77236
  this.pushImpactEvents(impacts);
77117
77237
  }
77118
77238
  resolveBotPlays(game, log) {
77119
- const bot = game.players.P2;
77120
- const hand = [...(bot.hand ?? [])];
77121
- const offeringId = hand.find(cardId => this.cardDefinition(cardId).type === 'offering' && canPlayCard(game, 'P2', this.cardDefinition(cardId)));
77122
- if (offeringId) {
77123
- const card = this.cardDefinition(offeringId);
77124
- const result = playCardFromHand(game, 'P2', card, {
77125
- displayName: this.translatedCardText(card).name,
77126
- });
77127
- if (result.played) {
77128
- Object.assign(game, result.game);
77129
- log.push(...result.log);
77130
- }
77131
- }
77132
- const playableCharacters = [...(game.players.P2.hand ?? [])]
77133
- .map(cardId => this.cardDefinition(cardId))
77134
- .filter(card => card.type === 'character' && canPlayCard(game, 'P2', card))
77135
- .sort((left, right) => (right.attack ?? 0) - (left.attack ?? 0) || (right.life ?? 0) - (left.life ?? 0));
77136
- const character = playableCharacters[0];
77137
- if (character) {
77138
- const result = playCardFromHand(game, 'P2', character, {
77139
- displayName: this.translatedCardText(character).name,
77140
- });
77141
- if (result.played) {
77142
- Object.assign(game, result.game);
77143
- log.push(...result.log);
77144
- }
77145
- }
77239
+ resolveBotPlays(game, log, c => this.translatedCardText(c).name, id => this.cardDefinition(id));
77146
77240
  }
77147
77241
  resolveBotAttack(game, log, impacts) {
77148
- const bot = game.players.P2;
77149
- const player = game.players.P1;
77150
- const attacker = bot.battleZone
77151
- .filter(card => card.canAttack && card.attack > 0)
77152
- .sort((left, right) => (left.slot ?? 0) - (right.slot ?? 0))[0];
77153
- if (!attacker) {
77154
- log.push(this.t('christianityBotNoAttackers'));
77155
- return;
77156
- }
77157
- const target = player.battleZone
77158
- .filter(card => card.currentHp > 0)
77159
- .sort((left, right) => left.currentHp - right.currentHp || (left.slot ?? 0) - (right.slot ?? 0))[0];
77160
- if (target) {
77161
- impacts.push({ text: `-${attacker.attack}`, tone: 'damage', seat: 'P1', target: 'battle' });
77162
- impacts.push({ text: `-${target.attack}`, tone: 'damage', seat: 'P2', target: 'battle' });
77163
- target.currentHp -= attacker.attack;
77164
- attacker.currentHp -= target.attack;
77165
- attacker.canAttack = false;
77166
- log.push(`${attacker.name} ${this.t('christianityCombatsAgainst')} ${target.name}.`);
77167
- cleanupDestroyed(player, game);
77168
- cleanupDestroyed(bot, game);
77169
- return;
77170
- }
77171
- impacts.push({ text: `-${attacker.attack}`, tone: 'damage', seat: 'P1', target: 'leader' });
77172
- player.leaderHp = Math.max(0, player.leaderHp - attacker.attack);
77173
- attacker.canAttack = false;
77174
- log.push(`${attacker.name} ${this.t('christianityAttacksYourLeader')}`);
77242
+ resolveBotAttack(game, log, impacts, {
77243
+ botNoAttackers: this.t('christianityBotNoAttackers'),
77244
+ combatsAgainst: this.t('christianityCombatsAgainst'),
77245
+ attacksYourLeader: this.t('christianityAttacksYourLeader'),
77246
+ });
77175
77247
  }
77176
77248
  ruleMessages() {
77177
77249
  return {