import { CardIds , Race , ReferenceCard } from '@firestone-hs/reference-data' ; import { CardsFacadeService } from '@services/cards-facade.service' ; import { DeckCard } from '../../../models/decktracker/deck-card' ; import { DeckState } from '../../../models/decktracker/deck-state' ; import { GameState } from '../../../models/decktracker/game-state' ; import { GameEvent } from '../../../models/game-event' ; import { COUNTERSPELLS , globalEffectCards } from '../../hs-utils' ; import { LocalizationFacadeService } from '../../localization-facade.service' ; import { modifyDeckForSpecialCards } from './deck-contents-utils' ; import { DeckManipulationHelper } from './deck-manipulation-helper' ; import { EventParser } from './event-parser' ; export class CardPlayedFromHandParser implements EventParser { constructor ( private readonly helper , private readonly allCards , private readonly i18n ) { } applies ( gameEvent , state ) { return state && gameEvent . type === GameEvent . CARD_PLAYED ; } async parse ( currentState , gameEvent , additionalInfo ? ) { const [ cardId , controllerId , localPlayer , entityId ] = gameEvent . parse ( ) ; const isPlayer = controllerId === localPlayer . PlayerId ; const deck = isPlayer ? currentState . playerDeck : currentState . opponentDeck ; const card = this . helper . findCardInZone ( deck . hand , cardId , entityId ) ; const [ newHand , removedCard ] = this . helper . removeSingleCardFromZone ( deck . hand , cardId , entityId , deck . deckList . length === 0 && ! gameEvent . additionalData . transientCard ) ; let newDeck = removedCard != null ? this . helper . updateDeckForAi ( gameEvent , currentState , removedCard ) : deck . deck ; if ( removedCard == null && cardId && ! gameEvent . additionalData . transientCard ) { const [ newDeckAfterReveal , removedCardFromDeck ] = this . helper . removeSingleCardFromZone ( newDeck , cardId , entityId , deck . deckList . length === 0 ) ; if ( removedCardFromDeck ) { newDeck = newDeckAfterReveal ; } } const refCard = this . allCards . getCard ( cardId ) ; const isOnBoard = refCard && refCard . type === 'Minion' ; const cardWithZone = card ?. update ( { zone : isOnBoard ? 'PLAY' : null , manaCost : card . manaCost ?? refCard ?. cost , rarity : card . rarity ?. toLowerCase ( ) ?? refCard ?. rarity ?. toLowerCase ( ) , temporaryCard : false , } as DeckCard ) || DeckCard . create ( { entityId : entityId , cardId : cardId , cardName : this . i18n . getCardName ( refCard ?. id ) , manaCost : refCard ?. cost , rarity : refCard ?. rarity ?. toLowerCase ( ) , zone : isOnBoard ? 'PLAY' : null , temporaryCard : false , } as DeckCard ) ; const isCardCountered = ( ( additionalInfo ?. secretWillTrigger ?. reactingToEntityId && additionalInfo ?. secretWillTrigger ?. reactingToEntityId === entityId ) || ( additionalInfo ?. secretWillTrigger ?. reactingToCardId && additionalInfo ?. secretWillTrigger ?. reactingToCardId === cardId ) ) && COUNTERSPELLS . includes ( additionalInfo ?. secretWillTrigger ?. cardId as CardIds ) ; const newBoard = isOnBoard && ! isCardCountered ? this . helper . addSingleCardToZone ( deck . board , cardWithZone ) : deck . board ; const newOtherZone = isOnBoard ? deck . otherZone : this . helper . addSingleCardToZone ( deck . otherZone , isCardCountered && additionalInfo ?. secretWillTrigger ?. cardId === CardIds . OhMyYogg ? cardWithZone . update ( { entityId : undefined , } as DeckCard ) : cardWithZone ) ; let newGlobalEffects = deck . globalEffects ; if ( ! isCardCountered && globalEffectCards . includes ( card ?. cardId as CardIds ) ) { newGlobalEffects = this . helper . addSingleCardToZone ( deck . globalEffects , cardWithZone ?. update ( { entityId : null , } as DeckCard ) ) ; } const isElemental = refCard ?. type === 'Minion' && refCard ?. race ?. toLowerCase ( ) === Race [ Race . ELEMENTAL ] . toLowerCase ( ) ; const newPlayerDeck = Object . assign ( new DeckState ( ) , deck , { hand : newHand , board : newBoard , deck : newDeck , otherZone : newOtherZone , cardsPlayedThisTurn : isCardCountered ? deck . cardsPlayedThisTurn : ( [ ... deck . cardsPlayedThisTurn , cardWithZone ] as readonly DeckCard [ ] ) , cardsPlayedThisMatch : isCardCountered ? deck . cardsPlayedThisMatch : ( [ ... deck . cardsPlayedThisMatch , cardWithZone ] as readonly DeckCard [ ] ) , globalEffects : newGlobalEffects , spellsPlayedThisMatch : ! isCardCountered && refCard ?. type === 'Spell' ? [ ... deck . spellsPlayedThisMatch , cardWithZone ] : deck . spellsPlayedThisMatch , watchpostsPlayedThisMatch : deck . watchpostsPlayedThisMatch + ( ! isCardCountered && this . isWatchpost ( refCard ) ? 1 : 0 ) , libramsPlayedThisMatch : deck . libramsPlayedThisMatch + ( ! isCardCountered && this . isLibram ( refCard ) ? 1 : 0 ) , elementalsPlayedThisTurn : deck . elementalsPlayedThisTurn + ( ! isCardCountered && isElemental ? 1 : 0 ) , } as DeckState ) ; const deckAfterSpecialCaseUpdate = isCardCountered ? newPlayerDeck : modifyDeckForSpecialCards ( cardId , newPlayerDeck , this . allCards , this . i18n ) ; return Object . assign ( new GameState ( ) , currentState , { [ isPlayer ? 'playerDeck' : 'opponentDeck' ] : deckAfterSpecialCaseUpdate , } ) ; } private isWatchpost ( refCard ) { return refCard ?. name ?. includes ( 'Watch Post' ) ; } private isLibram ( refCard ) { return refCard ?. name ?. startsWith ( 'Libram' ) ; } event ( ) { return GameEvent . CARD_PLAYED ; } }