class EventNavigator : public Event { public: EventNavigator(Card *_source) { source = _source; done = false; } bool Advance(State &s) { if(done) return true; PlayerState &p = s.players[s.player]; if(p.deck.Length() < 5) s.Shuffle(s.player); int cardsToReveal = Math::Min((int)p.deck.Length(), 5); if(cardsToReveal == 0) { if(logging) s.LogIndent(1, "has no cards to reveal"); return true; } for(int cardIndex = 0; cardIndex < cardsToReveal; cardIndex++) { Card *c = p.deck[p.deck.Length() - 1 - cardIndex]; if(logging) s.LogIndent(1, "reveals " + c->PrettyName()); } s.decision.MakeDiscreteChoice(source, 2); if(decisionText) s.decision.text = "Discard revealed cards?|Yes|No"; return false; } bool CanProcessDecisions() const { return true; } void ProcessDecision(State &s, const DecisionResponse &response) { PlayerState &p = s.players[s.player]; int cardsToReveal = Math::Min((int)p.deck.Length(), 5); if(response.choice == 0) { for(int cardIndex = 0; cardIndex < cardsToReveal; cardIndex++) { Card *c = p.deck.Last(); p.deck.PopEnd(); s.stack.PushEnd(new EventDiscardCard(s.player, c, DiscardFromSideZone)); } } else { if(logging) s.LogIndent(1, "puts the cards back on top of their deck"); s.ReorderDeck(source, s.player, cardsToReveal); } done = true; } Card *source; bool done; }; class EventLookout : public Event { public: EventLookout(Card *_source) { source = _source; done = false; cardToTrash = NULL; revealedCards[0] = NULL; revealedCards[1] = NULL; revealedCards[2] = NULL; } bool CanProcessDecisions() const { return true; } UINT Stage() const { int count = 0; if(revealedCards[0] != NULL) count++; if(revealedCards[1] != NULL) count++; if(revealedCards[2] != NULL) count++; if(count == 0) return 0; if(count == 3) return 1; if(count == 2) return 2; return -1; } bool Advance(State &s) { if(done) return true; PlayerState &p = s.players[s.player]; int stage = Stage(); if(stage == 0) { if(p.deck.Length() < 3) s.Shuffle(s.player); if(p.deck.Length() < 3) { if(logging) s.Log("does not have enough cards to reveal"); return true; } for(int cardIndex = 0; cardIndex < 3; cardIndex++) { revealedCards[cardIndex] = p.deck.Last(); p.deck.PopEnd(); if(logging) s.LogIndent(1, "reveals " + revealedCards[cardIndex]->PrettyName()); } s.decision.SelectCards(source, 1, 1); if(decisionText) s.decision.text = "Choose a card to trash:"; for(int i = 0; i < 3; i++) s.decision.AddUniqueCard(revealedCards[i]); } return false; } void ProcessDecision(State &s, const DecisionResponse &response) { int stage = Stage(); if(stage == 1) { cardToTrash = response.cards[0]; s.decision.SelectCards(source, 1, 1); if(decisionText) s.decision.text = "Choose a card to discard:"; bool cardFound = false; for(int i = 0; i < 3; i++) { if(!cardFound && revealedCards[i] == response.cards[0]) { cardFound = true; revealedCards[i] = NULL; } if(revealedCards[i] != NULL) s.decision.AddUniqueCard(revealedCards[i]); } } else if(stage == 2) { s.stack.PushEnd(new EventDiscardCard(s.player, response.cards[0], DiscardFromSideZone)); s.stack.PushEnd(new EventTrashCardFromSideZone(s.player, cardToTrash)); bool cardFound = false; for(int i = 0; i < 3; i++) { if(!cardFound && revealedCards[i] == response.cards[0]) { cardFound = true; revealedCards[i] = NULL; } } for(int i = 0; i < 3; i++) { if(revealedCards[i] != NULL) { if(logging) s.LogIndent(1, "puts " + revealedCards[i]->PrettyName() + " on top of their deck"); s.players[s.player].deck.PushEnd(revealedCards[i]); } } done = true; } } Card *source; Card *revealedCards[3]; Card *cardToTrash; bool done; }; class EventSeaHagAttack : public Event { public: EventSeaHagAttack(Card *_source, UINT _player) { source = _source; player = _player; } bool IsAttack() const { return true; } int AttackedPlayer() const { return player; } AttackAnnotations* Annotations() { return &annotations; } bool Advance(State &s) { PlayerState &p = s.players[player]; if(p.deck.Length() == 0) s.Shuffle(player); s.stack.PushEnd(new EventGainCard(player, s.data->baseCards.curse, false, false, GainToDeckTop)); if(p.deck.Length() == 0) { if(logging) s.LogIndent(1, player, "has no cards to reveal"); } else { if(logging) s.LogIndent(1, player, "reveals " + p.deck.Last()->PrettyName()); s.stack.PushEnd(new EventDiscardCard(player, p.deck.Last(), DiscardFromSideZone)); p.deck.PopEnd(); } return true; } Card *source; UINT player; AttackAnnotations annotations; }; class EventCutpurseAttack : public Event { public: EventCutpurseAttack(Card *_source, UINT _player) { source = _source; player = _player; } bool IsAttack() const { return true; } int AttackedPlayer() const { return player; } AttackAnnotations* Annotations() { return &annotations; } bool Advance(State &s) { PlayerState &p = s.players[player]; if(p.hand.Contains(s.data->baseCards.copper)) { s.stack.PushEnd(new EventDiscardCard(player, s.data->baseCards.copper, DiscardFromHand)); } else { if(logging) for(Card *c : p.hand) s.LogIndent(1, player, "reveals " + c->PrettyName()); } return true; } Card *source; UINT player; AttackAnnotations annotations; }; class EventAmbassador : public Event { public: EventAmbassador(Card *_source) { source = _source; revealedCard = NULL; done = false; } bool CanProcessDecisions() const { return true; } bool Advance(State &s) { if(done) return true; if(revealedCard == NULL) { PlayerState &p = s.players[s.player]; if(p.hand.Length() == 0) { if(logging) s.LogIndent(1, "has no cards to reveal"); return true; } s.decision.SelectCards(source, 1, 1); if(decisionText) s.decision.text = "Choose a card to reveal:"; s.decision.AddUniqueCards(p.hand); } return false; } void ProcessDecision(State &s, const DecisionResponse &response) { PlayerState &p = s.players[s.player]; if(revealedCard == NULL) { revealedCard = response.cards[0]; if(logging) s.LogIndent(1, "reveals " + revealedCard->PrettyName()); int handCount = 0; for(Card *c : p.hand) if(c == revealedCard) handCount++; handCount = Math::Min(handCount, 2); if(handCount == 1) { s.decision.MakeDiscreteChoice(source, 2); if(decisionText) s.decision.text = "Return how many cards to the supply?|0|1"; } else if(handCount == 2) { s.decision.MakeDiscreteChoice(source, 3); if(decisionText) s.decision.text = "Return how many cards to the supply?|0|1|2"; } } else { if(logging) s.LogIndent(1, "returns " + String(response.choice) + " " + revealedCard->PrettyName() + " to the supply"); for(UINT returnIndex = 0; returnIndex < response.choice; returnIndex++) { p.hand.RemoveSwap(p.hand.FindFirstIndex(revealedCard)); s.supply[s.data->SupplyIndex(revealedCard)].count++; } for(const PlayerInfo &p : s.data->players) { if(p.index != s.player) { s.stack.PushEnd(new EventGainCard(p.index, revealedCard, false, true, GainToDiscard)); } } done = true; } } Card *source; Card *revealedCard; bool done; }; class EventExplorer : public Event { public: EventExplorer(Card *_source) { source = _source; done = false; } bool Advance(State &s) { if(done) return true; PlayerState &p = s.players[s.player]; if(p.hand.Contains(s.data->baseCards.province)) { s.decision.MakeDiscreteChoice(source, 2); if(decisionText) s.decision.text = "Reveal a Province?|Yes|No"; return false; } else { s.stack.PushEnd(new EventGainCard(s.player, s.data->baseCards.silver, false, false, GainToHand)); return true; } } bool CanProcessDecisions() const { return true; } void ProcessDecision(State &s, const DecisionResponse &response) { PlayerState &p = s.players[s.player]; if(response.choice == 0) { if(logging) s.LogIndent(1, "reveals a " + s.data->baseCards.province->PrettyName()); s.stack.PushEnd(new EventGainCard(s.player, s.data->baseCards.gold, false, false, GainToHand)); } else { s.stack.PushEnd(new EventGainCard(s.player, s.data->baseCards.silver, false, false, GainToHand)); } done = true; } Card *source; bool done; };