2 * Copyright (C) 2010 Luca Barbieri
3 * Released under the terms of the Crawl General Public License.
16 #include "mgen_data.h"
17 #include "mon-place.h"
20 #include "tiledef-main.h"
21 #include "tiledef-dngn.h"
22 #include "tiledef-player.h"
24 #include "transform.h"
26 #include "mon-stuff.h"
28 #include "crawl_data.h"
29 #include "crawl_parser.h"
30 #include "crawl_defs.h"
35 #include <unordered_map>
36 #include <unordered_set>
38 #include <tr1/unordered_map>
39 #include <tr1/unordered_set>
46 #define HUGE_VALF ((float)HUGE_VAL)
51 void CrawlData::adjust_monster_tile(unsigned& fg
, monster_type mon
)
53 if (Options
.tile_show_demon_tier
)
55 switch (mons_char(mon
))
58 fg
|= TILE_FLAG_DEMON_1
;
61 fg
|= TILE_FLAG_DEMON_2
;
64 fg
|= TILE_FLAG_DEMON_3
;
67 fg
|= TILE_FLAG_DEMON_4
;
70 fg
|= TILE_FLAG_DEMON_5
;
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 */
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 */
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
)
96 pm
.type
= NUM_MONSTERS
;
97 pm
.base_type
= NUM_MONSTERS
;
99 pm
.is_shapeshifter
= false;
105 for(unsigned i
= 0; i
< s
.length(); ++i
)
109 else if(s
[i
] == ')') {
112 string qual
= s
.substr(open_paren
+ 1, i
- open_paren
- 1);
113 // TODO: do something with these (they are wandering, confused, etc.)
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)
146 size_t sp
= s
.find(' ');
147 if(sp
== string::npos
)
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"};
157 for(i
= 0; i
< 10; ++i
) {
158 if(heads_str
== cardinals
[i
]) {
164 istringstream
ss(heads_str
);
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
)
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
)
193 tok
= s
.substr(sp
+ 1);
194 if(tok
== "shaped shifter" || tok
== "shaped shifters")
195 pm
.is_shapeshifter
= true;
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
)
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
;
257 cerr
<< "Unknown monster name: " << pm
.name
<< endl
;
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
);
278 case MDAM_ALMOST_DEAD
:
279 pm
.tile
|= TILE_FLAG_MDAM_ADEAD
;
281 case MDAM_SEVERELY_DAMAGED
:
282 pm
.tile
|= TILE_FLAG_MDAM_SEV
;
284 case MDAM_HEAVILY_DAMAGED
:
285 pm
.tile
|= TILE_FLAG_MDAM_HEAVY
;
287 case MDAM_MODERATELY_DAMAGED
:
288 pm
.tile
|= TILE_FLAG_MDAM_MOD
;
290 case MDAM_LIGHTLY_DAMAGED
:
291 pm
.tile
|= TILE_FLAG_MDAM_LIGHT
;
302 pm
.tile
|= TILE_FLAG_PET
;
305 pm
.tile
|= TILE_FLAG_NEUTRAL
;
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
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
)
330 bool anything
= false;
331 for(unsigned j
= 1; j
< 16; ++j
) {
332 if(j
!= DARKGRAY
&& freqs
[j
]) {
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
];
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
];