/*
* File: describe.h
* Summary: Functions used to print information about various game objects.
* Written by: Linley Henzell
*/
#ifndef DESCRIBE_H
#define DESCRIBE_H
#include <string>
#include <sstream>
#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<class T> 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<class T>
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