summaryrefslogtreecommitdiffstats
path: root/crawl-ref
diff options
context:
space:
mode:
authorzelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573>2007-10-08 05:55:47 +0000
committerzelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573>2007-10-08 05:55:47 +0000
commite7240df9978d7ba42ed7dcfdf9ab385b2ce74d36 (patch)
tree31ecdc6a9945ba6bfb8e1b35cb484808df1f4f57 /crawl-ref
parent16a8b1fe4a5acec73e490b9f7d3e53f567673763 (diff)
downloadcrawl-ref-e7240df9978d7ba42ed7dcfdf9ab385b2ce74d36.tar.gz
crawl-ref-e7240df9978d7ba42ed7dcfdf9ab385b2ce74d36.zip
Added class CrawlHashTable, a savable/loadable string-keyed
associative array with heterogeneous values, capable of holding booleans, bytes, shorts, longs, floats, string, coordinates (coord_def), items (item_def) and nested hash tables. A table can be made to be homogeneous by giving it a value type, which causes dynamic type checking to be performed. The same type checking can be performed for individual member values of a heterogeneous table by setting a flag on that value. A flag can also be set on an individual member value to prevent its value from being changed. CrawlHashTable is currently only used for the props field of the item_def struct (though it could easily be added elsewhere), and is only being used by decks. The deck structure has been changed so that deck.plus is the original number of cards in the deck, deck.plus2 is either the number of cards used or the number of cards left, and deck.special hold the deck's rarity. The cards themselves are selected at deck creation time and stored in the nested hash table deck.props["cards"]. The Nemelex "Peek Deck" ability has been changed to identify the deck, draw three cards from it, show them to the user, and shuffle them back into the deck (with special cases for decks containing only one or two cards). A fourth Nemelex ability, "Mark Deck", has been added, which picks four cards from the deck, marks them, and then shuffles them back into the deck, creating a deck with a mixture of marked and unmarked cards. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@2370 c06c8d41-db1a-0410-9941-cceddc491573
Diffstat (limited to 'crawl-ref')
-rw-r--r--crawl-ref/source/abl-show.cc19
-rw-r--r--crawl-ref/source/debug.cc17
-rw-r--r--crawl-ref/source/decks.cc1037
-rw-r--r--crawl-ref/source/decks.h88
-rw-r--r--crawl-ref/source/enum.h59
-rw-r--r--crawl-ref/source/externs.h3
-rw-r--r--crawl-ref/source/hash.cc1260
-rw-r--r--crawl-ref/source/hash.h308
-rw-r--r--crawl-ref/source/itemname.cc36
-rw-r--r--crawl-ref/source/makefile.obj1
-rw-r--r--crawl-ref/source/makeitem.cc17
-rw-r--r--crawl-ref/source/religion.cc11
-rw-r--r--crawl-ref/source/tags.cc58
-rw-r--r--crawl-ref/source/tags.h2
14 files changed, 2526 insertions, 390 deletions
diff --git a/crawl-ref/source/abl-show.cc b/crawl-ref/source/abl-show.cc
index 2bee608be0..be68ecf8fd 100644
--- a/crawl-ref/source/abl-show.cc
+++ b/crawl-ref/source/abl-show.cc
@@ -146,7 +146,7 @@ ability_type god_abilities[MAX_NUM_GODS][MAX_GOD_ABILITIES] =
ABIL_TROG_BROTHERS_IN_ARMS, ABIL_NON_ABILITY },
// Nemelex
{ ABIL_NEMELEX_PEEK_DECK, ABIL_NEMELEX_DRAW_CARD,
- ABIL_NEMELEX_TRIPLE_DRAW, ABIL_NON_ABILITY,
+ ABIL_NEMELEX_TRIPLE_DRAW, ABIL_NEMELEX_MARK_DECK,
ABIL_NEMELEX_STACK_DECK },
// Elyvilon
{ ABIL_ELYVILON_LESSER_HEALING, ABIL_ELYVILON_PURIFICATION,
@@ -299,6 +299,7 @@ static const ability_def Ability_List[] =
{ ABIL_NEMELEX_PEEK_DECK, "Deck Peek", 3, 0, 0, 1, ABFLAG_INSTANT },
{ ABIL_NEMELEX_DRAW_CARD, "Draw Card", 2, 0, 0, 0, ABFLAG_NONE },
{ ABIL_NEMELEX_TRIPLE_DRAW, "Triple Draw", 2, 0, 100, 2, ABFLAG_NONE },
+ { ABIL_NEMELEX_MARK_DECK, "Mark Deck", 4, 0, 125, 5, ABFLAG_NONE },
{ ABIL_NEMELEX_STACK_DECK, "Stack Deck", 5, 0, 150, 6, ABFLAG_NONE },
// Beogh
@@ -728,9 +729,10 @@ static talent get_talent(ability_type ability, bool check_confused)
failure = 80 - (you.piety / 25) - (4 * you.skills[SK_EVOCATIONS]);
break;
- case ABIL_NEMELEX_PEEK_DECK:
+ case ABIL_NEMELEX_MARK_DECK:
invoc = true;
- failure = 40 - (you.piety / 20) - (5 * you.skills[SK_EVOCATIONS]);
+ failure = 70 - (you.piety * 2 / 45)
+ - (9 * you.skills[SK_EVOCATIONS] / 2);
break;
case ABIL_NEMELEX_TRIPLE_DRAW:
@@ -738,6 +740,11 @@ static talent get_talent(ability_type ability, bool check_confused)
failure = 60 - (you.piety / 20) - (5 * you.skills[SK_EVOCATIONS]);
break;
+ case ABIL_NEMELEX_PEEK_DECK:
+ invoc = true;
+ failure = 40 - (you.piety / 20) - (5 * you.skills[SK_EVOCATIONS]);
+ break;
+
case ABIL_NEMELEX_DRAW_CARD:
invoc = true;
perfect = true; // Tactically important to allow perfection
@@ -1679,6 +1686,12 @@ static bool do_ability(const ability_def& abil)
exercise(SK_EVOCATIONS, 2 + random2(2));
break;
+ case ABIL_NEMELEX_MARK_DECK:
+ if ( !deck_mark() )
+ return false;
+ exercise(SK_EVOCATIONS, 4 + random2(4));
+ break;
+
case ABIL_NEMELEX_STACK_DECK:
if ( !deck_stack() )
return false;
diff --git a/crawl-ref/source/debug.cc b/crawl-ref/source/debug.cc
index c493ed5d81..a0c4ed14d4 100644
--- a/crawl-ref/source/debug.cc
+++ b/crawl-ref/source/debug.cc
@@ -822,7 +822,7 @@ static void deck_from_specs(const char* _specs, item_def &item)
NUM_MISCELLANY
};
- item.colour = BLACK;
+ item.special = DECK_RARITY_COMMON;
item.sub_type = NUM_MISCELLANY;
if (type_str != "")
@@ -920,9 +920,20 @@ static void deck_from_specs(const char* _specs, item_def &item)
int base = static_cast<int>(DECK_RARITY_COMMON);
deck_rarity_type rarity =
static_cast<deck_rarity_type>(base + rarity_val);
- item.colour = deck_rarity_to_color(rarity);
+ item.special = rarity;
- item.plus = 4 + random2(10);
+ int num = debug_prompt_for_int("How many cards? ", false);
+
+ if (num <= 0)
+ {
+ canned_msg( MSG_OK );
+ item.base_type = OBJ_UNASSIGNED;
+ return;
+ }
+
+ item.plus = num;
+
+ init_deck(item);
}
static void rune_or_deck_from_specs(const char* specs, item_def &item)
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);
+}
diff --git a/crawl-ref/source/decks.h b/crawl-ref/source/decks.h
index 47c6d646ac..df5b3942e5 100644
--- a/crawl-ref/source/decks.h
+++ b/crawl-ref/source/decks.h
@@ -17,6 +17,19 @@
#include "externs.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.
+
enum deck_rarity_type
{
DECK_RARITY_COMMON,
@@ -34,17 +47,90 @@ enum deck_type
DECK_OF_WONDERS
};
+enum card_flags_type
+{
+ CFLAG_ODDITY = (1 << 0),
+ CFLAG_SEEN = (1 << 1),
+ CFLAG_MARKED = (1 << 2)
+};
+
+enum card_type
+{
+ CARD_BLANK1 = 0, // non-retried
+ CARD_BLANK2, // retried and failed
+ CARD_PORTAL, // "the mover"
+ CARD_WARP, // "the jumper"
+ CARD_SWAP, // "swap"
+ CARD_VELOCITY, // "the runner"
+
+ CARD_TOMB, // "the wall"
+ CARD_BANSHEE, // "the scream"
+ CARD_DAMNATION, // banishment
+ CARD_SOLITUDE, // dispersal
+ CARD_WARPWRIGHT, // create teleport trap
+
+ CARD_VITRIOL, // acid damage
+ CARD_FLAME, // fire damage
+ CARD_FROST, // cold damage
+ CARD_VENOM, // poison damage
+ CARD_HAMMER, // pure damage
+
+ CARD_ELIXIR, // healing
+ CARD_BATTLELUST, // melee boosts
+ CARD_METAMORPHOSIS, // transformation
+ CARD_HELM, // defense
+ CARD_BLADE, // weapon boosts
+ CARD_SHADOW, // assassin skills
+
+ CARD_SUMMON_ANIMAL,
+ CARD_SUMMON_DEMON,
+ CARD_SUMMON_WEAPON,
+ CARD_SUMMON_ANY,
+
+ CARD_POTION,
+ CARD_FOCUS,
+ CARD_SHUFFLE,
+
+ CARD_EXPERIENCE,
+ CARD_WILD_MAGIC,
+ CARD_HELIX, // remove one *bad* mutation
+
+ CARD_MAP, // magic mapping
+ CARD_DOWSING, // detect SD/traps/items/monsters
+ CARD_SPADE, // dig
+ CARD_TROWEL, // create feature/vault
+ CARD_MINEFIELD, // plant traps
+
+ CARD_GENIE, // acquirement OR rotting/deterioration
+ CARD_BARGAIN, // shopping discount
+ CARD_WRATH, // Godly wrath
+ CARD_WRAITH, // drain XP
+ CARD_XOM,
+ CARD_FEAST,
+ CARD_FAMINE,
+ CARD_CURSE, // Curse your items
+
+ NUM_CARDS
+};
+
const char* card_name(card_type card);
void evoke_deck(item_def& deck);
bool deck_triple_draw();
bool deck_peek();
+bool deck_mark();
bool deck_stack();
bool choose_deck_and_draw();
-void card_effect(card_type which_card, deck_rarity_type rarity);
+void card_effect(card_type which_card, deck_rarity_type rarity,
+ unsigned char card_flags = 0, bool tell_card = true);
void draw_from_deck_of_punishment();
+bool top_card_is_known(const item_def &item);
+card_type top_card(const item_def &item);
+
bool is_deck(const item_def &item);
+bool bad_deck(const item_def &item);
deck_rarity_type deck_rarity(const item_def &item);
unsigned char deck_rarity_to_color(deck_rarity_type rarity);
+void init_deck(item_def &item);
#endif
diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h
index dbb0e9aff2..21983398ac 100644
--- a/crawl-ref/source/enum.h
+++ b/crawl-ref/source/enum.h
@@ -121,6 +121,7 @@ enum ability_type
ABIL_NEMELEX_PEEK_DECK,
ABIL_NEMELEX_DRAW_CARD,
ABIL_NEMELEX_TRIPLE_DRAW,
+ ABIL_NEMELEX_MARK_DECK,
ABIL_NEMELEX_STACK_DECK,
ABIL_BEOGH_SMITING,
ABIL_BEOGH_RECALL_ORCISH_FOLLOWERS,
@@ -358,64 +359,6 @@ enum canned_message_type
MSG_EMPTY_HANDED
};
-enum card_type
-{
- CARD_BLANK = 0,
- CARD_PORTAL, // "the mover"
- CARD_WARP, // "the jumper"
- CARD_SWAP, // "swap"
- CARD_VELOCITY, // "the runner"
-
- CARD_TOMB, // "the wall"
- CARD_BANSHEE, // "the scream"
- CARD_DAMNATION, // banishment
- CARD_SOLITUDE, // dispersal
- CARD_WARPWRIGHT, // create teleport trap
-
- CARD_VITRIOL, // acid damage
- CARD_FLAME, // fire damage
- CARD_FROST, // cold damage
- CARD_VENOM, // poison damage
- CARD_HAMMER, // pure damage
-
- CARD_ELIXIR, // healing
- CARD_BATTLELUST, // melee boosts
- CARD_METAMORPHOSIS, // transformation
- CARD_HELM, // defense
- CARD_BLADE, // weapon boosts
- CARD_SHADOW, // assassin skills
-
- CARD_SUMMON_ANIMAL,
- CARD_SUMMON_DEMON,
- CARD_SUMMON_WEAPON,
- CARD_SUMMON_ANY,
-
- CARD_POTION,
- CARD_FOCUS,
- CARD_SHUFFLE,
-
- CARD_EXPERIENCE,
- CARD_WILD_MAGIC,
- CARD_HELIX, // remove one *bad* mutation
-
- CARD_MAP, // magic mapping
- CARD_DOWSING, // detect SD/traps/items/monsters
- CARD_SPADE, // dig
- CARD_TROWEL, // create feature/vault
- CARD_MINEFIELD, // plant traps
-
- CARD_GENIE, // acquirement OR rotting/deterioration
- CARD_BARGAIN, // shopping discount
- CARD_WRATH, // Godly wrath
- CARD_WRAITH, // drain XP
- CARD_XOM,
- CARD_FEAST,
- CARD_FAMINE,
- CARD_CURSE, // Curse your items
-
- NUM_CARDS
-};
-
enum char_set_type
{
CSET_ASCII, // flat 7-bit ASCII
diff --git a/crawl-ref/source/externs.h b/crawl-ref/source/externs.h
index 5bff06df19..5a6cfca733 100644
--- a/crawl-ref/source/externs.h
+++ b/crawl-ref/source/externs.h
@@ -30,6 +30,7 @@
#include "defines.h"
#include "enum.h"
#include "FixAry.h"
+#include "hash.h"
#include "libutil.h"
#include "mpr.h"
@@ -354,6 +355,8 @@ struct item_def
std::string inscription;
+ CrawlHashTable props;
+
public:
item_def() : base_type(OBJ_UNASSIGNED), sub_type(0), plus(0), plus2(0),
special(0L), colour(0), flags(0L), quantity(0),
diff --git a/crawl-ref/source/hash.cc b/crawl-ref/source/hash.cc
new file mode 100644
index 0000000000..60a5816f9c
--- /dev/null
+++ b/crawl-ref/source/hash.cc
@@ -0,0 +1,1260 @@
+/*
+ * File: hash.cc
+ * Summary: Saveable hash-table capable of storing multiple types
+ * of data.
+ * Written by: Matthew Cline
+ *
+ * Modified for Crawl Reference by $Author$ on $Date$
+ *
+ * Change History (most recent first):
+
+ * <1> 10/5/07 MPC Created
+ */
+
+#include "AppHdr.h"
+#include "hash.h"
+
+#include "externs.h"
+#include "tags.h"
+
+CrawlHashValue::CrawlHashValue()
+ : type(HV_NONE), flags(HFLAG_UNSET)
+{
+ val.ptr = NULL;
+}
+
+CrawlHashValue::CrawlHashValue(const CrawlHashValue &other)
+{
+ ASSERT(other.type >= HV_NONE && other.type < NUM_HASH_VAL_TYPES);
+
+ type = other.type;
+ flags = other.flags;
+
+ if (flags & HFLAG_UNSET)
+ {
+ val = other.val;
+ return;
+ }
+
+ switch (type)
+ {
+ case HV_NONE:
+ case HV_BOOL:
+ case HV_BYTE:
+ case HV_SHORT:
+ case HV_LONG:
+ case HV_FLOAT:
+ val = other.val;
+ break;
+
+ case HV_STR:
+ {
+ std::string* str;
+ str = new std::string(*static_cast<std::string*>(other.val.ptr));
+ val.ptr = static_cast<void*>(str);
+ break;
+ }
+
+ case HV_COORD:
+ {
+ coord_def* coord;
+ coord = new coord_def(*static_cast<coord_def*>(other.val.ptr));
+ val.ptr = static_cast<void*>(coord);
+ break;
+ }
+
+ case HV_HASH:
+ {
+ CrawlHashTable* hash;
+ CrawlHashTable* tmp = static_cast<CrawlHashTable*>(other.val.ptr);
+ hash = new CrawlHashTable(*tmp);
+ val.ptr = static_cast<void*>(hash);
+ break;
+ }
+
+ case HV_ITEM:
+ {
+ item_def* item;
+ item = new item_def(*static_cast<item_def*>(other.val.ptr));
+ val.ptr = static_cast<void*>(item);
+ break;
+ }
+
+ case NUM_HASH_VAL_TYPES:
+ ASSERT(false);
+ }
+}
+
+CrawlHashValue::CrawlHashValue(const unsigned char _flags,
+ const hash_val_type _type)
+ : type(_type), flags(_flags)
+{
+ ASSERT(type >= HV_NONE && type < NUM_HASH_VAL_TYPES);
+ ASSERT(!(flags & HFLAG_UNSET));
+
+ flags |= HFLAG_UNSET;
+ val.ptr = NULL;
+}
+
+CrawlHashValue::~CrawlHashValue()
+{
+ unset(true);
+}
+
+void CrawlHashValue::unset(bool force)
+{
+ if (flags & HFLAG_UNSET)
+ return;
+
+ if (force)
+ flags &= ~HFLAG_NO_ERASE;
+
+ ASSERT(!(flags & HFLAG_NO_ERASE));
+
+ switch (type)
+ {
+ case HV_BOOL:
+ val.boolean = false;
+ break;
+
+ case HV_BYTE:
+ val.byte = 0;
+ break;
+
+ case HV_SHORT:
+ val._short = 0;
+ break;
+
+ case HV_LONG:
+ val._long = 0;
+ break;
+
+ case HV_FLOAT:
+ val._float = 0.0;
+ break;
+
+ case HV_STR:
+ {
+ std::string* str = static_cast<std::string*>(val.ptr);
+ delete str;
+ val.ptr = NULL;
+ break;
+ }
+
+ case HV_COORD:
+ {
+ coord_def* coord = static_cast<coord_def*>(val.ptr);
+ delete coord;
+ val.ptr = NULL;
+ break;
+ }
+
+ case HV_HASH:
+ {
+ CrawlHashTable* hash = static_cast<CrawlHashTable*>(val.ptr);
+ delete hash;
+ val.ptr = NULL;
+ break;
+ }
+
+ case HV_ITEM:
+ {
+ item_def* item = static_cast<item_def*>(val.ptr);
+ delete item;
+ val.ptr = NULL;
+ break;
+ }
+
+ case HV_NONE:
+ ASSERT(false);
+
+ case NUM_HASH_VAL_TYPES:
+ ASSERT(false);
+ }
+
+ flags |= HFLAG_UNSET;
+}
+
+// Only needed to do some assertion checking.
+CrawlHashValue &CrawlHashValue::operator = (const CrawlHashValue &other)
+{
+ ASSERT(other.type >= HV_NONE && other.type < NUM_HASH_VAL_TYPES);
+ ASSERT(other.type != HV_NONE || type == HV_NONE);
+
+ // NOTE: We don't bother checking HFLAG_CONST_VAL, since the
+ // asignment operator is used when swapping two elements.
+
+ if (!(flags & HFLAG_UNSET))
+ {
+ if (flags & HFLAG_CONST_TYPE)
+ ASSERT(type == HV_NONE || type == other.type);
+ }
+
+ type = other.type;
+ flags = other.flags;
+ val = other.val;
+
+ return (*this);
+}
+
+///////////////////////////////////
+// Meta-data accessors and changers
+unsigned char CrawlHashValue::get_flags() const
+{
+ return flags;
+}
+
+unsigned char CrawlHashValue::set_flags(unsigned char _flags)
+{
+ flags |= _flags;
+ return flags;
+}
+
+unsigned char CrawlHashValue::unset_flags(unsigned char _flags)
+{
+ flags &= ~_flags;
+ return flags;
+}
+
+hash_val_type CrawlHashValue::get_type() const
+{
+ return type;
+}
+
+//////////////////////////////
+// Read/write from/to savefile
+void CrawlHashValue::write(tagHeader &th) const
+{
+ ASSERT(!(flags & HFLAG_UNSET));
+
+ marshallByte(th, (char) type);
+ marshallByte(th, (char) flags);
+
+ switch (type)
+ {
+ case HV_BOOL:
+ marshallBoolean(th, val.boolean);
+ break;
+
+ case HV_BYTE:
+ marshallByte(th, val.byte);
+ break;
+
+ case HV_SHORT:
+ marshallShort(th, val._short);
+ break;
+
+ case HV_LONG:
+ marshallLong(th, val._long);
+ break;
+
+ case HV_FLOAT:
+ marshallFloat(th, val._float);
+ break;
+
+ case HV_STR:
+ {
+ std::string* str = static_cast<std::string*>(val.ptr);
+ marshallString(th, *str);
+ break;
+ }
+
+ case HV_COORD:
+ {
+ coord_def* coord = static_cast<coord_def*>(val.ptr);
+ marshallCoord(th, *coord);
+ break;
+ }
+
+ case HV_HASH:
+ {
+ CrawlHashTable* hash = static_cast<CrawlHashTable*>(val.ptr);
+ hash->write(th);
+ break;
+ }
+
+ case HV_ITEM:
+ {
+ item_def* item = static_cast<item_def*>(val.ptr);
+ marshallItem(th, *item);
+ break;
+ }
+
+ case HV_NONE:
+ ASSERT(false);
+
+ case NUM_HASH_VAL_TYPES:
+ ASSERT(false);
+ }
+}
+
+void CrawlHashValue::read(tagHeader &th)
+{
+ type = static_cast<hash_val_type>(unmarshallByte(th));
+ flags = (unsigned char) unmarshallByte(th);
+
+ ASSERT(!(flags & HFLAG_UNSET));
+
+ switch (type)
+ {
+ case HV_BOOL:
+ val.boolean = unmarshallBoolean(th);
+ break;
+
+ case HV_BYTE:
+ val.byte = unmarshallByte(th);
+ break;
+
+ case HV_SHORT:
+ val._short = unmarshallShort(th);
+ break;
+
+ case HV_LONG:
+ val._long = unmarshallLong(th);
+ break;
+
+ case HV_FLOAT:
+ val._float = unmarshallFloat(th);
+ break;
+
+ case HV_STR:
+ {
+ std::string str = unmarshallString(th);
+ val.ptr = (void*) new std::string(str);
+ break;
+ }
+
+ case HV_COORD:
+ {
+ coord_def coord;
+ unmarshallCoord(th, coord);
+ val.ptr = (void*) new coord_def(coord);
+
+ break;
+ }
+
+ case HV_HASH:
+ {
+ CrawlHashTable* hash = new CrawlHashTable();
+ hash->read(th);
+ val.ptr = (void*) hash;
+
+ break;
+ }
+
+ case HV_ITEM:
+ {
+ item_def item;
+ unmarshallItem(th, item);
+ val.ptr = (void*) new item_def(item);
+
+ break;
+ }
+
+ case HV_NONE:
+ ASSERT(false);
+
+ case NUM_HASH_VAL_TYPES:
+ ASSERT(false);
+ }
+}
+
+////////////////////////////////////////////////////////////////
+// Setup a new table with the given flags and/or type; assert if
+// a table already exists.
+CrawlHashTable &CrawlHashValue::new_table(unsigned char _flags)
+{
+ return new_table(HV_NONE, flags);
+}
+
+CrawlHashTable &CrawlHashValue::new_table(hash_val_type _type,
+ unsigned char _flags)
+{
+ CrawlHashTable* old_table = static_cast<CrawlHashTable*>(val.ptr);
+
+ ASSERT(flags & HFLAG_UNSET);
+ ASSERT(type == HV_NONE
+ || (type == HV_HASH
+ && old_table->size() == 0
+ && old_table->get_type() == HV_NONE
+ && old_table->get_default_flags() == 0));
+
+ CrawlHashTable &table = get_table();
+
+ table.default_flags = _flags;
+ table.type = _type;
+
+ type = HV_HASH;
+ flags &= ~HFLAG_UNSET;
+
+ return table;
+}
+
+///////////////////////////////////////////
+// Dynamic type-checking accessor functions
+#define GET_VAL(x, _type, field, value) \
+ ASSERT((flags & HFLAG_UNSET) || !(flags & HFLAG_CONST_VAL)); \
+ if (type != (x)) \
+ { \
+ if (type == HV_NONE) \
+ { \
+ type = (x); \
+ field = (value); \
+ } \
+ else \
+ { \
+ ASSERT(!(flags & HFLAG_CONST_TYPE)); \
+ switch(type) \
+ { \
+ case HV_BOOL: \
+ field = (_type) val.boolean; \
+ break; \
+ case HV_BYTE: \
+ field = (_type) val.byte; \
+ break; \
+ case HV_SHORT: \
+ field = (_type) val._short; \
+ break; \
+ case HV_LONG: \
+ field = (_type) val._long; \
+ break; \
+ case HV_FLOAT: \
+ field = (_type) val._float; \
+ break; \
+ default: \
+ ASSERT(false); \
+ } \
+ type = (x); \
+ } \
+ } \
+ flags &= ~HFLAG_UNSET; \
+ return field;
+
+#define GET_VAL_PTR(x, _type, value) \
+ ASSERT((flags & HFLAG_UNSET) || !(flags & HFLAG_CONST_VAL)); \
+ if (type != (x)) \
+ { \
+ if (type == HV_NONE) \
+ { \
+ type = (x); \
+ val.ptr = (value); \
+ } \
+ else \
+ { \
+ unset(); \
+ val.ptr = (value); \
+ type = (x); \
+ } \
+ } \
+ flags &= ~HFLAG_UNSET; \
+ return *((_type) val.ptr);
+
+bool &CrawlHashValue::get_bool()
+{
+ GET_VAL(HV_BOOL, bool, val.boolean, false);
+}
+
+char &CrawlHashValue::get_byte()
+{
+ GET_VAL(HV_BYTE, char, val.byte, 0);
+}
+
+short &CrawlHashValue::get_short()
+{
+ GET_VAL(HV_SHORT, short, val._short, 0);
+}
+
+long &CrawlHashValue::get_long()
+{
+ GET_VAL(HV_LONG, long, val._long, 0);
+}
+
+float &CrawlHashValue::get_float()
+{
+ GET_VAL(HV_FLOAT, float, val._float, 0.0);
+}
+
+std::string &CrawlHashValue::get_string()
+{
+ GET_VAL_PTR(HV_STR, std::string*, new std::string(""));
+}
+
+coord_def &CrawlHashValue::get_coord()
+{
+ GET_VAL_PTR(HV_COORD, coord_def*, new coord_def());
+}
+
+CrawlHashTable &CrawlHashValue::get_table()
+{
+ GET_VAL_PTR(HV_HASH, CrawlHashTable*, new CrawlHashTable());
+}
+
+item_def &CrawlHashValue::get_item()
+{
+ GET_VAL_PTR(HV_ITEM, item_def*, new item_def());
+}
+
+///////////////////////////
+// Const accessor functions
+#define GET_CONST_SETUP(x) \
+ ASSERT(!(flags & HFLAG_UNSET)); \
+ ASSERT(type == (x));
+
+bool CrawlHashValue::get_bool() const
+{
+ GET_CONST_SETUP(HV_BOOL);
+ return val.boolean;
+}
+
+char CrawlHashValue::get_byte() const
+{
+ GET_CONST_SETUP(HV_BYTE);
+ return val.byte;
+}
+
+short CrawlHashValue::get_short() const
+{
+ GET_CONST_SETUP(HV_SHORT);
+ return val._short;
+}
+
+long CrawlHashValue::get_long() const
+{
+ GET_CONST_SETUP(HV_LONG);
+ return val._long;
+}
+
+float CrawlHashValue::get_float() const
+{
+ GET_CONST_SETUP(HV_FLOAT);
+ return val._float;
+}
+
+std::string CrawlHashValue::get_string() const
+{
+ GET_CONST_SETUP(HV_STR);
+ return *((std::string*)val.ptr);
+}
+
+coord_def CrawlHashValue::get_coord() const
+{
+ GET_CONST_SETUP(HV_COORD);
+ return *((coord_def*)val.ptr);
+}
+
+const CrawlHashTable& CrawlHashValue::get_table() const
+{
+ GET_CONST_SETUP(HV_HASH);
+ return *((CrawlHashTable*)val.ptr);
+}
+
+const item_def& CrawlHashValue::get_item() const
+{
+ GET_CONST_SETUP(HV_ITEM);
+ return *((item_def*)val.ptr);
+}
+
+/////////////////////
+// Typecast operators
+&CrawlHashValue::operator bool()
+{
+ return get_bool();
+}
+
+&CrawlHashValue::operator char()
+{
+ return get_byte();
+}
+
+&CrawlHashValue::operator short()
+{
+ return get_short();
+}
+
+&CrawlHashValue::operator float()
+{
+ return get_float();
+}
+
+&CrawlHashValue::operator long()
+{
+ return get_long();
+}
+
+&CrawlHashValue::operator std::string()
+{
+ return get_string();
+}
+
+&CrawlHashValue::operator coord_def()
+{
+ return get_coord();
+}
+
+&CrawlHashValue::operator CrawlHashTable()
+{
+ return get_table();
+}
+
+&CrawlHashValue::operator item_def()
+{
+ return get_item();
+}
+
+///////////////////////////
+// Const typecast operators
+CrawlHashValue::operator bool() const
+{
+ return get_bool();
+}
+
+#define CONST_INT_CAST() \
+ switch(type) \
+ { \
+ case HV_BYTE: \
+ return get_byte(); \
+ case HV_SHORT: \
+ return get_short(); \
+ case HV_LONG: \
+ return get_long(); \
+ default: \
+ ASSERT(false); \
+ return 0; \
+ }
+
+CrawlHashValue::operator char() const
+{
+ CONST_INT_CAST();
+}
+
+CrawlHashValue::operator short() const
+{
+ CONST_INT_CAST();
+}
+
+CrawlHashValue::operator long() const
+{
+ CONST_INT_CAST();
+}
+
+CrawlHashValue::operator float() const
+{
+ return get_float();
+}
+
+CrawlHashValue::operator std::string() const
+{
+ return get_string();
+}
+
+CrawlHashValue::operator coord_def() const
+{
+ return get_coord();
+}
+
+///////////////////////
+// Assignment operators
+bool &CrawlHashValue::operator = (const bool &_val)
+{
+ return (get_bool() = _val);
+}
+
+char &CrawlHashValue::operator = (const char &_val)
+{
+ return (get_byte() = _val);
+}
+
+short &CrawlHashValue::operator = (const short &_val)
+{
+ return (get_short() = _val);
+}
+
+long &CrawlHashValue::operator = (const long &_val)
+{
+ return (get_long() = _val);
+}
+
+float &CrawlHashValue::operator = (const float &_val)
+{
+ return (get_float() = _val);
+}
+
+std::string &CrawlHashValue::operator = (const std::string &_val)
+{
+ return (get_string() = _val);
+}
+
+const char* CrawlHashValue::operator = (const char* _val)
+{
+ get_string() = _val;
+ return get_string().c_str();
+}
+
+coord_def &CrawlHashValue::operator = (const coord_def &_val)
+{
+ return (get_coord() = _val);
+}
+
+CrawlHashTable &CrawlHashValue::operator = (const CrawlHashTable &_val)
+{
+ return (get_table() = _val);
+}
+
+item_def &CrawlHashValue::operator = (const item_def &_val)
+{
+ return (get_item() = _val);
+}
+
+///////////////////////////////////////////////////
+// Non-assignment operators which affect the lvalue
+#define INT_OPERATOR_UNARY(op) \
+ switch(type) \
+ { \
+ case HV_BYTE: \
+ { \
+ char &temp = get_byte(); \
+ temp op; \
+ return temp; \
+ } \
+ \
+ case HV_SHORT: \
+ { \
+ short &temp = get_short(); \
+ temp op; \
+ return temp; \
+ } \
+ case HV_LONG: \
+ { \
+ long &temp = get_long(); \
+ temp op; \
+ return temp; \
+ } \
+ \
+ default: \
+ ASSERT(false); \
+ return 0; \
+ }
+
+// Prefix
+long CrawlHashValue::operator ++ ()
+{
+ INT_OPERATOR_UNARY(++);
+}
+
+long CrawlHashValue::operator -- ()
+{
+ INT_OPERATOR_UNARY(--);
+}
+
+// Postfix
+long CrawlHashValue::operator ++ (int)
+{
+ INT_OPERATOR_UNARY(++);
+}
+
+long CrawlHashValue::operator -- (int)
+{
+ INT_OPERATOR_UNARY(--);
+}
+
+std::string &CrawlHashValue::operator += (const std::string &_val)
+{
+ return (get_string() += _val);
+}
+
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+
+CrawlHashTable::CrawlHashTable()
+ : type(HV_NONE), default_flags(0)
+{
+}
+
+CrawlHashTable::CrawlHashTable(unsigned char flags)
+ : type(HV_NONE), default_flags(flags)
+{
+ ASSERT(!(default_flags & HFLAG_UNSET));
+}
+
+CrawlHashTable::CrawlHashTable(hash_val_type _type, unsigned char flags)
+ : type(_type), default_flags(flags)
+{
+ ASSERT(type >= HV_NONE && type < NUM_HASH_VAL_TYPES);
+ ASSERT(!(default_flags & HFLAG_UNSET));
+}
+
+CrawlHashTable::~CrawlHashTable()
+{
+ assert_validity();
+}
+
+//////////////////////////////
+// Read/write from/to savefile
+void CrawlHashTable::write(tagHeader &th) const
+{
+ assert_validity();
+ if (empty())
+ {
+ marshallByte(th, 0);
+ return;
+ }
+
+ marshallByte(th, size());
+ marshallByte(th, static_cast<char>(type));
+ marshallByte(th, (char) default_flags);
+
+ CrawlHashTable::hash_map_type::const_iterator i = hash_map.begin();
+
+ for (; i != hash_map.end(); i++)
+ {
+ marshallString(th, i->first);
+ i->second.write(th);
+ }
+
+ assert_validity();
+}
+
+void CrawlHashTable::read(tagHeader &th)
+{
+ assert_validity();
+
+ ASSERT(empty());
+ ASSERT(type == HV_NONE);
+ ASSERT(default_flags == 0);
+
+ unsigned char _size = (unsigned char) unmarshallByte(th);
+
+ if (_size == 0)
+ return;
+
+ type = static_cast<hash_val_type>(unmarshallByte(th));
+ default_flags = (unsigned char) unmarshallByte(th);
+
+ for (unsigned char i = 0; i < _size; i++)
+ {
+ std::string key = unmarshallString(th);
+ CrawlHashValue &val = (*this)[key];
+
+ val.read(th);
+ }
+
+ assert_validity();
+}
+
+
+//////////////////
+// Misc functions
+
+unsigned char CrawlHashTable::get_default_flags() const
+{
+ assert_validity();
+ return default_flags;
+}
+
+unsigned char CrawlHashTable::set_default_flags(unsigned char flags)
+{
+ assert_validity();
+ ASSERT(!(flags & HFLAG_UNSET));
+ default_flags |= flags;
+
+ return default_flags;
+}
+
+unsigned char CrawlHashTable::unset_default_flags(unsigned char flags)
+{
+ assert_validity();
+ ASSERT(!(flags & HFLAG_UNSET));
+ default_flags &= ~flags;
+
+ return default_flags;
+}
+
+hash_val_type CrawlHashTable::get_type() const
+{
+ assert_validity();
+ return type;
+}
+
+bool CrawlHashTable::exists(const std::string key) const
+{
+ assert_validity();
+ hash_map_type::const_iterator i = hash_map.find(key);
+
+ return (i != hash_map.end());
+}
+
+bool CrawlHashTable::exists(const long index) const
+{
+ char buf[12];
+ sprintf(buf, "%ld", index);
+ return exists(buf);
+}
+
+void CrawlHashTable::assert_validity() const
+{
+#if DEBUG
+ ASSERT(!(default_flags & HFLAG_UNSET));
+
+ hash_map_type::const_iterator i = hash_map.begin();
+
+ unsigned long actual_size = 0;
+
+ for (; i != hash_map.end(); i++)
+ {
+ actual_size++;
+
+ const std::string &key = i->first;
+ const CrawlHashValue &val = i->second;
+
+ ASSERT(key != "");
+ std::string trimmed = trimmed_string(key);
+ ASSERT(key == trimmed);
+
+ ASSERT(val.type != HV_NONE);
+ ASSERT(!(val.flags & HFLAG_UNSET));
+
+ switch(val.type)
+ {
+ case HV_STR:
+ case HV_COORD:
+ case HV_ITEM:
+ ASSERT(val.val.ptr != NULL);
+ break;
+
+ case HV_HASH:
+ {
+ ASSERT(val.val.ptr != NULL);
+
+ CrawlHashTable* nested;
+ nested = static_cast<CrawlHashTable*>(val.val.ptr);
+
+ nested->assert_validity();
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
+ ASSERT(size() == actual_size);
+
+ ASSERT(size() <= 255);
+#endif
+}
+
+int CrawlHashTable::compact_indicies(long min_index, long max_index,
+ bool compact_down)
+{
+ ASSERT(min_index <= max_index);
+
+ if (min_index == max_index)
+ return 0;
+
+ long min_exists, max_exists;
+
+ for (min_exists = min_index; min_exists <= max_index; min_exists++)
+ if (exists(min_exists))
+ break;
+
+ if (min_exists > max_index)
+ return 0;
+
+ for (max_exists = max_index; max_exists >= min_exists; max_exists--)
+ if (exists(max_exists))
+ break;
+
+ if (max_exists <= min_exists)
+ return 0;
+
+ bool hole_found = false;
+ for (long i = min_exists; i < max_exists; i++)
+ if (!exists(i))
+ {
+ hole_found = true;
+ break;
+ }
+
+ if (!hole_found)
+ return 0;
+
+ long start, stop, dir;
+ if (compact_down)
+ {
+ start = min_exists;
+ stop = max_exists;
+ dir = +1;
+ }
+ else
+ {
+ start = max_exists;
+ stop = min_exists;
+ dir = -1;
+ }
+ stop += dir;
+
+ long move = 0;
+ for (long i = start; i != stop; i += dir)
+ {
+ if (!exists(i))
+ {
+ move++;
+ continue;
+ }
+
+ if (move == 0)
+ continue;
+
+ char buf1[12], buf2[12];
+ sprintf(buf1, "%ld", i - (move * dir));
+ sprintf(buf2, "%ld", i);
+
+ CrawlHashValue &val = hash_map[buf2];
+ hash_map[buf1] = val;
+
+ // Ensure a new()'d object isn't freed, since the other
+ // CrawlHashValue now owns it.
+ val.type = HV_BOOL;
+ erase(buf2);
+ }
+
+ return move;
+}
+
+bool CrawlHashTable::fixup_indexed_array(std::string name)
+{
+ bool problems = false;
+ long max_index = 0;
+ std::vector<std::string> bad_keys;
+ for (hash_map_type::iterator i = begin(); i != end(); i++)
+ {
+ int index = strtol(i->first.c_str(), NULL, 10);
+
+ if (index < 0)
+ {
+ if (name != "")
+ mprf("Negative index %ld found in %s.",
+ index, name.c_str());
+ problems = true;
+ bad_keys.push_back(i->first);
+ }
+ else if (index == 0 && i->first != "0")
+ {
+ if (name != "")
+ mprf("Non-numerical index '%s' found in %s.",
+ i->first.c_str(), name.c_str());
+ problems = true;
+ bad_keys.push_back(i->first);
+ }
+ else if (index > max_index)
+ max_index = index;
+ }
+ for (unsigned long i = 0, _size = bad_keys.size(); i < _size; i++)
+ erase(bad_keys[i]);
+ int holes = compact_indicies(0, max_index);
+ if (holes > 0 && name != "")
+ mprf("%d holes found in %s.", holes, name.c_str());
+
+ return problems;
+}
+
+////////////
+// Accessors
+
+CrawlHashValue& CrawlHashTable::get_value(const std::string &key)
+{
+ assert_validity();
+ hash_map_type::iterator i = hash_map.find(key);
+
+ if (i == hash_map.end())
+ {
+ hash_map[key] = CrawlHashValue(default_flags);
+ CrawlHashValue &val = hash_map[key];
+
+ if (type != HV_NONE)
+ {
+ val.type = type;
+ val.flags |= HFLAG_CONST_TYPE;
+ }
+
+ return (val);
+ }
+
+ return (i->second);
+}
+
+CrawlHashValue& CrawlHashTable::get_value(const long &index)
+{
+ char buf[12];
+ sprintf(buf, "%ld", index);
+ return get_value(buf);
+}
+
+const CrawlHashValue& CrawlHashTable::get_value(const std::string &key) const
+{
+ assert_validity();
+ hash_map_type::const_iterator i = hash_map.find(key);
+
+ ASSERT(i != hash_map.end());
+ ASSERT(i->second.type != HV_NONE);
+ ASSERT(!(i->second.flags & HFLAG_UNSET));
+
+ return (i->second);
+}
+
+const CrawlHashValue& CrawlHashTable::get_value(const long &index) const
+{
+ char buf[12];
+ sprintf(buf, "%ld", index);
+ return get_value(buf);
+}
+
+CrawlHashValue& CrawlHashTable::operator[] (const std::string &key)
+{
+ return get_value(key);
+}
+
+CrawlHashValue& CrawlHashTable::operator[] (const long &index)
+{
+ return get_value(index);
+}
+
+const CrawlHashValue& CrawlHashTable::operator[] (const std::string &key) const
+{
+ return get_value(key);
+}
+
+const CrawlHashValue& CrawlHashTable::operator[] (const long &index) const
+{
+ return get_value(index);
+}
+
+///////////////////////////
+// std::map style interface
+size_t CrawlHashTable::size() const
+{
+ return hash_map.size();
+}
+
+bool CrawlHashTable::empty() const
+{
+ return hash_map.empty();
+}
+
+void CrawlHashTable::erase(const std::string key)
+{
+ assert_validity();
+ hash_map_type::iterator i = hash_map.find(key);
+
+ if (i != hash_map.end())
+ {
+ CrawlHashValue &val = i->second;
+
+ ASSERT(!(val.flags & HFLAG_NO_ERASE));
+
+ hash_map.erase(i);
+ }
+}
+
+void CrawlHashTable::erase(const long index)
+{
+ char buf[12];
+ sprintf(buf, "%ld", index);
+ erase(buf);
+}
+
+void CrawlHashTable::clear()
+{
+ assert_validity();
+ ASSERT(!(default_flags & HFLAG_NO_ERASE));
+
+ hash_map_type::iterator i = hash_map.begin();
+ for (; i != hash_map.end(); i++)
+ ASSERT(!(i->second.flags & HFLAG_NO_ERASE));
+
+ hash_map.clear();
+}
+
+CrawlHashTable::hash_map_type::iterator CrawlHashTable::begin()
+{
+ assert_validity();
+ return hash_map.begin();
+}
+
+CrawlHashTable::hash_map_type::iterator CrawlHashTable::end()
+{
+ assert_validity();
+ return hash_map.end();
+}
+
+CrawlHashTable::hash_map_type::const_iterator CrawlHashTable::begin() const
+{
+ assert_validity();
+ return hash_map.begin();
+}
+
+CrawlHashTable::hash_map_type::const_iterator CrawlHashTable::end() const
+{
+ assert_validity();
+ return hash_map.end();
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+template <typename T, hash_val_type TYPE>
+CrawlTableWrapper<T, TYPE>::CrawlTableWrapper()
+{
+ table = NULL;
+}
+
+template <typename T, hash_val_type TYPE>
+CrawlTableWrapper<T, TYPE>::CrawlTableWrapper(CrawlHashTable& _table)
+{
+ wrap(_table);
+}
+
+template <typename T, hash_val_type TYPE>
+CrawlTableWrapper<T, TYPE>::CrawlTableWrapper(CrawlHashTable* _table)
+{
+ wrap(_table);
+}
+
+template <typename T, hash_val_type TYPE>
+void CrawlTableWrapper<T, TYPE>::wrap(CrawlHashTable& _table)
+{
+ wrap(&_table);
+}
+
+template <typename T, hash_val_type TYPE>
+void CrawlTableWrapper<T, TYPE>::wrap(CrawlHashTable* _table)
+{
+ ASSERT(_table != NULL);
+ ASSERT(_table->get_type() == TYPE);
+
+ table = _table;
+}
+
+template <typename T, hash_val_type TYPE>
+T& CrawlTableWrapper<T, TYPE>::operator[] (const std::string &key)
+{
+ return (T&) (*table)[key];
+}
+
+template <typename T, hash_val_type TYPE>
+T& CrawlTableWrapper<T, TYPE>::operator[] (const long &index)
+{
+ return (T&) (*table)[index];
+}
+
+template <typename T, hash_val_type TYPE>
+const T CrawlTableWrapper<T, TYPE>::operator[] (const std::string &key) const
+{
+ return (T) (*table)[key];
+}
+
+template <typename T, hash_val_type TYPE>
+const T CrawlTableWrapper<T, TYPE>::operator[] (const long &index) const
+{
+ return (T) (*table)[index];
+}
diff --git a/crawl-ref/source/hash.h b/crawl-ref/source/hash.h
new file mode 100644
index 0000000000..7c658c1667
--- /dev/null
+++ b/crawl-ref/source/hash.h
@@ -0,0 +1,308 @@
+/*
+ * File: hash.h
+ * Summary: Saveable hash-table capable of storing multiple types
+ * of data.
+ * Written by: Matthew Cline
+ *
+ * Modified for Crawl Reference by $Author$ on $Date$
+ *
+ * Change History (most recent first):
+ *
+ * <1> 10/5/07 MPC Created
+ */
+
+#ifndef HASH_H
+#define HASH_H
+
+#include <string>
+#include <map>
+
+struct tagHeader;
+class CrawlHashTable;
+class item_def;
+class coord_def;
+
+// NOTE: Changing the ordering of these enums will break savefile
+// compatibility.
+enum hash_val_type
+{
+ HV_NONE = 0,
+ HV_BOOL,
+ HV_BYTE,
+ HV_SHORT,
+ HV_LONG,
+ HV_FLOAT,
+ HV_STR,
+ HV_COORD,
+ HV_HASH,
+ HV_ITEM,
+ NUM_HASH_VAL_TYPES
+};
+
+enum hash_flag_type
+{
+ HFLAG_UNSET = (1 << 0),
+ HFLAG_CONST_VAL = (1 << 1),
+ HFLAG_CONST_TYPE = (1 << 2),
+ HFLAG_NO_ERASE = (1 << 3)
+};
+
+
+// Can't just cast everything into a void pointer, since a float might
+// not fit into a pointer on all systems.
+typedef union HashUnion HashUnion;
+union HashUnion
+{
+ bool boolean;
+ char byte;
+ short _short;
+ long _long;
+ float _float;
+ void* ptr;
+};
+
+
+class CrawlHashValue
+{
+public:
+ CrawlHashValue();
+ CrawlHashValue(const CrawlHashValue &other);
+
+ ~CrawlHashValue();
+
+ // Only needed for doing some assertion checking.
+ CrawlHashValue &operator = (const CrawlHashValue &other);
+
+protected:
+ hash_val_type type;
+ unsigned char flags;
+ HashUnion val;
+
+public:
+ unsigned char get_flags() const;
+ unsigned char set_flags(unsigned char flags);
+ unsigned char unset_flags(unsigned char flags);
+ hash_val_type get_type() const;
+
+ CrawlHashTable &new_table(unsigned char flags);
+ CrawlHashTable &new_table(hash_val_type type, unsigned char flags = 0);
+
+ bool &get_bool();
+ char &get_byte();
+ short &get_short();
+ long &get_long();
+ float &get_float();
+ std::string &get_string();
+ coord_def &get_coord();
+ CrawlHashTable &get_table();
+ item_def &get_item();
+
+ bool get_bool() const;
+ char get_byte() const;
+ short get_short() const;
+ long get_long() const;
+ float get_float() const;
+ std::string get_string() const;
+ coord_def get_coord() const;
+
+ const CrawlHashTable& get_table() const;
+ const item_def& get_item() const;
+
+ void set_bool(const bool val);
+ void set_byte(const char val);
+ void set_short(const short val);
+ void set_long(const long val);
+ void set_float(const float val);
+ void set_string(const std::string &val);
+ void set_coord(const coord_def &val);
+ void set_table(const CrawlHashTable &val);
+ void set_item(const item_def &val);
+
+public:
+ // NOTE: All operators will assert if the hash value is of the
+ // wrong type for the operation. If the value has no type yet,
+ // the operation will set it to the appropriate type. If the
+ // value has no type yet and the operation modifies the existing
+ // value rather than replacing it (i.e., ++) the value will be set
+ // to a default before the operation is done.
+
+ // If the hash value is a hash table, the table's values can be
+ // accessed with the [] operator.
+ CrawlHashValue &operator [] (const std::string &key);
+ CrawlHashValue &operator [] (const long &key);
+
+ const CrawlHashValue &operator [] (const std::string &key) const;
+ const CrawlHashValue &operator [] (const long &key) const;
+
+ // Typecast operators
+ &operator bool();
+ &operator char();
+ &operator short();
+ &operator long();
+ &operator float();
+ &operator std::string();
+ &operator coord_def();
+ &operator CrawlHashTable();
+ &operator item_def();
+
+ operator bool() const;
+ operator char() const;
+ operator short() const;
+ operator long() const;
+ operator float() const;
+ operator std::string() const;
+ operator coord_def() const;
+
+ // Assignment operators
+ bool &operator = (const bool &val);
+ char &operator = (const char &val);
+ short &operator = (const short &val);
+ long &operator = (const long &val);
+ float &operator = (const float &val);
+ std::string &operator = (const std::string &val);
+ const char* operator = (const char* val);
+ coord_def &operator = (const coord_def &val);
+ CrawlHashTable &operator = (const CrawlHashTable &val);
+ item_def &operator = (const item_def &val);
+
+ // Misc operators
+ std::string &operator += (const std::string &val);
+
+ // Prefix
+ long operator ++ ();
+ long operator -- ();
+
+ // Postfix
+ long operator ++ (int);
+ long operator -- (int);
+
+protected:
+ CrawlHashValue(const unsigned char flags,
+ const hash_val_type type = HV_NONE);
+
+ void write(tagHeader &th) const;
+ void read(tagHeader &th);
+
+ void unset(bool force = false);
+
+ friend class CrawlHashTable;
+};
+
+
+// A hash table can have a maximum of 255 key/value pairs. If you
+// want more than that you can use nested hash tables.
+//
+// By default a hash table's value data types are heterogeneous. To
+// make it homogeneous (which causes dynamic type checking) you have
+// to give a type to the hash table constructor; once it's been
+// created it's type (or lack of type) is immutable.
+//
+// An empty hash table will take up only 1 byte in the savefile. A
+// non-empty hash table will have an overhead of 3 bytes for the hash
+// table overall and 2 bytes per key/value pair, over and above the
+// number of bytes needed to store the keys and values themselves.
+class CrawlHashTable
+{
+public:
+ CrawlHashTable();
+ CrawlHashTable(unsigned char flags);
+ CrawlHashTable(hash_val_type type, unsigned char flags = 0);
+
+ ~CrawlHashTable();
+
+ typedef std::map<std::string, CrawlHashValue> hash_map_type;
+
+protected:
+ hash_val_type type;
+ unsigned char default_flags;
+ hash_map_type hash_map;
+
+ friend class CrawlHashValue;
+
+public:
+ void write(tagHeader &th) const;
+ void read(tagHeader &th);
+
+ unsigned char get_default_flags() const;
+ unsigned char set_default_flags(unsigned char flags);
+ unsigned char unset_default_flags(unsigned char flags);
+ hash_val_type get_type() const;
+ bool exists(const std::string key) const;
+ bool exists(const long key) const;
+ void assert_validity() const;
+ int compact_indicies(long min_index, long max_index,
+ bool compact_down = true);
+ bool fixup_indexed_array(std::string name = "");
+
+ // NOTE: If get_value() or [] is given a key which doesn't exist
+ // in the table, an unset/empty CrawlHashValue will be created
+ // with that key and returned. If it is not then given a value
+ // then the next call to assert_validity() will fail. If the
+ // hash table has a type (rather than being heterogeneous)
+ // then trying to assign a different type to the CrawlHashValue
+ // will assert.
+ CrawlHashValue& get_value(const std::string &key);
+ CrawlHashValue& get_value(const long &index);
+ CrawlHashValue& operator[] (const std::string &key);
+ CrawlHashValue& operator[] (const long &index);
+
+ // NOTE: If the const versions of get_value() or [] are given a
+ // key which doesn't exist, they will assert.
+ const CrawlHashValue& get_value(const std::string &key) const;
+ const CrawlHashValue& get_value(const long &index) const;
+ const CrawlHashValue& operator[] (const std::string &key) const;
+ const CrawlHashValue& operator[] (const long &index) const;
+
+ // std::map style interface
+ size_t size() const;
+ bool empty() const;
+
+ void erase(const std::string key);
+ void erase(const long index);
+ void clear();
+
+ hash_map_type::iterator begin();
+ hash_map_type::iterator end();
+
+ hash_map_type::const_iterator begin() const;
+ hash_map_type::const_iterator end() const;
+};
+
+
+// A wrapper for non-heterogeneous hash tables, so that the values can
+// be accessed without using get_foo(). T needs to have both normal
+// and const type-cast operators defined by CrawlHashValue for this
+// template to work.
+template <typename T, hash_val_type TYPE>
+class CrawlTableWrapper
+{
+public:
+ CrawlTableWrapper();
+ CrawlTableWrapper(CrawlHashTable& table);
+ CrawlTableWrapper(CrawlHashTable* table);
+
+protected:
+ CrawlHashTable* table;
+
+public:
+ void wrap(CrawlHashTable& table);
+ void wrap(CrawlHashTable* table);
+
+ CrawlHashTable* get_table();
+ T& operator[] (const std::string &key);
+ T& operator[] (const long &index);
+
+ const CrawlHashTable* get_table() const;
+ const T operator[] (const std::string &key) const;
+ const T operator[] (const long &index) const;
+};
+
+typedef CrawlTableWrapper<bool, HV_BOOL> CrawlBoolTable;
+typedef CrawlTableWrapper<char, HV_BYTE> CrawlByteTable;
+typedef CrawlTableWrapper<short, HV_SHORT> CrawlShortTable;
+typedef CrawlTableWrapper<long, HV_LONG> CrawlLongTable;
+typedef CrawlTableWrapper<float, HV_FLOAT> CrawlFloatTable;
+typedef CrawlTableWrapper<std::string, HV_STR> CrawlStringTable;
+typedef CrawlTableWrapper<coord_def, HV_COORD> CrawlCoordTable;
+
+#endif
diff --git a/crawl-ref/source/itemname.cc b/crawl-ref/source/itemname.cc
index accf5cf071..20cea75f1c 100644
--- a/crawl-ref/source/itemname.cc
+++ b/crawl-ref/source/itemname.cc
@@ -1402,15 +1402,41 @@ std::string item_def::name_aux( description_level_type desc,
buff << "deck of cards";
break;
}
+ else if (bad_deck(*this))
+ {
+ buff << "BUGGY deck of cards";
+ break;
+ }
buff << deck_rarity_name(deck_rarity(*this)) << ' ';
}
buff << misc_type_name(item_typ, know_type);
- if ( is_deck(*this) && item_plus2 != 0 )
+ if ( is_deck(*this)
+ && (top_card_is_known(*this) || this->plus2 != 0))
{
- // an inscribed deck!
- buff << " {"
- << card_name(static_cast<card_type>(item_plus2 - 1))
- << "}";
+ buff << " {";
+ // A marked deck!
+ if (top_card_is_known(*this))
+ buff << card_name(top_card(*this));
+
+ // How many cards have been drawn, or how many are
+ // left.
+ if (this->plus2 != 0)
+ {
+ if(top_card_is_known(*this))
+ buff << ", ";
+
+ buff << abs(this->plus2) << " card";
+
+ if (abs(this->plus2) > 1)
+ buff << "s";
+
+ if (this->plus2 > 0)
+ buff << " drawn";
+ else
+ buff << " left";
+ }
+
+ buff << "}";
}
}
break;
diff --git a/crawl-ref/source/makefile.obj b/crawl-ref/source/makefile.obj
index 8819ab5b27..f871fa43f6 100644
--- a/crawl-ref/source/makefile.obj
+++ b/crawl-ref/source/makefile.obj
@@ -24,6 +24,7 @@ files.o \
food.o \
format.o \
ghost.o \
+hash.o \
hiscores.o \
initfile.o \
insult.o \
diff --git a/crawl-ref/source/makeitem.cc b/crawl-ref/source/makeitem.cc
index d020e4a8cf..c97133ec9e 100644
--- a/crawl-ref/source/makeitem.cc
+++ b/crawl-ref/source/makeitem.cc
@@ -733,14 +733,7 @@ void item_colour( item_def &item )
case OBJ_MISCELLANY:
if ( is_deck(item) )
- {
- item.colour = GREEN;
- if ( one_chance_in(10) )
- item.colour = LIGHTMAGENTA; // legendary
- if ( one_chance_in(5) )
- item.colour = (coinflip() ? MAGENTA : BROWN);
break;
- }
switch (item.sub_type)
{
@@ -2835,8 +2828,18 @@ int items( int allow_uniques, // not just true-false,
}
if ( is_deck(mitm[p]) )
+ {
mitm[p].plus = 4 + random2(10);
+ mitm[p].special = DECK_RARITY_COMMON;
+ if ( one_chance_in(10) )
+ mitm[p].special = DECK_RARITY_LEGENDARY;
+ if ( one_chance_in(5) )
+ mitm[p].special = DECK_RARITY_RARE;
+
+ init_deck(mitm[p]);
+ }
+
if (mitm[p].sub_type == MISC_RUNE_OF_ZOT)
mitm[p].plus = item_race;
diff --git a/crawl-ref/source/religion.cc b/crawl-ref/source/religion.cc
index 961df346ea..3129a0808c 100644
--- a/crawl-ref/source/religion.cc
+++ b/crawl-ref/source/religion.cc
@@ -241,10 +241,10 @@ const char* god_gain_power_messages[NUM_GODS][MAX_GOD_ABILITIES] =
"call in reinforcement",
"" },
// Nemelex
- { "peek at the first card of a deck",
+ { "peek at three random cards from a deck",
"draw cards from decks in your inventory",
"draw cards with careful consideration",
- "",
+ "mark decks",
"stack decks" },
// Elyvilon
{ "call upon Elyvilon for minor healing",
@@ -327,10 +327,10 @@ const char* god_lose_power_messages[NUM_GODS][MAX_GOD_ABILITIES] =
"call in reinforcement",
"" },
// Nemelex
- { "peek at the first card of a deck",
+ { "peek at three random cards from a deck",
"draw cards from decks in your inventory",
"draw cards with careful consideration",
- "",
+ "mark decks",
"stack decks" },
// Elyvilon
{ "call upon Elyvilon for minor healing",
@@ -660,7 +660,8 @@ static void do_god_gift(bool prayed_for)
item_def &deck(mitm[thing_created]);
- deck.colour = deck_rarity_to_color(rarity);
+ deck.special = rarity;
+ deck.colour = deck_rarity_to_color(rarity);
move_item_to_grid( &thing_created, you.x_pos, you.y_pos );
origin_acquired(deck, you.religion);
diff --git a/crawl-ref/source/tags.cc b/crawl-ref/source/tags.cc
index 46505fdbe5..e26a4e9eeb 100644
--- a/crawl-ref/source/tags.cc
+++ b/crawl-ref/source/tags.cc
@@ -128,8 +128,6 @@ static void marshallGhost(tagHeader &th, const ghost_demon &ghost);
static ghost_demon unmarshallGhost( tagHeader &th );
static void marshall_monster(tagHeader &th, const monsters &m);
static void unmarshall_monster(tagHeader &th, monsters &m);
-static void marshall_item(tagHeader &th, const item_def &item);
-static void unmarshall_item(tagHeader &th, item_def &item);
template<typename T, typename T_iter, typename T_marshal>
static void marshall_iterator(struct tagHeader &th, T_iter beg, T_iter end,
@@ -939,19 +937,7 @@ static void tag_construct_you_items(struct tagHeader &th)
// how many inventory slots?
marshallByte(th, ENDOFPACK);
for (i = 0; i < ENDOFPACK; ++i)
- {
- marshallByte(th,you.inv[i].base_type);
- marshallByte(th,you.inv[i].sub_type);
- marshallShort(th,you.inv[i].plus);
- marshallLong(th,you.inv[i].special);
- marshallByte(th,you.inv[i].colour);
- marshallLong(th,you.inv[i].flags);
- marshallShort(th,you.inv[i].quantity);
- marshallShort(th,you.inv[i].plus2);
- marshallShort(th, you.inv[i].orig_place);
- marshallShort(th, you.inv[i].orig_monnum);
- marshallString(th, you.inv[i].inscription.c_str(), 80);
- }
+ marshallItem(th, you.inv[i]);
marshallByte(th, you.quiver);
@@ -1073,14 +1059,14 @@ static void marshall_follower(tagHeader &th, const follower &f)
{
marshall_monster(th, f.mons);
for (int i = 0; i < NUM_MONSTER_SLOTS; ++i)
- marshall_item(th, f.items[i]);
+ marshallItem(th, f.items[i]);
}
static void unmarshall_follower(tagHeader &th, follower &f)
{
unmarshall_monster(th, f.mons);
for (int i = 0; i < NUM_MONSTER_SLOTS; ++i)
- unmarshall_item(th, f.items[i]);
+ unmarshallItem(th, f.items[i]);
}
static void marshall_follower_list(tagHeader &th, const m_transit_list &mlist)
@@ -1288,26 +1274,7 @@ static void tag_read_you_items(struct tagHeader &th, char minorVersion)
// how many inventory slots?
count_c = unmarshallByte(th);
for (i = 0; i < count_c; ++i)
- {
- you.inv[i].base_type =
- static_cast<object_class_type>(unmarshallByte(th));
- you.inv[i].sub_type = (unsigned char) unmarshallByte(th);
- you.inv[i].plus = unmarshallShort(th);
- you.inv[i].special = unmarshallLong(th);
- you.inv[i].colour = (unsigned char) unmarshallByte(th);
- you.inv[i].flags = (unsigned long) unmarshallLong(th);
- you.inv[i].quantity = unmarshallShort(th);
- you.inv[i].plus2 = unmarshallShort(th);
- you.inv[i].orig_place = unmarshallShort(th);
- you.inv[i].orig_monnum = unmarshallShort(th);
- you.inv[i].inscription = unmarshallString(th, 80);
-
- // these never need to be saved for items in the inventory -- bwr
- you.inv[i].x = -1;
- you.inv[i].y = -1;
- you.inv[i].link = i;
- you.inv[i].slot = index_to_letter(i);
- }
+ unmarshallItem(th, you.inv[i]);
you.quiver = unmarshallByte(th);
@@ -1553,7 +1520,7 @@ static void tag_construct_level(struct tagHeader &th)
env.markers.write(th);
}
-static void marshall_item(tagHeader &th, const item_def &item)
+void marshallItem(tagHeader &th, const item_def &item)
{
marshallByte(th, item.base_type);
marshallByte(th, item.sub_type);
@@ -1568,16 +1535,21 @@ static void marshall_item(tagHeader &th, const item_def &item)
marshallLong(th, item.flags);
marshallShort(th, item.link); // unused
- marshallShort(th, igrd[item.x][item.y]); // unused
+ if (item.x == -1 && item.y == -1)
+ marshallShort(th, -1); // unused
+ else
+ marshallShort(th, igrd[item.x][item.y]); // unused
marshallByte(th, item.slot);
marshallShort(th, item.orig_place);
marshallShort(th, item.orig_monnum);
marshallString(th, item.inscription.c_str(), 80);
+
+ item.props.write(th);
}
-static void unmarshall_item(tagHeader &th, item_def &item)
+void unmarshallItem(tagHeader &th, item_def &item)
{
item.base_type = static_cast<object_class_type>(unmarshallByte(th));
item.sub_type = (unsigned char) unmarshallByte(th);
@@ -1603,6 +1575,8 @@ static void unmarshall_item(tagHeader &th, item_def &item)
item.orig_place = unmarshallShort(th);
item.orig_monnum = unmarshallShort(th);
item.inscription = unmarshallString(th, 80);
+
+ item.props.read(th);
}
static void tag_construct_level_items(struct tagHeader &th)
@@ -1619,7 +1593,7 @@ static void tag_construct_level_items(struct tagHeader &th)
// how many items?
marshallShort(th, MAX_ITEMS);
for (int i = 0; i < MAX_ITEMS; ++i)
- marshall_item(th, mitm[i]);
+ marshallItem(th, mitm[i]);
}
static void marshall_mon_enchant(tagHeader &th, const mon_enchant &me)
@@ -1799,7 +1773,7 @@ static void tag_read_level_items(struct tagHeader &th, char minorVersion)
// how many items?
const int item_count = unmarshallShort(th);
for (int i = 0; i < item_count; ++i)
- unmarshall_item(th, mitm[i]);
+ unmarshallItem(th, mitm[i]);
}
static void unmarshall_monster(tagHeader &th, monsters &m)
diff --git a/crawl-ref/source/tags.h b/crawl-ref/source/tags.h
index 40244224e9..df4c3d754b 100644
--- a/crawl-ref/source/tags.h
+++ b/crawl-ref/source/tags.h
@@ -72,6 +72,7 @@ void marshallBoolean(struct tagHeader &th, bool data);
void marshallString(struct tagHeader &th, const std::string &data,
int maxSize = 0);
void marshallCoord(tagHeader &th, const coord_def &c);
+void marshallItem(tagHeader &th, const item_def &item);
// last updated 22jan2001 {gdl}
/* ***********************************************************************
@@ -85,6 +86,7 @@ bool unmarshallBoolean(struct tagHeader &th);
int unmarshallCString(struct tagHeader &th, char *data, int maxSize);
std::string unmarshallString(tagHeader &th, int maxSize = 1000);
void unmarshallCoord(tagHeader &th, coord_def &c);
+void unmarshallItem(tagHeader &th, item_def &item);
std::string make_date_string( time_t in_date );
time_t parse_date_string( char[20] );