1 /**
2 * @file
3 * @brief Monster attitude changing due to religion.
4 **/
5
6 #include "AppHdr.h"
7
8 #include "attitude-change.h"
9
10 #include <sstream>
11
12 #include "act-iter.h"
13 #include "branch.h"
14 #include "coordit.h"
15 #include "database.h"
16 #include "env.h"
17 #include "god-abil.h"
18 #include "god-companions.h"
19 #include "god-passive.h" // passive_t::convert_orcs
20 #include "libutil.h"
21 #include "message.h"
22 #include "mon-behv.h"
23 #include "mon-death.h"
24 #include "mon-tentacle.h"
25 #include "religion.h"
26 #include "state.h"
27 #include "travel.h"
28
29 // Called whenever an already existing monster changes its attitude, possibly
30 // temporarily.
mons_att_changed(monster * mon)31 void mons_att_changed(monster* mon)
32 {
33 const mon_attitude_type att = mon->temp_attitude();
34 const monster_type mc = mons_base_type(*mon);
35
36 if (mons_is_tentacle_head(mc)
37 || mons_is_solo_tentacle(mc))
38 {
39 for (monster_iterator mi; mi; ++mi)
40 if (mi->is_child_tentacle_of(mon))
41 {
42 mi->attitude = att;
43 if (!mons_is_solo_tentacle(mc))
44 {
45 for (monster_iterator connect; connect; ++connect)
46 {
47 if (connect->is_child_tentacle_of(*mi))
48 connect->attitude = att;
49 }
50 }
51
52 // It's almost always flipping between hostile and friendly;
53 // enslaving a pacified starspawn is still a shock.
54 mi->stop_constricting_all();
55 }
56 }
57
58 if (mon->attitude == ATT_HOSTILE
59 && (mons_is_god_gift(*mon, GOD_BEOGH)
60 || mons_is_god_gift(*mon, GOD_YREDELEMNUL)))
61 {
62 remove_companion(mon);
63 }
64
65 mon->remove_summons(true);
66 }
67
68 static void _jiyva_convert_slime(monster* slime);
69 static void _fedhas_neutralise_plant(monster* plant);
70
beogh_follower_convert(monster * mons,bool orc_hit)71 void beogh_follower_convert(monster* mons, bool orc_hit)
72 {
73 if (!species::is_orcish(you.species) || crawl_state.game_is_arena())
74 return;
75
76 // For followers of Beogh, decide whether orcs will join you.
77 if (will_have_passive(passive_t::convert_orcs)
78 && mons_genus(mons->type) == MONS_ORC
79 && !mons->is_summoned()
80 && !mons->is_shapeshifter()
81 && !testbits(mons->flags, MF_ATT_CHANGE_ATTEMPT)
82 && !mons->friendly())
83 {
84 mons->flags |= MF_ATT_CHANGE_ATTEMPT;
85
86 const int hd = mons->get_experience_level();
87
88 if (have_passive(passive_t::convert_orcs)
89 && random2(you.piety / 15) + random2(4 + you.experience_level / 3)
90 > random2(hd) + hd + random2(5))
91 {
92 beogh_convert_orc(mons, orc_hit || !mons->alive() ? conv_t::deathbed
93 : conv_t::sight);
94 stop_running();
95 }
96 }
97 }
98
slime_convert(monster * mons)99 void slime_convert(monster* mons)
100 {
101 if (have_passive(passive_t::neutral_slimes) && mons_is_slime(*mons)
102 && !mons->neutral()
103 && !mons->friendly()
104 && !testbits(mons->flags, MF_ATT_CHANGE_ATTEMPT))
105 {
106 mons->flags |= MF_ATT_CHANGE_ATTEMPT;
107 if (!player_under_penance())
108 {
109 _jiyva_convert_slime(mons);
110 stop_running();
111 }
112 }
113 }
114
fedhas_neutralise(monster * mons)115 void fedhas_neutralise(monster* mons)
116 {
117 if (have_passive(passive_t::friendly_plants)
118 && mons->attitude == ATT_HOSTILE
119 && fedhas_neutralises(*mons)
120 && !testbits(mons->flags, MF_ATT_CHANGE_ATTEMPT))
121 {
122 _fedhas_neutralise_plant(mons);
123 mons->flags |= MF_ATT_CHANGE_ATTEMPT;
124 del_exclude(mons->pos());
125 }
126 }
127
128 // Make summoned (temporary) god gifts disappear on penance or when
129 // abandoning the god in question.
make_god_gifts_disappear()130 void make_god_gifts_disappear()
131 {
132 const god_type god =
133 (crawl_state.is_god_acting()) ? crawl_state.which_god_acting()
134 : GOD_NO_GOD;
135 for (monster_iterator mi; mi; ++mi)
136 {
137 if (is_follower(**mi)
138 && mi->has_ench(ENCH_ABJ)
139 && mons_is_god_gift(**mi, god))
140 {
141 // The monster disappears.
142 monster_die(**mi, KILL_DISMISSED, NON_MONSTER);
143 }
144 }
145 }
146
147 // When under penance, Yredelemnulites can lose all nearby undead slaves.
yred_slaves_abandon_you()148 bool yred_slaves_abandon_you()
149 {
150 int num_reclaim = 0;
151 int num_slaves = 0;
152
153 for (radius_iterator ri(you.pos(), LOS_DEFAULT); ri; ++ri)
154 {
155 monster* mons = monster_at(*ri);
156 if (mons == nullptr)
157 continue;
158
159 if (is_yred_undead_slave(*mons))
160 {
161 num_slaves++;
162
163 const int hd = mons->get_experience_level();
164
165 // During penance, followers get a saving throw.
166 if (random2(20) > random2(hd))
167 continue;
168
169 mons->attitude = ATT_HOSTILE;
170 behaviour_event(mons, ME_ALERT, &you);
171 // For now CREATED_FRIENDLY stays.
172 mons_att_changed(mons);
173
174 num_reclaim++;
175 }
176 }
177
178 if (num_reclaim > 0)
179 {
180 if (num_reclaim == 1 && num_slaves > 1)
181 simple_god_message(" reclaims one of your granted undead slaves!");
182 else if (num_reclaim == num_slaves)
183 simple_god_message(" reclaims your granted undead slaves!");
184 else
185 simple_god_message(" reclaims some of your granted undead slaves!");
186 return true;
187 }
188
189 return false;
190 }
191
192 // When under penance, Beoghites can lose all nearby orcish followers,
193 // subject to a few limitations.
beogh_followers_abandon_you()194 bool beogh_followers_abandon_you()
195 {
196 bool reconvert = false;
197 int num_reconvert = 0;
198 int num_followers = 0;
199
200 for (radius_iterator ri(you.pos(), LOS_DEFAULT); ri; ++ri)
201 {
202 monster* mons = monster_at(*ri);
203 if (mons == nullptr)
204 continue;
205
206 // Note that orc high priests' summons are gifts of Beogh,
207 // so we can't use is_orcish_follower(*) here.
208 if (mons_is_god_gift(*mons, GOD_BEOGH))
209 {
210 num_followers++;
211
212 if (you.visible_to(mons)
213 && !mons->asleep()
214 && !mons_is_confused(*mons)
215 && !mons->cannot_act())
216 {
217 const int hd = mons->get_experience_level();
218
219 // During penance, followers get a saving throw.
220 if (random2(20) > random2(hd))
221 continue;
222
223 mons->attitude = ATT_HOSTILE;
224 behaviour_event(mons, ME_ALERT, &you);
225 // For now CREATED_FRIENDLY stays.
226 mons_att_changed(mons);
227
228 if (you.can_see(*mons))
229 num_reconvert++; // Only visible ones.
230
231 reconvert = true;
232 }
233 }
234 }
235
236 if (reconvert) // Maybe all of them are invisible.
237 {
238 simple_god_message("'s voice booms out, \"Who do you think you "
239 "are?\"", GOD_BEOGH);
240
241 ostream& chan = msg::streams(MSGCH_MONSTER_ENCHANT);
242
243 if (num_reconvert > 0)
244 {
245 if (num_reconvert == 1 && num_followers > 1)
246 chan << "One of your followers decides to abandon you.";
247 else if (num_reconvert == num_followers)
248 chan << "Your followers decide to abandon you.";
249 else
250 chan << "Some of your followers decide to abandon you.";
251 }
252
253 chan << endl;
254
255 return true;
256 }
257
258 return false;
259 }
260
_print_converted_orc_speech(const string & key,monster * mon,msg_channel_type channel)261 static void _print_converted_orc_speech(const string& key,
262 monster* mon,
263 msg_channel_type channel)
264 {
265 string msg = getSpeakString("beogh_converted_orc_" + key);
266
267 if (!msg.empty())
268 {
269 msg = do_mon_str_replacements(msg, *mon);
270 strip_channel_prefix(msg, channel);
271 mprf(channel, "%s", msg.c_str());
272 }
273 }
274
275 // Orcs may turn friendly when encountering followers of Beogh, and be
276 // made gifts of Beogh.
beogh_convert_orc(monster * orc,conv_t conv)277 void beogh_convert_orc(monster* orc, conv_t conv)
278 {
279 ASSERT(orc); // XXX: change to monster &orc
280 ASSERT(mons_genus(orc->type) == MONS_ORC);
281
282 switch (conv)
283 {
284 case conv_t::deathbed_follower:
285 _print_converted_orc_speech("reaction_battle_follower", orc,
286 MSGCH_FRIEND_ENCHANT);
287 _print_converted_orc_speech("speech_battle_follower", orc,
288 MSGCH_TALK);
289 break;
290 case conv_t::deathbed:
291 _print_converted_orc_speech("reaction_battle", orc,
292 MSGCH_FRIEND_ENCHANT);
293 _print_converted_orc_speech("speech_battle", orc, MSGCH_TALK);
294 break;
295 case conv_t::sight:
296 _print_converted_orc_speech("reaction_sight", orc,
297 MSGCH_FRIEND_ENCHANT);
298
299 if (!one_chance_in(3))
300 _print_converted_orc_speech("speech_sight", orc, MSGCH_TALK);
301 break;
302 case conv_t::resurrection:
303 _print_converted_orc_speech("resurrection", orc,
304 MSGCH_FRIEND_ENCHANT);
305 break;
306 }
307
308 orc->attitude = ATT_FRIENDLY;
309
310 // The monster is not really *created* friendly, but should it
311 // become hostile later on, it won't count as a good kill.
312 orc->flags |= MF_NO_REWARD;
313
314 if (orc->is_patrolling())
315 {
316 // Make orcs stop patrolling and forget their patrol point,
317 // they're supposed to follow you now.
318 orc->patrol_point = coord_def(0, 0);
319 }
320
321 if (!orc->alive())
322 orc->hit_points = min(random_range(1, 4), orc->max_hit_points);
323
324 mons_make_god_gift(*orc, GOD_BEOGH);
325 add_companion(orc);
326
327 // Avoid immobile "followers".
328 behaviour_event(orc, ME_ALERT);
329
330 mons_att_changed(orc);
331 }
332
_fedhas_neutralise_plant(monster * plant)333 static void _fedhas_neutralise_plant(monster* plant)
334 {
335 if (!plant
336 || !fedhas_neutralises(*plant)
337 || plant->attitude != ATT_HOSTILE
338 || testbits(plant->flags, MF_ATT_CHANGE_ATTEMPT))
339 {
340 return;
341 }
342
343 plant->attitude = ATT_GOOD_NEUTRAL;
344 plant->flags |= MF_WAS_NEUTRAL;
345 mons_att_changed(plant);
346 }
347
_jiyva_convert_slime(monster * slime)348 static void _jiyva_convert_slime(monster* slime)
349 {
350 ASSERT(slime); // XXX: change to monster &slime
351 ASSERT(mons_is_slime(*slime));
352
353 behaviour_event(slime, ME_ALERT);
354
355 if (you.can_see(*slime))
356 {
357 if (mons_genus(slime->type) == MONS_FLOATING_EYE)
358 {
359 mprf(MSGCH_GOD, "%s stares at you suspiciously for a moment, "
360 "then relaxes.",
361
362 slime->name(DESC_THE).c_str());
363 }
364 else
365 {
366 mprf(MSGCH_GOD, "%s trembles before you.",
367 slime->name(DESC_THE).c_str());
368 }
369 }
370
371 slime->attitude = ATT_STRICT_NEUTRAL;
372 slime->flags |= MF_WAS_NEUTRAL;
373
374 mons_make_god_gift(*slime, GOD_JIYVA);
375
376 mons_att_changed(slime);
377 }
378
gozag_set_bribe(monster * traitor)379 void gozag_set_bribe(monster* traitor)
380 {
381 // Try to bribe the monster.
382 const int bribability = gozag_type_bribable(traitor->type);
383
384 if (bribability <= 0 || traitor->friendly() || traitor->is_summoned())
385 return;
386
387 const monster* leader =
388 traitor->props.exists("band_leader")
389 ? monster_by_mid(traitor->props["band_leader"].get_int())
390 : nullptr;
391
392 if (leader)
393 {
394 if (leader->has_ench(ENCH_FRIENDLY_BRIBED)
395 || leader->props.exists(FRIENDLY_BRIBE_KEY))
396 {
397 traitor->props[FRIENDLY_BRIBE_KEY].get_bool() = true;
398 }
399 else if (leader->has_ench(ENCH_NEUTRAL_BRIBED)
400 || leader->props.exists(NEUTRAL_BRIBE_KEY))
401 {
402 traitor->props[NEUTRAL_BRIBE_KEY].get_bool() = true;
403 }
404 }
405 else if (x_chance_in_y(bribability, GOZAG_MAX_BRIBABILITY))
406 {
407 // Sometimes get permanent followers
408 if (one_chance_in(3))
409 traitor->props[FRIENDLY_BRIBE_KEY].get_bool() = true;
410 else
411 traitor->props[NEUTRAL_BRIBE_KEY].get_bool() = true;
412 }
413 }
414
gozag_check_bribe(monster * traitor)415 void gozag_check_bribe(monster* traitor)
416 {
417 branch_type branch = gozag_fixup_branch(you.where_are_you);
418
419 if (branch_bribe[branch] == 0)
420 return; // Do nothing if branch isn't currently bribed.
421
422 const int base_cost = max(1, exper_value(*traitor) / 20);
423
424 int cost = 0;
425
426 string msg;
427
428 if (traitor->props.exists(FRIENDLY_BRIBE_KEY))
429 {
430 traitor->props.erase(FRIENDLY_BRIBE_KEY);
431 traitor->add_ench(mon_enchant(ENCH_FRIENDLY_BRIBED, 0, 0,
432 INFINITE_DURATION));
433 msg = getSpeakString(traitor->name(DESC_DBNAME, true)
434 + " Gozag permabribe");
435 if (msg.empty())
436 msg = getSpeakString("Gozag permabribe");
437
438 // Actual allies deduct more gold.
439 cost = 2 * base_cost;
440 }
441 else if (traitor->props.exists(NEUTRAL_BRIBE_KEY))
442 {
443 traitor->props.erase(NEUTRAL_BRIBE_KEY);
444 traitor->add_ench(ENCH_NEUTRAL_BRIBED);
445 msg = getSpeakString(traitor->name(DESC_DBNAME, true)
446 + " Gozag bribe");
447 if (msg.empty())
448 msg = getSpeakString("Gozag bribe");
449
450 cost = base_cost;
451 }
452
453 if (!msg.empty())
454 {
455 msg_channel_type channel = MSGCH_FRIEND_ENCHANT;
456 msg = do_mon_str_replacements(msg, *traitor);
457 strip_channel_prefix(msg, channel);
458 mprf(channel, "%s", msg.c_str());
459 // !msg.empty means a monster was bribed.
460 gozag_deduct_bribe(branch, cost);
461 }
462 }
463
gozag_break_bribe(monster * victim)464 void gozag_break_bribe(monster* victim)
465 {
466 ASSERT(victim); // XXX: change to monster &victim
467
468 if (!victim->has_ench(ENCH_NEUTRAL_BRIBED)
469 && !victim->has_ench(ENCH_FRIENDLY_BRIBED))
470 {
471 return;
472 }
473
474 // Un-bribe the victim.
475 victim->del_ench(ENCH_NEUTRAL_BRIBED);
476 victim->del_ench(ENCH_FRIENDLY_BRIBED);
477
478 // Make other nearby bribed monsters un-bribed, too.
479 for (monster_iterator mi; mi; ++mi)
480 if (mi->can_see(*victim))
481 gozag_break_bribe(*mi);
482 }
483
484 // Conversions and bribes.
do_conversions(monster * target)485 void do_conversions(monster* target)
486 {
487 beogh_follower_convert(target);
488 gozag_check_bribe(target);
489 }
490