git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@9030 c06c8d41-db1a-0410-9941-cceddc491573
XKSYV5QUG2PET6BNBZPNI4D7LDBJGWOH335OD3J24AKJWE63J3WQC
Q57GOIHVFNJHTBYNUGJNME3UF2QFRJ6CN2UXVY47NHU7XAT7JJYAC
HM6NOS7BN5665KWIFGBXOHZIDMRSVCESN72GMGI6NVBFZGCF3L6QC
LJPUO3NNFABVOQOBA5LUDNT2R5H4LMP6TPJBNVJ7VJBERCBYZIXQC
2VHVHQYNKZF65BHNKJ3LCA3WM4DRAEMWDQJATNJCOOXVDBS56YRQC
YKUVKAIE3IVK377PRJKAXPM45POXYW3ATAKNI3XWKXVFBDRU5LSQC
7OC5HCGLN26GDA5SQTWKBSUZYZDZJXWIVMJ4NKKXREOEEPUGQ4YQC
HZEC4TVVUHE6YKEXPOUMNQJOCFGF2MSEU7AUD6VID6FSMYU4XX2QC
RZTIYZABUF6FQZEBZOPVWTIMYI3ZI637PTR4MZTBPLOCDRAXPPOQC
Q3B3UVMYEVC4YJUPYVSNTR4DJH4E6J4JJDHZNT5LNOCHCPPMEMXAC
XKNRIFG2ULQLAAQ5GC4RBZPOERV73DLCNILEA5EP37JLIX5EP3OQC
PBTLQZHBQK5TAIO7SNSCKSHOQQ65CFFI55OTTETV7FG2FCJOXKHQC
NDE6CROMCVOJRMRMEID7QISFPZKUK2VCV6ISP5OEQRBH7EIXUIEQC
K57Q5SRNXYQOX5FP3L3VSFBWNR57NLO33GV2YPS6PQI54KKIFFFQC
ZGWMTQ4GG7W5HDVHPQCWJIUK4LK4PS3CDOOIQSFANTU33D45DCPAC
MV5USMLTBKVRWBAD67UFJ2BS4Y5HEOMYASRSB44DS24BBMRP75RQC
IP4A3VRYFYIVLRUAU4DF4KDNP6E4UISWJX3LI2F4EGSSMIJYRJXAC
NSHZVI2TCGOCTEKV72C5K4CNZW3I6MCHG372NYXYZI7U2U5BQ66AC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
IIN7AVA6JYRBXH6ZYRR7BY7TV6PW7ANAQ2A3PD55FKBKKQFEEF2AC
TAHSTXR7ROOMDFUSBUU4ZAIEWQLAS5CIRCTARLD4Q2BGNLSL7E5QC
MSRJ3N4NX255RVISUOPTTTY2GG4JVVNNM76HWUZ2EKCYESNI6MLQC
SVY2PTCLXR3KNPQAWXVXTTGCC5DR334HOAKHYO3VDDRWM2BWMALAC
CRX4RG35C3DH57NIXCW4CG4X7SBMSX7TAJR2BAFQ4LQKCLYUNANQC
LZBHBSCXDX2JKU2BSO5QP3WDHFIGHPXK4CKUZV6FWYDRFWLQK26QC
4JHALUQLYAZQSH6MHLCS4EIIRHIT3Y4IYT2CE66NHSSWZFZY3SZQC
HNPSSHGZFQ3E2I6X6VTKZ3WBBM2G25P2D7SIL2SZYKV2CCEA2ADAC
IU2CR2KXWJ6QGCVGNCXUWKK2HDUYYVR5IU4D65WVPLV7KWS6PASAC
ZIFFVCQ72K35WGIUMZYN3KOXIUXF2CNXWKG6ZWEZ6LT3NSF3XOQAC
NGDLKK45NREMXETWADSYSC6NNAGN4X5BZDJXHLL432WTAJKCG36AC
KVPP3CYPEFADQCL5Y56ELSUBA47SFNAJNCSVCSOK2GF67DHVMBCQC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
PRQVFUGUGPKI2Q74ODEI3CYUJQB63CKC6ONQTE3BTOHKXG4JCNLQC
5YWWRDK5FK7BY5EPDN5YUHWM5MZUSHBARTQEQXGX3327OCM4O3AAC
5V47S4NNTHWTSAHV3YLO2VGH7JTUIYJ3GBPDN5ZM4UQALT2ZEXDQC
5B5DP5S6A6LQMKZYVLQAEMHQZWFWYDHPCKQGRNSCNNYIBQYZ6BIQC
IHOKNI4O7EEW4UBMKHP6XOZNEIQSW4AYOT4O3JXUIXNNR553IQAQC
CNBEMJYK6RATHVHUXN5RFKJCRHZJUCPOGHKWPVD4QLVNDNLDDCFQC
GL7TJGUGV4JPLZCDWH6QVST4RYW7UJV6IP7UAEWEKNBWJO2H2ZHAC
EJ64OVL7LJHMMGNKBJK5STXIL47GM2DFXVA4HEQWSCQWTNJL5D4AC
IXLNOTBJGHKESBCTME6QAR6XVWFNCHYGMS62V62ZJEA7VLQHXO2QC
RQR4PTMGQLTRWLYRU3KXIIDGND7FBORESJWMWFVD3WYU5SISZXAAC
QCUMH3C7GXV7ZW444WT5SFAXQOJKJSE2YCQCEHMRYXCWF4QI7UMAC
4RFKVDJKTCRBZU6WPJ2E5OVI5IRPY3UTRPOBLC5QHY4CQJJTLZKQC
YFIVTYI7PMVAXV23DUPXPAQNEY3YSFIXQGSN32I3WVHMMD5XS5DQC
25CH7HH4LKXFIZ75YNMXS3TSXO6O27DYSOPLOD45K4OCNFWLS4LQC
DMG73XDQHY2X2PHKWIY56XKD3O4NPGZKKIO6GX3IV2LLRVXPGKYQC
UOWPKKEVEMM6D67GLKB2PKCAWJU2GJJQTOLF6IQ6ZQB5WB3UH5JQC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
BFCFMN2BXNLXJCYYCL2V2XTDQQNM3PJPSRB6CU76FM3332NC7GMAC
SHFSQYLN7WPQC35V3XYYEIZ4CQMREASUNR474CIKFG3C2FYI4SHQC
WXZQJUZXMYS7R6ORNB7DWE5KEUXT262GXWMRPOJYYB4I3BFGYLLAC
TYCM7ZQG5JPPK4A5K6IBVRKJYQS2F2A3SESBC53MUNXHMZCKYF5QC
VRFQK6S2TXOFFO5K5HRDXPR7QEKKAZAVCASSIJVPWQ4GE26UOGTQC
QDWDUURSNLMT6AXNNJ3DEQCWAKCAIHV6MP5F7QGIBGXOG2BI2NPQC
3ZWALZFSTSIVYXY4BAY6ANGINTDACZC6RSSJTEMQSTSUIE66YOBQC
MLZSEZWNNZMSIDQNAAIOJJR4K7VSVJICL5SAGHD3ROM7SYDVZABAC
RYT42Z6CED4KV5CCJ45CHZ3DQGLFMDCVH6CSQZNXOILULDG4MXVQC
ZCPJIAG7SEY2TUBAGOFWCEJYXSBT36CZU5VO7BWEGSLJ4K24B4TAC
X2FMEN4E345XD26Z2X7JMJ7VGHOGCGIELMHQRE2ITLVNQACP3NOQC
IPQ63XIUPHFMCQOZZAVSGCJOZFDRDWZTUUJSAUMARNDUFLBEMYIAC
6L4EP4ZRWWYLT55PD5KTTJON5J2JB5VV5MWNHF5VPZQZ5BKEYZ4QC
HKQTMQVLLOBG2VO47TUGSTQALA3D2YLMEVADXXYNR4RGGKD3F2ZAC
UW2ZVPHGKMKJVTB5LIV2DZCHXY6DSFRZ6DMKHLHFNHZUPRUAKHUAC
Z6XF4AIERIW4U4AR3HU2ILYFZ54IK4K4ORQ6JKCEWRO5LZODWDDAC
PAYI4UTJCR3XZSFOX5L35EURHRXQ6STO4Z7AQ3525QPNL3QYLNBAC
33MBISZXMRGZMQ37PVINHKNWAXHYPHURMRG6ST7O2SKIRGGBHI2AC
RW3H34H6MQOI3CW4ILNH3VXRKQY73KIUGZKGDQDXPGGB5D6N4RFAC
m_region_msg->alt_text().clear();
}
// TODO enne - need to find a better time to decide when
// to generate a tip or some way to say "yes, but unchanged".
if (ticks > m_last_tick_moved)
{
m_region_msg->alt_text().clear();
for (unsigned int i = 0;
i < m_layers[m_active_layer].m_regions.size(); i++)
{
Region *reg = m_layers[m_active_layer].m_regions[i];
if (!reg->inside(m_mouse.x, m_mouse.y))
continue;
if (reg->update_alt_text(m_region_msg->alt_text()))
break;
}
}
}
return true;
}
class alt_desc_proc
{
public:
alt_desc_proc(int _w, int _h) { w = _w; h = _h; }
int width() { return w; }
int height() { return h; }
void nextline()
{
ostr << "\n";
}
void print(const std::string &str)
{
ostr << str;
}
static int count_newlines(const std::string &str)
{
int count = 0;
for (size_t i = 0; i < str.size(); i++)
{
if (str[i] == '\n')
count++;
}
return count;
}
// Remove trailing newlines.
static void trim(std::string &str)
{
int idx = str.size();
while (--idx >= 0)
{
if (str[idx] != '\n')
break;
}
str.resize(idx + 1);
}
// rfind consecutive newlines and truncate.
static bool chop(std::string &str)
{
int loc = -1;
for (size_t i = 1; i < str.size(); i++)
{
if (str[i] == '\n' && str[i-1] == '\n')
loc = i;
}
if (loc == -1)
return false;
str.resize(loc);
return true;
}
void get_string(std::string &str)
{
str = replace_all(ostr.str(), "\n\n\n\n", "\n\n");
str = replace_all(str, "\n\n\n", "\n\n");
trim(str);
while (count_newlines(str) > h)
{
if (!chop(str))
break;
}
}
protected:
int w;
int h;
std::ostringstream ostr;
};
bool DungeonRegion::update_alt_text(std::string &alt)
{
if (mouse_control::current_mode() != MOUSE_MODE_COMMAND)
return false;
const coord_def &gc = m_cursor[CURSOR_MOUSE];
if (gc == NO_CURSOR)
return false;
if (!map_bounds(gc))
return false;
if (!see_grid(gc))
return false;
describe_info inf;
get_square_desc(gc, inf);
alt_desc_proc proc(crawl_view.msgsz.x, crawl_view.msgsz.y);
process_description<alt_desc_proc>(proc, inf);
proc.get_string(alt);
// Suppress floor description
if (alt == "Floor.")
{
alt.clear();
return false;
}
return true;
}
bool InventoryRegion::update_alt_text(std::string &alt)
{
if (m_cursor == NO_CURSOR)
return false;
unsigned int item_idx = cursor_index();
if (item_idx >= m_items.size() || m_items[item_idx].empty())
return false;
int idx = m_items[item_idx].idx;
const item_def *item;
if (m_items[item_idx].flag & TILEI_FLAG_FLOOR)
item = &mitm[idx];
else
item = &you.inv[idx];
describe_info inf;
get_item_desc(*item, inf);
alt_desc_proc proc(crawl_view.msgsz.x, crawl_view.msgsz.y);
process_description<alt_desc_proc>(proc, inf);
box_vert verts[4];
for (int i = 0; i < 4; i++)
{
verts[i].r = 100;
verts[i].g = 100;
verts[i].b = 100;
verts[i].a = 100;
}
verts[0].x = sx;
verts[0].y = sy;
verts[1].x = sx;
verts[1].y = sy + height;
verts[2].x = ex;
verts[2].y = sy + height;
verts[3].x = ex;
verts[3].y = sy;
glVertexPointer(2, GL_FLOAT, sizeof(box_vert), &verts[0].x);
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(box_vert), &verts[0].r);
glDrawArrays(GL_QUADS, 0, sizeof(verts) / sizeof(box_vert));
ShapeBuffer buff;
VColour col(100, 100, 100, 100);
buff.add(sx, sy, ex, sy + height, col);
buff.draw();
}
void get_square_desc(const coord_def &c, describe_info &inf)
{
// NOTE: Keep this function in sync with full_describe_square.
// Don't give out information for things outside LOS
if (!see_grid(c.x, c.y))
return;
const int mid = mgrd(c);
const int oid = igrd(c);
if (mid != NON_MONSTER && player_monster_visible(&menv[mid]))
{
// First priority: monsters.
get_monster_desc(menv[mid], inf);
}
else if (oid != NON_ITEM)
{
// Second priority: objects.
get_item_desc(mitm[oid], inf);
}
else
{
// Third priority: features.
get_feature_desc(c, inf);
}
void describe_item( item_def &item, bool allow_inscribe = false );
void inscribe_item( item_def &item, bool proper_prompt );
void describe_item(item_def &item, bool allow_inscribe = false);
void get_item_desc(const item_def &item, describe_info &inf);
void inscribe_item(item_def &item, bool proper_prompt);
void print_description(const std::string &d, const std::string title = "",
const std::string suffix = "",
const std::string prefix = "",
const std::string footer = "",
const std::string quote = "");
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);
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;
if (inf.title.empty())
desc = inf.body.str();
else
{
desc = inf.title + "$$";
desc += inf.body.str();
}
int num_lines = count_desc_lines(desc, line_width) + 1;
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);
// 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;
}
}
void print_description(const std::string &d, const std::string title,
const std::string suffix, const std::string prefix,
const std::string footer, const std::string quote)
{
const unsigned int lineWidth = get_number_of_cols() - 1;
const int height = get_number_of_lines();
std::string desc;
if (title.empty())
desc = d;
else
{
desc = title + "$$";
desc += d;
}
int num_lines = _count_desc_lines(desc, lineWidth) + 1;
const int suffix_lines = _count_desc_lines(suffix, lineWidth);
const int prefix_lines = _count_desc_lines(prefix, lineWidth);
const int footer_lines = _count_desc_lines(footer, lineWidth)
+ (footer.empty() ? 0 : 1);
const int quote_lines = _count_desc_lines(quote, lineWidth);
// Prefer the footer over the suffix.
if (num_lines + suffix_lines + footer_lines <= height)
{
desc = desc + suffix;
num_lines += suffix_lines;
}
// Prefer the footer over the prefix.
if (num_lines + prefix_lines + footer_lines <= height)
{
desc = prefix + desc;
num_lines += prefix_lines;
}
void print_description(const std::string &body)
{
describe_info inf;
inf.body << body;
print_description(inf);
}
// Prefer the footer over the quote.
if (num_lines + footer_lines + quote_lines + 1 <= height)
{
if (!desc.empty())
{
desc += "$";
num_lines++;
}
desc = desc + quote;
num_lines += quote_lines;
}
class default_desc_proc
{
public:
int width() { return get_number_of_cols() - 1; }
int height() { return get_number_of_lines(); }
void print(const std::string &str) { cprintf("%s", str.c_str()); }
void nextline() { cgotoxy(1, wherey() + 1); }
};
if (!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 + footer;
}
}
std::string::size_type nextLine = std::string::npos;
unsigned int currentPos = 0;
void print_description(const describe_info &inf)
{
// See if $ sign is within one lineWidth.
nextLine = desc.find('$', currentPos);
if (nextLine >= currentPos && nextLine < currentPos + lineWidth)
{
cprintf("%s",
(desc.substr(currentPos, nextLine-currentPos)).c_str());
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 + lineWidth)
{
cprintf("%s",
(desc.substr(currentPos, nextLine-currentPos)).c_str());
currentPos = nextLine + 1;
continue;
}
// No newline -- see if rest of string will fit.
if (currentPos + lineWidth >= desc.length())
{
cprintf((desc.substr(currentPos)).c_str());
return;
}
// Ok, try to truncate at space.
nextLine = desc.rfind(' ', currentPos + lineWidth);
if (nextLine > 0)
{
cprintf((desc.substr(currentPos, nextLine - currentPos)).c_str());
currentPos = nextLine + 1;
continue;
}
// Oops. Just truncate.
nextLine = currentPos + lineWidth;
nextLine = std::min(d.length(), nextLine);
cprintf((desc.substr(currentPos, nextLine - currentPos)).c_str());
currentPos = nextLine;
}
default_desc_proc proc;
process_description<default_desc_proc>(proc, inf);