1 /**
2  * @file
3  * @brief Code to clone existing monsters and to create player illusions.
4 **/
5 
6 #include "AppHdr.h"
7 
8 #include "mon-clone.h"
9 
10 #include "act-iter.h"
11 #include "arena.h"
12 #include "artefact.h"
13 #include "coordit.h"
14 #include "env.h"
15 #include "items.h"
16 #include "message.h"
17 #include "mgen-data.h"
18 #include "mon-behv.h"
19 #include "mon-death.h"
20 #include "mon-place.h"
21 #include "mon-tentacle.h"
22 #include "state.h"
23 #include "stringutil.h"
24 #include "terrain.h"
25 #include "transform.h"
26 #include "view.h"
27 
_monster_clone_id_for(monster * mons)28 static string _monster_clone_id_for(monster* mons)
29 {
30     return make_stringf("%s%d",
31                         mons->name(DESC_PLAIN, true).c_str(),
32                         you.num_turns);
33 }
34 
_monster_clone_exists(monster * mons)35 static bool _monster_clone_exists(monster* mons)
36 {
37     if (!mons->props.exists(CLONE_MASTER_KEY))
38         return false;
39 
40     const string clone_id = mons->props[CLONE_MASTER_KEY].get_string();
41     for (monster_iterator mi; mi; ++mi)
42     {
43         monster* thing(*mi);
44         if (thing->props.exists(CLONE_SLAVE_KEY)
45             && thing->props[CLONE_SLAVE_KEY].get_string() == clone_id)
46         {
47             return true;
48         }
49     }
50     return false;
51 }
52 
_mons_is_illusion_cloneable(monster * mons)53 static bool _mons_is_illusion_cloneable(monster* mons)
54 {
55     return !mons_is_conjured(mons->type)
56            && !mons_is_tentacle_or_tentacle_segment(mons->type)
57            && !mons->is_illusion()
58            && !_monster_clone_exists(mons);
59 }
60 
_player_is_illusion_cloneable()61 static bool _player_is_illusion_cloneable()
62 {
63     for (monster_iterator mi; mi; ++mi)
64     {
65         if (mi->type == MONS_PLAYER_ILLUSION && mi->mname == you.your_name)
66             return false;
67     }
68     return true;
69 }
70 
actor_is_illusion_cloneable(actor * target)71 bool actor_is_illusion_cloneable(actor *target)
72 {
73     if (target->is_player())
74         return _player_is_illusion_cloneable();
75     else
76         return _mons_is_illusion_cloneable(target->as_monster());
77 }
78 
_mons_summon_monster_illusion(monster * caster,monster * foe)79 static void _mons_summon_monster_illusion(monster* caster,
80                                           monster* foe)
81 {
82     // If the monster's clone is still kicking around, don't clone it again.
83     if (!_mons_is_illusion_cloneable(foe))
84         return;
85 
86     bool cloning_visible = false;
87 
88     // If an enslaved caster creates a clone from a regular hostile,
89     // the clone should still be friendly.
90     if (monster *clone = clone_mons(foe, true, &cloning_visible,
91                                     caster->friendly() ?
92                                     ATT_FRIENDLY : caster->attitude))
93     {
94         const string clone_id = _monster_clone_id_for(foe);
95         clone->props[CLONE_SLAVE_KEY] = clone_id;
96         foe->props[CLONE_MASTER_KEY] = clone_id;
97         mons_add_blame(clone,
98                        "woven by " + caster->name(DESC_THE));
99         if (!clone->has_ench(ENCH_ABJ))
100             clone->mark_summoned(6, true, MON_SUMM_CLONE);
101         clone->add_ench(ENCH_PHANTOM_MIRROR);
102         clone->summoner = caster->mid;
103 
104         // Discard unsuitable enchantments.
105         clone->del_ench(ENCH_CHARM);
106         clone->del_ench(ENCH_STICKY_FLAME);
107         clone->del_ench(ENCH_CORONA);
108         clone->del_ench(ENCH_SILVER_CORONA);
109         clone->del_ench(ENCH_HEXED);
110 
111         behaviour_event(clone, ME_ALERT, 0, caster->pos());
112 
113         if (cloning_visible)
114         {
115             if (!you.can_see(*caster))
116             {
117                 mprf("%s seems to step out of %s!",
118                      foe->name(DESC_THE).c_str(),
119                      foe->pronoun(PRONOUN_REFLEXIVE).c_str());
120             }
121             else
122                 mprf("%s seems to draw %s out of %s!",
123                      caster->name(DESC_THE).c_str(),
124                      foe->name(DESC_THE).c_str(),
125                      foe->pronoun(PRONOUN_REFLEXIVE).c_str());
126         }
127     }
128 }
129 
_init_player_illusion_properties(monsterentry * me)130 static void _init_player_illusion_properties(monsterentry *me)
131 {
132     me->holiness = you.holiness();
133     // [ds] If we're cloning the player, use their base holiness, not
134     // the effects of their Necromutation spell. This is important
135     // since Necromutation users presumably also have Dispel Undead
136     // available to them. :P
137     if (form_changed_physiology() && me->holiness & MH_UNDEAD)
138         me->holiness = MH_NATURAL;
139 }
140 
141 // [ds] Not *all* appropriate enchantments are mapped -- only things
142 // that are (presumably) internal to the body, like haste and
143 // poisoning, and specifically not external effects like corona and
144 // sticky flame.
_player_duration_to_mons_enchantment(duration_type dur)145 static enchant_type _player_duration_to_mons_enchantment(duration_type dur)
146 {
147     switch (dur)
148     {
149     case DUR_INVIS:     return ENCH_INVIS;
150     case DUR_CONF:      return ENCH_CONFUSION;
151     case DUR_PARALYSIS: return ENCH_PARALYSIS;
152     case DUR_SLOW:      return ENCH_SLOW;
153     case DUR_HASTE:     return ENCH_HASTE;
154     case DUR_MIGHT:     return ENCH_MIGHT;
155     case DUR_BERSERK:   return ENCH_BERSERK;
156     case DUR_POISONING: return ENCH_POISON;
157 
158     default:            return ENCH_NONE;
159     }
160 }
161 
_mons_load_player_enchantments(monster * creator,monster * target)162 static void _mons_load_player_enchantments(monster* creator, monster* target)
163 {
164     for (int i = 0; i < NUM_DURATIONS; ++i)
165     {
166         if (you.duration[i] > 0)
167         {
168             const duration_type dur(static_cast<duration_type>(i));
169             const enchant_type ench =
170                 _player_duration_to_mons_enchantment(dur);
171             if (ench == ENCH_NONE)
172                 continue;
173             target->add_ench(mon_enchant(ench,
174                                          0,
175                                          creator,
176                                          you.duration[i]));
177         }
178     }
179 }
180 
mons_summon_illusion_from(monster * mons,actor * foe,spell_type spell_cast,int card_power)181 void mons_summon_illusion_from(monster* mons, actor *foe,
182                                spell_type spell_cast, int card_power)
183 {
184     if (foe->is_player())
185     {
186         int abj = 6;
187 
188         if (card_power >= 0)
189         {
190           // card effect
191           abj = 2 + random2(card_power);
192         }
193 
194         if (monster *clone = create_monster(
195                 mgen_data(MONS_PLAYER_ILLUSION, SAME_ATTITUDE(mons),
196                           mons->pos(), mons->foe)
197                  .set_summoned(mons, abj, spell_cast)))
198         {
199             if (card_power >= 0)
200                 mpr("Suddenly you stand beside yourself.");
201             else
202                 mprf(MSGCH_WARN, "There is a horrible, sudden wrenching feeling in your soul!");
203 
204             _init_player_illusion_properties(
205                 get_monster_data(MONS_PLAYER_ILLUSION));
206             _mons_load_player_enchantments(mons, clone);
207             clone->add_ench(ENCH_PHANTOM_MIRROR);
208         }
209         else if (card_power >= 0)
210             mpr("You see a puff of smoke.");
211     }
212     else
213     {
214         monster* mfoe = foe->as_monster();
215         _mons_summon_monster_illusion(mons, mfoe);
216     }
217 }
218 
mons_clonable(const monster * mon,bool needs_adjacent)219 bool mons_clonable(const monster* mon, bool needs_adjacent)
220 {
221     // No uniques or ghost demon monsters. Also, figuring out the name
222     // for the clone of a named monster isn't worth it, and duplicate
223     // battlespheres with the same owner cause problems with the spell
224     if (mons_is_unique(mon->type)
225         || mons_is_ghost_demon(mon->type)
226         || mon->is_named()
227         || mon->type == MONS_BATTLESPHERE)
228     {
229         return false;
230     }
231 
232     if (needs_adjacent)
233     {
234         // Is there space for the clone?
235         bool square_found = false;
236         for (adjacent_iterator ai(mon->pos()); ai; ++ai)
237         {
238             if (in_bounds(*ai)
239                 && !actor_at(*ai)
240                 && monster_habitable_grid(mon, env.grid(*ai)))
241             {
242                 square_found = true;
243                 break;
244             }
245         }
246         if (!square_found)
247             return false;
248     }
249 
250     // Is the monster carrying an artefact?
251     for (mon_inv_iterator ii(const_cast<monster &>(*mon)); ii; ++ii)
252         if (is_artefact(*ii))
253             return false;
254 
255     return true;
256 }
257 
258 /*
259  * @param orig          The original monster to clone.
260  * @param quiet         If true, suppress messages
261  * @param obvious       If true, player can see the orig & cloned monster
262  * @return              Returns the cloned monster
263  */
clone_mons(const monster * orig,bool quiet,bool * obvious)264 monster* clone_mons(const monster* orig, bool quiet, bool* obvious)
265 {
266     // Pass temp_attitude to handle enslaved monsters cloning monsters
267     return clone_mons(orig, quiet, obvious, orig->temp_attitude());
268 }
269 
270 /**
271  * @param orig          The original monster to clone.
272  * @param quiet         If true, suppress messages
273  * @param obvious       If true, player can see the orig & cloned monster
274  * @param mon_att       The attitude to set for the cloned monster
275  * @return              Returns the cloned monster
276  */
clone_mons(const monster * orig,bool quiet,bool * obvious,mon_attitude_type mon_att)277 monster* clone_mons(const monster* orig, bool quiet, bool* obvious,
278                     mon_attitude_type mon_att)
279 {
280     // Is there an open slot in env.mons?
281     monster* mons = get_free_monster();
282     coord_def pos(0, 0);
283 
284     if (!mons)
285         return nullptr;
286 
287     for (fair_adjacent_iterator ai(orig->pos()); ai; ++ai)
288     {
289         if (in_bounds(*ai)
290             && !actor_at(*ai)
291             && monster_habitable_grid(orig, env.grid(*ai)))
292         {
293             pos = *ai;
294         }
295     }
296 
297     if (!in_bounds(pos))
298         return nullptr;
299 
300     ASSERT(!actor_at(pos));
301 
302     *mons          = *orig;
303     mons->set_new_monster_id();
304     mons->move_to_pos(pos);
305     mons->attitude = mon_att;
306 
307     // The monster copy constructor doesn't copy constriction, so no need to
308     // worry about that.
309 
310     // Don't copy death triggers - phantom royal jellies should not open the
311     // Slime vaults on death.
312     if (mons->props.exists(MONSTER_DIES_LUA_KEY))
313         mons->props.erase(MONSTER_DIES_LUA_KEY);
314 
315     // Duplicate objects, or unequip them if they can't be duplicated.
316     for (mon_inv_iterator ii(*mons); ii; ++ii)
317     {
318         const int old_index = ii->index();
319 
320         const int new_index = get_mitm_slot(0);
321         if (new_index == NON_ITEM)
322         {
323             mons->unequip(env.item[old_index], false, true);
324             mons->inv[ii.slot()] = NON_ITEM;
325             continue;
326         }
327 
328         mons->inv[ii.slot()] = new_index;
329         env.item[new_index] = env.item[old_index];
330         env.item[new_index].set_holding_monster(*mons);
331     }
332 
333     bool _obvious;
334     if (obvious == nullptr)
335         obvious = &_obvious;
336     *obvious = false;
337 
338     if (you.can_see(*orig) && you.can_see(*mons))
339     {
340         if (!quiet)
341             simple_monster_message(*orig, " is duplicated!");
342         *obvious = true;
343     }
344 
345     if (you.can_see(*mons))
346     {
347         handle_seen_interrupt(mons);
348         viewwindow();
349         update_screen();
350     }
351 
352     if (crawl_state.game_is_arena())
353         arena_placed_monster(mons);
354 
355     return mons;
356 }
357