diff options
Diffstat (limited to 'crawl-ref/source/decks.cc')
-rw-r--r-- | crawl-ref/source/decks.cc | 1037 |
1 files changed, 771 insertions, 266 deletions
diff --git a/crawl-ref/source/decks.cc b/crawl-ref/source/decks.cc index 6f3ebb4537..0c759cea73 100644 --- a/crawl-ref/source/decks.cc +++ b/crawl-ref/source/decks.cc @@ -47,6 +47,19 @@ #include "view.h" #include "xom.h" +// DECK STRUCTURE: deck.plus is the number of cards the deck *started* +// with, deck.plus2 is the number of cards drawn, deck.special is the +// deck rarity, deck.props["cards"] holds the list of cards (with the +// highest index card being the top card, and index 0 being the bottom +// card), deck.props.["card_flags"] holds the flags for each card, +// deck.props["num_marked"] is the number of marked cards left in the +// deck, and deck.props["non_brownie_draws"] is the number of +// non-marked draws you have to make from that deck before earning +// brownie points from it again. +// +// The card type and per-card flags are each stored as unsigned bytes, +// for a maximum of 256 differetn kinds of cards and 8 bits of flags. + #define VECFROM(x) (x), (x) + ARRAYSIZE(x) #define DEFVEC(Z) static std::vector<card_type> Z(VECFROM(a_##Z)) @@ -118,11 +131,85 @@ DEFVEC(deck_of_punishment); #undef DEFVEC #undef VECFROM +static void check_odd_card(unsigned char flags) +{ + if ((flags & CFLAG_ODDITY) && !(flags & CFLAG_SEEN)) + mpr("This card doesn't seem to belong here."); +} + +static unsigned long cards_in_deck(const item_def &deck) +{ + ASSERT(is_deck(deck)); + + const CrawlHashTable &props = deck.props; + ASSERT(props.exists("cards")); + + return (unsigned long) props["cards"].get_table().size(); +} + +static void shuffle_deck(item_def &deck) +{ + ASSERT(is_deck(deck)); + + CrawlHashTable &props = deck.props; + ASSERT(props.exists("cards")); + + CrawlHashTable &cards = props["cards"]; + ASSERT(cards.size() > 1); + + CrawlHashTable &flags = props["card_flags"]; + ASSERT(flags.size() == cards.size()); + + // Don't use std::shuffle(), since we want to apply exactly the + // same shuffling to both the cards vector and the flags vector. + // Also, the CrawlHashTable iterator doesn't provide the interface + // required for using it with std::shuffle(). + std::vector<long> pos; + for (unsigned long i = 0, size = cards.size(); i < size; i++) + pos.push_back(random2(size)); + + for (unsigned long i = 0, size = pos.size(); i < size; i++) + { + std::swap(cards[i], cards[pos[i]]); + std::swap(flags[i], flags[pos[i]]); + } +} + +static card_type get_card_and_flags(const item_def& deck, int idx, + unsigned char& _flags) +{ + const CrawlHashTable &props = deck.props; + const CrawlHashTable &cards = props["cards"].get_table(); + const CrawlHashTable &flags = props["card_flags"].get_table(); + + if (idx == -1) + idx = (int) cards.size() - 1; + + _flags = (unsigned char) flags[idx].get_byte(); + + return static_cast<card_type>(cards[idx].get_byte()); +} + +static void set_card_and_flags(item_def& deck, int idx, card_type card, + unsigned char _flags) +{ + CrawlHashTable &props = deck.props; + CrawlHashTable &cards = props["cards"]; + CrawlHashTable &flags = props["card_flags"]; + + if (idx == -1) + idx = (int) cards.size() - 1; + + cards[idx] = (char) card; + flags[idx] = (char) _flags; +} + const char* card_name(card_type card) { switch (card) { - case CARD_BLANK: return "blank card"; + case CARD_BLANK1: return "blank card"; + case CARD_BLANK2: return "blank card"; case CARD_PORTAL: return "the Portal"; case CARD_WARP: return "the Warp"; case CARD_SWAP: return "Swap"; @@ -171,11 +258,10 @@ const char* card_name(card_type card) return "a very buggy card"; } -static card_type choose_one_card(const item_def& item, bool message, - bool &was_oddity) +static std::vector<card_type>* random_sub_deck(unsigned char deck_type) { std::vector<card_type> *pdeck = NULL; - switch ( item.sub_type ) + switch ( deck_type ) { case MISC_DECK_OF_ESCAPE: pdeck = (coinflip() ? &deck_of_transport : &deck_of_emergency); @@ -210,34 +296,114 @@ static card_type choose_one_card(const item_def& item, bool message, } ASSERT( pdeck ); - + + return pdeck; +} + +static card_type random_card(unsigned char deck_type, bool &was_oddity) +{ + std::vector<card_type> *pdeck = random_sub_deck(deck_type); + if ( one_chance_in(100) ) { - if ( message ) - mpr("This card doesn't seem to belong here."); pdeck = &deck_of_oddities; was_oddity = true; } card_type chosen = (*pdeck)[random2(pdeck->size())]; - // High Evocations gives you another shot (but not at being punished...) - if (pdeck != &deck_of_punishment && chosen == CARD_BLANK && - you.skills[SK_EVOCATIONS] > random2(30)) - chosen = (*pdeck)[random2(pdeck->size())]; - // Paranoia - if (chosen < CARD_BLANK || chosen > NUM_CARDS) + if (chosen < CARD_BLANK1 || chosen >= NUM_CARDS) chosen = NUM_CARDS; return chosen; } -static card_type choose_one_card(const item_def& item, bool message) +static card_type random_card(const item_def& item, bool &was_oddity) +{ + return random_card(item.sub_type, was_oddity); +} + +static void retry_blank_card(card_type &card, unsigned char deck_type, + unsigned char flags) +{ + // BLANK1 == hasn't been retried + if (card != CARD_BLANK1) + return; + + if (flags & (CFLAG_MARKED | CFLAG_SEEN)) + { + // Can't retry a card which has been seen or marked. + card = CARD_BLANK2; + return; + } + + std::vector<card_type> *pdeck = random_sub_deck(deck_type); + + if (flags & CFLAG_ODDITY) + pdeck = &deck_of_oddities; + + // High Evocations gives you another shot (but not at being punished...) + if (pdeck != &deck_of_punishment + && you.skills[SK_EVOCATIONS] > random2(30)) + { + card = (*pdeck)[random2(pdeck->size())]; + } + + // BLANK2 == retried and failed + if (card == CARD_BLANK1) + card = CARD_BLANK2; +} + +static void retry_blank_card(card_type &card, item_def& deck, + unsigned char flags) +{ + retry_blank_card(card, deck.sub_type, flags); +} + +static card_type draw_top_card(item_def& deck, bool message, + unsigned char &_flags) +{ + CrawlHashTable &props = deck.props; + CrawlHashTable &cards = props["cards"].get_table(); + CrawlHashTable &flags = props["card_flags"].get_table(); + + int num_cards = cards.size(); + int idx = num_cards - 1; + + ASSERT(num_cards > 0); + + card_type card = get_card_and_flags(deck, idx, _flags); + + cards.erase(idx); + flags.erase(idx); + + retry_blank_card(card, deck, _flags); + + if (message) + { + if (_flags & CFLAG_MARKED) + mprf("You draw %s.", card_name(card)); + else + mprf("You draw a card... It is %s.", card_name(card)); + + check_odd_card(_flags); + } + + return card; +} + +static void push_top_card(item_def& deck, card_type card, + unsigned char _flags) { - bool garbage; + CrawlHashTable &props = deck.props; + CrawlHashTable &cards = props["cards"].get_table(); + CrawlHashTable &flags = props["card_flags"].get_table(); + + int idx = cards.size(); - return choose_one_card(item, message, garbage); + cards[idx] = (char) card; + flags[idx] = (char) _flags; } static bool wielding_deck() @@ -249,128 +415,110 @@ static bool wielding_deck() static bool check_buggy_deck(item_def& deck) { - bool stacked_wrong = false; - bool buggy_cards = false; + if (!is_deck(deck)) + { + crawl_state.zero_turns_taken(); + + mpr("This isn't a deck at all!"); + return true; + } + + CrawlHashTable &props = deck.props; - if (deck.special != 0) + if (!props.exists("cards") || cards_in_deck(deck) == 0) { - long special = deck.special; - long fixed = 0; - long holes = 0; + crawl_state.zero_turns_taken(); - for (int i = 0; i < 4 && special != 0; i++) + std::string msg = ""; + + if (!props.exists("cards")) + msg += "Seems this deck never had any cards in the first place!"; + else { - const short next_card = special & 0xFF; + msg += "Strange, this deck is already empty."; - special >>= 8; + int cards_left = 0; + if (deck.plus2 >= 0) + cards_left = deck.plus - deck.plus2; + else + cards_left = -deck.plus; - if (next_card == 0 || next_card > NUM_CARDS) + if (cards_left != 0) { - if (next_card == 0) - { -#ifdef WIZARD - if (you.wizard && !stacked_wrong && !buggy_cards) - { - mpr("Stacked deck has a hole in it (1)."); - if (yesno("Preserve state for debugging?")) - { - crawl_state.zero_turns_taken(); - return true; - } - } -#endif - mpr("Stacked deck has a hole in it (1), fixing..."); - stacked_wrong = true; - } - else if (next_card > NUM_CARDS) - { -#ifdef WIZARD - if (you.wizard && !buggy_cards && !stacked_wrong) - { - mprf("Buggy card (%d) in deck.", next_card - 1); - if (yesno("Preserve state for debugging?")) - { - crawl_state.zero_turns_taken(); - return true; - } - } -#endif - mprf("Buggy card (%d) in deck, discarding it.", - next_card - 1); - buggy_cards = true; - } - holes++; - continue; + msg += " But there should be been "; + msg += cards_left; + msg += " cards left."; } - - fixed |= next_card << (8 * (i - holes)); } - if (stacked_wrong || buggy_cards) - { - mprf("Original deck.special was 0x%x, is now 0x%x", - deck.special, fixed); + mpr(msg.c_str()); + mpr("A swarm of software bugs snatches the deck from you and " + "whisk it away."); - deck.special = fixed; - } + if ( deck.link == you.equip[EQ_WEAPON] ) + unwield_item(); + + dec_inv_item_quantity( deck.link, 1 ); + did_god_conduct(DID_CARDS, 1); + + return true; } - if (deck.plus2 == 0 && deck.special != 0) + bool problems = false; + + CrawlHashTable &cards = props["cards"].get_table(); + CrawlHashTable &flags = props["card_flags"].get_table(); + + problems = cards.fixup_indexed_array("the card stack"); + problems = flags.fixup_indexed_array("the flag stack"); + + unsigned long num_cards = cards.size(); + unsigned long num_flags = flags.size(); + + unsigned int num_buggy = 0; + unsigned int num_marked = 0; + unsigned int counted_cards = 0; + + for (unsigned long i = 0; i < num_cards; i++) { -#ifdef WIZARD - if (you.wizard && !stacked_wrong && !buggy_cards) + unsigned char card = (unsigned char) cards[i].get_byte(); + unsigned char _flags = (unsigned char) flags[i].get_byte(); + if (card >= NUM_CARDS) { - mpr("Stacked deck has a hole in it (2)."); - if (yesno("Preserve state for debugging?")) - { - crawl_state.zero_turns_taken(); - return true; - } + cards.erase(i); + flags.erase(i); + num_buggy++; + } + else + { + counted_cards++; + if (_flags & CFLAG_MARKED) + num_marked++; } -#endif - mpr("Stacked deck has a hole in it (2), fixing..."); - - const short next_card = (deck.special & 0xFF); - deck.special >>= 8; - deck.plus2 = next_card; - stacked_wrong = true; } - if (deck.plus2 > NUM_CARDS) + if (num_buggy > 0) { -#ifdef WIZARD - if (you.wizard && !buggy_cards && !stacked_wrong) - { - mprf("Buggy card (%d) in deck.", deck.plus2 - 1); - if (yesno("Preserve state for debugging?")) - { - crawl_state.zero_turns_taken(); - return true; - } - } -#endif - mprf("Buggy card (%d) in deck, discarding it.", deck.plus2 - 1); + mprf("%d buggy cards found in the deck, discarding them.", + num_buggy); + cards.compact_indicies(0, num_cards - 1); + flags.compact_indicies(0, num_flags - 1); - buggy_cards = true; - deck.plus2 = 0; + deck.plus2 += num_buggy; + + num_cards = cards.size(); + num_flags = cards.size(); + + problems = true; } - if (deck.plus <= 0) + if (num_cards == 0) { crawl_state.zero_turns_taken(); - mpr("Strange, that deck is already empty."); - if (deck.plus2 != 0) - mpr("And it was stacked, too. Weird."); - -#ifdef WIZARD - if (you.wizard) - if (yesno("Preserve deck for debugging?")) - return true; -#endif - + mpr("Oops, all of the cards seem to be gone."); mpr("A swarm of software bugs snatches the deck from you and " - "carries it away."); + "whisk it away."); if ( deck.link == you.equip[EQ_WEAPON] ) unwield_item(); @@ -381,22 +529,105 @@ static bool check_buggy_deck(item_def& deck) return true; } - if (stacked_wrong || buggy_cards) + if ((long) num_cards > deck.plus) { - you.wield_change = true; - print_stats(); + if (deck.plus == 0) + mpr("Deck was created with zero cards???"); + else if (deck.plus < 0) + mpr("Deck was created with *negative* cards?!"); + else + mpr("Deck has more cards than it was created with?"); + + deck.plus = num_cards; + problems = true; + } + + if (num_cards > num_flags) + { +#ifdef WIZARD + mprf("%d more cards than flags.", num_cards - num_flags); +#else + mpr("More cards than flags."); +#endif + for (unsigned int i = num_flags + 1; i <= num_cards; i++) + flags[i] = (char) 0; + + problems = true; + } + else if (num_flags > num_cards) + { +#ifdef WIZARD + mprf("%d more flags than cards.", num_flags - num_cards); +#else + mpr("More flags than cards."); +#endif + for (unsigned int i = num_flags; i > num_cards; i--) + flags.erase(i); + + problems = true; + } - if(!yesno("Deck had problems; use it anyways?")) + if (props["num_marked"].get_byte() > (char) counted_cards) + { + mpr("More cards marked than in the deck?"); + props["num_marked"] = (char) num_marked; + problems = true; + } + else if (props["num_marked"].get_byte() != (char) num_marked) + { +#ifdef WIZARD + mprf("Oops, counted %d marked cards, but num_marked is %d.", + (int) num_marked, (int) props["num_marked"].get_byte()); +#else + mpr("Oops, book-keeping on marked cards is wrong."); +#endif + props["num_marked"] = (char) num_marked; + problems = true; + } + + if (deck.plus2 >= 0) + { + if (deck.plus != (deck.plus2 + (long) num_cards)) + { +#ifdef WIZARD + mprf("Have you used %d cards, or %d? Oops.", + deck.plus2, deck.plus - num_cards); +#else + mpr("Oops, book-keeping on used cards is wrong."); +#endif + deck.plus2 = deck.plus - num_cards; + problems = true; + } + } + else + { + if (-deck.plus2 != (long) num_cards) { - crawl_state.zero_turns_taken(); - return true; +#ifdef WIZARD + mprf("There are %d cards left, not %d. Oops.", + num_cards, -deck.plus2); +#else + mpr("Oops, book-keeping on cards left is wrong."); +#endif + deck.plus2 = -num_cards; + problems = true; } } + if (!problems) + return false; + + you.wield_change = true; + + if (!yesno("Problems might not have been completely fixed; " + "still use deck?")) + { + crawl_state.zero_turns_taken(); + return true; + } return false; } - // Select a deck from inventory and draw a card from it. bool choose_deck_and_draw() { @@ -421,6 +652,18 @@ bool choose_deck_and_draw() return true; } +static void deck_peek_ident(item_def& deck) +{ + if (in_inventory(deck) && !item_ident(deck, ISFLAG_KNOW_TYPE)) + { + set_ident_flags(deck, ISFLAG_KNOW_TYPE); + + mprf("This is %s.", deck.name(DESC_NOCAP_A).c_str()); + + you.wield_change = true; + } +} + // Peek at a deck (show what the next card will be.) // Return false if the operation was failed/aborted along the way. bool deck_peek() @@ -431,33 +674,177 @@ bool deck_peek() crawl_state.zero_turns_taken(); return false; } - item_def& item(you.inv[you.equip[EQ_WEAPON]]); + item_def& deck(you.inv[you.equip[EQ_WEAPON]]); - if (check_buggy_deck(item)) + if (check_buggy_deck(deck)) return false; - if ( item.plus2 != 0 ) + if (deck.props["num_marked"].get_byte() > 0) { - mpr("You already know what the next card will be."); + mpr("You can't peek into a marked deck."); crawl_state.zero_turns_taken(); return false; } - const card_type chosen = choose_one_card(item, false); + CrawlHashTable &cards = deck.props["cards"]; + int num_cards = cards.size(); - msg::stream << "You see " << card_name(chosen) << '.' << std::endl; - item.plus2 = chosen + 1; - you.wield_change = true; + card_type card1, card2, card3; + unsigned char flags1, flags2, flags3; + + card1 = get_card_and_flags(deck, 0, flags1); + retry_blank_card(card1, deck, flags1); - // You lose 1d2 cards when peeking. - if ( item.plus > 1 ) + if (num_cards == 1) { - mpr("Some cards drop out of the deck."); - if ( item.plus > 2 && coinflip() ) - item.plus -= 2; - else - item.plus -= 1; + deck_peek_ident(deck); + + mpr("There's only one card in the deck!"); + + set_card_and_flags(deck, 0, card1, flags1 | CFLAG_SEEN | CFLAG_MARKED); + deck.props["num_marked"]++; + deck.plus2 = -1; + you.wield_change = true; + + return true; + } + + card2 = get_card_and_flags(deck, 1, flags2); + retry_blank_card(card2, deck, flags2); + + if (num_cards == 2) + { + deck.props["non_brownie_draws"] = (char) 2; + deck.plus2 = -2; + + deck_peek_ident(deck); + + mprf("Only two cards in the deck: %s and %s.", + card_name(card1), card_name(card2)); + + mpr("You shuffle the deck."); + + // If both cards are the same, then you know which card you're + // going to draw both times. + if (card1 == card2) + { + flags1 |= CFLAG_MARKED; + flags2 |= CFLAG_MARKED; + you.wield_change = true; + deck.props["num_marked"] = (char) 2; + } + + // "Shuffle" the two cards (even if they're teh same, since + // the flags might differ). + if (coinflip()) + { + std::swap(card1, card2); + std::swap(flags1, flags2); + } + + // After the first of two differing cards is drawn, you know + // what the second card is going to be. + if (card1 != card2) + { + flags1 |= CFLAG_MARKED; + deck.props["num_marked"]++; + } + + set_card_and_flags(deck, 0, card1, flags1 | CFLAG_SEEN); + set_card_and_flags(deck, 1, card2, flags2 | CFLAG_SEEN); + + return true; + } + + deck_peek_ident(deck); + + card3 = get_card_and_flags(deck, 2, flags3); + retry_blank_card(card3, deck, flags3); + + int already_seen = 0; + if (flags1 & CFLAG_SEEN) + already_seen++; + if (flags2 & CFLAG_SEEN) + already_seen++; + if (flags3 & CFLAG_SEEN) + already_seen++; + + if (random2(3) < already_seen) + deck.props["non_brownie_draws"]++; + + mprf("You draw three cards from the deck. They are: %s, %s and %s.", + card_name(card1), card_name(card2), card_name(card3)); + + set_card_and_flags(deck, 0, card1, flags1 | CFLAG_SEEN); + set_card_and_flags(deck, 1, card2, flags2 | CFLAG_SEEN); + set_card_and_flags(deck, 2, card3, flags3 | CFLAG_SEEN); + + mpr("You shuffle the cards back into the deck."); + shuffle_deck(deck); + + return true; +} + +// Mark a deck: look at the next four cards, mark them, and shuffle +// them back into the deck without losing any cards. The player won't +// know whate order they're in, and the if the top card is non-marked +// then the player won't know what the next card is. +// Return false if the operation was failed/aborted along the way. +bool deck_mark() +{ + if ( !wielding_deck() ) + { + mpr("You aren't wielding a deck!"); + crawl_state.zero_turns_taken(); + return false; + } + item_def& deck(you.inv[you.equip[EQ_WEAPON]]); + if (check_buggy_deck(deck)) + return false; + + CrawlHashTable &props = deck.props; + if (props["num_marked"].get_byte() > 0) + { + mpr("Deck is already marked."); + crawl_state.zero_turns_taken(); + return false; + } + + const int num_cards = cards_in_deck(deck); + const int num_to_mark = (num_cards < 4 ? num_cards : 4); + + if (num_cards == 1) + mpr("There's only one card left!"); + else if (num_cards < 4) + mprf("The deck only has %d cards.", num_cards); + + std::vector<std::string> names; + for ( int i = 0; i < num_to_mark; ++i ) + { + unsigned char flags; + card_type card = get_card_and_flags(deck, i, flags); + + flags |= CFLAG_SEEN | CFLAG_MARKED; + set_card_and_flags(deck, i, card, flags); + + names.push_back(card_name(card)); } + mpr_comma_separated_list("You draw and mark ", names); + props["num_marked"] = (char) num_to_mark; + + if (num_cards == 1) + return true; + + if (num_cards < 4) + { + mprf("You shuffle the deck."); + deck.plus2 = -num_cards; + } + else + mprf("You shuffle the cards back into the deck."); + + shuffle_deck(deck); + you.wield_change = true; return true; } @@ -473,27 +860,47 @@ bool deck_stack() crawl_state.zero_turns_taken(); return false; } - item_def& item(you.inv[you.equip[EQ_WEAPON]]); - - if (check_buggy_deck(item)) + item_def& deck(you.inv[you.equip[EQ_WEAPON]]); + if (check_buggy_deck(deck)) return false; - if ( item.plus2 != 0 ) + CrawlHashTable &props = deck.props; + if (props["num_marked"].get_byte() > 0) { mpr("You can't stack a marked deck."); crawl_state.zero_turns_taken(); return false; } - const int num_to_stack = (item.plus < 5 ? item.plus : 5); - std::vector<card_type> draws; - for ( int i = 0; i < num_to_stack; ++i ) - draws.push_back(choose_one_card(item, false)); + const int num_cards = cards_in_deck(deck); + const int num_to_stack = (num_cards < 5 ? num_cards : 5); - if ( draws.size() == 1 ) - mpr("There's only one card left!"); + std::vector<card_type> draws; + std::vector<unsigned char> flags; + for ( int i = 0; i < num_cards; ++i ) + { + unsigned char _flags; + card_type card = draw_top_card(deck, false, _flags); - item.special = 0; + if (i < num_to_stack) + { + draws.push_back(card); + flags.push_back(_flags | CFLAG_SEEN | CFLAG_MARKED); + } + else + ; // Rest of deck is discarded. + } + + if ( num_cards == 1 ) + mpr("There's only one card left!"); + else if (num_cards < 5) + mprf("The deck only has %d cards.", num_to_stack); + else if (num_cards == 5) + mpr("The deck has exactly five cards."); + else + mprf("You draw the first five cards out of %d and discard the rest.", + num_cards); + more(); while ( draws.size() > 1 ) { @@ -519,14 +926,18 @@ bool deck_stack() canned_msg(MSG_HUH); } } - item.special <<= 8; - item.special += draws[selected] + 1; + push_top_card(deck, draws[selected], flags[selected]); draws.erase(draws.begin() + selected); - mpr("Next card?", MSGCH_PROMPT); + flags.erase(flags.begin() + selected); } - item.plus2 = draws[0] + 1; - item.plus = num_to_stack; // no more deck after the stack + mesclr(); + deck.plus2 = -num_to_stack; + push_top_card(deck, draws[0], flags[0]); + props["num_marked"] = (char) num_to_stack; you.wield_change = true; + + check_buggy_deck(deck); + return true; } @@ -541,36 +952,47 @@ bool deck_triple_draw() } const int slot = you.equip[EQ_WEAPON]; - item_def& item(you.inv[slot]); + item_def& deck(you.inv[slot]); - if (check_buggy_deck(item)) + if (check_buggy_deck(deck)) return false; - if ( item.plus2 != 0 ) + CrawlHashTable &props = deck.props; + if (props["num_marked"].get_byte() > 0) { mpr("You can't triple draw from a marked deck."); crawl_state.zero_turns_taken(); return false; } - if (item.plus == 1) + const int num_cards = cards_in_deck(deck); + + if (num_cards == 1) { // only one card to draw, so just draw it - evoke_deck(item); + evoke_deck(deck); return true; } - const int num_to_draw = (item.plus < 3 ? item.plus : 3); - std::vector<card_type> draws; + const int num_to_draw = (num_cards < 3 ? num_cards : 3); + std::vector<card_type> draws; + std::vector<unsigned char> flags; + for ( int i = 0; i < num_to_draw; ++i ) - draws.push_back(choose_one_card(item, false)); + { + unsigned char _flags; + card_type card = draw_top_card(deck, false, _flags); + + draws.push_back(card); + flags.push_back(_flags | CFLAG_SEEN | CFLAG_MARKED); + } mpr("You draw... (choose one card)"); for ( int i = 0; i < num_to_draw; ++i ) msg::streams(MSGCH_PROMPT) << (static_cast<char>(i + 'a')) << " - " << card_name(draws[i]) << std::endl; int selected = -1; - while ( 1 ) + while ( true ) { const int keyin = tolower(get_ch()); if (keyin >= 'a' && keyin < 'a' + num_to_draw) @@ -582,12 +1004,14 @@ bool deck_triple_draw() canned_msg(MSG_HUH); } - // Note that card_effect() might cause you to unwield the deck. - card_effect(draws[selected], deck_rarity(item)); + // Note how many cards were removed from the deck. + deck.plus2 += num_to_draw; + you.wield_change = true; - // remove the cards from the deck - item.plus -= num_to_draw; - if (item.plus <= 0) + // Make deck disappear *before* the card effect, since we + // don't want to unwield an empty deck. + deck_rarity_type rarity = deck_rarity(deck); + if (cards_in_deck(deck) == 0) { mpr("The deck of cards disappears in a puff of smoke."); if ( slot == you.equip[EQ_WEAPON] ) @@ -595,20 +1019,67 @@ bool deck_triple_draw() dec_inv_item_quantity( slot, 1 ); } - you.wield_change = true; + + // Note that card_effect() might cause you to unwield the deck. + card_effect(draws[selected], rarity, flags[selected], false); + return true; } // This is Nemelex retribution. void draw_from_deck_of_punishment() { - item_def deck; - deck.plus = 10; // don't let it puff away - deck.plus2 = 0; - deck.colour = BLACK; // for rarity - deck.base_type = OBJ_MISCELLANY; - deck.sub_type = MISC_DECK_OF_PUNISHMENT; - evoke_deck(deck); + bool oddity; + card_type card = random_card(MISC_DECK_OF_PUNISHMENT, oddity); + + mpr("You draw a card..."); + card_effect(card, DECK_RARITY_COMMON); +} + +static int xom_check_card(item_def &deck, card_type card, + unsigned char flags) +{ + int amusement = 64; + + if (!item_type_known(deck)) + amusement *= 2; + // Expecting one type of card but got another, real funny. + else if (flags & CFLAG_ODDITY) + amusement = 255; + + if (player_in_a_dangerous_place()) + amusement *= 2; + + switch (card) + { + case CARD_XOM: + // Handled elswehre + amusement = 0; + break; + + case CARD_BLANK1: + case CARD_BLANK2: + // Boring + amusement = 0; + break; + + case CARD_DAMNATION: + // Nothing happened, boring. + if (you.level_type != LEVEL_DUNGEON) + amusement = 0; + break; + + case CARD_MINEFIELD: + case CARD_FAMINE: + case CARD_CURSE: + // Always hilarious. + amusement = 255; + + default: + break; + } + + return amusement; } // In general, if the next cards in a deck are known, they will @@ -622,110 +1093,72 @@ void evoke_deck( item_def& deck ) return; int brownie_points = 0; - mpr("You draw a card..."); bool allow_id = in_inventory(deck) && !item_ident(deck, ISFLAG_KNOW_TYPE); - // If the deck wasn't marked, draw a fair card. - if ( deck.plus2 == 0 ) - { - // Could a Xom worshipper ever get a stacked deck in the first - // place? - int amusement = 64; - bool was_oddity = false; - card_type card = choose_one_card(deck, true, was_oddity); - - if (!item_type_known(deck)) - amusement *= 2; - // Expecting one type of card but got another, real funny. - else if (was_oddity) - amusement = 255; - - if (player_in_a_dangerous_place()) - amusement *= 2; - - switch (card) - { - case CARD_XOM: - // Handled elswehre - amusement = 0; - break; - - case CARD_BLANK: - // Boring - amusement = 0; - break; + unsigned char flags = 0; + card_type card = draw_top_card(deck, true, flags); + int amusement = xom_check_card(deck, card, flags); + bool deck_gone = false; + deck_rarity_type rarity = deck_rarity(deck); + CrawlHashTable &props = deck.props; + bool no_brownie = (props["non_brownie_draws"].get_byte() > 0); - case CARD_DAMNATION: - // Nothing happened, boring. - if (you.level_type != LEVEL_DUNGEON) - amusement = 0; - break; + // Do these before the deck item_def object is gone. + if (flags & CFLAG_MARKED) + props["num_marked"]--; + if (no_brownie) + props["non_brownie_draws"]--; - case CARD_MINEFIELD: - case CARD_FAMINE: - case CARD_CURSE: - // Always hilarious. - amusement = 255; + deck.plus2++; - default: - break; - } + // Get rid of the deck *before* the card effect because a card + // might cause a wielded deck to be swapped out for something else, + // in which case we don't want an empty deck to go through the + // swapping process. + if ( cards_in_deck(deck) == 0 ) + { + mpr("The deck of cards disappears in a puff of smoke."); + dec_inv_item_quantity( deck.link, 1 ); + deck_gone = true; + // Finishing the deck will earn a point, even if it + // was marked or stacked. + brownie_points++; + } - card_effect(card, deck_rarity(deck) ); + card_effect(card, rarity, flags, false); + if (!(flags & CFLAG_MARKED)) + { + // Could a Xom worshipper ever get a stacked deck in the first + // place? xom_is_stimulated(amusement); - if ( deck.sub_type != MISC_DECK_OF_PUNISHMENT ) + // Nemelex likes gamblers. + if (!no_brownie) { - // Nemelex likes gamblers. brownie_points = 1; if (one_chance_in(3)) brownie_points++; } - } - else - { // You can't ID off a marked card allow_id = false; - - // draw the marked card - card_effect(static_cast<card_type>(deck.plus2 - 1), - deck_rarity(deck)); - - // If there are more marked cards, shift them up - if ( deck.special ) - { - const short next_card = (deck.special & 0xFF); - deck.special >>= 8; - deck.plus2 = next_card; - } - else - { - deck.plus2 = 0; - } - you.wield_change = true; - } - deck.plus--; - - if ( deck.plus == 0 ) - { - mpr("The deck of cards disappears in a puff of smoke."); - dec_inv_item_quantity( deck.link, 1 ); - // Finishing the deck will earn a point, even if it - // was marked or stacked. - brownie_points++; } - else if (allow_id && (you.skills[SK_EVOCATIONS] > 5 + random2(35))) + + if (!deck_gone && allow_id + && (you.skills[SK_EVOCATIONS] > 5 + random2(35))) { mpr("Your skill with magical items lets you identify the deck."); set_ident_flags( deck, ISFLAG_KNOW_TYPE ); msg::streams(MSGCH_EQUIPMENT) << deck.name(DESC_INVENTORY) << std::endl; - you.wield_change = true; } did_god_conduct(DID_CARDS, brownie_points); + + // Always wield change, since the number of cards used/left has + // changed. + you.wield_change = true; } int get_power_level(int power, deck_rarity_type rarity) @@ -1580,7 +2013,8 @@ static int card_power(deck_rarity_type rarity) return result; } -void card_effect(card_type which_card, deck_rarity_type rarity) +void card_effect(card_type which_card, deck_rarity_type rarity, + unsigned char flags, bool tell_card) { const int power = card_power(rarity); #ifdef DEBUG_DIAGNOSTICS @@ -1589,8 +2023,9 @@ void card_effect(card_type which_card, deck_rarity_type rarity) << std::endl; #endif - msg::stream << "You have drawn " << card_name( which_card ) - << '.' << std::endl; + if (tell_card) + msg::stream << "You have drawn " << card_name( which_card ) + << '.' << std::endl; if (which_card == CARD_XOM && !crawl_state.is_god_acting()) { @@ -1607,7 +2042,8 @@ void card_effect(card_type which_card, deck_rarity_type rarity) switch (which_card) { - case CARD_BLANK: break; + case CARD_BLANK1: break; + case CARD_BLANK2: break; case CARD_PORTAL: portal_card(power, rarity); break; case CARD_WARP: warp_card(power, rarity); break; case CARD_SWAP: swap_monster_card(power, rarity); break; @@ -1689,7 +2125,8 @@ void card_effect(card_type which_card, deck_rarity_type rarity) break; } - if (you.religion == GOD_XOM && which_card == CARD_BLANK) + if (you.religion == GOD_XOM + && (which_card == CARD_BLANK1 || which_card == CARD_BLANK2)) { god_speaks(GOD_XOM, "\"How boring, lets spice things up a little.\""); @@ -1699,6 +2136,34 @@ void card_effect(card_type which_card, deck_rarity_type rarity) return; } +bool top_card_is_known(const item_def &deck) +{ + if (!is_deck(deck)) + return false; + + ASSERT(cards_in_deck(deck) > 0); + + unsigned char flags; + get_card_and_flags(deck, -1, flags); + + return (flags & CFLAG_MARKED); +} + +card_type top_card(const item_def &deck) +{ + if (!is_deck(deck)) + return NUM_CARDS; + + ASSERT(cards_in_deck(deck) > 0); + + unsigned char flags; + card_type card = get_card_and_flags(deck, -1, flags); + + UNUSED(flags); + + return card; +} + bool is_deck(const item_def &item) { return item.base_type == OBJ_MISCELLANY @@ -1706,19 +2171,20 @@ bool is_deck(const item_def &item) item.sub_type <= MISC_DECK_OF_DEFENSE); } +bool bad_deck(const item_def &item) +{ + if (!is_deck(item)) + return false; + + return (!item.props.exists("cards") + || item.props["cards"].get_table().get_type() != HV_BYTE); +} + deck_rarity_type deck_rarity(const item_def &item) { ASSERT( is_deck(item) ); - switch (item.colour) - { - case BLACK: case BLUE: case GREEN: case CYAN: case RED: - default: - return DECK_RARITY_COMMON; - case MAGENTA: case BROWN: - return DECK_RARITY_RARE; - case LIGHTMAGENTA: - return DECK_RARITY_LEGENDARY; - } + + return static_cast<deck_rarity_type>(item.special); } unsigned char deck_rarity_to_color(deck_rarity_type rarity) @@ -1727,7 +2193,7 @@ unsigned char deck_rarity_to_color(deck_rarity_type rarity) { case DECK_RARITY_COMMON: { - const unsigned char colours[] = {BLACK, BLUE, GREEN, CYAN, RED}; + const unsigned char colours[] = {LIGHTBLUE, GREEN, CYAN, RED}; return RANDOM_ELEMENT(colours); } @@ -1743,3 +2209,42 @@ unsigned char deck_rarity_to_color(deck_rarity_type rarity) return (WHITE); } + +void init_deck(item_def &item) +{ + CrawlHashTable &props = item.props; + + ASSERT(is_deck(item)); + ASSERT(!props.exists("cards")); + ASSERT(item.plus > 0); + ASSERT(item.plus <= 127); + ASSERT(item.special >= DECK_RARITY_COMMON + && item.special <= DECK_RARITY_LEGENDARY); + + props.set_default_flags(HFLAG_CONST_TYPE); + + props["cards"].new_table(HV_BYTE); + props["card_flags"].new_table(HV_BYTE); + + for (int i = 0; i < item.plus; i++) + { + bool was_odd = false; + card_type card = random_card(item, was_odd); + + unsigned char flags = 0; + if (was_odd) + flags = CFLAG_ODDITY; + + set_card_and_flags(item, i, card, flags); + } + + ASSERT(cards_in_deck(item) == (unsigned long) item.plus); + + props["num_marked"] = (char) 0; + props["non_brownie_draws"] = (char) 0; + + props.assert_validity(); + + item.plus2 = 0; + item.colour = deck_rarity_to_color((deck_rarity_type) item.special); +} |