jcbantuelle/dominion-meteor

View on GitHub
app/game/server/services/buy_event_processor.js

Summary

Maintainability
F
4 days
Test Coverage
BuyEventProcessor = class BuyEventProcessor {

  static reaction_cards() {
    return ['Hovel']
  }

  static landmark_cards() {
    return ['Basilica', 'Colonnade', 'Defiled Shrine']
  }

  static event_cards() {
    return ['Noble Brigand', 'Farmland', 'Mint', 'Messenger', 'Port', 'Forum']
  }

  static in_play_event_cards() {
    return ['Haggler', 'Talisman', 'Hoard', 'Goons', 'Merchant Guild']
  }

  static overpay_cards() {
    return ['Stonemason', 'Doctor', 'Masterpiece', 'Herald']
  }

  static duration_attack_cards() {
    return ['Haunted Woods', 'Swamp Hag']
  }

  constructor(buyer) {
    this.buyer = buyer
    this.event_id = 2000
    this.find_buy_events()
  }

  find_buy_events() {
    this.buy_events = []
    if (_.includes(BuyEventProcessor.event_cards(), this.buyer.card.name)) {
      if (this.buyer.card.name === 'Messenger') {
        if (_.size(this.buyer.game.turn.bought_things) === 1) {
          this.buy_events.push(this.buyer.card)
        }
      } else {
        this.buy_events.push(this.buyer.card)
      }
    }

    _.each(this.buyer.game.landmarks, (card) => {
      if (_.includes(BuyEventProcessor.landmark_cards(), card.name)) {
        if (card.name === 'Basilica' && card.victory_tokens > 0 && this.buyer.game.turn.coins >= 2) {
            this.buy_events.push(card)
        } else if (card.name === 'Colonnade' && card.victory_tokens > 0 && _.includes(_.words(this.buyer.card.types), 'action') && _.some(this.buyer.player_cards.in_play, (card) => { return card.name === this.buyer.card.name})) {
            this.buy_events.push(card)
        } else if (card.name === 'Defiled Shrine' && card.victory_tokens > 0 && this.buyer.card.name === 'Curse') {
            this.buy_events.push(card)
        }
      }
    })

    if (_.includes(BuyEventProcessor.overpay_cards(), this.buyer.card.name) && this.buyer.game.turn.coins > 0) {
      this.buy_events.push(this.buyer.card)
    }

    _.each(this.buyer.player_cards.hand, (card) => {
      if (_.includes(BuyEventProcessor.reaction_cards(), card.name)) {
        if (card.name === 'Hovel') {
          if (_.includes(_.words(this.buyer.card.types), 'victory')) {
            this.buy_events.push(card)
          }
        }
      }
    })

    _.each(this.buyer.player_cards.in_play, (card) => {
      if (_.includes(BuyEventProcessor.in_play_event_cards(), card.name)) {
        if (card.name === 'Talisman') {
          if (!_.includes(_.words(this.buyer.card.types), 'victory') && CardCostComparer.coin_less_than(this.buyer.game, this.buyer.card, 5)) {
            this.buy_events.push(card)
          }
        } else if (card.name === 'Hoard') {
          if (_.includes(_.words(this.buyer.card.types), 'victory')) {
            this.buy_events.push(card)
          }
        } else {
          this.buy_events.push(card)
        }
      }
    })

    _.each(this.buyer.player_cards.duration_attacks, (card) => {
      if (_.includes(BuyEventProcessor.duration_attack_cards(), card.name)) {
        this.buy_events.push(card)
      }
    })

    if (this.buyer.game_card.embargos > 0) {
      let embargo = ClassCreator.create('Embargo').to_h()
      embargo.id = this.generate_event_id()
      this.buy_events.push(embargo)
    }

    _.times(this.buyer.game.turn.charms, () => {
      let charm = ClassCreator.create('Charm').to_h()
      charm.id = this.generate_event_id()
      this.buy_events.push(charm)
    })

    if (this.buyer.game_card.debt_tokens > 0) {
      let tax = ClassCreator.create('Tax').to_h()
      tax.id = this.generate_event_id()
      this.buy_events.push(tax)
    }

    let trashing_token = _.find(this.buyer.player_cards.tokens.pile, (token) => {
      return token.effect === 'trashing'
    })
    if (trashing_token && trashing_token.card.stack_name === this.buyer.card.stack_name) {
      this.buy_events.push({
        name: 'Trash Token',
        id: this.generate_event_id(),
        trashing_token: true,
        types: 'trashing_token',
        image: 'trashing_token'
      })
    }
  }

  process() {
    if (!_.isEmpty(this.buy_events)) {
      let mandatory_buy_events = _.filter(this.buy_events, (event) => {
        return _.includes(BuyEventProcessor.event_cards().concat(BuyEventProcessor.in_play_event_cards()).concat(BuyEventProcessor.overpay_cards()).concat(BuyEventProcessor.duration_attack_cards()).concat(BuyEventProcessor.landmark_cards()).concat(['Embargo', 'Charm', 'Tax']), event.name)
      })
      if (_.size(this.buy_events) === 1 && !_.isEmpty(mandatory_buy_events)) {
        BuyEventProcessor.buy_event(this.buyer.game, this.buyer.player_cards, this.buy_events, this)
      } else {
        GameModel.update(this.buyer.game._id, this.buyer.game)
        let instructions = `Choose Buy Event To Resolve for ${CardView.render(this.buyer.card)}`
        let minimum = 1
        if (_.isEmpty(mandatory_buy_events)) {
          instructions += ' (or none to skip)'
          minimum = 0
        }
        let turn_event_id = TurnEventModel.insert({
          game_id: this.buyer.game._id,
          player_id: this.buyer.player_cards.player_id,
          username: this.buyer.player_cards.username,
          type: 'choose_cards',
          player_cards: true,
          instructions: `${instructions}:`,
          cards: this.buy_events,
          minimum: minimum,
          maximum: 1
        })
        let turn_event_processor = new TurnEventProcessor(this.buyer.game, this.buyer.player_cards, turn_event_id, this)
        turn_event_processor.process(BuyEventProcessor.buy_event)
      }
    }
  }

  static buy_event(game, player_cards, selected_cards, buy_event_processor) {
    if (!_.isEmpty(selected_cards)) {
      let card = selected_cards[0]
      if (card.name === 'Trash Token') {
        if (_.size(player_cards.hand) > 0) {
          let turn_event_id = TurnEventModel.insert({
            game_id: game._id,
            player_id: player_cards.player_id,
            username: player_cards.username,
            type: 'choose_cards',
            player_cards: true,
            instructions: 'Choose a card to trash: (or none to skip)',
            cards: player_cards.hand,
            minimum: 0,
            maximum: 1
          })
          let turn_event_processor = new TurnEventProcessor(game, player_cards, turn_event_id)
          turn_event_processor.process(BuyEventProcessor.trash_card)
        } else {
          game.log.push(`  but there are no cards in hand for the Trash Token`)
        }
      } else {
        let card_object = ClassCreator.create(card.name)
        if (_.includes(BuyEventProcessor.reaction_cards(), card.name)) {
          card_object.buy_reaction(game, player_cards, buy_event_processor.buyer, card)
        } else {
          card_object.buy_event(buy_event_processor.buyer, card)
        }
      }

      let buy_event_index = _.findIndex(buy_event_processor.buy_events, (event) => {
        return event.id === card.id
      })
      buy_event_processor.buy_events.splice(buy_event_index, 1)

      GameModel.update(game._id, game)
      PlayerCardsModel.update(game._id, player_cards)
      buy_event_processor.process()
    }
  }

  generate_event_id() {
    let event_id = _.toString(this.event_id)
    this.event_id += 1
    return event_id
  }

  static trash_card(game, player_cards, selected_cards) {
    let card_trasher = new CardTrasher(game, player_cards, 'hand', selected_cards)
    card_trasher.trash()
  }

}