Add NetTiles, a new Tiles interface with online play support (v24)
[crawl/crawl-nettiles.git] / crawl-ref / source / nettiles / crawl_data_funcs.cc
blob02babcdcfc036438af5f8d58e1f6ee0e2cba6a55
1 /*
2 * Copyright (C) 2010 Luca Barbieri
3 * Released under the terms of the Crawl General Public License.
4 */
6 #include "AppHdr.h"
7 #include "branch.h"
8 #include "output.h"
9 #include "skills2.h"
10 #include "colour.h"
11 #include "feature.h"
12 #include "env.h"
13 #include "view.h"
14 #include "mon-util.h"
15 #include "items.h"
16 #include "mgen_data.h"
17 #include "mon-place.h"
18 #include "coord.h"
19 #include "coordit.h"
20 #include "tiledef-main.h"
21 #include "tiledef-dngn.h"
22 #include "tiledef-player.h"
23 #include "libutil.h"
24 #include "transform.h"
25 #include "options.h"
26 #include "mon-stuff.h"
28 #include "crawl_data.h"
29 #include "crawl_parser.h"
30 #include "crawl_defs.h"
32 #include <sstream>
33 #include <iostream>
34 #ifdef _MSC_VER
35 #include <unordered_map>
36 #include <unordered_set>
37 #else
38 #include <tr1/unordered_map>
39 #include <tr1/unordered_set>
40 #endif
41 #include <map>
42 #include <iomanip>
43 #include <math.h>
44 #include <malloc.h>
45 #ifndef HUGE_VALF
46 #define HUGE_VALF ((float)HUGE_VAL)
47 #endif
49 using namespace std;
51 void CrawlData::adjust_monster_tile(unsigned& fg, monster_type mon)
53 if (Options.tile_show_demon_tier)
55 switch (mons_char(mon))
57 case '1':
58 fg |= TILE_FLAG_DEMON_1;
59 break;
60 case '2':
61 fg |= TILE_FLAG_DEMON_2;
62 break;
63 case '3':
64 fg |= TILE_FLAG_DEMON_3;
65 break;
66 case '4':
67 fg |= TILE_FLAG_DEMON_4;
68 break;
69 case '5':
70 fg |= TILE_FLAG_DEMON_5;
71 break;
75 if(mons_class_flies(mon))
76 fg |= TILE_FLAG_FLYING;
77 if(mons_is_mimic(mon))
78 fg |= TILE_FLAG_ANIM_WEP;
81 void CrawlData::adjust_monster_tile_bg(unsigned& fg, unsigned abg)
83 if(abg == ((Options.friend_brand >> 8) & 15)) /* GREEN */
84 fg |= TILE_FLAG_PET;
85 else if(abg == ((Options.neutral_brand >> 8) & 15)) /* LIGHTGREY */
86 fg |= TILE_FLAG_NEUTRAL;
87 else if(abg == ((Options.stab_brand >> 8) & 15)) /* BLUE */
88 fg |= TILE_FLAG_STAB;
89 else if(abg == ((Options.may_stab_brand >> 8) & 15)) /* YELLOW */
90 fg |= TILE_FLAG_MAY_STAB;
93 parsed_monster CrawlData::parse_monster_name(string s)
95 parsed_monster pm;
96 pm.type = NUM_MONSTERS;
97 pm.base_type = NUM_MONSTERS;
98 pm.number = 0;
99 pm.is_shapeshifter = false;
100 pm.tile = 0;
103 int open_paren = -1;
104 ostringstream ss;
105 for(unsigned i = 0; i < s.length(); ++i)
107 if(s[i] == '(')
108 open_paren = i;
109 else if(s[i] == ')') {
110 if(open_paren >= 0)
112 string qual = s.substr(open_paren + 1, i - open_paren - 1);
113 // TODO: do something with these (they are wandering, confused, etc.)
114 if(qual == "caught")
115 pm.tile |= TILE_FLAG_NET;
116 else if(qual == "poisoned") // unused
117 pm.tile |= TILE_FLAG_POISON;
118 else if(qual == "burning")
119 pm.tile |= TILE_FLAG_FLAME;
120 else if(qual == "berserk" || qual == "frenzied")
121 pm.tile |= TILE_FLAG_BERSERK;
122 else if(qual == "friendly") /* or GREEN */
123 pm.tile |= TILE_FLAG_PET;
124 else if(qual == "neutral" || qual == "fellow slime") /* or BROWN */
125 pm.tile |= TILE_FLAG_NEUTRAL;
126 else if(qual == "resting" || qual == "dormant" || qual == "sleeping"|| qual == "paralysed")
127 pm.tile |= TILE_FLAG_STAB;
128 else if(qual == "distracted" || qual == "unaware" || qual == "wandering" || qual == "confused" || qual == "fleeing" || qual == "petrified" || qual == "petrifying")
129 pm.tile |= TILE_FLAG_MAY_STAB;
130 else if(qual == "invisible")
134 else if(open_paren >= 0)
136 else
137 ss << s[i];
139 s = ss.str();
140 s = trim_string(s);
143 pm.name = s;
145 for(;;) {
146 size_t sp = s.find(' ');
147 if(sp == string::npos)
148 break;
149 string tok = s.substr(0, sp);
151 if(tok == "spectral")
152 pm.type = MONS_SPECTRAL_THING;
153 else if(ends_with(tok, "-headed")) {
154 string heads_str = tok.substr(0, tok.size() - 7);
155 const char* cardinals[] = {"one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"};
156 int i;
157 for(i = 0; i < 10; ++i) {
158 if(heads_str == cardinals[i]) {
159 pm.number = i;
160 break;
163 if(i == 10) {
164 istringstream ss(heads_str);
165 ss >> pm.number;
167 } else
168 break;
170 s = s.substr(sp + 1);
173 while((int)pm.base_type == NUM_MONSTERS) {
174 size_t sp = s.rfind(' ');
175 if(sp == string::npos)
176 break;
177 string tok = s.substr(sp + 1);
179 if(tok == "zombie" || tok == "zombies")
180 pm.type = MONS_ZOMBIE_SMALL;
181 else if(tok == "simulacrum" || tok == "simulacrums" || tok == "simulacra")
182 pm.type = MONS_SIMULACRUM_SMALL;
183 else if(tok == "skeleton" || tok == "skeletons")
184 pm.type = MONS_SKELETON_SMALL;
185 else if(tok == "ghost" && sp >= 2 && (s[sp - 1] == '\'' || s[sp - 2] == '\''))
186 pm.base_type = pm.type = MONS_PLAYER_GHOST;
187 else if(tok == "illusion") // TODO: is this correct?
188 pm.base_type = pm.type = MONS_PLAYER_ILLUSION;
189 else if(tok == "shifter" || tok == "shifters") {
190 sp = s.rfind(' ', sp - 1);
191 if(sp == string::npos)
192 break;
193 tok = s.substr(sp + 1);
194 if(tok == "shaped shifter" || tok == "shaped shifters")
195 pm.is_shapeshifter = true;
196 else
197 break;
199 else
200 break;
202 s = s.substr(0, sp);
205 if(pm.base_type == MONS_PLAYER_GHOST || pm.base_type == MONS_PLAYER_ILLUSION) {
206 size_t ap = s.rfind('\'');
207 if(ap != string::npos)
208 s = s.substr(0, ap);
209 } else {
210 if(pm.type == MONS_SPECTRAL_THING && s == "warrior") {
211 pm.type = NUM_MONSTERS;
212 pm.base_type = MONS_SPECTRAL_WARRIOR;
215 if(ends_with(s, "the Cloud Mage"))
216 s = "the Cloud Mage";
217 else if(ends_with(s, "the Hellbinder"))
218 s = "the Hellbinder";
221 name_to_monster_t::iterator i = name_to_monster.find(s);
222 if(i != name_to_monster.end()) {
223 pm.base_type = i->second.first;
224 pm.number = max(pm.number, i->second.second);
229 name_to_monster_tile_t::iterator i = name_to_monster_tile.find(s);
230 if(i != name_to_monster.end()) {
231 pm.base_type = i->second.first;
232 pm.tile = i->second.second;
237 if(pm.base_type != NUM_MONSTERS) {
238 if(pm.type == MONS_ZOMBIE_SMALL && mons_zombie_size(pm.base_type) == Z_BIG)
239 pm.type = MONS_ZOMBIE_LARGE;
241 if(pm.type == MONS_SKELETON_SMALL && mons_zombie_size(pm.base_type) == Z_BIG)
242 pm.type = MONS_SKELETON_LARGE;
244 if(pm.type == MONS_SIMULACRUM_SMALL && mons_zombie_size(pm.base_type) == Z_BIG)
245 pm.type = MONS_SIMULACRUM_LARGE;
247 if((int)pm.type == NUM_MONSTERS)
248 pm.type = pm.base_type;
249 } else if(pm.type != NUM_MONSTERS)
250 pm.base_type = pm.type;
252 if(pm.type == NUM_MONSTERS) {
253 if(!s.empty() && isupper(s[0])) {
254 cerr << "Unknown monster name (assuming Pan lord): " << pm.name << endl;
255 pm.type = pm.base_type = MONS_PANDEMONIUM_DEMON;
256 } else {
257 cerr << "Unknown monster name: " << pm.name << endl;
261 return pm;
264 parsed_monster CrawlData::parse_monster(char c, cattr_t a, const string& s, cattr_t sa, int health)
266 parsed_monster pm = parse_monster_name(s);
268 if(!(pm.tile & TILE_FLAG_MASK))
269 pm.tile |= tileidx_monster_type(pm.type, pm.base_type, a.fg != BLACK ? a.fg : a.bg, pm.number);
271 adjust_monster_tile(pm.tile, pm.type);
272 adjust_monster_tile(pm.tile, pm.base_type);
273 adjust_monster_tile_bg(pm.tile, a.bg);
275 switch (health)
277 case MDAM_DEAD:
278 case MDAM_ALMOST_DEAD:
279 pm.tile |= TILE_FLAG_MDAM_ADEAD;
280 break;
281 case MDAM_SEVERELY_DAMAGED:
282 pm.tile |= TILE_FLAG_MDAM_SEV;
283 break;
284 case MDAM_HEAVILY_DAMAGED:
285 pm.tile |= TILE_FLAG_MDAM_HEAVY;
286 break;
287 case MDAM_MODERATELY_DAMAGED:
288 pm.tile |= TILE_FLAG_MDAM_MOD;
289 break;
290 case MDAM_LIGHTLY_DAMAGED:
291 pm.tile |= TILE_FLAG_MDAM_LIGHT;
292 break;
293 case MDAM_OKAY:
294 default:
295 // no flag for okay.
296 break;
299 switch(sa.fg)
301 case GREEN:
302 pm.tile |= TILE_FLAG_PET;
303 break;
304 case BROWN:
305 pm.tile |= TILE_FLAG_NEUTRAL;
306 break;
307 default:
308 if(sa.fg == Options.evil_colour)
309 {} // TODO: add a tile for unchivalric actions
312 if(pm.is_shapeshifter)
313 {} // TODO: add a tile for shapeshifter
315 return pm;
318 int CrawlData::guess_element_color(vector<unsigned> elems, unsigned char* freqs)
320 // P(e | c...) = (P(c1 | e) P(c2 | e) P(e)) / P(c...)
321 // max P(e | c...) = (prod P(c_i | e)^freq[c[i]] P(e))
322 // max log P(e | c...) = (sum freq[c[i]] log P(c_i | e) + log P(e))
324 unsigned n = elems.size();
325 float* probs = (float*)alloca(sizeof(float) * n);
327 for(unsigned i = 0; i < n; ++i)
328 probs[i] = 0.0f;
330 bool anything = false;
331 for(unsigned j = 1; j < 16; ++j) {
332 if(j != DARKGRAY && freqs[j]) {
333 anything = true;
334 for(unsigned i = 0; i < n; ++i) {
335 unsigned elem = elems[i];
336 // cout << i << ' ' << j << ": " << log_element_to_color_prob[elem][j] * freqs[j] << endl;
337 probs[i] += log_element_to_color_prob[elem][j] * freqs[j];
342 if(!anything)
343 return -1;
345 int best_elem = -1;
346 float best_prob = -HUGE_VALF;
348 for(unsigned i = 0; i < n; ++i) {
349 // cout << "Element " << elems[i] << " has prob " << exp(probs[i]) << endl;
350 if(probs[i] > best_prob) {
351 best_prob = probs[i];
352 best_elem = elems[i];
356 return best_elem;