summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/describe.h
blob: 3b68325015f79f3847303f35d42c0f00db98fb03 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
/*
 *  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 &quote);

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