import { Injectable } from '@angular/core' ; import { getBaseCardId } from '@firestone-hs/reference-data' ; import { CardsFacadeService } from '@services/cards-facade.service' ; import { BoardSecret } from '../../../models/decktracker/board-secret' ; import { CardMetaInfo } from '../../../models/decktracker/card-meta-info' ; import { DeckCard } from '../../../models/decktracker/deck-card' ; import { DeckState } from '../../../models/decktracker/deck-state' ; import { GameState } from '../../../models/decktracker/game-state' ; import { SecretOption } from '../../../models/decktracker/secret-option' ; import { EntityGameState , GameEvent , GameState as EventGameState , PlayerGameState } from '../../../models/game-event' ; import { LocalizationFacadeService } from '../../localization-facade.service' ; @ Injectable ( ) export class DeckManipulationHelper { constructor ( private readonly allCards , private readonly i18n ) { } public removeSingleCardFromZone ( zone , cardId , entityId , removeFillerCard = false , normalizeUpgradedCards = false , debug = false ) { const normalizedCardId = this . normalizeCardId ( cardId , normalizeUpgradedCards ) ; if ( debug ) { console . debug ( 'removing' , cardId , normalizedCardId , entityId , removeFillerCard , normalizeUpgradedCards , zone ) ; } if ( zone . some ( ( card ) => card . entityId === entityId ) ) { if ( debug ) { console . debug ( 'removing' , zone . find ( ( card ) => card . entityId === entityId ) ) ; } return [ zone . map ( ( card ) => ( card . entityId === entityId ? null : card ) ) . filter ( ( card ) => card ) , zone . find ( ( card ) => card . entityId === entityId ) , ] ; } if ( ! normalizedCardId ) { if ( zone . some ( ( card ) => ! card . entityId && ! card . cardId && ! card . cardName && ! card . creatorCardId ) ) { let hasRemovedOnce = false ; const result = [ ] ; let removedCard = undefined ; if ( debug ) { console . debug ( 'removing filler card' , zone , zone . filter ( ( card ) => ! card . entityId && ! card . cardId && ! card . cardName && ! card . creatorCardId ) ) ; } for ( const card of zone ) { if ( ! card . entityId && ! card . cardId && ! card . cardName && ! card . creatorCardId && ! card . entityId && ! hasRemovedOnce ) { hasRemovedOnce = true ; removedCard = card ; continue ; } result . push ( card ) ; } return [ result , removedCard ] ; } if ( debug ) { console . debug ( 'returning initial zone' , zone , cardId , entityId ) ; } return [ zone , null ] ; } if ( debug ) { console . debug ( 'trying to remove a card from zone without using the entityId' , normalizedCardId , entityId , zone . length , removeFillerCard ) ; } let hasRemovedOnce = false ; let removedCard = null ; const result = [ ] ; const shouldIgnoreEntityId = zone . some ( ( card ) => this . normalizeCardId ( card . cardId , normalizeUpgradedCards ) === normalizedCardId ) && zone . filter ( ( card ) => this . normalizeCardId ( card . cardId , normalizeUpgradedCards ) === normalizedCardId ) . every ( ( card ) => card . entityId ) ; for ( const card of zone ) { const refCardId = this . normalizeCardId ( card . cardId , normalizeUpgradedCards ) ; if ( refCardId === normalizedCardId && ( shouldIgnoreEntityId || ! card . entityId ) && ! hasRemovedOnce ) { if ( debug ) { console . debug ( 'removing card' , card ) ; } hasRemovedOnce = true ; removedCard = card ; continue ; } result . push ( card ) ; } if ( debug ) { console . debug ( 'found card to remove?' , removedCard , result ) ; } if ( ! removedCard ) { const drawnCard = normalizedCardId ? this . allCards . getCard ( normalizedCardId ) : null ; if ( debug ) { console . debug ( 'trying to remove special cards' , drawnCard , normalizedCardId ) ; } if ( drawnCard ) { const candidates = zone . filter ( ( card ) => card . cardMatchCondition ) . filter ( ( card ) => card . cardMatchCondition ( drawnCard ) ) ; if ( debug ) { console . debug ( 'found candidates to remove' , candidates ) ; } if ( candidates ?. length ) { let hasRemovedOnce = false ; const result = zone . filter ( ( card ) => ! card . cardMatchCondition || ! card . cardMatchCondition ( drawnCard ) ) ; let removedCard = undefined ; for ( const card of candidates ) { if ( ! hasRemovedOnce ) { hasRemovedOnce = true ; removedCard = card ; continue ; } result . push ( card ) ; } if ( debug ) { console . debug ( 'returning' , result , removedCard ) ; } return [ result , removedCard ] ; } } } if ( ! removedCard && removeFillerCard ) { if ( zone . some ( ( card ) => ! card . entityId && ! card . cardId && ! card . cardName && ! card . creatorCardId ) ) { if ( debug ) { console . debug ( 'could not find card to remove with entity and card ids, removing filler card' , zone . filter ( ( card ) => ! card . entityId && ! card . cardId && ! card . cardName && ! card . creatorCardId ) ) ; } let hasRemovedOnce = false ; const result = [ ] ; let removedCard = undefined ; for ( const card of zone ) { if ( ! card . entityId && ! card . cardId && ! card . cardName && ! card . creatorCardId && ! card . entityId && ! hasRemovedOnce ) { hasRemovedOnce = true ; removedCard = card ; continue ; } result . push ( card ) ; } return [ result , removedCard ] ; } console . warn ( 'could not find card to remove' ) ; } return [ result , removedCard ] ; } public addSingleCardToZone ( zone , cardTemplate , keepBuffs = false , debug = false ) { if ( cardTemplate . entityId != null && zone . filter ( ( card ) => card . entityId === cardTemplate . entityId ) . length > 0 ) { return zone ; } const newCard = DeckCard . create ( { ... cardTemplate , buffingEntityCardIds : keepBuffs ? cardTemplate . buffingEntityCardIds : undefined , buffCardIds : keepBuffs ? cardTemplate . buffCardIds : undefined , metaInfo : new CardMetaInfo ( ) , } as DeckCard ) ; return [ ... zone , newCard ] ; } public updateCardInDeck ( deck , card ) { if ( ! card ?. entityId ) { console . warn ( 'missing entityId for card update' , card ) ; return deck ; } return deck . update ( { hand : this . updateCardInZone ( deck . hand , card . entityId , card . cardId ) , board : this . updateCardInZone ( deck . board , card . entityId , card . cardId ) , deck : this . updateCardInZone ( deck . deck , card . entityId , card . cardId ) , otherZone : this . updateCardInZone ( deck . otherZone , card . entityId , card . cardId ) , } as DeckState ) ; } public updateCardInZone ( zone , entityId , cardId ) { if ( ! cardId ) { return zone ; } return zone . map ( ( card ) => card . entityId !== entityId ? card : card . update ( { cardId : cardId , cardName : this . i18n . getCardName ( cardId ) , } as DeckCard ) ) ; } public findCardInZone ( zone , cardId , entityId , normalizeUpgradedCards = false ) { const normalizedCardId = this . normalizeCardId ( cardId , normalizeUpgradedCards ) ; if ( entityId ) { const found = zone . find ( ( card ) => card . entityId === entityId ) ; if ( ! found ) { if ( cardId ) { const idByCardId = zone . find ( ( card ) => { const refCardId = this . normalizeCardId ( card . cardId , normalizeUpgradedCards ) ; return refCardId === normalizedCardId && ! card . entityId ; } ) ; if ( idByCardId ) { const card = this . allCards . getCard ( normalizedCardId ) ; return idByCardId . update ( { entityId : entityId , cardId : cardId , cardName : card && this . i18n . getCardName ( card . id ) , } as DeckCard ) ; } else { } } else if ( cardId == null ) { return null ; } else { return DeckCard . create ( { entityId : entityId , } as DeckCard ) ; } } if ( found && cardId ) { const card = this . allCards . getCard ( cardId ) ; return found . update ( { cardId : cardId , cardName : ( card && this . i18n . getCardName ( card . id ) ) || found . cardName , } as DeckCard ) ; } if ( found ) { return found ; } } if ( cardId ) { const found = zone . find ( ( card ) => { const refCardId = this . normalizeCardId ( card . cardId , normalizeUpgradedCards ) ; return refCardId === normalizedCardId && ! card . entityId ; } ) ; if ( ! found ) { const card = this . allCards . getCard ( cardId ) ; return DeckCard . create ( { cardId : cardId , cardName : card ? this . i18n . getCardName ( card . id ) : null , entityId : entityId , } as DeckCard ) ; } return found ; } console . error ( 'invalid call to findCard' , entityId , cardId ) ; return DeckCard . create ( ) ; } public assignCardIdToEntity ( deck , entityId , cardId ) { const cardInHand = this . findCardInZone ( deck . hand , null , entityId ) ; if ( cardInHand && ! cardInHand . cardId ) { const newCard = cardInHand . update ( { cardId : cardId , cardName : this . i18n . getCardName ( cardId ) , } as DeckCard ) ; const newHand = this . replaceCardInZone ( deck . hand , newCard ) ; return Object . assign ( new DeckState ( ) , deck , { hand : newHand , } as DeckState ) ; } return deck ; } public replaceCardInZone ( zone , newCard ) { const cardIndex = zone . map ( ( card ) => card . entityId ) . indexOf ( newCard . entityId ) ; const newZone = [ ... zone ] ; newZone [ cardIndex ] = newCard ; return newZone ; } public updateDeckForAi ( gameEvent , currentState , removedCard ) { const [ cardId , controllerId , localPlayer , entityId ] = gameEvent . parse ( ) ; const isPlayer = controllerId === localPlayer . PlayerId ; const deck = isPlayer ? currentState . playerDeck : currentState . opponentDeck ; if ( ! isPlayer && currentState . opponentDeck . deckList && ! removedCard ?. creatorCardId && ! removedCard ?. cardId ) { const newCardId = getBaseCardId ( cardId ) ; const result = this . removeSingleCardFromZone ( deck . deck , newCardId , entityId ) ; return result [ 0 ] ; } return deck . deck ; } public removeSecretOption ( deck , secretCardId ) { return deck . update ( { secrets : deck . secrets . map ( ( secret ) => this . removeSecretOptionFromSecret ( secret , secretCardId ) ) as readonly BoardSecret [ ] , } as DeckState ) ; } public removeSecretOptionFromSecrets ( secrets , secretCardId ) { return secrets . map ( ( secret ) => this . removeSecretOptionFromSecret ( secret , secretCardId ) ) ; } public findEntityInGameState ( gameState , entityId ) { const playerState = gameState . Player || ( { } as PlayerGameState ) ; const opponentState = gameState . Opponent || ( { } as PlayerGameState ) ; const allEntities = [ ... ( playerState . Board || [ ] ) , ... ( playerState . Hand || [ ] ) , ... ( opponentState . Board || [ ] ) , ... ( opponentState . Hand || [ ] ) , ] ; return allEntities . find ( ( entity ) => entity . entityId === entityId ) ; } public removeSecretOptionFromSecret ( secret , secretCardId ) { const newOptions = secret . allPossibleOptions . map ( ( option ) => option . cardId === secretCardId ? option . update ( { isValidOption : false } as SecretOption ) : option ) ; return secret . update ( { allPossibleOptions : newOptions , } as BoardSecret ) ; } private normalizeCardId ( cardId , shouldNormalize ) { if ( ! shouldNormalize ) { return cardId ; } return getBaseCardId ( cardId ) ; } }