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