File: game-options.cc

package info (click to toggle)
crawl 2%3A0.33.1-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 95,264 kB
  • sloc: cpp: 358,145; ansic: 27,203; javascript: 9,491; python: 8,359; perl: 3,327; java: 2,667; xml: 2,191; makefile: 1,830; sh: 611; objc: 250; cs: 15; sed: 9; lisp: 3
file content (251 lines) | stat: -rw-r--r-- 8,125 bytes parent folder | download | duplicates (2)
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
/*
 *  @file
 *  @brief Global game options controlled by the rcfile.
 */

#include "AppHdr.h"

#include "game-options.h"
#include "options.h"
#include "misc.h"
#include "tiles-build-specific.h"

static unsigned _curses_attribute(const string &field, string &error)
{
    if (field == "standout")               // probably reverses
        return CHATTR_STANDOUT;
    if (field == "bold")              // probably brightens fg
        return CHATTR_BOLD;
    if (field == "blink")             // probably brightens bg
        return CHATTR_BLINK;
    if (field == "underline")
        return CHATTR_UNDERLINE;
    if (field == "reverse")
        return CHATTR_REVERSE;
    if (field == "dim")
        return CHATTR_DIM;
    if (starts_with(field, "hi:")
        || starts_with(field, "hilite:")
        || starts_with(field, "highlight:"))
    {
        const int col = field.find(":");
        const int colour = str_to_colour(field.substr(col + 1));
        if (colour != -1)
            return CHATTR_HILITE | (colour << 8);

        error = make_stringf("Bad highlight string -- %s",
                             field.c_str());
    }
    else if (field != "none")
        error = make_stringf("Bad colour -- %s", field.c_str());
    return CHATTR_NORMAL;
}

/**
 * Read a maybe bool field. Accepts anything for the third value.
 */
maybe_bool read_maybe_bool(const string &field)
{
    // TODO: check for "maybe" explicitly or something?
    if (field == "true" || field == "1" || field == "yes")
        return true;

    if (field == "false" || field == "0" || field == "no")
        return false;

    return maybe_bool::maybe;
}

bool read_bool(const string &field, bool def_value)
{
    const maybe_bool result = read_maybe_bool(field);
    if (result.is_bool())
        return bool(result);

    Options.report_error("Bad boolean: %s (should be true or false)", field.c_str());
    return def_value;
}

string GameOption::loadFromString(const std::string &, rc_line_type)
{
    loaded = true;
    if (on_set)
        on_set();
    return "";
}

string GameOption::loadFromParseState(const opt_parse_state &state)
{
    // if an override doesn't call loadFromString here, make sure to call
    // on_set() directly
    return loadFromString(
        case_sensitive ? state.raw_field : state.field,
        state.line_type);
}

string DisabledGameOption::loadFromParseState(const opt_parse_state &state)
{
    // use the parse state key: this lets this class group together
    // multiple unrelated options for convenience
    if (on_set)
        on_set();
    return make_stringf("Option '%s' is disabled in this build.",
            state.key.c_str());
}

string BoolGameOption::loadFromString(const string &field, rc_line_type ltyp)
{
    string error;
    const maybe_bool result = read_maybe_bool(field);
    if (!result.is_bool())
    {
        return make_stringf("Bad %s value: %s (should be true or false)",
                            name().c_str(), field.c_str());
    }

    value = bool(result);
    return GameOption::loadFromString(field, ltyp);
}

string ColourGameOption::loadFromString(const string &field, rc_line_type ltyp)
{
    const int col = str_to_colour(field, -1, true, elemental);
    if (col == -1)
        return make_stringf("Bad %s -- %s\n", name().c_str(), field.c_str());

    value = col;
    return GameOption::loadFromString(field, ltyp);
}

string CursesGameOption::loadFromString(const string &field, rc_line_type ltyp)
{
    string error;
    const unsigned result = _curses_attribute(field, error);
    if (!error.empty())
        return make_stringf("%s (for %s)", error.c_str(), name().c_str());

    value = result;
    return GameOption::loadFromString(field, ltyp);
}

