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