summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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] );