#ifdef USE_TILE
TileColGameOption::TileColGameOption(VColour &val, std::set<std::string> _names,
                    string _default)
        : GameOption(_names), value(val),
          default_value(str_to_tile_colour(_default)) { }

string TileColGameOption::loadFromString(const string &field, rc_line_type ltyp)
{
    value = str_to_tile_colour(field);
    return GameOption::loadFromString(field, ltyp);
}
#endif

string IntGameOption::loadFromString(const string &field, rc_line_type ltyp)
{
    int val = default_value;
    if (!parse_int(field.c_str(), val))
        return make_stringf("Couldn't parse integer option %s: \"%s\"", name().c_str(), field.c_str());
    if (val < min_value)
        return make_stringf("Bad %s: %d should be >= %d", name().c_str(), val, min_value);
    if (val > max_value)
        return make_stringf("Bad %s: %d should be <= %d", name().c_str(), val, max_value);
    value = val;
    return GameOption::loadFromString(field, ltyp);
}

string FixedpGameOption::loadFromString(const string &field, rc_line_type ltyp)
{
    // is this still the best way to handle this with error checking in 2023??
    // strtod doesn't give good error checking, maybe std::stod?
    float tmp_float;
    if (!sscanf(field.c_str(), "%f", &tmp_float))
        return make_stringf("Couldn't parse decimal option %s: \"%s\"", name().c_str(), field.c_str());
    fixedp<> val = tmp_float; // implicit cast
    if (val < min_value)
        return make_stringf("Bad %s: %g should be >= %g", name().c_str(), (float) val, (float) min_value);
    if (val > max_value)
        return make_stringf("Bad %s: %g should be <= %g", name().c_str(), (float) val, (float) max_value);

    value = val;
    return GameOption::loadFromString(field, ltyp);
}

string StringGameOption::loadFromString(const string &field, rc_line_type ltyp)
{
    value = field;
    return GameOption::loadFromString(field, ltyp);
}

string ColourThresholdOption::loadFromString(const string &field,
                                             rc_line_type ltyp)
{
    string error;
    const colour_thresholds result = parse_colour_thresholds(field, &error);
    if (!error.empty())
        return error;

    switch (ltyp)
    {
        case RCFILE_LINE_EQUALS:
            value = result;
            break;
        case RCFILE_LINE_PLUS:
        case RCFILE_LINE_CARET:
            value.insert(value.end(), result.begin(), result.end());
            stable_sort(value.begin(), value.end(), ordering_function);
            break;
        case RCFILE_LINE_MINUS:
            for (pair<int, int> entry : result)
                remove_matching(value, entry);
            break;
        default:
            // XX should this really be a die?
            die("Unknown rc line type for %s: %d!", name().c_str(), ltyp);
    }
    return GameOption::loadFromString(field, ltyp);
}

colour_thresholds
    ColourThresholdOption::parse_colour_thresholds(const string &field,
                                                   string* error) const
{
    colour_thresholds result;
    for (string pair_str : split_string(",", field))
    {
        const vector<string> insplit = split_string(":", pair_str);

        if (insplit.size() != 2)
        {
            const string failure = make_stringf("Bad %s pair: '%s'",
                                                name().c_str(),
                                                pair_str.c_str());
            // XX should this really be a `die`? I *think* it can only be
            // triggered by someone setting a bad default in this file...
            if (!error)
                die("%s", failure.c_str());
            *error = failure;
            break;
        }

        const int threshold = atoi(insplit[0].c_str());

        const string colstr = insplit[1];
        const int scolour = str_to_colour(colstr, -1, true, false);
        if (scolour <= 0)
        {
            const string failure = make_stringf("Bad %s: '%s'",
                                                name().c_str(),
                                                colstr.c_str());
            // see note above
            if (!error)
                die("%s", failure.c_str());
            *error = failure;
            break;
        }

        result.push_back({threshold, scolour});
    }
    stable_sort(result.begin(), result.end(), ordering_function);
    return result;
}