/* * File: describe.h * Summary: Functions used to print information about various game objects. * Written by: Linley Henzell */ #ifndef DESCRIBE_H #define DESCRIBE_H #include #include #include "externs.h" #include "enum.h" // If you add any more description types, remember to also // change item_description in externs.h enum item_description_type { IDESC_WANDS = 0, IDESC_POTIONS, IDESC_SCROLLS, // special field (like the others) IDESC_RINGS, IDESC_SCROLLS_II, IDESC_STAVES, NUM_IDESC }; struct describe_info { std::ostringstream body; std::string title; std::string prefix; std::string suffix; std::string footer; std::string quote; }; void append_spells(std::string &desc, const item_def &item); bool is_dumpable_artefact( const item_def &item, bool verbose ); std::string get_item_description( const item_def &item, bool verbose, bool dump = false, bool noquote = false ); std::string god_title(god_type which_god); void describe_god( god_type which_god, bool give_title ); void describe_feature_wide(const coord_def& pos); void get_feature_desc(const coord_def &gc, describe_info &inf); void set_feature_desc_long(const std::string &raw_name, const std::string &desc); void set_feature_quote(const std::string &raw_name, const std::string "e); void describe_item(item_def &item, bool allow_inscribe = false, bool shopping = false); void get_item_desc(const item_def &item, describe_info &inf, bool terse = false); void inscribe_item(item_def &item, bool proper_prompt); void append_weapon_stats(std::string &description, const item_def &item); void append_armour_stats(std::string &description, const item_def &item); void append_missile_info(std::string &description); void describe_monsters(const monsters &mons, bool force_seen = false); void get_monster_db_desc(const monsters &item, describe_info &inf, bool force_seen = false); void get_spell_desc(const spell_type spell, describe_info &inf); void describe_spell(spell_type spelled, const item_def* item = NULL); std::string get_ghost_description(const monsters &mons, bool concise = false); std::string get_skill_description(int skill, bool need_title = false); void describe_skill(int skill); void print_description(const std::string &desc); void print_description(const describe_info &inf); template void process_description(T &proc, const describe_info &inf); std::string artefact_auto_inscription( const item_def& item ); void add_autoinscription( item_def &item, std::string ainscrip); const char *trap_name(trap_type trap); int str_to_trap(const std::string &s); extern const char* god_gain_power_messages[NUM_GODS][MAX_GOD_ABILITIES]; int count_desc_lines(const std::string _desc, const int width); /* *********************************************************************** * template implementations * *********************************************************************** */ // My kingdom for a closure. template inline void process_description(T &proc, const describe_info &inf) { const unsigned int line_width = proc.width(); const int height = proc.height(); std::string desc; // How many lines is the title; we also seem to be adding 1 to // start with. int num_lines = count_desc_lines(inf.title, line_width) + 1; int body_lines = count_desc_lines(inf.body.str(), line_width); const int suffix_lines = count_desc_lines(inf.suffix, line_width); const int prefix_lines = count_desc_lines(inf.prefix, line_width); const int footer_lines = count_desc_lines(inf.footer, line_width) + (inf.footer.empty() ? 0 : 1); const int quote_lines = count_desc_lines(inf.quote, line_width); // Maybe skip the body if body + title would be too many lines. if (inf.title.empty()) { desc = inf.body.str(); // There is a default 1 line addition for some reason. num_lines = body_lines + 1; } else if(body_lines + num_lines + 2 <= height) { desc = inf.title + "$$"; desc += inf.body.str(); // Got 2 lines from the two $s that weren't counted yet. num_lines += body_lines + 2; } else desc = inf.title + "$"; // Prefer the footer over the suffix. if (num_lines + suffix_lines + footer_lines <= height) { desc = desc + inf.suffix; num_lines += suffix_lines; } // Prefer the footer over the prefix. if (num_lines + prefix_lines + footer_lines <= height) { desc = inf.prefix + desc; num_lines += prefix_lines; } // Prefer the footer over the quote. if (num_lines + footer_lines + quote_lines + 1 <= height) { if (!desc.empty()) { desc += "$"; num_lines++; } desc = desc + inf.quote; num_lines += quote_lines; } if (!inf.footer.empty() && num_lines + footer_lines <= height) { const int bottom_line = std::min(std::max(24, num_lines + 2), height - footer_lines + 1); const int newlines = bottom_line - num_lines; if (newlines >= 0) { desc.append(newlines, '\n'); desc = desc + inf.footer; } } std::string::size_type nextLine = std::string::npos; unsigned int currentPos = 0; while (currentPos < desc.length()) { if (currentPos != 0) proc.nextline(); // See if $ sign is within one line_width. nextLine = desc.find('$', currentPos); if (nextLine >= currentPos && nextLine < currentPos + line_width) { proc.print(desc.substr(currentPos, nextLine-currentPos)); currentPos = nextLine + 1; continue; } // Handle real line breaks. No substitutions necessary, just update // the counts. nextLine = desc.find('\n', currentPos); if (nextLine >= currentPos && nextLine < currentPos + line_width) { proc.print(desc.substr(currentPos, nextLine-currentPos)); currentPos = nextLine + 1; continue; } // No newline -- see if rest of string will fit. if (currentPos + line_width >= desc.length()) { proc.print(desc.substr(currentPos)); return; } // Ok, try to truncate at space. nextLine = desc.rfind(' ', currentPos + line_width); if (nextLine > 0) { proc.print(desc.substr(currentPos, nextLine - currentPos)); currentPos = nextLine + 1; continue; } // Oops. Just truncate. nextLine = currentPos + line_width; nextLine = std::min(inf.body.str().length(), nextLine); proc.print(desc.substr(currentPos, nextLine - currentPos)); currentPos = nextLine; } } #endif