1 /**
2 * \file effects.c
3 * \brief Handler and auxiliary functions for every effect in the game
4 *
5 * Copyright (c) 2007 Andi Sidwell
6 * Copyright (c) 2016 Ben Semmler, Nick McConnell
7 *
8 * This work is free software; you can redistribute it and/or modify it
9 * under the terms of either:
10 *
11 * a) the GNU General Public License as published by the Free Software
12 * Foundation, version 2, or
13 *
14 * b) the "Angband licence":
15 * This software may be copied and distributed for educational, research,
16 * and not for profit purposes provided that this copyright and statement
17 * are included in all such copies. Other copyrights may also apply.
18 */
19
20 #include "angband.h"
21 #include "cave.h"
22 #include "effects.h"
23 #include "game-input.h"
24 #include "generate.h"
25 #include "init.h"
26 #include "mon-desc.h"
27 #include "mon-lore.h"
28 #include "mon-make.h"
29 #include "mon-msg.h"
30 #include "mon-predicate.h"
31 #include "mon-spell.h"
32 #include "mon-summon.h"
33 #include "mon-util.h"
34 #include "mon-timed.h"
35 #include "obj-chest.h"
36 #include "obj-curse.h"
37 #include "obj-desc.h"
38 #include "obj-gear.h"
39 #include "obj-ignore.h"
40 #include "obj-knowledge.h"
41 #include "obj-make.h"
42 #include "obj-pile.h"
43 #include "obj-power.h"
44 #include "obj-tval.h"
45 #include "obj-util.h"
46 #include "player-calcs.h"
47 #include "player-history.h"
48 #include "player-spell.h"
49 #include "player-timed.h"
50 #include "player-util.h"
51 #include "project.h"
52 #include "source.h"
53 #include "target.h"
54 #include "trap.h"
55
56
57 /**
58 * ------------------------------------------------------------------------
59 * Structures and helper functions for effects
60 * ------------------------------------------------------------------------ */
61 typedef struct effect_handler_context_s {
62 const effect_index effect;
63 const struct source origin;
64 const struct object *obj;
65 const bool aware;
66 const int dir;
67 const int beam;
68 const int boost;
69 const random_value value;
70 const int subtype, radius, other, y, x;
71 const char *msg;
72 bool ident;
73 struct command *cmd;
74 } effect_handler_context_t;
75
76 typedef bool (*effect_handler_f)(effect_handler_context_t *);
77
78 /**
79 * Structure for effects
80 */
81 struct effect_kind {
82 u16b index; /* Effect index */
83 bool aim; /* Whether the effect requires aiming */
84 const char *info; /* Effect info (for spell tips) */
85 effect_handler_f handler; /* Function to perform the effect */
86 const char *desc; /* Effect description */
87 };
88
89
90 /**
91 * Stat adjectives
92 */
desc_stat(int stat,bool positive)93 static const char *desc_stat(int stat, bool positive)
94 {
95 struct obj_property *prop = lookup_obj_property(OBJ_PROPERTY_STAT, stat);
96 if (positive) {
97 return prop->adjective;
98 }
99 return prop->neg_adj;
100 }
101
102
effect_calculate_value(effect_handler_context_t * context,bool use_boost)103 int effect_calculate_value(effect_handler_context_t *context, bool use_boost)
104 {
105 int final = 0;
106
107 if (context->value.base > 0 ||
108 (context->value.dice > 0 && context->value.sides > 0)) {
109 final = context->value.base +
110 damroll(context->value.dice, context->value.sides);
111 }
112
113 /* Device boost */
114 if (use_boost) {
115 final *= (100 + context->boost);
116 final /= 100;
117 }
118
119 return final;
120 }
121
get_target(struct source origin,int dir,struct loc * grid,int * flags)122 static void get_target(struct source origin, int dir, struct loc *grid,
123 int *flags)
124 {
125 switch (origin.what) {
126 case SRC_MONSTER: {
127 struct monster *monster = cave_monster(cave, origin.which.monster);
128 int conf_level, accuracy = 100;
129
130 if (!monster) break;
131
132 conf_level = monster_effect_level(monster, MON_TMD_CONF);
133 while (conf_level) {
134 accuracy *= (100 - CONF_RANDOM_CHANCE);
135 accuracy /= 100;
136 conf_level--;
137 }
138
139 *flags |= (PROJECT_PLAY);
140
141 if (randint1(100) > accuracy) {
142 dir = randint1(9);
143 *grid = loc_sum(monster->grid, ddgrid[dir]);
144 } else if (monster->target.midx > 0) {
145 struct monster *mon = cave_monster(cave, monster->target.midx);
146 *grid = mon->grid;
147 } else {
148 struct loc decoy = cave_find_decoy(cave);
149 if (!loc_is_zero(decoy)) {
150 *grid = decoy;
151 } else {
152 *grid = player->grid;
153 }
154 }
155
156 break;
157 }
158
159 case SRC_PLAYER:
160 if (dir == DIR_TARGET && target_okay()) {
161 target_get(grid);
162 } else {
163 /* Use the adjacent grid in the given direction as target */
164 *grid = loc_sum(player->grid, ddgrid[dir]);
165 }
166
167 break;
168
169 default:
170 *flags |= PROJECT_PLAY;
171 *grid = player->grid;
172 break;
173 }
174 }
175
176 /**
177 * Check for monster targeting another monster
178 */
monster_target_monster(effect_handler_context_t * context)179 static struct monster *monster_target_monster(effect_handler_context_t *context)
180 {
181 if (context->origin.what == SRC_MONSTER) {
182 struct monster *mon = cave_monster(cave, context->origin.which.monster);
183 if (!mon) return NULL;
184 if (mon->target.midx > 0) {
185 struct monster *t_mon = cave_monster(cave, mon->target.midx);
186 assert(t_mon);
187 return t_mon;
188 }
189 }
190 return NULL;
191 }
192
193 /**
194 * Apply the project() function in a direction, or at a target
195 */
project_aimed(struct source origin,int typ,int dir,int dam,int flg,const struct object * obj)196 static bool project_aimed(struct source origin,
197 int typ, int dir, int dam, int flg,
198 const struct object *obj)
199 {
200 struct loc grid = loc(-1, -1);
201
202 /* Pass through the target if needed */
203 flg |= (PROJECT_THRU);
204
205 get_target(origin, dir, &grid, &flg);
206
207 /* Aim at the target, do NOT explode */
208 return (project(origin, 0, grid, dam, typ, flg, 0, 0, obj));
209 }
210
211 /**
212 * Apply the project() function to grids around the target
213 */
project_touch(int dam,int rad,int typ,bool aware,const struct object * obj)214 static bool project_touch(int dam, int rad, int typ, bool aware,
215 const struct object *obj)
216 {
217 struct loc pgrid = player->grid;
218
219 int flg = PROJECT_GRID | PROJECT_KILL | PROJECT_HIDE | PROJECT_ITEM | PROJECT_THRU;
220 if (aware) flg |= PROJECT_AWARE;
221 return (project(source_player(), rad, pgrid, dam, typ, flg, 0, 0, obj));
222 }
223
224 /**
225 * Selects items that have at least one removable curse.
226 */
item_tester_uncursable(const struct object * obj)227 static bool item_tester_uncursable(const struct object *obj)
228 {
229 struct curse_data *c = obj->known->curses;
230 if (c) {
231 size_t i;
232 for (i = 1; i < z_info->curse_max; i++) {
233 if (c[i].power < 100) {
234 return true;
235 }
236 }
237 }
238 return false;
239 }
240
241 /**
242 * Removes an individual curse from an object.
243 */
remove_object_curse(struct object * obj,int index,bool message)244 static void remove_object_curse(struct object *obj, int index, bool message)
245 {
246 struct curse_data *c = &obj->curses[index];
247 char *name = curses[index].name;
248 char *removed = format("The %s curse is removed!", name);
249 int i;
250
251 c->power = 0;
252 c->timeout = 0;
253 if (message) {
254 msg(removed);
255 }
256
257 /* Check to see if that was the last one */
258 for (i = 1; i < z_info->curse_max; i++) {
259 if (obj->curses[i].power) {
260 return;
261 }
262 }
263
264 mem_free(obj->curses);
265 obj->curses = NULL;
266 }
267
268 /**
269 * Attempts to remove a curse from an object.
270 */
uncurse_object(struct object * obj,int strength,char * dice_string)271 static bool uncurse_object(struct object *obj, int strength, char *dice_string)
272 {
273 int index = 0;
274
275 if (get_curse(&index, obj, dice_string)) {
276 struct curse_data curse = obj->curses[index];
277 char o_name[80];
278
279 if (curse.power >= 100) {
280 /* Curse is permanent */
281 return false;
282 } else if (strength >= curse.power) {
283 /* Successfully removed this curse */
284 remove_object_curse(obj->known, index, false);
285 remove_object_curse(obj, index, true);
286 } else if (!of_has(obj->flags, OF_FRAGILE)) {
287 /* Failure to remove, object is now fragile */
288 object_desc(o_name, sizeof(o_name), obj, ODESC_FULL);
289 msgt(MSG_CURSED, "The spell fails; your %s is now fragile.", o_name);
290 of_on(obj->flags, OF_FRAGILE);
291 player_learn_flag(player, OF_FRAGILE);
292 } else if (one_in_(4)) {
293 /* Failure - unlucky fragile object is destroyed */
294 struct object *destroyed;
295 bool none_left = false;
296 msg("There is a bang and a flash!");
297 take_hit(player, damroll(5, 5), "Failed uncursing");
298 if (object_is_carried(player, obj)) {
299 destroyed = gear_object_for_use(obj, 1, false, &none_left);
300 if (destroyed->artifact) {
301 /* Artifacts are marked as lost */
302 history_lose_artifact(player, destroyed->artifact);
303 }
304 object_delete(&destroyed->known);
305 object_delete(&destroyed);
306 } else {
307 square_delete_object(cave, obj->grid, obj, true, true);
308 }
309 } else {
310 /* Non-destructive failure */
311 msg("The removal fails.");
312 }
313 } else {
314 return false;
315 }
316 player->upkeep->notice |= (PN_COMBINE);
317 player->upkeep->update |= (PU_BONUS);
318 player->upkeep->redraw |= (PR_EQUIP | PR_INVEN);
319 return true;
320 }
321
322 /**
323 * Selects items that have at least one unknown rune.
324 */
item_tester_unknown(const struct object * obj)325 static bool item_tester_unknown(const struct object *obj)
326 {
327 return object_runes_known(obj) ? false : true;
328 }
329
330 /**
331 * Bit flags for the enchant() function
332 */
333 #define ENCH_TOHIT 0x01
334 #define ENCH_TODAM 0x02
335 #define ENCH_TOBOTH 0x03
336 #define ENCH_TOAC 0x04
337
338 /**
339 * Used by the enchant() function (chance of failure)
340 */
341 static const int enchant_table[16] =
342 {
343 0, 10, 20, 40, 80,
344 160, 280, 400, 550, 700,
345 800, 900, 950, 970, 990,
346 1000
347 };
348
349 /**
350 * Hook to specify "weapon"
351 */
item_tester_hook_weapon(const struct object * obj)352 static bool item_tester_hook_weapon(const struct object *obj)
353 {
354 return tval_is_weapon(obj);
355 }
356
357
358 /**
359 * Hook to specify "armour"
360 */
item_tester_hook_armour(const struct object * obj)361 static bool item_tester_hook_armour(const struct object *obj)
362 {
363 return tval_is_armor(obj);
364 }
365
366 /**
367 * Tries to increase an items bonus score, if possible.
368 *
369 * \returns true if the bonus was increased
370 */
enchant_score(s16b * score,bool is_artifact)371 static bool enchant_score(s16b *score, bool is_artifact)
372 {
373 int chance;
374
375 /* Artifacts resist enchantment half the time */
376 if (is_artifact && randint0(100) < 50) return false;
377
378 /* Figure out the chance to enchant */
379 if (*score < 0) chance = 0;
380 else if (*score > 15) chance = 1000;
381 else chance = enchant_table[*score];
382
383 /* If we roll less-than-or-equal to chance, it fails */
384 if (randint1(1000) <= chance) return false;
385
386 /* Increment the score */
387 ++*score;
388
389 return true;
390 }
391
392 /**
393 * Helper function for enchant() which tries increasing an item's bonuses
394 *
395 * \returns true if a bonus was increased
396 */
enchant2(struct object * obj,s16b * score)397 static bool enchant2(struct object *obj, s16b *score)
398 {
399 bool result = false;
400 bool is_artifact = obj->artifact ? true : false;
401 if (enchant_score(score, is_artifact)) result = true;
402 return result;
403 }
404
405 /**
406 * Enchant an item
407 *
408 * Revamped! Now takes item pointer, number of times to try enchanting, and a
409 * flag of what to try enchanting. Artifacts resist enchantment some of the
410 * time. Also, any enchantment attempt (even unsuccessful) kicks off a parallel
411 * attempt to uncurse a cursed item.
412 *
413 * Note that an item can technically be enchanted all the way to +15 if you
414 * wait a very, very, long time. Going from +9 to +10 only works about 5% of
415 * the time, and from +10 to +11 only about 1% of the time.
416 *
417 * Note that this function can now be used on "piles" of items, and the larger
418 * the pile, the lower the chance of success.
419 *
420 * \returns true if the item was changed in some way
421 */
enchant(struct object * obj,int n,int eflag)422 bool enchant(struct object *obj, int n, int eflag)
423 {
424 int i, prob;
425 bool res = false;
426
427 /* Large piles resist enchantment */
428 prob = obj->number * 100;
429
430 /* Missiles are easy to enchant */
431 if (tval_is_ammo(obj)) prob = prob / 20;
432
433 /* Try "n" times */
434 for (i = 0; i < n; i++)
435 {
436 /* Roll for pile resistance */
437 if (prob > 100 && randint0(prob) >= 100) continue;
438
439 /* Try the three kinds of enchantment we can do */
440 if ((eflag & ENCH_TOHIT) && enchant2(obj, &obj->to_h)) res = true;
441 if ((eflag & ENCH_TODAM) && enchant2(obj, &obj->to_d)) res = true;
442 if ((eflag & ENCH_TOAC) && enchant2(obj, &obj->to_a)) res = true;
443 }
444
445 /* Update knowledge */
446 assert(obj->known);
447 obj->known->to_h = obj->to_h;
448 obj->known->to_d = obj->to_d;
449 obj->known->to_a = obj->to_a;
450
451 /* Failure */
452 if (!res) return (false);
453
454 /* Recalculate bonuses, gear */
455 player->upkeep->update |= (PU_BONUS | PU_INVEN);
456
457 /* Combine the pack (later) */
458 player->upkeep->notice |= (PN_COMBINE);
459
460 /* Redraw stuff */
461 player->upkeep->redraw |= (PR_INVEN | PR_EQUIP );
462
463 /* Success */
464 return (true);
465 }
466
467 /**
468 * Enchant an item (in the inventory or on the floor)
469 * Note that "num_ac" requires armour, else weapon
470 * Returns true if attempted, false if cancelled
471 *
472 * Enchanting with the TOBOTH flag will try to enchant
473 * both to_hit and to_dam with the same flag. This
474 * may not be the most desirable behavior (ACB).
475 */
enchant_spell(int num_hit,int num_dam,int num_ac,struct command * cmd)476 bool enchant_spell(int num_hit, int num_dam, int num_ac, struct command *cmd)
477 {
478 bool okay = false;
479
480 struct object *obj;
481
482 char o_name[80];
483
484 const char *q, *s;
485 int itemmode = (USE_EQUIP | USE_INVEN | USE_QUIVER | USE_FLOOR);
486 item_tester filter = num_ac ?
487 item_tester_hook_armour : item_tester_hook_weapon;
488
489 /* Get an item */
490 q = "Enchant which item? ";
491 s = "You have nothing to enchant.";
492 if (cmd) {
493 if (cmd_get_item(cmd, "tgtitem", &obj, q, s, filter,
494 itemmode)) {
495 return false;
496 }
497 } else if (!get_item(&obj, q, s, 0, filter, itemmode))
498 return false;
499
500 /* Description */
501 object_desc(o_name, sizeof(o_name), obj, ODESC_BASE);
502
503 /* Describe */
504 msg("%s %s glow%s brightly!",
505 (object_is_carried(player, obj) ? "Your" : "The"), o_name,
506 ((obj->number > 1) ? "" : "s"));
507
508 /* Enchant */
509 if (num_dam && enchant(obj, num_hit, ENCH_TOBOTH)) okay = true;
510 else if (enchant(obj, num_hit, ENCH_TOHIT)) okay = true;
511 else if (enchant(obj, num_dam, ENCH_TODAM)) okay = true;
512 if (enchant(obj, num_ac, ENCH_TOAC)) okay = true;
513
514 /* Failure */
515 if (!okay) {
516 event_signal(EVENT_INPUT_FLUSH);
517
518 /* Message */
519 msg("The enchantment failed.");
520 }
521
522 /* Something happened */
523 return (true);
524 }
525
526 /**
527 * Brand weapons (or ammo)
528 *
529 * Turns the (non-magical) object into an ego-item of 'brand_type'.
530 */
brand_object(struct object * obj,const char * name)531 void brand_object(struct object *obj, const char *name)
532 {
533 int i;
534 struct ego_item *ego;
535 bool ok = false;
536
537 /* You can never modify artifacts, ego items or worthless items */
538 if (obj && obj->kind->cost && !obj->artifact && !obj->ego) {
539 char o_name[80];
540 char brand[20];
541
542 object_desc(o_name, sizeof(o_name), obj, ODESC_BASE);
543 strnfmt(brand, sizeof(brand), "of %s", name);
544
545 /* Describe */
546 msg("The %s %s surrounded with an aura of %s.", o_name,
547 (obj->number > 1) ? "are" : "is", name);
548
549 /* Get the right ego type for the object */
550 for (i = 0; i < z_info->e_max; i++) {
551 ego = &e_info[i];
552
553 /* Match the name */
554 if (!ego->name) continue;
555 if (streq(ego->name, brand)) {
556 struct poss_item *poss;
557 for (poss = ego->poss_items; poss; poss = poss->next)
558 if (poss->kidx == obj->kind->kidx)
559 ok = true;
560 }
561 if (ok) break;
562 }
563
564 assert(ok);
565
566 /* Make it an ego item */
567 obj->ego = &e_info[i];
568 ego_apply_magic(obj, 0);
569 player_know_object(player, obj);
570
571 /* Update the gear */
572 player->upkeep->update |= (PU_INVEN);
573
574 /* Combine the pack (later) */
575 player->upkeep->notice |= (PN_COMBINE);
576
577 /* Window stuff */
578 player->upkeep->redraw |= (PR_INVEN | PR_EQUIP);
579
580 /* Enchant */
581 enchant(obj, randint0(3) + 4, ENCH_TOHIT | ENCH_TODAM);
582 } else {
583 event_signal(EVENT_INPUT_FLUSH);
584 msg("The branding failed.");
585 }
586 }
587
588 /**
589 * Hook for "get_item()". Determine if something is rechargable.
590 */
item_tester_hook_recharge(const struct object * obj)591 static bool item_tester_hook_recharge(const struct object *obj)
592 {
593 /* Recharge staves and wands */
594 if (tval_can_have_charges(obj)) return true;
595
596 return false;
597 }
598
599 /**
600 * Hook to specify a staff
601 */
item_tester_hook_staff(const struct object * obj)602 static bool item_tester_hook_staff(const struct object *obj)
603 {
604 return obj->tval == TV_STAFF;
605 }
606
607 /**
608 * Hook to specify "ammo"
609 */
item_tester_hook_ammo(const struct object * obj)610 static bool item_tester_hook_ammo(const struct object *obj)
611 {
612 return tval_is_ammo(obj);
613 }
614
615 /**
616 * Hook to specify bolts
617 */
item_tester_hook_bolt(const struct object * obj)618 static bool item_tester_hook_bolt(const struct object *obj)
619 {
620 return obj->tval == TV_BOLT;
621 }
622
623 /**
624 * ------------------------------------------------------------------------
625 * Effect handlers
626 * ------------------------------------------------------------------------ */
627 /**
628 * Dummy effect, to tell the effect code to pick one of the next
629 * context->value.base effects at random.
630 */
effect_handler_RANDOM(effect_handler_context_t * context)631 bool effect_handler_RANDOM(effect_handler_context_t *context)
632 {
633 return true;
634 }
635
636 /**
637 * Deal damage from the current monster or trap to the player
638 */
effect_handler_DAMAGE(effect_handler_context_t * context)639 bool effect_handler_DAMAGE(effect_handler_context_t *context)
640 {
641 int dam = effect_calculate_value(context, false);
642 char killer[80];
643
644 /* Always ID */
645 context->ident = true;
646
647 switch (context->origin.what) {
648 case SRC_MONSTER: {
649 struct monster *mon = cave_monster(cave,
650 context->origin.which.monster);
651 struct monster *t_mon = monster_target_monster(context);
652 struct loc decoy = cave_find_decoy(cave);
653
654 /* Damage another monster */
655 if (t_mon) {
656 bool fear = false;
657
658 mon_take_nonplayer_hit(dam, t_mon, MON_MSG_NONE, MON_MSG_DIE);
659 if (fear && monster_is_visible(t_mon)) {
660 add_monster_message(t_mon, MON_MSG_FLEE_IN_TERROR, true);
661 }
662 return true;
663 }
664
665 /* Destroy a decoy */
666 if (decoy.y && decoy.x) {
667 square_destroy_decoy(cave, decoy);
668 return true;
669 }
670
671 monster_desc(killer, sizeof(killer), mon, MDESC_DIED_FROM);
672 break;
673 }
674
675 case SRC_TRAP: {
676 struct trap *trap = context->origin.which.trap;
677 char *article = is_a_vowel(trap->kind->desc[0]) ? "an " : "a ";
678 strnfmt(killer, sizeof(killer), "%s%s", article, trap->kind->desc);
679 break;
680 }
681
682 case SRC_OBJECT: {
683 /* Must be a cursed weapon */
684 struct object *obj = context->origin.which.object;
685 object_desc(killer, sizeof(killer), obj, ODESC_PREFIX | ODESC_BASE);
686 break;
687 }
688
689 case SRC_CHEST_TRAP: {
690 struct chest_trap *trap = context->origin.which.chest_trap;
691 strnfmt(killer, sizeof(killer), "%s", trap->msg_death);
692 break;
693 }
694
695 case SRC_PLAYER: {
696 if (context->msg) {
697 my_strcpy(killer, context->msg, sizeof(killer));
698 } else {
699 my_strcpy(killer, "yourself", sizeof(killer));
700 }
701 break;
702 }
703
704 case SRC_NONE: {
705 my_strcpy(killer, "a bug", sizeof(killer));
706 break;
707 }
708 }
709
710 /* Hit the player */
711 take_hit(player, dam, killer);
712
713 return true;
714 }
715
716
717 /**
718 * Heal the player by a given percentage of their wounds, or a minimum
719 * amount, whichever is larger.
720 *
721 * context->value.base should be the minimum, and
722 * context->value.m_bonus the percentage
723 */
effect_handler_HEAL_HP(effect_handler_context_t * context)724 bool effect_handler_HEAL_HP(effect_handler_context_t *context)
725 {
726 int num;
727
728 /* Paranoia */
729 if ((context->value.m_bonus <= 0) && (context->value.base <= 0))
730 return (true);
731
732 /* Always ID */
733 context->ident = true;
734
735 /* No healing needed */
736 if (player->chp >= player->mhp) return (true);
737
738 /* Figure percentage healing level */
739 num = ((player->mhp - player->chp) * context->value.m_bonus) / 100;
740
741 /* Enforce minimum */
742 if (num < context->value.base) num = context->value.base;
743
744 /* Gain hitpoints */
745 player->chp += num;
746
747 /* Enforce maximum */
748 if (player->chp >= player->mhp) {
749 player->chp = player->mhp;
750 player->chp_frac = 0;
751 }
752
753 /* Redraw */
754 player->upkeep->redraw |= (PR_HP);
755
756 /* Print a nice message */
757 if (num < 5)
758 msg("You feel a little better.");
759 else if (num < 15)
760 msg("You feel better.");
761 else if (num < 35)
762 msg("You feel much better.");
763 else
764 msg("You feel very good.");
765
766 return (true);
767 }
768
769
770 /**
771 * Monster self-healing.
772 */
effect_handler_MON_HEAL_HP(effect_handler_context_t * context)773 bool effect_handler_MON_HEAL_HP(effect_handler_context_t *context)
774 {
775 assert(context->origin.what == SRC_MONSTER);
776
777 int midx = context->origin.which.monster;
778 struct monster *mon = midx > 0 ? cave_monster(cave, midx) : NULL;
779
780 int amount = effect_calculate_value(context, false);
781 char m_name[80], m_poss[80];
782 bool seen;
783
784 if (!mon) return true;
785
786 /* Get the monster name (or "it") */
787 monster_desc(m_name, sizeof(m_name), mon, MDESC_STANDARD);
788
789 /* Get the monster possessive ("his"/"her"/"its") */
790 monster_desc(m_poss, sizeof(m_poss), mon, MDESC_PRO_VIS | MDESC_POSS);
791
792 seen = (!player->timed[TMD_BLIND] && monster_is_visible(mon));
793
794 /* Heal some */
795 mon->hp += amount;
796
797 /* Fully healed */
798 if (mon->hp >= mon->maxhp) {
799 mon->hp = mon->maxhp;
800
801 if (seen)
802 msg("%s looks REALLY healthy!", m_name);
803 else
804 msg("%s sounds REALLY healthy!", m_name);
805 } else if (seen) { /* Partially healed */
806 msg("%s looks healthier.", m_name);
807 } else {
808 msg("%s sounds healthier.", m_name);
809 }
810
811 /* Redraw (later) if needed */
812 if (player->upkeep->health_who == mon)
813 player->upkeep->redraw |= (PR_HEALTH);
814
815 /* Cancel fear */
816 if (mon->m_timed[MON_TMD_FEAR]) {
817 mon_clear_timed(mon, MON_TMD_FEAR, MON_TMD_FLG_NOMESSAGE);
818 msg("%s recovers %s courage.", m_name, m_poss);
819 }
820
821 /* ID */
822 context->ident = true;
823
824 return true;
825 }
826
827 /**
828 * Monster healing of kin.
829 */
effect_handler_MON_HEAL_KIN(effect_handler_context_t * context)830 bool effect_handler_MON_HEAL_KIN(effect_handler_context_t *context)
831 {
832 assert(context->origin.what == SRC_MONSTER);
833
834 int midx = context->origin.which.monster;
835 struct monster *mon = midx > 0 ? cave_monster(cave, midx) : NULL;
836 if (!mon) return true;
837
838 int amount = effect_calculate_value(context, false);
839 char m_name[80], m_poss[80];
840 bool seen;
841
842 /* Find a nearby monster */
843 mon = choose_nearby_injured_kin(cave, mon);
844 if (!mon) return true;
845
846 /* Get the monster name (or "it") */
847 monster_desc(m_name, sizeof(m_name), mon, MDESC_STANDARD);
848
849 /* Get the monster possessive ("his"/"her"/"its") */
850 monster_desc(m_poss, sizeof(m_poss), mon, MDESC_PRO_VIS | MDESC_POSS);
851
852 seen = (!player->timed[TMD_BLIND] && monster_is_visible(mon));
853
854 /* Heal some */
855 mon->hp = MIN(mon->hp + amount, mon->maxhp);
856
857 if (seen) {
858 if (mon->hp == mon->maxhp) {
859 msg("%s looks REALLY healthy!", m_name);
860 } else if (seen) { /* Partially healed */
861 msg("%s looks healthier.", m_name);
862 }
863 }
864
865 /* Redraw (later) if needed */
866 if (player->upkeep->health_who == mon)
867 player->upkeep->redraw |= (PR_HEALTH);
868
869 /* Cancel fear */
870 if (mon->m_timed[MON_TMD_FEAR]) {
871 mon_clear_timed(mon, MON_TMD_FEAR, MON_TMD_FLG_NOMESSAGE);
872 msg("%s recovers %s courage.", m_name, m_poss);
873 }
874
875 /* ID */
876 context->ident = true;
877
878 return true;
879 }
880
881 /**
882 * Feed the player, or set their satiety level.
883 */
effect_handler_NOURISH(effect_handler_context_t * context)884 bool effect_handler_NOURISH(effect_handler_context_t *context)
885 {
886 int amount = effect_calculate_value(context, false);
887 amount *= z_info->food_value;
888 if (context->subtype == 0) {
889 /* Increase food level by amount */
890 player_inc_timed(player, TMD_FOOD, MAX(amount, 0), false, false);
891 } else if (context->subtype == 1) {
892 /* Decrease food level by amount */
893 player_dec_timed(player, TMD_FOOD, MAX(amount, 0), false);
894 } else if (context->subtype == 2) {
895 /* Set food level to amount, vomiting if necessary */
896 bool message = player->timed[TMD_FOOD] > amount;
897 if (message) {
898 msg("You vomit!");
899 }
900 player_set_timed(player, TMD_FOOD, MAX(amount, 0), false);
901 } else if (context->subtype == 3) {
902 /* Increase food level to amount if needed */
903 if (player->timed[TMD_FOOD] < amount) {
904 player_set_timed(player, TMD_FOOD, MAX(amount + 1, 0), false);
905 }
906 } else {
907 return false;
908 }
909 context->ident = true;
910 return true;
911 }
912
effect_handler_CRUNCH(effect_handler_context_t * context)913 bool effect_handler_CRUNCH(effect_handler_context_t *context)
914 {
915 if (one_in_(2))
916 msg("It's crunchy.");
917 else
918 msg("It nearly breaks your tooth!");
919 context->ident = true;
920 return true;
921 }
922
923 /**
924 * Cure a player status condition.
925 */
effect_handler_CURE(effect_handler_context_t * context)926 bool effect_handler_CURE(effect_handler_context_t *context)
927 {
928 int type = context->subtype;
929 (void) player_clear_timed(player, type, true);
930 context->ident = true;
931 return true;
932 }
933
934 /**
935 * Set a (positive or negative) player status condition.
936 */
effect_handler_TIMED_SET(effect_handler_context_t * context)937 bool effect_handler_TIMED_SET(effect_handler_context_t *context)
938 {
939 int amount = effect_calculate_value(context, false);
940 player_set_timed(player, context->subtype, MAX(amount, 0), true);
941 context->ident = true;
942 return true;
943
944 }
945
946 /**
947 * Extend a (positive or negative) player status condition.
948 * If context->other is set, increase by that amount if the player already
949 * has the status
950 */
effect_handler_TIMED_INC(effect_handler_context_t * context)951 bool effect_handler_TIMED_INC(effect_handler_context_t *context)
952 {
953 int amount = effect_calculate_value(context, false);
954 struct monster *t_mon = monster_target_monster(context);
955 struct loc decoy = cave_find_decoy(cave);
956
957 context->ident = true;
958
959 /* Destroy decoy if it's a monster attack */
960 if (cave->mon_current > 0 && decoy.y && decoy.x) {
961 square_destroy_decoy(cave, decoy);
962 return true;
963 }
964
965 /* Check for monster targeting another monster */
966 if (t_mon) {
967 int mon_tmd_effect = -1;
968
969 /* Will do until monster and player timed effects are fused */
970 switch (context->subtype) {
971 case TMD_CONFUSED: {
972 mon_tmd_effect = MON_TMD_CONF;
973 break;
974 }
975 case TMD_SLOW: {
976 mon_tmd_effect = MON_TMD_SLOW;
977 break;
978 }
979 case TMD_PARALYZED: {
980 mon_tmd_effect = MON_TMD_HOLD;
981 break;
982 }
983 case TMD_BLIND: {
984 mon_tmd_effect = MON_TMD_STUN;
985 break;
986 }
987 case TMD_AFRAID: {
988 mon_tmd_effect = MON_TMD_FEAR;
989 break;
990 }
991 case TMD_AMNESIA: {
992 mon_tmd_effect = MON_TMD_SLEEP;
993 break;
994 }
995 default: {
996 break;
997 }
998 }
999 if (mon_tmd_effect >= 0) {
1000 mon_inc_timed(t_mon, mon_tmd_effect, MAX(amount, 0), 0);
1001 }
1002 return true;
1003 }
1004
1005 if (!player->timed[context->subtype] || !context->other) {
1006 player_inc_timed(player, context->subtype, MAX(amount, 0), true, true);
1007 } else {
1008 player_inc_timed(player, context->subtype, context->other, true, true);
1009 }
1010 return true;
1011 }
1012
1013 /**
1014 * Extend a (positive or negative) player status condition unresistably.
1015 * If context->other is set, increase by that amount if the player already
1016 * has the status
1017 */
effect_handler_TIMED_INC_NO_RES(effect_handler_context_t * context)1018 bool effect_handler_TIMED_INC_NO_RES(effect_handler_context_t *context)
1019 {
1020 int amount = effect_calculate_value(context, false);
1021
1022 if (!player->timed[context->subtype] || !context->other)
1023 player_inc_timed(player, context->subtype, MAX(amount, 0), true, false);
1024 else
1025 player_inc_timed(player, context->subtype, context->other, true, false);
1026 context->ident = true;
1027 return true;
1028 }
1029
1030 /**
1031 * Extend a (positive or negative) monster status condition.
1032 */
effect_handler_MON_TIMED_INC(effect_handler_context_t * context)1033 bool effect_handler_MON_TIMED_INC(effect_handler_context_t *context)
1034 {
1035 assert(context->origin.what == SRC_MONSTER);
1036
1037 int amount = effect_calculate_value(context, false);
1038 struct monster *mon = cave_monster(cave, context->origin.which.monster);
1039
1040 if (mon) {
1041 mon_inc_timed(mon, context->subtype, MAX(amount, 0), 0);
1042 context->ident = true;
1043 }
1044
1045 return true;
1046 }
1047
1048 /**
1049 * Reduce a (positive or negative) player status condition.
1050 * If context->other is set, decrease by the current value / context->other
1051 */
effect_handler_TIMED_DEC(effect_handler_context_t * context)1052 bool effect_handler_TIMED_DEC(effect_handler_context_t *context)
1053 {
1054 int amount = effect_calculate_value(context, false);
1055 if (context->other)
1056 amount = player->timed[context->subtype] / context->other;
1057 (void) player_dec_timed(player, context->subtype, MAX(amount, 0), true);
1058 context->ident = true;
1059 return true;
1060 }
1061
1062 /**
1063 * Create a glyph.
1064 */
effect_handler_GLYPH(effect_handler_context_t * context)1065 bool effect_handler_GLYPH(effect_handler_context_t *context)
1066 {
1067 struct loc decoy = cave_find_decoy(cave);
1068
1069 /* Always notice */
1070 context->ident = true;
1071
1072 /* Only one decoy at a time */
1073 if (!loc_is_zero(decoy) && (context->subtype == GLYPH_DECOY)) {
1074 msg("You can only deploy one decoy at a time.");
1075 return false;
1076 }
1077
1078 /* See if the effect works */
1079 if (!square_istrappable(cave, player->grid)) {
1080 msg("There is no clear floor on which to cast the spell.");
1081 return false;
1082 }
1083
1084 /* Push objects off the grid */
1085 if (square_object(cave, player->grid))
1086 push_object(player->grid);
1087
1088 /* Create a glyph */
1089 square_add_glyph(cave, player->grid, context->subtype);
1090
1091 return true;
1092 }
1093
1094 /**
1095 * Create a web.
1096 */
effect_handler_WEB(effect_handler_context_t * context)1097 bool effect_handler_WEB(effect_handler_context_t *context)
1098 {
1099 int rad = 1;
1100 struct monster *mon = NULL;
1101 struct loc grid;
1102
1103 /* Get the monster creating */
1104 if (cave->mon_current > 0) {
1105 mon = cave_monster(cave, cave->mon_current);
1106 } else {
1107 /* Player can't currently create webs */
1108 return false;
1109 }
1110
1111 /* Always notice */
1112 context->ident = true;
1113
1114 /* Increase the radius for higher spell power */
1115 if (mon->race->spell_power > 40) rad++;
1116 if (mon->race->spell_power > 80) rad++;
1117
1118 /* Check within the radius for clear floor */
1119 for (grid.y = mon->grid.y - rad; grid.y <= mon->grid.y + rad; grid.y++) {
1120 for (grid.x = mon->grid.x - rad; grid.x <= mon->grid.x + rad; grid.x++){
1121 if (distance(grid, mon->grid) > rad) continue;
1122
1123 /* Require a floor grid with no existing traps or glyphs */
1124 if (!square_iswebbable(cave, grid)) continue;
1125
1126 /* Create a web */
1127 square_add_web(cave, grid);
1128 }
1129 }
1130
1131 return true;
1132 }
1133
1134 /**
1135 * Restore a stat; the stat index is context->subtype
1136 */
effect_handler_RESTORE_STAT(effect_handler_context_t * context)1137 bool effect_handler_RESTORE_STAT(effect_handler_context_t *context)
1138 {
1139 int stat = context->subtype;
1140
1141 /* ID */
1142 context->ident = true;
1143
1144 /* Check bounds */
1145 if (stat < 0 || stat >= STAT_MAX) return false;
1146
1147 /* Not needed */
1148 if (player->stat_cur[stat] == player->stat_max[stat])
1149 return true;
1150
1151 /* Restore */
1152 player->stat_cur[stat] = player->stat_max[stat];
1153
1154 /* Recalculate bonuses */
1155 player->upkeep->update |= (PU_BONUS);
1156 update_stuff(player);
1157
1158 /* Message */
1159 msg("You feel less %s.", desc_stat(stat, false));
1160
1161 return (true);
1162 }
1163
1164 /**
1165 * Drain a stat temporarily. The stat index is context->subtype.
1166 */
effect_handler_DRAIN_STAT(effect_handler_context_t * context)1167 bool effect_handler_DRAIN_STAT(effect_handler_context_t *context)
1168 {
1169 int stat = context->subtype;
1170 int flag = sustain_flag(stat);
1171
1172 /* Bounds check */
1173 if (flag < 0) return false;
1174
1175 /* ID */
1176 context->ident = true;
1177
1178 /* Sustain */
1179 if (player_of_has(player, flag)) {
1180 /* Notice effect */
1181 equip_learn_flag(player, flag);
1182
1183 /* Message */
1184 msg("You feel very %s for a moment, but the feeling passes.",
1185 desc_stat(stat, false));
1186
1187 return (true);
1188 }
1189
1190 /* Attempt to reduce the stat */
1191 if (player_stat_dec(player, stat, false)){
1192 int dam = effect_calculate_value(context, false);
1193
1194 /* Notice effect */
1195 equip_learn_flag(player, flag);
1196
1197 /* Message */
1198 msgt(MSG_DRAIN_STAT, "You feel very %s.", desc_stat(stat, false));
1199 if (dam)
1200 take_hit(player, dam, "stat drain");
1201 }
1202
1203 return (true);
1204 }
1205
1206 /**
1207 * Lose a stat point permanently, in a stat other than the one specified
1208 * in context->subtype.
1209 */
effect_handler_LOSE_RANDOM_STAT(effect_handler_context_t * context)1210 bool effect_handler_LOSE_RANDOM_STAT(effect_handler_context_t *context)
1211 {
1212 int safe_stat = context->subtype;
1213 int loss_stat = randint1(STAT_MAX - 1);
1214
1215 /* Avoid the safe stat */
1216 loss_stat = (loss_stat + safe_stat) % STAT_MAX;
1217
1218 /* Attempt to reduce the stat */
1219 if (player_stat_dec(player, loss_stat, true)) {
1220 msgt(MSG_DRAIN_STAT, "You feel very %s.", desc_stat(loss_stat, false));
1221 }
1222
1223 /* ID */
1224 context->ident = true;
1225
1226 return (true);
1227 }
1228
1229
1230 /**
1231 * Gain a stat point. The stat index is context->subtype.
1232 */
effect_handler_GAIN_STAT(effect_handler_context_t * context)1233 bool effect_handler_GAIN_STAT(effect_handler_context_t *context)
1234 {
1235 int stat = context->subtype;
1236
1237 /* Attempt to increase */
1238 if (player_stat_inc(player, stat)) {
1239 msg("You feel very %s!", desc_stat(stat, true));
1240 }
1241
1242 /* Notice */
1243 context->ident = true;
1244
1245 return (true);
1246 }
1247
1248 /**
1249 * Restores any drained experience
1250 */
effect_handler_RESTORE_EXP(effect_handler_context_t * context)1251 bool effect_handler_RESTORE_EXP(effect_handler_context_t *context)
1252 {
1253 /* Restore experience */
1254 if (player->exp < player->max_exp) {
1255 /* Message */
1256 if (context->origin.what != SRC_NONE)
1257 msg("You feel your life energies returning.");
1258 player_exp_gain(player, player->max_exp - player->exp);
1259
1260 /* Recalculate max. hitpoints */
1261 update_stuff(player);
1262 }
1263
1264 /* Did something */
1265 context->ident = true;
1266
1267 return (true);
1268 }
1269
1270 /* Note the divisor of 2, a slight hack to simplify food description */
effect_handler_GAIN_EXP(effect_handler_context_t * context)1271 bool effect_handler_GAIN_EXP(effect_handler_context_t *context)
1272 {
1273 int amount = effect_calculate_value(context, false);
1274 if (player->exp < PY_MAX_EXP) {
1275 msg("You feel more experienced.");
1276 player_exp_gain(player, amount / 2);
1277 }
1278 context->ident = true;
1279
1280 return true;
1281 }
1282
1283 /**
1284 * Drain some light from the player's light source, if possible
1285 */
effect_handler_DRAIN_LIGHT(effect_handler_context_t * context)1286 bool effect_handler_DRAIN_LIGHT(effect_handler_context_t *context)
1287 {
1288 int drain = effect_calculate_value(context, false);
1289
1290 int light_slot = slot_by_name(player, "light");
1291 struct object *obj = slot_object(player, light_slot);
1292
1293 if (obj && !of_has(obj->flags, OF_NO_FUEL) && (obj->timeout > 0)) {
1294 /* Reduce fuel */
1295 obj->timeout -= drain;
1296 if (obj->timeout < 1) obj->timeout = 1;
1297
1298 /* Notice */
1299 if (!player->timed[TMD_BLIND]) {
1300 msg("Your light dims.");
1301 context->ident = true;
1302 }
1303
1304 /* Redraw stuff */
1305 player->upkeep->redraw |= (PR_EQUIP);
1306 }
1307
1308 return true;
1309 }
1310
1311 /**
1312 * Drain mana from the player, healing the caster.
1313 */
effect_handler_DRAIN_MANA(effect_handler_context_t * context)1314 bool effect_handler_DRAIN_MANA(effect_handler_context_t *context)
1315 {
1316 int drain = effect_calculate_value(context, false);
1317 bool monster = context->origin.what != SRC_TRAP;
1318 char m_name[80];
1319 struct monster *mon = NULL;
1320 struct monster *t_mon = monster_target_monster(context);
1321 struct loc decoy = cave_find_decoy(cave);
1322
1323 context->ident = true;
1324
1325 if (monster) {
1326 assert(context->origin.what == SRC_MONSTER);
1327
1328 mon = cave_monster(cave, context->origin.which.monster);
1329
1330 /* Get the monster name (or "it") */
1331 monster_desc(m_name, sizeof(m_name), mon, MDESC_STANDARD);
1332 }
1333
1334 /* Target is another monster - disenchant it */
1335 if (t_mon) {
1336 mon_inc_timed(t_mon, MON_TMD_DISEN, MAX(drain, 0), 0);
1337 return true;
1338 }
1339
1340 /* Target was a decoy - destroy it */
1341 if (decoy.y && decoy.x) {
1342 square_destroy_decoy(cave, decoy);
1343 return true;
1344 }
1345
1346 /* The player has no mana */
1347 if (!player->csp) {
1348 msg("The draining fails.");
1349 if (monster) {
1350 update_smart_learn(mon, player, 0, PF_NO_MANA, -1);
1351 }
1352 return true;
1353 }
1354
1355 /* Drain the given amount if the player has that much, or all of it */
1356 if (drain >= player->csp) {
1357 drain = player->csp;
1358 player->csp = 0;
1359 player->csp_frac = 0;
1360 } else {
1361 player->csp -= drain;
1362 }
1363
1364 /* Heal the monster */
1365 if (monster) {
1366 if (mon->hp < mon->maxhp) {
1367 mon->hp += (6 * drain);
1368 if (mon->hp > mon->maxhp)
1369 mon->hp = mon->maxhp;
1370
1371 /* Redraw (later) if needed */
1372 if (player->upkeep->health_who == mon)
1373 player->upkeep->redraw |= (PR_HEALTH);
1374
1375 /* Special message */
1376 if (monster_is_visible(mon))
1377 msg("%s appears healthier.", m_name);
1378 }
1379 }
1380
1381 /* Redraw mana */
1382 player->upkeep->redraw |= PR_MANA;
1383
1384 return true;
1385 }
1386
effect_handler_RESTORE_MANA(effect_handler_context_t * context)1387 bool effect_handler_RESTORE_MANA(effect_handler_context_t *context)
1388 {
1389 int amount = effect_calculate_value(context, false);
1390 if (!amount) amount = player->msp;
1391 if (player->csp < player->msp) {
1392 player->csp += amount;
1393 if (player->csp > player->msp) {
1394 player->csp = player->msp;
1395 player->csp_frac = 0;
1396 msg("You feel your head clear.");
1397 } else
1398 msg("You feel your head clear somewhat.");
1399 player->upkeep->redraw |= (PR_MANA);
1400 }
1401 context->ident = true;
1402
1403 return true;
1404 }
1405
1406 /**
1407 * Attempt to uncurse an object
1408 */
effect_handler_REMOVE_CURSE(effect_handler_context_t * context)1409 bool effect_handler_REMOVE_CURSE(effect_handler_context_t *context)
1410 {
1411 const char *prompt = "Uncurse which item? ";
1412 const char *rejmsg = "You have no curses to remove.";
1413 int itemmode = (USE_EQUIP | USE_INVEN | USE_QUIVER | USE_FLOOR);
1414 int strength = effect_calculate_value(context, false);
1415 struct object *obj = NULL;
1416 char dice_string[20];
1417
1418 context->ident = true;
1419
1420 if (context->cmd) {
1421 if (cmd_get_item(context->cmd, "tgtitem", &obj, prompt,
1422 rejmsg, item_tester_uncursable, itemmode)) {
1423 return false;
1424 }
1425 } else if (!get_item(&obj, prompt, rejmsg, 0, item_tester_uncursable,
1426 itemmode))
1427 return false;
1428
1429 /* Get the possible dice strings */
1430 if ((context->value.dice == 1) && context->value.base) {
1431 strnfmt(dice_string, sizeof(dice_string), "%d+d%d",
1432 context->value.base, context->value.sides);
1433 } else if (context->value.dice && context->value.base) {
1434 strnfmt(dice_string, sizeof(dice_string), "%d+%dd%d",
1435 context->value.base, context->value.dice, context->value.sides);
1436 } else if (context->value.dice == 1) {
1437 strnfmt(dice_string, sizeof(dice_string), "d%d", context->value.sides);
1438 } else if (context->value.dice) {
1439 strnfmt(dice_string, sizeof(dice_string), "%dd%d",
1440 context->value.dice, context->value.sides);
1441 } else {
1442 strnfmt(dice_string, sizeof(dice_string), "%d", context->value.base);
1443 }
1444
1445 return uncurse_object(obj, strength, dice_string);
1446 }
1447
1448 /**
1449 * Set word of recall as appropriate
1450 */
effect_handler_RECALL(effect_handler_context_t * context)1451 bool effect_handler_RECALL(effect_handler_context_t *context)
1452 {
1453 int target_depth;
1454 context->ident = true;
1455
1456 /* No recall */
1457 if (OPT(player, birth_no_recall) && !player->total_winner) {
1458 msg("Nothing happens.");
1459 return true;
1460 }
1461
1462 /* No recall from quest levels with force_descend */
1463 if (OPT(player, birth_force_descend) && (is_quest(player->depth))) {
1464 msg("Nothing happens.");
1465 return true;
1466 }
1467
1468 /* No recall from single combat */
1469 if (player->upkeep->arena_level) {
1470 msg("Nothing happens.");
1471 return true;
1472 }
1473
1474 /* Warn the player if they're descending to an unrecallable level */
1475 target_depth = dungeon_get_next_level(player->max_depth, 1);
1476 if (OPT(player, birth_force_descend) && !(player->depth) &&
1477 (is_quest(target_depth))) {
1478 if (!get_check("Are you sure you want to descend? ")) {
1479 return false;
1480 }
1481 }
1482
1483 /* Activate recall */
1484 if (!player->word_recall) {
1485 /* Reset recall depth */
1486 if (player->depth > 0) {
1487 if (player->depth != player->max_depth) {
1488 if (get_check("Set recall depth to current depth? ")) {
1489 player->recall_depth = player->max_depth = player->depth;
1490 }
1491 } else {
1492 player->recall_depth = player->max_depth;
1493 }
1494 } else {
1495 if (OPT(player, birth_levels_persist)) {
1496 /* Persistent levels players get to choose */
1497 if (!player_get_recall_depth(player)) return false;
1498 }
1499 }
1500
1501 player->word_recall = randint0(20) + 15;
1502 msg("The air about you becomes charged...");
1503 } else {
1504 /* Deactivate recall */
1505 if (!get_check("Word of Recall is already active. Do you want to cancel it? "))
1506 return false;
1507
1508 player->word_recall = 0;
1509 msg("A tension leaves the air around you...");
1510 }
1511
1512 /* Redraw status line */
1513 player->upkeep->redraw |= PR_STATUS;
1514 handle_stuff(player);
1515
1516 return true;
1517 }
1518
effect_handler_DEEP_DESCENT(effect_handler_context_t * context)1519 bool effect_handler_DEEP_DESCENT(effect_handler_context_t *context)
1520 {
1521 int i;
1522
1523 /* Calculate target depth */
1524 int target_increment = (4 / z_info->stair_skip) + 1;
1525 int target_depth = dungeon_get_next_level(player->max_depth,
1526 target_increment);
1527 for (i = 5; i > 0; i--) {
1528 if (is_quest(target_depth)) break;
1529 if (target_depth >= z_info->max_depth - 1) break;
1530
1531 target_depth++;
1532 }
1533
1534 if (target_depth > player->depth) {
1535 msgt(MSG_TPLEVEL, "The air around you starts to swirl...");
1536 player->deep_descent = 3 + randint1(4);
1537
1538 /* Redraw status line */
1539 player->upkeep->redraw |= PR_STATUS;
1540 handle_stuff(player);
1541 } else {
1542 msgt(MSG_TPLEVEL, "You sense a malevolent presence blocking passage to the levels below.");
1543 }
1544 context->ident = true;
1545 return true;
1546 }
1547
effect_handler_ALTER_REALITY(effect_handler_context_t * context)1548 bool effect_handler_ALTER_REALITY(effect_handler_context_t *context)
1549 {
1550 msg("The world changes!");
1551 dungeon_change_level(player, player->depth);
1552 context->ident = true;
1553 return true;
1554 }
1555
1556 /**
1557 * Map an area around a point, usually the player.
1558 * The height to map above and below the player is context->y,
1559 * the width either side of the player context->x.
1560 * For player level dependent areas, we use the hack of applying value dice
1561 * and sides as the height and width.
1562 */
effect_handler_MAP_AREA(effect_handler_context_t * context)1563 bool effect_handler_MAP_AREA(effect_handler_context_t *context)
1564 {
1565 int i, x, y;
1566 int x1, x2, y1, y2;
1567 int dist_y = context->y ? context->y : context->value.dice;
1568 int dist_x = context->x ? context->x : context->value.sides;
1569 struct loc centre = origin_get_loc(context->origin);
1570
1571 /* Pick an area to map */
1572 y1 = centre.y - dist_y;
1573 y2 = centre.y + dist_y;
1574 x1 = centre.x - dist_x;
1575 x2 = centre.x + dist_x;
1576
1577 /* Drag the co-ordinates into the dungeon */
1578 if (y1 < 0) y1 = 0;
1579 if (x1 < 0) x1 = 0;
1580 if (y2 > cave->height - 1) y2 = cave->height - 1;
1581 if (x2 > cave->width - 1) x2 = cave->width - 1;
1582
1583 /* Scan the dungeon */
1584 for (y = y1; y < y2; y++) {
1585 for (x = x1; x < x2; x++) {
1586 struct loc grid = loc(x, y);
1587
1588 /* Some squares can't be mapped */
1589 if (square_isno_map(cave, grid)) continue;
1590
1591 /* All non-walls are "checked" */
1592 if (!square_seemslikewall(cave, grid)) {
1593 if (!square_in_bounds_fully(cave, grid)) continue;
1594
1595 /* Memorize normal features */
1596 if (!square_isfloor(cave, grid))
1597 square_memorize(cave, grid);
1598
1599 /* Memorize known walls */
1600 for (i = 0; i < 8; i++) {
1601 int yy = y + ddy_ddd[i];
1602 int xx = x + ddx_ddd[i];
1603
1604 /* Memorize walls (etc) */
1605 if (square_seemslikewall(cave, loc(xx, yy)))
1606 square_memorize(cave, loc(xx, yy));
1607 }
1608 }
1609
1610 /* Forget unprocessed, unknown grids in the mapping area */
1611 if (square_isnotknown(cave, grid))
1612 square_forget(cave, grid);
1613 }
1614 }
1615
1616 /* Unmark grids */
1617 for (y = y1 - 1; y < y2 + 1; y++) {
1618 for (x = x1 - 1; x < x2 + 1; x++) {
1619 struct loc grid = loc(x, y);
1620 if (!square_in_bounds(cave, grid)) continue;
1621 square_unmark(cave, grid);
1622 }
1623 }
1624
1625 /* Fully update the visuals */
1626 player->upkeep->update |= (PU_UPDATE_VIEW | PU_MONSTERS);
1627
1628 /* Redraw whole map, monster list */
1629 player->upkeep->redraw |= (PR_MAP | PR_MONLIST | PR_ITEMLIST);
1630
1631 /* Notice */
1632 context->ident = true;
1633
1634 return true;
1635 }
1636
1637 /**
1638 * Map an area around the recently detected monsters.
1639 * The height to map above and below each monster is context->y,
1640 * the width either side of each monster context->x.
1641 * For player level dependent areas, we use the hack of applying value dice
1642 * and sides as the height and width.
1643 */
effect_handler_READ_MINDS(effect_handler_context_t * context)1644 bool effect_handler_READ_MINDS(effect_handler_context_t *context)
1645 {
1646 int i;
1647 int dist_y = context->y ? context->y : context->value.dice;
1648 int dist_x = context->x ? context->x : context->value.sides;
1649 bool found = false;
1650
1651 /* Scan monsters */
1652 for (i = 1; i < cave_monster_max(cave); i++) {
1653 struct monster *mon = cave_monster(cave, i);
1654
1655 /* Skip dead monsters */
1656 if (!mon->race) continue;
1657
1658 /* Detect all appropriate monsters */
1659 if (mflag_has(mon->mflag, MFLAG_MARK)) {
1660 /* Map around it */
1661 effect_simple(EF_MAP_AREA, source_monster(i), "0", 0, 0, 0,
1662 dist_y, dist_x, NULL);
1663 found = true;
1664 }
1665 }
1666
1667 if (found) {
1668 msg("Images form in your mind!");
1669 context->ident = true;
1670 return true;
1671 }
1672
1673 return false;
1674 }
1675
1676 /**
1677 * Detect traps around the player. The height to detect above and below the
1678 * player is context->y, the width either side of the player context->x.
1679 */
effect_handler_DETECT_TRAPS(effect_handler_context_t * context)1680 bool effect_handler_DETECT_TRAPS(effect_handler_context_t *context)
1681 {
1682 int x, y;
1683 int x1, x2, y1, y2;
1684
1685 bool detect = false;
1686
1687 struct object *obj;
1688
1689 /* Pick an area to detect */
1690 y1 = player->grid.y - context->y;
1691 y2 = player->grid.y + context->y;
1692 x1 = player->grid.x - context->x;
1693 x2 = player->grid.x + context->x;
1694
1695 if (y1 < 0) y1 = 0;
1696 if (x1 < 0) x1 = 0;
1697 if (y2 > cave->height - 1) y2 = cave->height - 1;
1698 if (x2 > cave->width - 1) x2 = cave->width - 1;
1699
1700
1701 /* Scan the dungeon */
1702 for (y = y1; y < y2; y++) {
1703 for (x = x1; x < x2; x++) {
1704 struct loc grid = loc(x, y);
1705
1706 if (!square_in_bounds_fully(cave, grid)) continue;
1707
1708 /* Detect traps */
1709 if (square_isplayertrap(cave, grid))
1710 /* Reveal trap */
1711 if (square_reveal_trap(cave, grid, true, false))
1712 detect = true;
1713
1714 /* Scan all objects in the grid to look for traps on chests */
1715 for (obj = square_object(cave, grid); obj; obj = obj->next) {
1716 /* Skip anything not a trapped chest */
1717 if (!is_trapped_chest(obj)) continue;
1718
1719 /* Identify once */
1720 if (!obj->known || obj->known->pval != obj->pval) {
1721 /* Hack - know the pile */
1722 square_know_pile(cave, grid);
1723
1724 /* Know the trap */
1725 obj->known->pval = obj->pval;
1726
1727 /* Notice it */
1728 disturb(player);
1729
1730 /* We found something to detect */
1731 detect = true;
1732 }
1733 }
1734 /* Mark as trap-detected */
1735 sqinfo_on(square(cave, loc(x, y))->info, SQUARE_DTRAP);
1736 }
1737 }
1738
1739 /* Describe */
1740 if (detect)
1741 msg("You sense the presence of traps!");
1742
1743 /* Trap detection always makes you aware, even if no traps are present */
1744 else
1745 msg("You sense no traps.");
1746
1747 /* Notice */
1748 context->ident = true;
1749
1750 return true;
1751 }
1752
1753 /**
1754 * Detect doors around the player. The height to detect above and below the
1755 * player is context->y, the width either side of the player context->x.
1756 */
effect_handler_DETECT_DOORS(effect_handler_context_t * context)1757 bool effect_handler_DETECT_DOORS(effect_handler_context_t *context)
1758 {
1759 int x, y;
1760 int x1, x2, y1, y2;
1761
1762 bool doors = false;
1763
1764 /* Pick an area to detect */
1765 y1 = player->grid.y - context->y;
1766 y2 = player->grid.y + context->y;
1767 x1 = player->grid.x - context->x;
1768 x2 = player->grid.x + context->x;
1769
1770 if (y1 < 0) y1 = 0;
1771 if (x1 < 0) x1 = 0;
1772 if (y2 > cave->height - 1) y2 = cave->height - 1;
1773 if (x2 > cave->width - 1) x2 = cave->width - 1;
1774
1775 /* Scan the dungeon */
1776 for (y = y1; y < y2; y++) {
1777 for (x = x1; x < x2; x++) {
1778 struct loc grid = loc(x, y);
1779
1780 if (!square_in_bounds_fully(cave, grid)) continue;
1781
1782 /* Detect secret doors */
1783 if (square_issecretdoor(cave, grid)) {
1784 /* Put an actual door */
1785 place_closed_door(cave, grid);
1786
1787 /* Memorize */
1788 square_memorize(cave, grid);
1789 square_light_spot(cave, grid);
1790
1791 /* Obvious */
1792 doors = true;
1793 }
1794
1795 /* Forget unknown doors in the mapping area */
1796 if (square_isdoor(player->cave, grid) &&
1797 square_isnotknown(cave, grid)) {
1798 square_forget(cave, grid);
1799 }
1800 }
1801 }
1802
1803 /* Describe */
1804 if (doors)
1805 msg("You sense the presence of doors!");
1806 else if (context->aware)
1807 msg("You sense no doors.");
1808
1809 context->ident = true;
1810
1811 return true;
1812 }
1813
1814 /**
1815 * Detect stairs around the player. The height to detect above and below the
1816 * player is context->y, the width either side of the player context->x.
1817 */
effect_handler_DETECT_STAIRS(effect_handler_context_t * context)1818 bool effect_handler_DETECT_STAIRS(effect_handler_context_t *context)
1819 {
1820 int x, y;
1821 int x1, x2, y1, y2;
1822
1823 bool stairs = false;
1824
1825 /* Pick an area to detect */
1826 y1 = player->grid.y - context->y;
1827 y2 = player->grid.y + context->y;
1828 x1 = player->grid.x - context->x;
1829 x2 = player->grid.x + context->x;
1830
1831 if (y1 < 0) y1 = 0;
1832 if (x1 < 0) x1 = 0;
1833 if (y2 > cave->height - 1) y2 = cave->height - 1;
1834 if (x2 > cave->width - 1) x2 = cave->width - 1;
1835
1836 /* Scan the dungeon */
1837 for (y = y1; y < y2; y++) {
1838 for (x = x1; x < x2; x++) {
1839 struct loc grid = loc(x, y);
1840
1841 if (!square_in_bounds_fully(cave, grid)) continue;
1842
1843 /* Detect stairs */
1844 if (square_isstairs(cave, grid)) {
1845 /* Memorize */
1846 square_memorize(cave, grid);
1847 square_light_spot(cave, grid);
1848
1849 /* Obvious */
1850 stairs = true;
1851 }
1852 }
1853 }
1854
1855 /* Describe */
1856 if (stairs)
1857 msg("You sense the presence of stairs!");
1858 else if (context->aware)
1859 msg("You sense no stairs.");
1860
1861 context->ident = true;
1862 return true;
1863 }
1864
1865
1866 /**
1867 * Detect buried gold around the player. The height to detect above and below
1868 * the player is context->y, the width either side of the player context->x.
1869 */
effect_handler_DETECT_GOLD(effect_handler_context_t * context)1870 bool effect_handler_DETECT_GOLD(effect_handler_context_t *context)
1871 {
1872 int x, y;
1873 int x1, x2, y1, y2;
1874
1875 bool gold_buried = false;
1876
1877 /* Pick an area to detect */
1878 y1 = player->grid.y - context->y;
1879 y2 = player->grid.y + context->y;
1880 x1 = player->grid.x - context->x;
1881 x2 = player->grid.x + context->x;
1882
1883 if (y1 < 0) y1 = 0;
1884 if (x1 < 0) x1 = 0;
1885 if (y2 > cave->height - 1) y2 = cave->height - 1;
1886 if (x2 > cave->width - 1) x2 = cave->width - 1;
1887
1888 /* Scan the dungeon */
1889 for (y = y1; y < y2; y++) {
1890 for (x = x1; x < x2; x++) {
1891 struct loc grid = loc(x, y);
1892
1893 if (!square_in_bounds_fully(cave, grid)) continue;
1894
1895 /* Magma/Quartz + Known Gold */
1896 if (square_hasgoldvein(cave, grid)) {
1897 /* Memorize */
1898 square_memorize(cave, grid);
1899 square_light_spot(cave, grid);
1900
1901 /* Detect */
1902 gold_buried = true;
1903 } else if (square_hasgoldvein(player->cave, grid)) {
1904 /* Something removed previously seen or
1905 * detected buried gold. Notice the change. */
1906 square_forget(cave, grid);
1907 }
1908 }
1909 }
1910
1911 /* Message unless we're silently detecting */
1912 if (context->origin.what != SRC_NONE) {
1913 if (gold_buried) {
1914 msg("You sense the presence of buried treasure!");
1915 } else if (context->aware) {
1916 msg("You sense no buried treasure.");
1917 }
1918 }
1919
1920 context->ident = true;
1921 return true;
1922 }
1923
1924 /**
1925 * This is a helper for effect_handler_SENSE_OBJECTS and
1926 * effect_handler_DETECT_OBJECTS to remove remembered objects at locations
1927 * sensed or detected as empty.
1928 */
forget_remembered_objects(struct chunk * c,struct chunk * knownc,struct loc grid)1929 static void forget_remembered_objects(struct chunk *c, struct chunk *knownc, struct loc grid)
1930 {
1931 struct object *obj = square_object(knownc, grid);
1932
1933 while (obj) {
1934 struct object *next = obj->next;
1935 struct object *original = c->objects[obj->oidx];
1936
1937 assert(original);
1938 square_excise_object(knownc, grid, obj);
1939 obj->grid = loc(0, 0);
1940
1941 /* Delete objects which no longer exist anywhere */
1942 if (obj->notice & OBJ_NOTICE_IMAGINED) {
1943 delist_object(knownc, obj);
1944 object_delete(&obj);
1945 original->known = NULL;
1946 delist_object(c, original);
1947 object_delete(&original);
1948 }
1949 obj = next;
1950 }
1951 }
1952
1953 /**
1954 * Sense objects around the player. The height to sense above and below the
1955 * player is context->y, the width either side of the player context->x
1956 */
effect_handler_SENSE_OBJECTS(effect_handler_context_t * context)1957 bool effect_handler_SENSE_OBJECTS(effect_handler_context_t *context)
1958 {
1959 int x, y;
1960 int x1, x2, y1, y2;
1961
1962 bool objects = false;
1963
1964 /* Pick an area to sense */
1965 y1 = player->grid.y - context->y;
1966 y2 = player->grid.y + context->y;
1967 x1 = player->grid.x - context->x;
1968 x2 = player->grid.x + context->x;
1969
1970 if (y1 < 0) y1 = 0;
1971 if (x1 < 0) x1 = 0;
1972 if (y2 > cave->height - 1) y2 = cave->height - 1;
1973 if (x2 > cave->width - 1) x2 = cave->width - 1;
1974
1975 /* Scan the area for objects */
1976 for (y = y1; y <= y2; y++) {
1977 for (x = x1; x <= x2; x++) {
1978 struct loc grid = loc(x, y);
1979 struct object *obj = square_object(cave, grid);
1980
1981 if (!obj) {
1982 /* If empty, remove any remembered objects. */
1983 forget_remembered_objects(cave, player->cave, grid);
1984 continue;
1985 }
1986
1987 /* Notice an object is detected */
1988 objects = true;
1989
1990 /* Mark the pile as aware */
1991 square_sense_pile(cave, grid);
1992 }
1993 }
1994
1995 if (objects)
1996 msg("You sense the presence of objects!");
1997 else if (context->aware)
1998 msg("You sense no objects.");
1999
2000 /* Redraw whole map, monster list */
2001 player->upkeep->redraw |= PR_ITEMLIST;
2002
2003 context->ident = true;
2004 return true;
2005 }
2006
2007 /**
2008 * Detect objects around the player. The height to detect above and below the
2009 * player is context->y, the width either side of the player context->x
2010 */
effect_handler_DETECT_OBJECTS(effect_handler_context_t * context)2011 bool effect_handler_DETECT_OBJECTS(effect_handler_context_t *context)
2012 {
2013 int x, y;
2014 int x1, x2, y1, y2;
2015
2016 bool objects = false;
2017
2018 /* Pick an area to detect */
2019 y1 = player->grid.y - context->y;
2020 y2 = player->grid.y + context->y;
2021 x1 = player->grid.x - context->x;
2022 x2 = player->grid.x + context->x;
2023
2024 if (y1 < 0) y1 = 0;
2025 if (x1 < 0) x1 = 0;
2026 if (y2 > cave->height - 1) y2 = cave->height - 1;
2027 if (x2 > cave->width - 1) x2 = cave->width - 1;
2028
2029 /* Scan the area for objects */
2030 for (y = y1; y <= y2; y++) {
2031 for (x = x1; x <= x2; x++) {
2032 struct loc grid = loc(x, y);
2033 struct object *obj = square_object(cave, grid);
2034
2035 if (!obj) {
2036 /* If empty, remove any remembered objects. */
2037 forget_remembered_objects(cave, player->cave, grid);
2038 continue;
2039 }
2040
2041 /* Notice an object is detected */
2042 if (!ignore_item_ok(obj)) {
2043 objects = true;
2044 }
2045
2046 /* Mark the pile as seen */
2047 square_know_pile(cave, grid);
2048 }
2049 }
2050
2051 if (objects)
2052 msg("You detect the presence of objects!");
2053 else if (context->aware)
2054 msg("You detect no objects.");
2055
2056 /* Redraw whole map, monster list */
2057 player->upkeep->redraw |= PR_ITEMLIST;
2058
2059 context->ident = true;
2060 return true;
2061 }
2062
2063 /**
2064 * Detect monsters which satisfy the given predicate around the player.
2065 * The height to detect above and below the player is y_dist,
2066 * the width either side of the player x_dist.
2067 */
detect_monsters(int y_dist,int x_dist,monster_predicate pred)2068 static bool detect_monsters(int y_dist, int x_dist, monster_predicate pred)
2069 {
2070 int i, x, y;
2071 int x1, x2, y1, y2;
2072
2073 bool monsters = false;
2074
2075 /* Set the detection area */
2076 y1 = player->grid.y - y_dist;
2077 y2 = player->grid.y + y_dist;
2078 x1 = player->grid.x - x_dist;
2079 x2 = player->grid.x + x_dist;
2080
2081 if (y1 < 0) y1 = 0;
2082 if (x1 < 0) x1 = 0;
2083 if (y2 > cave->height - 1) y2 = cave->height - 1;
2084 if (x2 > cave->width - 1) x2 = cave->width - 1;
2085
2086 /* Scan monsters */
2087 for (i = 1; i < cave_monster_max(cave); i++) {
2088 struct monster *mon = cave_monster(cave, i);
2089
2090 /* Skip dead monsters */
2091 if (!mon->race) continue;
2092
2093 /* Location */
2094 y = mon->grid.y;
2095 x = mon->grid.x;
2096
2097 /* Only detect nearby monsters */
2098 if (x < x1 || y < y1 || x > x2 || y > y2) continue;
2099
2100 /* Detect all appropriate, obvious monsters */
2101 if (pred(mon) && !monster_is_camouflaged(mon)) {
2102 /* Detect the monster */
2103 mflag_on(mon->mflag, MFLAG_MARK);
2104 mflag_on(mon->mflag, MFLAG_SHOW);
2105
2106 /* Note invisible monsters */
2107 if (monster_is_invisible(mon)) {
2108 struct monster_lore *lore = get_lore(mon->race);
2109 rf_on(lore->flags, RF_INVISIBLE);
2110 }
2111
2112 /* Update monster recall window */
2113 if (player->upkeep->monster_race == mon->race)
2114 /* Redraw stuff */
2115 player->upkeep->redraw |= (PR_MONSTER);
2116
2117 /* Update the monster */
2118 update_mon(mon, cave, false);
2119
2120 /* Detect */
2121 monsters = true;
2122 }
2123 }
2124
2125 return monsters;
2126 }
2127
2128 /**
2129 * Detect living monsters around the player. The height to detect above and
2130 * below the player is context->value.dice, the width either side of the player
2131 * context->value.sides.
2132 */
effect_handler_DETECT_LIVING_MONSTERS(effect_handler_context_t * context)2133 bool effect_handler_DETECT_LIVING_MONSTERS(effect_handler_context_t *context)
2134 {
2135 bool monsters = detect_monsters(context->y, context->x, monster_is_living);
2136
2137 if (monsters)
2138 msg("You sense life!");
2139 else if (context->aware)
2140 msg("You sense no life.");
2141
2142 context->ident = true;
2143 return true;
2144 }
2145
2146
2147 /**
2148 * Detect visible monsters around the player; note that this means monsters
2149 * which are in principle visible, not monsters the player can currently see.
2150 *
2151 * The height to detect above and
2152 * below the player is context->value.dice, the width either side of the player
2153 * context->value.sides.
2154 */
effect_handler_DETECT_VISIBLE_MONSTERS(effect_handler_context_t * context)2155 bool effect_handler_DETECT_VISIBLE_MONSTERS(effect_handler_context_t *context)
2156 {
2157 bool monsters = detect_monsters(context->y, context->x,
2158 monster_is_not_invisible);
2159
2160 if (monsters)
2161 msg("You sense the presence of monsters!");
2162 else if (context->aware)
2163 msg("You sense no monsters.");
2164
2165 context->ident = true;
2166 return true;
2167 }
2168
2169
2170 /**
2171 * Detect invisible monsters around the player. The height to detect above and
2172 * below the player is context->value.dice, the width either side of the player
2173 * context->value.sides.
2174 */
effect_handler_DETECT_INVISIBLE_MONSTERS(effect_handler_context_t * context)2175 bool effect_handler_DETECT_INVISIBLE_MONSTERS(effect_handler_context_t *context)
2176 {
2177 bool monsters = detect_monsters(context->y, context->x,
2178 monster_is_invisible);
2179
2180 if (monsters)
2181 msg("You sense the presence of invisible creatures!");
2182 else if (context->aware)
2183 msg("You sense no invisible creatures.");
2184
2185 context->ident = true;
2186 return true;
2187 }
2188
2189 /**
2190 * Detect monsters susceptible to fear around the player. The height to detect
2191 * above and below the player is context->value.dice, the width either side of
2192 * the player context->value.sides.
2193 */
effect_handler_DETECT_FEARFUL_MONSTERS(effect_handler_context_t * context)2194 bool effect_handler_DETECT_FEARFUL_MONSTERS(effect_handler_context_t *context)
2195 {
2196 bool monsters = detect_monsters(context->y, context->x, monster_is_fearful);
2197
2198 if (monsters)
2199 msg("These monsters could provide good sport.");
2200 else if (context->aware)
2201 msg("You smell no fear in the air.");
2202
2203 context->ident = true;
2204 return true;
2205 }
2206
2207 /**
2208 * Detect evil monsters around the player. The height to detect above and
2209 * below the player is context->value.dice, the width either side of the player
2210 * context->value.sides.
2211 */
effect_handler_DETECT_EVIL(effect_handler_context_t * context)2212 bool effect_handler_DETECT_EVIL(effect_handler_context_t *context)
2213 {
2214 bool monsters = detect_monsters(context->y, context->x, monster_is_evil);
2215
2216 if (monsters)
2217 msg("You sense the presence of evil creatures!");
2218 else if (context->aware)
2219 msg("You sense no evil creatures.");
2220
2221 context->ident = true;
2222 return true;
2223 }
2224
2225 /**
2226 * Detect monsters possessing a spirit around the player.
2227 * The height to detect above and below the player is context->value.dice,
2228 * the width either side of the player context->value.sides.
2229 */
effect_handler_DETECT_SOUL(effect_handler_context_t * context)2230 bool effect_handler_DETECT_SOUL(effect_handler_context_t *context)
2231 {
2232 bool monsters = detect_monsters(context->y, context->x, monster_has_spirit);
2233
2234 if (monsters)
2235 msg("You sense the presence of spirits!");
2236 else if (context->aware)
2237 msg("You sense no spirits.");
2238
2239 context->ident = true;
2240 return true;
2241 }
2242
2243 /**
2244 * Identify an unknown rune of an item.
2245 */
effect_handler_IDENTIFY(effect_handler_context_t * context)2246 bool effect_handler_IDENTIFY(effect_handler_context_t *context)
2247 {
2248 struct object *obj;
2249 const char *q, *s;
2250 int itemmode = (USE_EQUIP | USE_INVEN | USE_QUIVER | USE_FLOOR);
2251 bool used = false;
2252
2253 context->ident = true;
2254
2255 /* Get an item */
2256 q = "Identify which item? ";
2257 s = "You have nothing to identify.";
2258 if (context->cmd) {
2259 if (cmd_get_item(context->cmd, "tgtitem", &obj, q, s,
2260 item_tester_unknown, itemmode)) {
2261 return used;
2262 }
2263 } else if (!get_item(&obj, q, s, 0, item_tester_unknown, itemmode))
2264 return used;
2265
2266 /* Identify the object */
2267 object_learn_unknown_rune(player, obj);
2268
2269 return true;
2270 }
2271
2272
2273 /**
2274 * Create stairs at the player location
2275 */
effect_handler_CREATE_STAIRS(effect_handler_context_t * context)2276 bool effect_handler_CREATE_STAIRS(effect_handler_context_t *context)
2277 {
2278 context->ident = true;
2279
2280 /* Only allow stairs to be created on empty floor */
2281 if (!square_isfloor(cave, player->grid)) {
2282 msg("There is no empty floor here.");
2283 return false;
2284 }
2285
2286 /* Fails for persistent levels (for now) */
2287 if (OPT(player, birth_levels_persist)) {
2288 msg("Nothing happens!");
2289 return false;
2290 }
2291
2292 /* Push objects off the grid */
2293 if (square_object(cave, player->grid))
2294 push_object(player->grid);
2295
2296 square_add_stairs(cave, player->grid, player->depth);
2297
2298 return true;
2299 }
2300
2301 /**
2302 * Apply disenchantment to the player's stuff.
2303 */
effect_handler_DISENCHANT(effect_handler_context_t * context)2304 bool effect_handler_DISENCHANT(effect_handler_context_t *context)
2305 {
2306 int i, count = 0;
2307 struct object *obj;
2308 char o_name[80];
2309
2310 /* Count slots */
2311 for (i = 0; i < player->body.count; i++) {
2312 /* Ignore rings, amulets and lights */
2313 if (slot_type_is(i, EQUIP_RING)) continue;
2314 if (slot_type_is(i, EQUIP_AMULET)) continue;
2315 if (slot_type_is(i, EQUIP_LIGHT)) continue;
2316
2317 /* Count disenchantable slots */
2318 count++;
2319 }
2320
2321 /* Pick one at random */
2322 for (i = player->body.count - 1; i >= 0; i--) {
2323 /* Ignore rings, amulets and lights */
2324 if (slot_type_is(i, EQUIP_RING)) continue;
2325 if (slot_type_is(i, EQUIP_AMULET)) continue;
2326 if (slot_type_is(i, EQUIP_LIGHT)) continue;
2327
2328 if (one_in_(count--)) break;
2329 }
2330
2331 /* Notice */
2332 context->ident = true;
2333
2334 /* Get the item */
2335 obj = slot_object(player, i);
2336
2337 /* No item, nothing happens */
2338 if (!obj) return true;
2339
2340 /* Nothing to disenchant */
2341 if ((obj->to_h <= 0) && (obj->to_d <= 0) && (obj->to_a <= 0))
2342 return true;
2343
2344 /* Describe the object */
2345 object_desc(o_name, sizeof(o_name), obj, ODESC_BASE);
2346
2347 /* Artifacts have a 60% chance to resist */
2348 if (obj->artifact && (randint0(100) < 60)) {
2349 /* Message */
2350 msg("Your %s (%c) resist%s disenchantment!", o_name, I2A(i),
2351 ((obj->number != 1) ? "" : "s"));
2352
2353 return true;
2354 }
2355
2356 /* Apply disenchantment, depending on which kind of equipment */
2357 if (slot_type_is(i, EQUIP_WEAPON) || slot_type_is(i, EQUIP_BOW)) {
2358 /* Disenchant to-hit */
2359 if (obj->to_h > 0) obj->to_h--;
2360 if ((obj->to_h > 5) && (randint0(100) < 20)) obj->to_h--;
2361 obj->known->to_h = obj->to_h;
2362
2363 /* Disenchant to-dam */
2364 if (obj->to_d > 0) obj->to_d--;
2365 if ((obj->to_d > 5) && (randint0(100) < 20)) obj->to_d--;
2366 obj->known->to_d = obj->to_d;
2367 } else {
2368 /* Disenchant to-ac */
2369 if (obj->to_a > 0) obj->to_a--;
2370 if ((obj->to_a > 5) && (randint0(100) < 20)) obj->to_a--;
2371 obj->known->to_a = obj->to_a;
2372 }
2373
2374 /* Message */
2375 msg("Your %s (%c) %s disenchanted!", o_name, I2A(i),
2376 ((obj->number != 1) ? "were" : "was"));
2377
2378 /* Recalculate bonuses */
2379 player->upkeep->update |= (PU_BONUS);
2380
2381 /* Window stuff */
2382 player->upkeep->redraw |= (PR_EQUIP);
2383
2384 return true;
2385 }
2386
2387 /**
2388 * Enchant an item (in the inventory or on the floor)
2389 * Note that armour, to hit or to dam is controlled by context->subtype
2390 *
2391 * Work on incorporating enchant_spell() has been postponed...NRM
2392 */
effect_handler_ENCHANT(effect_handler_context_t * context)2393 bool effect_handler_ENCHANT(effect_handler_context_t *context)
2394 {
2395 int value = randcalc(context->value, player->depth, RANDOMISE);
2396 bool used = false;
2397 context->ident = true;
2398
2399 if ((context->subtype & ENCH_TOBOTH) == ENCH_TOBOTH) {
2400 if (enchant_spell(value, value, 0, context->cmd))
2401 used = true;
2402 }
2403 else if (context->subtype & ENCH_TOHIT) {
2404 if (enchant_spell(value, 0, 0, context->cmd))
2405 used = true;
2406 }
2407 else if (context->subtype & ENCH_TODAM) {
2408 if (enchant_spell(0, value, 0, context->cmd))
2409 used = true;
2410 }
2411 if (context->subtype & ENCH_TOAC) {
2412 if (enchant_spell(0, 0, value, context->cmd))
2413 used = true;
2414 }
2415
2416 return used;
2417 }
2418
2419 /**
2420 * Returns N which is the 1 in N chance for recharging to fail.
2421 */
recharge_failure_chance(const struct object * obj,int strength)2422 int recharge_failure_chance(const struct object *obj, int strength) {
2423 /* Ease of recharge ranges from 9 down to 4 (wands) or 3 (staffs) */
2424 int ease_of_recharge = (100 - obj->kind->level) / 10;
2425 int raw_chance = strength + ease_of_recharge
2426 - 2 * (obj->pval / obj->number);
2427 return raw_chance > 1 ? raw_chance : 1;
2428 }
2429
2430 /**
2431 * Recharge a wand or staff from the pack or on the floor. Recharge strength
2432 * is context->value.base.
2433 *
2434 * It is harder to recharge high level, and highly charged wands.
2435 */
effect_handler_RECHARGE(effect_handler_context_t * context)2436 bool effect_handler_RECHARGE(effect_handler_context_t *context)
2437 {
2438 int i, t;
2439 int strength = context->value.base;
2440 int itemmode = (USE_INVEN | USE_FLOOR | SHOW_RECHARGE);
2441 struct object *obj;
2442 bool used = false;
2443 const char *q, *s;
2444
2445 /* Immediately obvious */
2446 context->ident = true;
2447
2448 /* Used to show recharge failure rates */
2449 player->upkeep->recharge_pow = strength;
2450
2451 /* Get an item */
2452 q = "Recharge which item? ";
2453 s = "You have nothing to recharge.";
2454 if (context->cmd) {
2455 if (cmd_get_item(context->cmd, "tgtitem", &obj, q, s,
2456 item_tester_hook_recharge, itemmode)) {
2457 return used;
2458 }
2459 } else if (!get_item(&obj, q, s, 0, item_tester_hook_recharge,
2460 itemmode)) {
2461 return (used);
2462 }
2463
2464 i = recharge_failure_chance(obj, strength);
2465 /* Back-fire */
2466 if ((i <= 1) || one_in_(i)) {
2467 struct object *destroyed;
2468 bool none_left = false;
2469
2470 msg("The recharge backfires!");
2471 msg("There is a bright flash of light.");
2472
2473 /* Reduce and describe inventory */
2474 if (object_is_carried(player, obj))
2475 destroyed = gear_object_for_use(obj, 1, true, &none_left);
2476 else
2477 destroyed = floor_object_for_use(obj, 1, true, &none_left);
2478 if (destroyed->known)
2479 object_delete(&destroyed->known);
2480 object_delete(&destroyed);
2481 } else {
2482 /* Extract a "power" */
2483 int ease_of_recharge = (100 - obj->kind->level) / 10;
2484 t = (strength / (10 - ease_of_recharge)) + 1;
2485
2486 /* Recharge based on the power */
2487 if (t > 0) obj->pval += 2 + randint1(t);
2488 }
2489
2490 /* Combine the pack (later) */
2491 player->upkeep->notice |= (PN_COMBINE);
2492
2493 /* Redraw stuff */
2494 player->upkeep->redraw |= (PR_INVEN);
2495
2496 /* Something was done */
2497 return true;
2498 }
2499
2500 /**
2501 * Apply a "project()" directly to all viewable monsters. If context->other is
2502 * set, the effect damage boost is applied. This is a hack - NRM
2503 *
2504 * Note that affected monsters are NOT auto-tracked by this usage.
2505 */
effect_handler_PROJECT_LOS(effect_handler_context_t * context)2506 bool effect_handler_PROJECT_LOS(effect_handler_context_t *context)
2507 {
2508 int i;
2509 int dam = effect_calculate_value(context, context->other ? true : false);
2510 int typ = context->subtype;
2511 struct loc origin = origin_get_loc(context->origin);
2512 int flg = PROJECT_JUMP | PROJECT_KILL | PROJECT_HIDE;
2513
2514 /* Affect all (nearby) monsters */
2515 for (i = 1; i < cave_monster_max(cave); i++) {
2516 struct monster *mon = cave_monster(cave, i);
2517
2518 /* Paranoia -- Skip dead monsters */
2519 if (!mon->race) continue;
2520
2521 /* Require line of sight */
2522 if (!los(cave, origin, mon->grid)) continue;
2523
2524 /* Jump directly to the monster */
2525 (void)project(source_player(), 0, mon->grid, dam, typ, flg, 0, 0,
2526 context->obj);
2527 context->ident = true;
2528 }
2529
2530 /* Result */
2531 return true;
2532 }
2533
2534 /**
2535 * Just like PROJECT_LOS except the player's awareness of an object using
2536 * this effect is relevant.
2537 *
2538 * Note that affected monsters are NOT auto-tracked by this usage.
2539 */
effect_handler_PROJECT_LOS_AWARE(effect_handler_context_t * context)2540 bool effect_handler_PROJECT_LOS_AWARE(effect_handler_context_t *context)
2541 {
2542 int i;
2543 int dam = effect_calculate_value(context, context->other ? true : false);
2544 int typ = context->subtype;
2545
2546 int flg = PROJECT_JUMP | PROJECT_KILL | PROJECT_HIDE;
2547
2548 if (context->aware) flg |= PROJECT_AWARE;
2549
2550 /* Affect all (nearby) monsters */
2551 for (i = 1; i < cave_monster_max(cave); i++) {
2552 struct monster *mon = cave_monster(cave, i);
2553 struct loc grid;
2554
2555 /* Paranoia -- Skip dead monsters */
2556 if (!mon->race) continue;
2557
2558 /* Location */
2559 grid = mon->grid;
2560
2561 /* Require line of sight */
2562 if (!square_isview(cave, grid)) continue;
2563
2564 /* Jump directly to the target monster */
2565 (void)project(source_player(), 0, grid, dam, typ, flg, 0, 0, context->obj);
2566 context->ident = true;
2567 }
2568
2569 /* Result */
2570 return true;
2571 }
2572
effect_handler_ACQUIRE(effect_handler_context_t * context)2573 bool effect_handler_ACQUIRE(effect_handler_context_t *context)
2574 {
2575 int num = effect_calculate_value(context, false);
2576 acquirement(player->grid, player->depth, num, true);
2577 context->ident = true;
2578 return true;
2579 }
2580
2581 /**
2582 * Wake up all monsters in line of sight
2583 */
effect_handler_WAKE(effect_handler_context_t * context)2584 bool effect_handler_WAKE(effect_handler_context_t *context)
2585 {
2586 int i;
2587 bool woken = false;
2588
2589 struct loc origin = origin_get_loc(context->origin);
2590
2591 /* Wake everyone nearby */
2592 for (i = 1; i < cave_monster_max(cave); i++) {
2593 struct monster *mon = cave_monster(cave, i);
2594 if (mon->race) {
2595 int radius = z_info->max_sight * 2;
2596 int dist = distance(origin, mon->grid);
2597
2598 /* Skip monsters too far away */
2599 if ((dist < radius) && mon->m_timed[MON_TMD_SLEEP]) {
2600 /* Monster wakes, closer means likelier to become aware */
2601 monster_wake(mon, false, 100 - 2 * dist);
2602 woken = true;
2603 }
2604 }
2605 }
2606
2607 /* Messages */
2608 if (woken) {
2609 msg("You hear a sudden stirring in the distance!");
2610 }
2611
2612 context->ident = true;
2613
2614 return true;
2615 }
2616
2617 /**
2618 * Summon context->value monsters of context->subtype type.
2619 */
effect_handler_SUMMON(effect_handler_context_t * context)2620 bool effect_handler_SUMMON(effect_handler_context_t *context)
2621 {
2622 int summon_max = effect_calculate_value(context, false);
2623 int summon_type = context->subtype;
2624 int level_boost = context->other;
2625 int message_type = summon_message_type(summon_type);
2626 int fallback_type = summon_fallback_type(summon_type);
2627 int count = 0, val = 0, attempts = 0;
2628
2629 sound(message_type);
2630
2631 /* No summoning in arena levels */
2632 if (player->upkeep->arena_level) return true;
2633
2634 /* Monster summon */
2635 if (context->origin.what == SRC_MONSTER) {
2636 struct monster *mon = cave_monster(cave, context->origin.which.monster);
2637 int rlev;
2638
2639 assert(mon);
2640
2641 /* Set the kin_base if necessary */
2642 if (summon_type == summon_name_to_idx("KIN")) {
2643 kin_base = mon->race->base;
2644 }
2645
2646 /* Continue summoning until we reach the current dungeon level */
2647 rlev = mon->race->level;
2648 while ((val < player->depth * rlev) && (attempts < summon_max)) {
2649 int temp;
2650
2651 /* Get a monster */
2652 temp = summon_specific(mon->grid, rlev + level_boost, summon_type,
2653 false, false);
2654
2655 val += temp * temp;
2656
2657 /* Increase the attempt in case no monsters were available. */
2658 attempts++;
2659
2660 /* Increase count of summoned monsters */
2661 if (val > 0)
2662 count++;
2663 }
2664
2665 /* If the summon failed and there's a fallback type, use that */
2666 if ((count == 0) && (fallback_type >= 0)) {
2667 attempts = 0;
2668 while ((val < player->depth * rlev) && (attempts < summon_max)) {
2669 int temp;
2670
2671 /* Get a monster */
2672 temp = summon_specific(mon->grid, rlev + level_boost,
2673 fallback_type, false, false);
2674
2675 val += temp * temp;
2676
2677 /* Increase the attempt in case no monsters were available. */
2678 attempts++;
2679
2680 /* Increase count of summoned monsters */
2681 if (val > 0)
2682 count++;
2683 }
2684 }
2685
2686 /* Summoner failed */
2687 if (!count)
2688 msg("But nothing comes.");
2689 } else {
2690 /* If not a monster summon, it's simple */
2691 while (summon_max) {
2692 count += summon_specific(player->grid, player->depth + level_boost,
2693 summon_type, true, one_in_(4));
2694 summon_max--;
2695 }
2696 }
2697
2698 /* Identify */
2699 context->ident = true;
2700
2701 /* Message for the blind */
2702 if (count && player->timed[TMD_BLIND])
2703 msgt(message_type, "You hear %s appear nearby.",
2704 (count > 1 ? "many things" : "something"));
2705
2706 return true;
2707 }
2708
2709 /**
2710 * Delete all non-unique monsters of a given "type" from the level
2711 * -------
2712 * Warning - this function assumes that the entered monster symbol is an ASCII
2713 * character, which may not be true in the future - NRM
2714 * -------
2715 */
effect_handler_BANISH(effect_handler_context_t * context)2716 bool effect_handler_BANISH(effect_handler_context_t *context)
2717 {
2718 int i;
2719 unsigned dam = 0;
2720
2721 char typ;
2722
2723 context->ident = true;
2724
2725 if (!get_com("Choose a monster race (by symbol) to banish: ", &typ))
2726 return false;
2727
2728 /* Delete the monsters of that "type" */
2729 for (i = 1; i < cave_monster_max(cave); i++) {
2730 struct monster *mon = cave_monster(cave, i);
2731
2732 /* Paranoia -- Skip dead monsters */
2733 if (!mon->race) continue;
2734
2735 /* Hack -- Skip Unique Monsters */
2736 if (monster_is_unique(mon)) continue;
2737
2738 /* Skip "wrong" monsters (see warning above) */
2739 if ((char) mon->race->d_char != typ) continue;
2740
2741 /* Delete the monster */
2742 delete_monster_idx(i);
2743
2744 /* Take some damage */
2745 dam += randint1(4);
2746 }
2747
2748 /* Hurt the player */
2749 take_hit(player, dam, "the strain of casting Banishment");
2750
2751 /* Update monster list window */
2752 player->upkeep->redraw |= PR_MONLIST;
2753
2754 /* Success */
2755 return true;
2756 }
2757
2758 /**
2759 * Delete all nearby (non-unique) monsters. The radius of effect is
2760 * context->radius if passed, otherwise the player view radius.
2761 */
effect_handler_MASS_BANISH(effect_handler_context_t * context)2762 bool effect_handler_MASS_BANISH(effect_handler_context_t *context)
2763 {
2764 int i;
2765 int radius = context->radius ? context->radius : z_info->max_sight;
2766 unsigned dam = 0;
2767
2768 context->ident = true;
2769
2770 /* Delete the (nearby) monsters */
2771 for (i = 1; i < cave_monster_max(cave); i++) {
2772 struct monster *mon = cave_monster(cave, i);
2773
2774 /* Paranoia -- Skip dead monsters */
2775 if (!mon->race) continue;
2776
2777 /* Hack -- Skip unique monsters */
2778 if (monster_is_unique(mon)) continue;
2779
2780 /* Skip distant monsters */
2781 if (mon->cdis > radius) continue;
2782
2783 /* Delete the monster */
2784 delete_monster_idx(i);
2785
2786 /* Take some damage */
2787 dam += randint1(3);
2788 }
2789
2790 /* Hurt the player */
2791 take_hit(player, dam, "the strain of casting Mass Banishment");
2792
2793 /* Update monster list window */
2794 player->upkeep->redraw |= PR_MONLIST;
2795
2796 return true;
2797 }
2798
2799 /**
2800 * Probe nearby monsters
2801 */
effect_handler_PROBE(effect_handler_context_t * context)2802 bool effect_handler_PROBE(effect_handler_context_t *context)
2803 {
2804 int i;
2805
2806 bool probe = false;
2807
2808 /* Probe all (nearby) monsters */
2809 for (i = 1; i < cave_monster_max(cave); i++) {
2810 struct monster *mon = cave_monster(cave, i);
2811
2812 /* Paranoia -- Skip dead monsters */
2813 if (!mon->race) continue;
2814
2815 /* Require line of sight */
2816 if (!square_isview(cave, mon->grid)) continue;
2817
2818 /* Probe visible monsters */
2819 if (monster_is_visible(mon)) {
2820 char m_name[80];
2821
2822 /* Start the message */
2823 if (!probe) msg("Probing...");
2824
2825 /* Get "the monster" or "something" */
2826 monster_desc(m_name, sizeof(m_name), mon,
2827 MDESC_IND_HID | MDESC_CAPITAL);
2828
2829 /* Describe the monster */
2830 msg("%s has %d hit point%s.", m_name, mon->hp, (mon->hp == 1) ? "" : "s");
2831
2832 /* Learn all of the non-spell, non-treasure flags */
2833 lore_do_probe(mon);
2834
2835 /* Probe worked */
2836 probe = true;
2837 }
2838 }
2839
2840 /* Done */
2841 if (probe) {
2842 msg("That's all.");
2843 context->ident = true;
2844 }
2845
2846 return true;
2847 }
2848
2849 /**
2850 * Teleport player or monster up to context->value.base grids away.
2851 *
2852 * If no spaces are readily available, the distance may increase.
2853 * Try very hard to move the player/monster at least a quarter that distance.
2854 * Setting context->subtype allows monsters to teleport the player away.
2855 * Setting context->y and context->x treats them as y and x coordinates
2856 * and teleports the monster from that grid.
2857 */
effect_handler_TELEPORT(effect_handler_context_t * context)2858 bool effect_handler_TELEPORT(effect_handler_context_t *context)
2859 {
2860 struct loc start = loc(context->x, context->y);
2861 int dis = context->value.base;
2862 int perc = context->value.m_bonus;
2863 int pick;
2864 struct loc grid;
2865
2866 struct jumps {
2867 struct loc grid;
2868 struct jumps *next;
2869 } *spots = NULL;
2870 int num_spots = 0;
2871 int current_score = 2 * MAX(z_info->dungeon_wid, z_info->dungeon_hgt);
2872 bool only_vault_grids_possible = true;
2873
2874 bool is_player = (context->origin.what != SRC_MONSTER || context->subtype);
2875 struct monster *t_mon = monster_target_monster(context);
2876
2877 context->ident = true;
2878
2879 /* No teleporting in arena levels */
2880 if (player->upkeep->arena_level) return true;
2881
2882 /* Establish the coordinates to teleport from, if we don't know already */
2883 if (!loc_is_zero(start)) {
2884 /* We're good */
2885 } else if (t_mon) {
2886 /* Monster targeting another monster */
2887 start = t_mon->grid;
2888 } else if (is_player) {
2889 /* Decoys get destroyed */
2890 struct loc decoy = cave_find_decoy(cave);
2891 if (!loc_is_zero(decoy) && context->subtype) {
2892 square_destroy_decoy(cave, decoy);
2893 return true;
2894 }
2895
2896 start = player->grid;
2897
2898 /* Check for a no teleport grid */
2899 if (square_isno_teleport(cave, start) &&
2900 ((dis > 10) || (dis == 0))) {
2901 msg("Teleportation forbidden!");
2902 return true;
2903 }
2904
2905 /* Check for a no teleport curse */
2906 if (player_of_has(player, OF_NO_TELEPORT)) {
2907 equip_learn_flag(player, OF_NO_TELEPORT);
2908 msg("Teleportation forbidden!");
2909 return true;
2910 }
2911 } else {
2912 assert(context->origin.what == SRC_MONSTER);
2913 struct monster *mon = cave_monster(cave, context->origin.which.monster);
2914 start = mon->grid;
2915 }
2916
2917 /* Percentage of the largest cardinal distance to an edge */
2918 if (perc) {
2919 int vertical = MAX(start.y, cave->height - start.y);
2920 int horizontal = MAX(start.x, cave->width - start.x);
2921 dis = (MAX(vertical, horizontal) * perc) / 100;
2922 }
2923
2924 /* Randomise the distance a little */
2925 if (one_in_(2)) {
2926 dis -= randint0(dis / 4);
2927 } else {
2928 dis += randint0(dis / 4);
2929 }
2930
2931 /* Make a list of the best grids, scoring by how good an approximation
2932 * the distance from the start is to the distance we want */
2933 for (grid.y = 1; grid.y < cave->height - 1; grid.y++) {
2934 for (grid.x = 1; grid.x < cave->width - 1; grid.x++) {
2935 int d = distance(grid, start);
2936 int score = ABS(d - dis);
2937 struct jumps *new;
2938
2939 /* Must move */
2940 if (d == 0) continue;
2941
2942 /* Require "naked" floor space */
2943 if (!square_isempty(cave, grid)) continue;
2944
2945 /* No monster teleport onto glyph of warding */
2946 if (!is_player && square_iswarded(cave, grid)) continue;
2947
2948 /* No teleporting into vaults and such, unless there's no choice */
2949 if (square_isvault(cave, grid)) {
2950 if (!only_vault_grids_possible) {
2951 continue;
2952 }
2953 } else {
2954 /* Just starting to consider non-vault grids, so reset score */
2955 if (only_vault_grids_possible) {
2956 current_score = 2 * MAX(z_info->dungeon_wid,
2957 z_info->dungeon_hgt);
2958 }
2959 only_vault_grids_possible = false;
2960 }
2961
2962 /* Do we have better spots already? */
2963 if (score > current_score) continue;
2964
2965 /* Make a new spot */
2966 new = mem_zalloc(sizeof(struct jumps));
2967 new->grid = grid;
2968
2969 /* If improving start a new list, otherwise extend the old one */
2970 if (score < current_score) {
2971 current_score = score;
2972 while (spots) {
2973 struct jumps *next = spots->next;
2974 mem_free(spots);
2975 spots = next;
2976 }
2977 spots = new;
2978 num_spots = 1;
2979 } else {
2980 new->next = spots;
2981 spots = new;
2982 num_spots++;
2983 }
2984 }
2985 }
2986
2987 /* Report failure (very unlikely) */
2988 if (!num_spots) {
2989 msg("Failed to find teleport destination!");
2990 return true;
2991 }
2992
2993 /* Pick a spot */
2994 pick = randint0(num_spots);
2995 while (pick) {
2996 struct jumps *next = spots->next;
2997 mem_free(spots);
2998 spots = next;
2999 pick--;
3000 }
3001
3002 /* Sound */
3003 sound(is_player ? MSG_TELEPORT : MSG_TPOTHER);
3004
3005 /* Move player */
3006 monster_swap(start, spots->grid);
3007
3008 /* Clear any projection marker to prevent double processing */
3009 sqinfo_off(square(cave, spots->grid)->info, SQUARE_PROJECT);
3010
3011 /* Clear monster target if it's no longer visible */
3012 if (!target_able(target_get_monster())) {
3013 target_set_monster(NULL);
3014 }
3015
3016 /* Lots of updates after monster_swap */
3017 handle_stuff(player);
3018
3019 while (spots) {
3020 struct jumps *next = spots->next;
3021 mem_free(spots);
3022 spots = next;
3023 }
3024
3025 return true;
3026 }
3027
3028 /**
3029 * Teleport player or target monster to a grid near the given location
3030 * Setting context->y and context->x treats them as y and x coordinates
3031 * Setting context->subtype allows monsters to teleport toward the player.
3032 *
3033 * This function is slightly obsessive about correctness.
3034 * This function allows teleporting into vaults (!)
3035 */
effect_handler_TELEPORT_TO(effect_handler_context_t * context)3036 bool effect_handler_TELEPORT_TO(effect_handler_context_t *context)
3037 {
3038 struct monster *mon = NULL;
3039 struct loc start, aim, land;
3040 int dis = 0, ctr = 0, dir = DIR_TARGET;
3041 struct monster *t_mon = monster_target_monster(context);
3042
3043 context->ident = true;
3044
3045 /* No teleporting in arena levels */
3046 if (player->upkeep->arena_level) return true;
3047
3048 if (context->origin.what == SRC_MONSTER) {
3049 mon = cave_monster(cave, context->origin.which.monster);
3050 assert(mon);
3051 }
3052
3053 /* Where are we coming from? */
3054 if (t_mon) {
3055 /* Monster being teleported */
3056 start = t_mon->grid;
3057 } else if (context->subtype) {
3058 /* Monster teleporting to the player */
3059 start = mon->grid;
3060 } else {
3061 /* Targeted decoys get destroyed */
3062 struct loc decoy = cave_find_decoy(cave);
3063 if (!loc_is_zero(decoy) && mon) {
3064 square_destroy_decoy(cave, decoy);
3065 return true;
3066 }
3067
3068 /* Player being teleported */
3069 start = player->grid;
3070
3071 /* Check for a no teleport grid */
3072 if (square_isno_teleport(cave, start)) {
3073 msg("Teleportation forbidden!");
3074 return true;
3075 }
3076
3077 /* Check for a no teleport curse */
3078 if (player_of_has(player, OF_NO_TELEPORT)) {
3079 equip_learn_flag(player, OF_NO_TELEPORT);
3080 msg("Teleportation forbidden!");
3081 return true;
3082 }
3083 }
3084
3085 /* Where are we going? */
3086 if (context->y && context->x) {
3087 /* Effect was given co-ordinates */
3088 aim = loc(context->x, context->y);
3089 } else if (mon) {
3090 /* Spell cast by monster */
3091 if (context->subtype) {
3092 /* Monster teleporting to player */
3093 aim = player->grid;
3094 dis = 2;
3095 } else {
3096 /* Player being teleported to monster */
3097 aim = mon->grid;
3098 }
3099 } else {
3100 /* Player choice */
3101 do {
3102 get_aim_dir(&dir);
3103 } while (dir == DIR_TARGET && !target_okay());
3104
3105 if (dir == DIR_TARGET)
3106 target_get(&aim);
3107 else
3108 aim = loc_offset(start, ddx[dir], ddy[dir]);
3109
3110 /* Randomise the landing a bit if it's a vault */
3111 if (square_isvault(cave, aim)) dis = 10;
3112 }
3113
3114 /* Find a usable location */
3115 while (1) {
3116 /* Pick a nearby legal location */
3117 while (1) {
3118 land = rand_loc(aim, dis, dis);
3119 if (square_in_bounds_fully(cave, land)) break;
3120 }
3121
3122 /* Accept "naked" floor grids */
3123 if (square_isempty(cave, land)) break;
3124
3125 /* Occasionally advance the distance */
3126 if (++ctr > (4 * dis * dis + 4 * dis + 1)) {
3127 ctr = 0;
3128 dis++;
3129 }
3130 }
3131
3132 /* Sound */
3133 sound(MSG_TELEPORT);
3134
3135 /* Move player or monster */
3136 monster_swap(start, land);
3137
3138 /* Clear any projection marker to prevent double processing */
3139 sqinfo_off(square(cave, land)->info, SQUARE_PROJECT);
3140
3141 /* Lots of updates after monster_swap */
3142 handle_stuff(player);
3143
3144 return true;
3145 }
3146
3147 /**
3148 * Teleport the player one level up or down (random when legal)
3149 */
effect_handler_TELEPORT_LEVEL(effect_handler_context_t * context)3150 bool effect_handler_TELEPORT_LEVEL(effect_handler_context_t *context)
3151 {
3152 bool up = true;
3153 bool down = true;
3154 int target_depth = dungeon_get_next_level(player->max_depth, 1);
3155 struct monster *t_mon = monster_target_monster(context);
3156 struct loc decoy = cave_find_decoy(cave);
3157
3158 context->ident = true;
3159
3160 /* No teleporting in arena levels */
3161 if (player->upkeep->arena_level) return true;
3162
3163 /* Check for monster targeting another monster */
3164 if (t_mon) {
3165 /* Monster is just gone */
3166 add_monster_message(t_mon, MON_MSG_DISAPPEAR, false);
3167 delete_monster_idx(t_mon->midx);
3168 return true;
3169 }
3170
3171 /* Targeted decoys get destroyed */
3172 if (decoy.y && decoy.x) {
3173 square_destroy_decoy(cave, decoy);
3174 return true;
3175 }
3176
3177 /* Check for a no teleport grid */
3178 if (square_isno_teleport(cave, player->grid)) {
3179 msg("Teleportation forbidden!");
3180 return true;
3181 }
3182
3183 /* Check for a no teleport curse */
3184 if (player_of_has(player, OF_NO_TELEPORT)) {
3185 equip_learn_flag(player, OF_NO_TELEPORT);
3186 msg("Teleportation forbidden!");
3187 return true;
3188 }
3189
3190 /* Resist hostile teleport */
3191 if (context->origin.what == SRC_MONSTER &&
3192 player_resists(player, ELEM_NEXUS)) {
3193 msg("You resist the effect!");
3194 return true;
3195 }
3196
3197 /* No going up with force_descend or in the town */
3198 if (OPT(player, birth_force_descend) || !player->depth)
3199 up = false;
3200
3201 /* No forcing player down to quest levels if they can't leave */
3202 if (!up && is_quest(target_depth))
3203 down = false;
3204
3205 /* Can't leave quest levels or go down deeper than the dungeon */
3206 if (is_quest(player->depth) || (player->depth >= z_info->max_depth - 1))
3207 down = false;
3208
3209 /* Determine up/down if not already done */
3210 if (up && down) {
3211 if (randint0(100) < 50)
3212 up = false;
3213 else
3214 down = false;
3215 }
3216
3217 /* Now actually do the level change */
3218 if (up) {
3219 msgt(MSG_TPLEVEL, "You rise up through the ceiling.");
3220 target_depth = dungeon_get_next_level(player->depth, -1);
3221 dungeon_change_level(player, target_depth);
3222 } else if (down) {
3223 msgt(MSG_TPLEVEL, "You sink through the floor.");
3224
3225 if (OPT(player, birth_force_descend)) {
3226 target_depth = dungeon_get_next_level(player->max_depth, 1);
3227 dungeon_change_level(player, target_depth);
3228 } else {
3229 target_depth = dungeon_get_next_level(player->depth, 1);
3230 dungeon_change_level(player, target_depth);
3231 }
3232 } else {
3233 msg("Nothing happens.");
3234 }
3235
3236 return true;
3237 }
3238
3239 /**
3240 * The rubble effect
3241 *
3242 * This causes rubble to fall into empty squares.
3243 */
effect_handler_RUBBLE(effect_handler_context_t * context)3244 bool effect_handler_RUBBLE(effect_handler_context_t *context)
3245 {
3246 /*
3247 * First we work out how many grids we want to fill with rubble. Then we
3248 * check that we can actually do this, by counting the number of grids
3249 * available, limiting the number of rubble grids to this number if
3250 * necessary.
3251 */
3252 int rubble_grids = randint1(3);
3253 int open_grids = count_feats(NULL, square_isempty, false);
3254
3255 if (rubble_grids > open_grids) {
3256 rubble_grids = open_grids;
3257 }
3258
3259 /* Avoid infinite loops */
3260 int iterations = 0;
3261
3262 while (rubble_grids > 0 && iterations < 10) {
3263 /* Look around the player */
3264 for (int d = 0; d < 8; d++) {
3265 /* Extract adjacent (legal) location */
3266 struct loc grid = loc_sum(player->grid, ddgrid_ddd[d]);
3267 if (!square_in_bounds_fully(cave, grid)) continue;
3268 if (!square_isempty(cave, grid)) continue;
3269
3270 if (one_in_(3)) {
3271 if (one_in_(2))
3272 square_set_feat(cave, grid, FEAT_PASS_RUBBLE);
3273 else
3274 square_set_feat(cave, grid, FEAT_RUBBLE);
3275 rubble_grids--;
3276 }
3277 }
3278
3279 iterations++;
3280 }
3281
3282 context->ident = true;
3283
3284 /* Fully update the visuals */
3285 player->upkeep->update |= (PU_UPDATE_VIEW | PU_MONSTERS);
3286
3287 /* Redraw monster list */
3288 player->upkeep->redraw |= (PR_MONLIST | PR_ITEMLIST);
3289
3290 return true;
3291 }
3292
effect_handler_GRANITE(effect_handler_context_t * context)3293 bool effect_handler_GRANITE(effect_handler_context_t *context)
3294 {
3295 struct trap *trap = context->origin.which.trap;
3296 square_set_feat(cave, trap->grid, FEAT_GRANITE);
3297
3298 player->upkeep->update |= (PU_UPDATE_VIEW | PU_MONSTERS);
3299 player->upkeep->redraw |= (PR_MONLIST | PR_ITEMLIST);
3300
3301 return true;
3302 }
3303
3304 /**
3305 * The destruction effect
3306 *
3307 * This effect "deletes" monsters (instead of killing them).
3308 *
3309 * This is always an effect centred on the player; it is similar to the
3310 * earthquake effect.
3311 */
effect_handler_DESTRUCTION(effect_handler_context_t * context)3312 bool effect_handler_DESTRUCTION(effect_handler_context_t *context)
3313 {
3314 int k, r = context->radius;
3315 int elem = context->subtype;
3316 int py = player->grid.y;
3317 int px = player->grid.x;
3318 struct loc grid;
3319
3320 context->ident = true;
3321
3322 /* No effect in town or arena */
3323 if ((!player->depth) || (player->upkeep->arena_level)) {
3324 msg("The ground shakes for a moment.");
3325 return true;
3326 }
3327
3328 /* Big area of affect */
3329 for (grid.y = (py - r); grid.y <= (py + r); grid.y++) {
3330 for (grid.x = (px - r); grid.x <= (px + r); grid.x++) {
3331 /* Skip illegal grids */
3332 if (!square_in_bounds_fully(cave, grid)) continue;
3333
3334 /* Extract the distance */
3335 k = distance(loc(px, py), grid);
3336
3337 /* Stay in the circle of death */
3338 if (k > r) continue;
3339
3340 /* Lose room and vault */
3341 sqinfo_off(square(cave, grid)->info, SQUARE_ROOM);
3342 sqinfo_off(square(cave, grid)->info, SQUARE_VAULT);
3343
3344 /* Forget completely */
3345 if (!square_isbright(cave, grid)) {
3346 sqinfo_off(square(cave, grid)->info, SQUARE_GLOW);
3347 }
3348 sqinfo_off(square(cave, grid)->info, SQUARE_SEEN);
3349 square_forget(cave, grid);
3350 square_light_spot(cave, grid);
3351
3352 /* Deal with player later */
3353 if (loc_eq(grid, player->grid)) continue;
3354
3355 /* Delete the monster (if any) */
3356 delete_monster(grid);
3357
3358 /* Don't remove stairs */
3359 if (square_isstairs(cave, grid)) continue;
3360
3361 /* Destroy any grid that isn't a permament wall */
3362 if (!square_isperm(cave, grid)) {
3363 /* Deal with artifacts */
3364 struct object *obj = square_object(cave, grid);
3365 while (obj) {
3366 if (obj->artifact) {
3367 if (OPT(player, birth_lose_arts) ||
3368 obj_is_known_artifact(obj)) {
3369 history_lose_artifact(player, obj->artifact);
3370 obj->artifact->created = true;
3371 } else {
3372 obj->artifact->created = false;
3373 }
3374 }
3375 obj = obj->next;
3376 }
3377
3378 /* Delete objects */
3379 square_excise_pile(player->cave, grid);
3380 square_excise_pile(cave, grid);
3381 square_destroy(cave, grid);
3382 }
3383 }
3384 }
3385
3386 /* Player is affected */
3387 if (elem == ELEM_LIGHT) {
3388 msg("There is a searing blast of light!");
3389 equip_learn_element(player, ELEM_LIGHT);
3390 if (!player_resists(player, ELEM_LIGHT)) {
3391 (void)player_inc_timed(player, TMD_BLIND, 10 + randint1(10), true,
3392 true);
3393 }
3394 } else if (elem == ELEM_DARK) {
3395 msg("Darkness seems to crush you!");
3396 equip_learn_element(player, ELEM_DARK);
3397 if (!player_resists(player, ELEM_DARK)) {
3398 (void)player_inc_timed(player, TMD_BLIND, 10 + randint1(10), true,
3399 true);
3400 }
3401 }
3402
3403 /* Fully update the visuals */
3404 player->upkeep->update |= (PU_UPDATE_VIEW | PU_MONSTERS);
3405
3406 /* Redraw monster list */
3407 player->upkeep->redraw |= (PR_MONLIST | PR_ITEMLIST);
3408
3409 return true;
3410 }
3411
3412 /**
3413 * Induce an earthquake of the radius context->radius centred on the instigator.
3414 *
3415 * This will turn some walls into floors and some floors into walls.
3416 *
3417 * The player will take damage and jump into a safe grid if possible,
3418 * otherwise, he will tunnel through the rubble instantaneously.
3419 *
3420 * Monsters will take damage, and jump into a safe grid if possible,
3421 * otherwise they will be buried in the rubble, disappearing from
3422 * the level in the same way that they do when banished.
3423 *
3424 * Note that players and monsters (except eaters of walls and passers
3425 * through walls) will never occupy the same grid as a wall (or door).
3426 */
effect_handler_EARTHQUAKE(effect_handler_context_t * context)3427 bool effect_handler_EARTHQUAKE(effect_handler_context_t *context)
3428 {
3429 int r = context->radius;
3430 bool targeted = context->subtype ? true : false;
3431
3432 struct loc pgrid = player->grid;
3433 int i, y, x;
3434 struct loc offset, safe_grid = loc(0, 0);
3435 int safe_grids = 0;
3436 int damage = 0;
3437 bool hurt = false;
3438 bool map[32][32];
3439
3440 struct loc centre = origin_get_loc(context->origin);
3441
3442 context->ident = true;
3443
3444 if ((player->depth) && ((!player->upkeep->arena_level)
3445 || (context->origin.what == SRC_MONSTER))) {
3446 msg("The ground shakes! The ceiling caves in!");
3447 } else {
3448 /* No effect in town or arena */
3449 msg("The ground shakes for a moment.");
3450 return true;
3451 }
3452
3453 /* Sometimes ask for a target */
3454 if (targeted) {
3455 int dir = DIR_TARGET;
3456 get_aim_dir(&dir);
3457 if ((dir == DIR_TARGET) && target_okay()) {
3458 target_get(¢re);
3459 }
3460 }
3461
3462 /* Paranoia -- Enforce maximum range */
3463 if (r > 15) r = 15;
3464
3465 /* Initialize a map of the maximal blast area */
3466 for (y = 0; y < 32; y++)
3467 for (x = 0; x < 32; x++)
3468 map[y][x] = false;
3469
3470 /* Check around the epicenter */
3471 for (offset.y = -r; offset.y <= r; offset.y++) {
3472 for (offset.x = -r; offset.x <= r; offset.x++) {
3473 /* Extract the location */
3474 struct loc grid = loc_sum(centre, offset);
3475
3476 /* Skip illegal grids */
3477 if (!square_in_bounds_fully(cave, grid)) continue;
3478
3479 /* Skip distant grids */
3480 if (distance(centre, grid) > r) continue;
3481
3482 /* Lose room and vault */
3483 sqinfo_off(square(cave, grid)->info, SQUARE_ROOM);
3484 sqinfo_off(square(cave, grid)->info, SQUARE_VAULT);
3485
3486 /* Forget completely */
3487 if (!square_isbright(cave, grid)) {
3488 sqinfo_off(square(cave, grid)->info, SQUARE_GLOW);
3489 }
3490 sqinfo_off(square(cave, grid)->info, SQUARE_SEEN);
3491 square_forget(cave, grid);
3492 square_light_spot(cave, grid);
3493
3494 /* Skip the epicenter */
3495 if (loc_is_zero(offset)) continue;
3496
3497 /* Skip most grids */
3498 if (randint0(100) < 85) continue;
3499
3500 /* Damage this grid */
3501 map[16 + grid.y - centre.y][16 + grid.x - centre.x] = true;
3502
3503 /* Take note of player damage */
3504 if (loc_eq(grid, pgrid)) hurt = true;
3505 }
3506 }
3507
3508 /* First, affect the player (if necessary) */
3509 if (hurt) {
3510 /* Check around the player */
3511 for (i = 0; i < 8; i++) {
3512 /* Get the location */
3513 struct loc grid = loc_sum(pgrid, ddgrid_ddd[i]);
3514
3515 /* Skip non-empty grids - allow pushing into traps and webs */
3516 if (!square_isopen(cave, grid)) continue;
3517
3518 /* Important -- Skip grids marked for damage */
3519 if (map[16 + grid.y - centre.y][16 + grid.x - centre.x]) continue;
3520
3521 /* Count "safe" grids, apply the randomizer */
3522 if ((++safe_grids > 1) && (randint0(safe_grids) != 0)) continue;
3523
3524 /* Save the safe location */
3525 safe_grid = grid;
3526 }
3527
3528 /* Random message */
3529 switch (randint1(3))
3530 {
3531 case 1:
3532 {
3533 msg("The cave ceiling collapses on you!");
3534 break;
3535 }
3536 case 2:
3537 {
3538 msg("The cave floor twists in an unnatural way!");
3539 break;
3540 }
3541 default:
3542 {
3543 msg("The cave quakes!");
3544 msg("You are pummeled with debris!");
3545 break;
3546 }
3547 }
3548
3549 /* Hurt the player a lot */
3550 if (!safe_grids) {
3551 /* Message and damage */
3552 msg("You are severely crushed!");
3553 damage = 300;
3554 } else {
3555 /* Destroy the grid, and push the player to (relative) safety */
3556 switch (randint1(3)) {
3557 case 1: {
3558 msg("You nimbly dodge the blast!");
3559 damage = 0;
3560 break;
3561 }
3562 case 2: {
3563 msg("You are bashed by rubble!");
3564 damage = damroll(10, 4);
3565 (void)player_inc_timed(player, TMD_STUN, randint1(50),
3566 true, true);
3567 break;
3568 }
3569 case 3: {
3570 msg("You are crushed between the floor and ceiling!");
3571 damage = damroll(10, 4);
3572 (void)player_inc_timed(player, TMD_STUN, randint1(50),
3573 true, true);
3574 break;
3575 }
3576 }
3577
3578 /* Move player */
3579 monster_swap(pgrid, safe_grid);
3580 }
3581
3582 /* Take some damage */
3583 if (damage) take_hit(player, damage, "an earthquake");
3584 }
3585
3586
3587 /* Examine the quaked region */
3588 for (offset.y = -r; offset.y <= r; offset.y++) {
3589 for (offset.x = -r; offset.x <= r; offset.x++) {
3590 /* Extract the location */
3591 struct loc grid = loc_sum(centre, offset);
3592
3593 /* Skip unaffected grids */
3594 if (!map[16 + grid.y - centre.y][16 + grid.x - centre.x]) continue;
3595
3596 /* Process monsters */
3597 if (square(cave, grid)->mon > 0) {
3598 struct monster *mon = square_monster(cave, grid);
3599
3600 /* Most monsters cannot co-exist with rock */
3601 if (!flags_test(mon->race->flags, RF_SIZE, RF_KILL_WALL,
3602 RF_PASS_WALL, FLAG_END)) {
3603 char m_name[80];
3604
3605 /* Assume not safe */
3606 safe_grids = 0;
3607
3608 /* Monster can move to escape the wall */
3609 if (!rf_has(mon->race->flags, RF_NEVER_MOVE)) {
3610 /* Look for safety */
3611 for (i = 0; i < 8; i++) {
3612 /* Get the grid */
3613 struct loc safe = loc_sum(grid, ddgrid_ddd[i]);
3614
3615 /* Skip non-empty grids */
3616 if (!square_isempty(cave, safe)) continue;
3617
3618 /* Hack -- no safety on glyph of warding */
3619 if (square_iswarded(cave, safe)) continue;
3620
3621 /* Important -- Skip quake grids */
3622 if (map[16 + safe.y - centre.y]
3623 [16 + safe.x - centre.x]) continue;
3624
3625 /* Count safe grids, apply the randomizer */
3626 if ((++safe_grids > 1) &&
3627 (randint0(safe_grids) != 0))
3628 continue;
3629
3630 /* Save the safe grid */
3631 safe_grid = safe;
3632 }
3633 }
3634
3635 /* Describe the monster */
3636 monster_desc(m_name, sizeof(m_name), mon, MDESC_STANDARD);
3637
3638 /* Scream in pain */
3639 msg("%s wails out in pain!", m_name);
3640
3641 /* Take damage from the quake */
3642 damage = (safe_grids ? damroll(4, 8) : (mon->hp + 1));
3643
3644 /* Monster is certainly awake, not thinking about player */
3645 monster_wake(mon, false, 0);
3646
3647 /* If the quake finished the monster off, show message */
3648 if (mon->hp < damage && mon->hp >= 0)
3649 msg("%s is embedded in the rock!", m_name);
3650
3651 /* Apply damage directly */
3652 mon->hp -= damage;
3653
3654 /* Delete (not kill) "dead" monsters */
3655 if (mon->hp < 0) {
3656 /* Delete the monster */
3657 delete_monster(grid);
3658
3659 /* No longer safe */
3660 safe_grids = 0;
3661 }
3662
3663 /* Escape from the rock */
3664 if (safe_grids)
3665 /* Move the monster */
3666 monster_swap(grid, safe_grid);
3667 }
3668 }
3669 }
3670 }
3671
3672 /* Player may have moved */
3673 pgrid = player->grid;
3674
3675 /* Important -- no wall on player */
3676 map[16 + pgrid.y - centre.y][16 + pgrid.x - centre.x] = false;
3677
3678
3679 /* Examine the quaked region and damage marked grids if possible */
3680 for (offset.y = -r; offset.y <= r; offset.y++) {
3681 for (offset.x = -r; offset.x <= r; offset.x++) {
3682 /* Extract the location */
3683 struct loc grid = loc_sum(centre, offset);
3684
3685 /* Ignore invalid grids */
3686 if (!square_in_bounds_fully(cave, grid)) continue;
3687
3688 /* Note unaffected grids for light changes, etc. */
3689 if (!map[16 + grid.y - centre.y][16 + grid.x - centre.x])
3690 square_light_spot(cave, grid);
3691
3692 /* Destroy location and all objects (if valid) */
3693 else if (square_changeable(cave, grid)) {
3694 square_excise_pile(cave, grid);
3695 square_earthquake(cave, grid);
3696 }
3697 }
3698 }
3699
3700 /* Fully update the visuals */
3701 player->upkeep->update |= (PU_UPDATE_VIEW | PU_MONSTERS);
3702
3703 /* Update the health bar */
3704 player->upkeep->redraw |= (PR_HEALTH);
3705
3706 /* Window stuff */
3707 player->upkeep->redraw |= (PR_MONLIST | PR_ITEMLIST);
3708
3709 return true;
3710 }
3711
effect_handler_LIGHT_LEVEL(effect_handler_context_t * context)3712 bool effect_handler_LIGHT_LEVEL(effect_handler_context_t *context)
3713 {
3714 bool full = context->value.base ? true : false;
3715 if (full)
3716 msg("An image of your surroundings forms in your mind...");
3717 wiz_light(cave, player, full);
3718 context->ident = true;
3719 return true;
3720 }
3721
effect_handler_DARKEN_LEVEL(effect_handler_context_t * context)3722 bool effect_handler_DARKEN_LEVEL(effect_handler_context_t *context)
3723 {
3724 bool full = context->value.base ? true : false;
3725 if (full)
3726 msg("A great blackness rolls through the dungeon...");
3727 wiz_dark(cave, player, full);
3728 context->ident = true;
3729 return true;
3730 }
3731
3732 /**
3733 * Call light around the player
3734 */
effect_handler_LIGHT_AREA(effect_handler_context_t * context)3735 bool effect_handler_LIGHT_AREA(effect_handler_context_t *context)
3736 {
3737 /* Message */
3738 if (!player->timed[TMD_BLIND])
3739 msg("You are surrounded by a white light.");
3740
3741 /* Light up the room */
3742 light_room(player->grid, true);
3743
3744 /* Assume seen */
3745 context->ident = true;
3746 return (true);
3747 }
3748
3749
3750 /**
3751 * Call darkness around the player or target monster
3752 */
effect_handler_DARKEN_AREA(effect_handler_context_t * context)3753 bool effect_handler_DARKEN_AREA(effect_handler_context_t *context)
3754 {
3755 struct loc target = player->grid;
3756 bool message = player->timed[TMD_BLIND] ? false : true;
3757 struct monster *t_mon = monster_target_monster(context);
3758 struct loc decoy = cave_find_decoy(cave);
3759 bool decoy_unseen = false;
3760
3761 /* Check for monster targeting another monster */
3762 if (t_mon) {
3763 char m_name[80];
3764 target = t_mon->grid;
3765 monster_desc(m_name, sizeof(m_name), t_mon, MDESC_TARG);
3766 if (message) {
3767 msg("Darkness surrounds %s.", m_name);
3768 message = false;
3769 }
3770 }
3771
3772 /* Check for decoy */
3773 if (!loc_is_zero(decoy)) {
3774 target = decoy;
3775 if (!los(cave, player->grid, decoy) ||
3776 player->timed[TMD_BLIND]) {
3777 decoy_unseen = true;
3778 }
3779 if (message && !decoy_unseen) {
3780 msg("Darkness surrounds the decoy.");
3781 message = false;
3782 }
3783 }
3784
3785 if (message) {
3786 msg("Darkness surrounds you.");
3787 }
3788
3789 /* Darken the room */
3790 light_room(target, false);
3791
3792 /* Hack - blind the player directly if player-cast */
3793 if (context->origin.what == SRC_PLAYER &&
3794 !player_resists(player, ELEM_DARK)) {
3795 (void)player_inc_timed(player, TMD_BLIND, 3 + randint1(5), true, true);
3796 }
3797
3798 /* Assume seen */
3799 context->ident = !decoy_unseen;
3800 return (true);
3801 }
3802
3803 /**
3804 * Project from the player's grid at the player, with full intensity out to
3805 * its radius
3806 * Affect the player (even when player-cast), grids, objects, and monsters
3807 */
effect_handler_SPOT(effect_handler_context_t * context)3808 bool effect_handler_SPOT(effect_handler_context_t *context)
3809 {
3810 struct loc pgrid = player->grid;
3811 int dam = effect_calculate_value(context, false);
3812 int rad = context->radius ? context->radius : 0;
3813
3814 int flg = PROJECT_STOP | PROJECT_PLAY | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL | PROJECT_SELF;
3815
3816 /* Handle increasing radius with player level */
3817 if (context->other && context->origin.what == SRC_PLAYER) {
3818 rad += player->lev / context->other;
3819 }
3820
3821 /* Aim at the target, explode */
3822 if (project(context->origin, rad, pgrid, dam, context->subtype, flg, 0,
3823 rad, NULL))
3824 context->ident = true;
3825
3826 return true;
3827 }
3828
3829 /**
3830 * Project from the player's grid, act as a ball, with full intensity out as
3831 * far as the given diameter
3832 * Affect grids, objects, and monsters
3833 */
effect_handler_SPHERE(effect_handler_context_t * context)3834 bool effect_handler_SPHERE(effect_handler_context_t *context)
3835 {
3836 struct loc pgrid = player->grid;
3837 int dam = effect_calculate_value(context, false);
3838 int rad = context->radius ? context->radius : 0;
3839 int diameter_of_source = context->other ? context->other : 0;
3840
3841 int flg = PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;
3842
3843 /* Aim at the target, explode */
3844 if (project(context->origin, rad, pgrid, dam, context->subtype, flg, 0,
3845 diameter_of_source, NULL))
3846 context->ident = true;
3847
3848 return true;
3849 }
3850
3851 /**
3852 * Cast a ball spell
3853 * Stop if we hit a monster or the player, act as a ball
3854 * Allow target mode to pass over monsters
3855 * Affect grids, objects, and monsters
3856 */
effect_handler_BALL(effect_handler_context_t * context)3857 bool effect_handler_BALL(effect_handler_context_t *context)
3858 {
3859 int dam = effect_calculate_value(context, true);
3860 int rad = context->radius ? context->radius : 2;
3861 struct loc target = loc(-1, -1);
3862
3863 int flg = PROJECT_THRU | PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;
3864
3865 /* Player or monster? */
3866 switch (context->origin.what) {
3867 case SRC_MONSTER: {
3868 struct monster *mon = cave_monster(cave, context->origin.which.monster);
3869 int conf_level, accuracy = 100;
3870 struct monster *t_mon = monster_target_monster(context);
3871
3872 assert(mon);
3873
3874 conf_level = monster_effect_level(mon, MON_TMD_CONF);
3875 while (conf_level) {
3876 accuracy *= (100 - CONF_RANDOM_CHANCE);
3877 accuracy /= 100;
3878 conf_level--;
3879 }
3880
3881 /* Powerful monster */
3882 if (monster_is_powerful(mon)) {
3883 rad++;
3884 }
3885
3886 flg |= PROJECT_PLAY;
3887 flg &= ~(PROJECT_STOP | PROJECT_THRU);
3888
3889 if (randint1(100) > accuracy) {
3890 /* Confused direction */
3891 int dir = randint1(9);
3892 target = loc_sum(mon->grid, ddgrid[dir]);
3893 } else if (t_mon) {
3894 /* Target monster */
3895 target = t_mon->grid;
3896 } else {
3897 /* Target player */
3898 struct loc decoy = cave_find_decoy(cave);
3899 if (!loc_is_zero(decoy)) {
3900 target = decoy;
3901 } else {
3902 target = player->grid;
3903 }
3904 }
3905
3906 break;
3907 }
3908
3909 case SRC_TRAP: {
3910 struct trap *trap = context->origin.which.trap;
3911 flg |= PROJECT_PLAY;
3912 target = trap->grid;
3913 break;
3914 }
3915
3916 case SRC_PLAYER:
3917 /* Ask for a target if no direction given */
3918 if (context->dir == DIR_TARGET && target_okay()) {
3919 flg &= ~(PROJECT_STOP | PROJECT_THRU);
3920 target_get(&target);
3921 } else {
3922 target = loc_sum(player->grid, ddgrid[context->dir]);
3923 }
3924
3925 if (context->other) rad += player->lev / context->other;
3926 break;
3927
3928 default:
3929 break;
3930 }
3931
3932 /* Aim at the target, explode */
3933 if (project(context->origin, rad, target, dam, context->subtype, flg, 0, 0, context->obj))
3934 context->ident = true;
3935
3936 return true;
3937 }
3938
3939
3940 /**
3941 * Breathe an element, in a cone from the breather
3942 * Affect grids, objects, and monsters
3943 * context->subtype is element, context->other degrees of arc
3944 * If context->radius is set it is radius of breath, but it usually isn't
3945 */
effect_handler_BREATH(effect_handler_context_t * context)3946 bool effect_handler_BREATH(effect_handler_context_t *context)
3947 {
3948 int dam = effect_calculate_value(context, false);
3949 int type = context->subtype;
3950
3951 struct loc target = loc(-1, -1);
3952
3953 /* Diameter of source starts at 4, so full strength up to 3 grids from
3954 * the breather. */
3955 int diameter_of_source = 4;
3956
3957 /* Minimum breath width is 20 degrees */
3958 int degrees_of_arc = MAX(context->other, 20);
3959
3960 int flg = PROJECT_ARC | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;
3961
3962 /* Distance breathed generally has no fixed limit. */
3963 int rad = context->radius ? context->radius : z_info->max_range;
3964
3965 /* Player or monster? */
3966 if (context->origin.what == SRC_MONSTER) {
3967 struct monster *mon = cave_monster(cave, context->origin.which.monster);
3968 struct monster *t_mon = monster_target_monster(context);
3969 int conf_level, accuracy = 100;
3970
3971 flg |= PROJECT_PLAY;
3972
3973 conf_level = monster_effect_level(mon, MON_TMD_CONF);
3974 while (conf_level) {
3975 accuracy *= (100 - CONF_RANDOM_CHANCE);
3976 accuracy /= 100;
3977 conf_level--;
3978 }
3979
3980 if (randint1(100) > accuracy) {
3981 /* Confused direction. */
3982 int dir = randint1(9);
3983
3984 target = loc_sum(mon->grid, ddgrid[dir]);
3985 } else if (t_mon) {
3986 /* Target monster. */
3987 target = t_mon->grid;
3988 } else {
3989 /* Target player. */
3990 struct loc decoy = cave_find_decoy(cave);
3991 if (!loc_is_zero(decoy)) {
3992 target = decoy;
3993 } else {
3994 target = player->grid;
3995 }
3996 }
3997
3998 dam = breath_dam(type, mon->hp);
3999
4000 /* Powerful monster */
4001 if (monster_is_powerful(mon)) {
4002 /* Breath is now full strength at 5 grids */
4003 diameter_of_source *= 3;
4004 diameter_of_source /= 2;
4005 }
4006 } else if (context->origin.what == SRC_PLAYER) {
4007 msgt(projections[type].msgt, "You breathe %s.", projections[type].desc);
4008
4009 /* Ask for a target if no direction given */
4010 if (context->dir == DIR_TARGET && target_okay()) {
4011 target_get(&target);
4012 } else {
4013 target = loc_sum(player->grid, ddgrid[context->dir]);
4014 }
4015 }
4016
4017 /* Adjust the diameter of the energy source */
4018 if (degrees_of_arc < 60) {
4019 /* Narrower cone means energy drops off less quickly. We now have:
4020 * - 30 degree regular breath | full strength at 5 grids
4021 * - 30 degree powerful breath | full strength at 9 grids
4022 * - 20 degree regular breath | full strength at 11 grids
4023 * - 20 degree powerful breath | full strength at 17 grids
4024 * where grids are measured from the breather. */
4025 diameter_of_source = diameter_of_source * 60 / degrees_of_arc;
4026
4027 /* Max */
4028 if (diameter_of_source > 25)
4029 diameter_of_source = 25;
4030 }
4031
4032 /* Breathe at the target */
4033 if (project(context->origin, rad, target, dam, type, flg, degrees_of_arc,
4034 diameter_of_source, context->obj))
4035 context->ident = true;
4036
4037 return true;
4038 }
4039
4040
4041 /**
4042 * Cast an arc-shaped spell. This is nothing more than a sphere spell
4043 * centered on the caster with a value for degrees_of_arc (how many degrees
4044 * wide the the arc is) that is not 360, essentially the same as a breath.
4045 * The direction given will be the center of the arc, which travels outwards
4046 * from the caster to a distance given by rad. -LM-
4047 *
4048 * Because all arcs start out as being one grid wide, arc spells with a
4049 * value for degrees_of_arc less than (roughly) 60 do not dissipate as
4050 * quickly.
4051 *
4052 * Affect grids, objects, and monsters
4053 * context->subtype is element, context->radius radius,
4054 * context->other degrees of arc (minimum 20)
4055 */
effect_handler_ARC(effect_handler_context_t * context)4056 bool effect_handler_ARC(effect_handler_context_t *context)
4057 {
4058 int dam = effect_calculate_value(context, true);
4059 int type = context->subtype;
4060 int rad = context->radius;
4061
4062 struct loc target = loc(-1, -1);
4063
4064 /* Diameter of source starts at 4, so full strength up to 3 grids from
4065 * the caster. */
4066 int diameter_of_source = 4;
4067
4068 /* Short beams now have their own effect, so we set a minimum arc width */
4069 int degrees_of_arc = MAX(context->other, 20);
4070
4071 int flg = PROJECT_ARC | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;
4072
4073 /* Radius of zero means no fixed limit. */
4074 if (rad == 0) {
4075 rad = z_info->max_range;
4076 }
4077
4078 /* Player or monster? */
4079 if (context->origin.what == SRC_MONSTER) {
4080 flg |= PROJECT_PLAY;
4081 target = player->grid;
4082 } else if (context->origin.what == SRC_PLAYER) {
4083 /* Ask for a target if no direction given */
4084 if (context->dir == DIR_TARGET && target_okay()) {
4085 target_get(&target);
4086 } else {
4087 target = loc_sum(player->grid, ddgrid[context->dir]);
4088 }
4089 }
4090
4091 /* Diameter of the energy source. */
4092 if (degrees_of_arc < 60) {
4093 diameter_of_source = diameter_of_source * 60 / degrees_of_arc;
4094 }
4095
4096 /* Max */
4097 if (diameter_of_source > 25) {
4098 diameter_of_source = 25;
4099 }
4100
4101 /* Aim at the target */
4102 if (project(context->origin, rad, target, dam, type, flg, degrees_of_arc,
4103 diameter_of_source, context->obj)) {
4104 context->ident = true;
4105 }
4106
4107 return true;
4108 }
4109
4110 /**
4111 * Cast an defined length beam spell.
4112 *
4113 * Affect grids, objects, and monsters
4114 * context->subtype is element, context->radius radius
4115 * context->other allows an added radius of 1 every time the player level
4116 * increases by a multiple of context->other, and will only take effect for
4117 * player spells
4118 */
effect_handler_SHORT_BEAM(effect_handler_context_t * context)4119 bool effect_handler_SHORT_BEAM(effect_handler_context_t *context)
4120 {
4121 int dam = effect_calculate_value(context, false);
4122 int type = context->subtype;
4123 bool addons = (context->origin.what == SRC_PLAYER) && (context->other > 0);
4124 int rad = context->radius + (addons ? player->lev / context->other : 0);
4125
4126 struct loc target = loc(-1, -1);
4127
4128 /* Diameter of source is the same as the radius, so the effect is
4129 * essentially full strength for its entire length. */
4130 int diameter_of_source = rad;
4131
4132 int flg = PROJECT_ARC | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;
4133
4134 /* Player or monster? */
4135 if (context->origin.what == SRC_MONSTER) {
4136 flg |= PROJECT_PLAY;
4137 target = player->grid;
4138 } else if (context->origin.what == SRC_PLAYER) {
4139 /* Ask for a target if no direction given */
4140 if (context->dir == DIR_TARGET && target_okay()) {
4141 target_get(&target);
4142 } else {
4143 target = loc_sum(player->grid, ddgrid[context->dir]);
4144 }
4145 }
4146
4147 /* Check bounds */
4148 if (diameter_of_source > 25) {
4149 diameter_of_source = 25;
4150 }
4151
4152 /* Aim at the target */
4153 if (project(context->origin, rad, target, dam, type, flg, 0,
4154 diameter_of_source, context->obj)) {
4155 context->ident = true;
4156 }
4157
4158 return true;
4159 }
4160
4161 /**
4162 * Crack a whip, or spit at the player; actually just a finite length beam
4163 * Affect grids, objects, and monsters
4164 * context->radius is length of beam
4165 */
effect_handler_LASH(effect_handler_context_t * context)4166 bool effect_handler_LASH(effect_handler_context_t *context)
4167 {
4168 int dam = effect_calculate_value(context, false);
4169 int rad = context->radius;
4170
4171 int flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL | PROJECT_ARC;
4172 int type;
4173
4174 struct loc target = loc(-1, -1);
4175
4176 /* Diameter of source is the same as the radius, so the effect is
4177 * essentially full strength for its entire length. */
4178 int diameter_of_source = rad;
4179
4180 /* Monsters only */
4181 if (context->origin.what == SRC_MONSTER) {
4182 struct monster *mon = cave_monster(cave, context->origin.which.monster);
4183 struct monster *t_mon = monster_target_monster(context);
4184 int i;
4185
4186 flg |= PROJECT_PLAY;
4187
4188 /* Target player or monster? */
4189 if (t_mon) {
4190 target = t_mon->grid;
4191 } else {
4192 struct loc decoy = cave_find_decoy(cave);
4193 if (!loc_is_zero(decoy)) {
4194 target = decoy;
4195 } else {
4196 target = player->grid;
4197 }
4198 }
4199
4200 /* Paranoia */
4201 if (rad > z_info->max_range) rad = z_info->max_range;
4202
4203 /* Get the type (default is PROJ_MISSILE) */
4204 type = mon->race->blow[0].effect->lash_type;
4205
4206 /* Scan through all blows for damage */
4207 for (i = 0; i < z_info->mon_blows_max; i++) {
4208 /* Extract the attack infomation */
4209 random_value dice = mon->race->blow[i].dice;
4210
4211 /* Full damage of first blow, plus half damage of others */
4212 dam += randcalc(dice, mon->race->level, RANDOMISE) / (i ? 2 : 1);
4213 if (!mon->race->blow[i].next) break;
4214 }
4215
4216 /* No damaging blows */
4217 if (!dam) return false;
4218 } else {
4219 return false;
4220 }
4221
4222 /* Check bounds */
4223 if (diameter_of_source > 25) {
4224 diameter_of_source = 25;
4225 }
4226
4227 /* Lash the target */
4228 if (project(context->origin, rad, target, dam, type, flg, 0,
4229 diameter_of_source, context->obj)) {
4230 context->ident = true;
4231 }
4232
4233 return true;
4234 }
4235
4236 /**
4237 * Cast multiple non-jumping ball spells at the same target.
4238 *
4239 * Targets absolute coordinates instead of a specific monster, so that
4240 * the death of the monster doesn't change the target's location.
4241 */
effect_handler_SWARM(effect_handler_context_t * context)4242 bool effect_handler_SWARM(effect_handler_context_t *context)
4243 {
4244 int dam = effect_calculate_value(context, true);
4245 int num = context->value.m_bonus;
4246
4247 struct loc target = loc_sum(player->grid, ddgrid[context->dir]);
4248
4249 int flg = PROJECT_THRU | PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;
4250
4251 /* Ask for a target if no direction given (early detonation) */
4252 if ((context->dir == DIR_TARGET) && target_okay()) {
4253 target_get(&target);
4254 }
4255
4256 while (num--) {
4257 /* Aim at the target. Hurt items on floor. */
4258 if (project(source_player(), context->radius, target, dam,
4259 context->subtype, flg, 0, 0, context->obj))
4260 context->ident = true;
4261 }
4262
4263 return true;
4264 }
4265
4266 /**
4267 * Strike the target with a ball from above
4268 */
effect_handler_STRIKE(effect_handler_context_t * context)4269 bool effect_handler_STRIKE(effect_handler_context_t *context)
4270 {
4271 int dam = effect_calculate_value(context, true);
4272 struct loc target = player->grid;
4273 int flg = PROJECT_JUMP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;
4274
4275 /* Ask for a target; if no direction given, the player is struck */
4276 if ((context->dir == DIR_TARGET) && target_okay()) {
4277 target_get(&target);
4278 }
4279
4280 /* Enforce line of sight */
4281 if (!projectable(cave, player->grid, target, PROJECT_NONE) ||
4282 !square_isknown(cave, target)) {
4283 return false;
4284 }
4285
4286 /* Aim at the target. Hurt items on floor. */
4287 if (project(source_player(), context->radius, target, dam, context->subtype,
4288 flg, 0, 0, context->obj)) {
4289 context->ident = true;
4290 }
4291
4292 return true;
4293 }
4294
4295 /**
4296 * Cast a line spell in every direction
4297 * Stop if we hit a monster, act as a ball
4298 * Affect grids, objects, and monsters
4299 */
effect_handler_STAR(effect_handler_context_t * context)4300 bool effect_handler_STAR(effect_handler_context_t *context)
4301 {
4302 int dam = effect_calculate_value(context, true);
4303 int i;
4304 struct loc target;
4305
4306 int flg = PROJECT_THRU | PROJECT_BEAM | PROJECT_GRID | PROJECT_KILL;
4307
4308 /* Describe */
4309 if (!player->timed[TMD_BLIND])
4310 msg("Light shoots in all directions!");
4311
4312 for (i = 0; i < 8; i++) {
4313 /* Use the current direction */
4314 target = loc_sum(player->grid, ddgrid_ddd[i]);
4315
4316 /* Aim at the target */
4317 if (project(source_player(), 0, target, dam, context->subtype, flg, 0,
4318 0, context->obj))
4319 context->ident = true;
4320 }
4321
4322 return true;
4323 }
4324
4325
4326 /**
4327 * Cast a ball spell in every direction
4328 * Stop if we hit a monster, act as a ball
4329 * Affect grids, objects, and monsters
4330 */
effect_handler_STAR_BALL(effect_handler_context_t * context)4331 bool effect_handler_STAR_BALL(effect_handler_context_t *context)
4332 {
4333 int dam = effect_calculate_value(context, true);
4334 int i;
4335 struct loc target;
4336
4337 int flg = PROJECT_STOP | PROJECT_THRU | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;
4338
4339 for (i = 0; i < 8; i++) {
4340 /* Use the current direction */
4341 target = loc_sum(player->grid, ddgrid_ddd[i]);
4342
4343 /* Aim at the target, explode */
4344 if (project(source_player(), context->radius, target, dam,
4345 context->subtype, flg, 0, 0, context->obj))
4346 context->ident = true;
4347 }
4348 return true;
4349 }
4350
4351 /**
4352 * Cast a bolt spell
4353 * Stop if we hit a monster, as a bolt
4354 * Affect monsters (not grids or objects)
4355 */
effect_handler_BOLT(effect_handler_context_t * context)4356 bool effect_handler_BOLT(effect_handler_context_t *context)
4357 {
4358 int dam = effect_calculate_value(context, true);
4359 int flg = PROJECT_STOP | PROJECT_KILL;
4360 (void) project_aimed(context->origin, context->subtype, context->dir, dam,
4361 flg, context->obj);
4362 if (!player->timed[TMD_BLIND])
4363 context->ident = true;
4364 return true;
4365 }
4366
4367 /**
4368 * Cast a beam spell
4369 * Pass through monsters, as a beam
4370 * Affect monsters (not grids or objects)
4371 */
effect_handler_BEAM(effect_handler_context_t * context)4372 bool effect_handler_BEAM(effect_handler_context_t *context)
4373 {
4374 int dam = effect_calculate_value(context, true);
4375 int flg = PROJECT_BEAM | PROJECT_KILL;
4376 (void) project_aimed(context->origin, context->subtype, context->dir, dam,
4377 flg, context->obj);
4378 if (!player->timed[TMD_BLIND])
4379 context->ident = true;
4380 return true;
4381 }
4382
4383 /**
4384 * Cast a bolt spell, or rarely, a beam spell
4385 * context->other is used as any adjustment to the regular beam chance
4386 */
effect_handler_BOLT_OR_BEAM(effect_handler_context_t * context)4387 bool effect_handler_BOLT_OR_BEAM(effect_handler_context_t *context)
4388 {
4389 int beam = context->beam + context->other;
4390
4391 if (randint0(100) < beam)
4392 return effect_handler_BEAM(context);
4393 else
4394 return effect_handler_BOLT(context);
4395 }
4396
4397 /**
4398 * Cast a line spell
4399 * Pass through monsters, as a beam
4400 * Affect monsters and grids (not objects)
4401 */
effect_handler_LINE(effect_handler_context_t * context)4402 bool effect_handler_LINE(effect_handler_context_t *context)
4403 {
4404 int dam = effect_calculate_value(context, true);
4405 int flg = PROJECT_BEAM | PROJECT_GRID | PROJECT_KILL;
4406 if (project_aimed(context->origin, context->subtype, context->dir, dam, flg, context->obj))
4407 context->ident = true;
4408 return true;
4409 }
4410
4411 /**
4412 * Cast an alter spell
4413 * Affect objects and grids (not monsters)
4414 */
effect_handler_ALTER(effect_handler_context_t * context)4415 bool effect_handler_ALTER(effect_handler_context_t *context)
4416 {
4417 int flg = PROJECT_BEAM | PROJECT_GRID | PROJECT_ITEM;
4418 if (project_aimed(context->origin, context->subtype, context->dir, 0, flg, context->obj))
4419 context->ident = true;
4420 return true;
4421 }
4422
4423 /**
4424 * Cast a bolt spell
4425 * Stop if we hit a monster, as a bolt
4426 * Affect monsters (not grids or objects)
4427 * Like BOLT, but only identifies on noticing an effect
4428 */
effect_handler_BOLT_STATUS(effect_handler_context_t * context)4429 bool effect_handler_BOLT_STATUS(effect_handler_context_t *context)
4430 {
4431 int dam = effect_calculate_value(context, true);
4432 int flg = PROJECT_STOP | PROJECT_KILL;
4433 if (project_aimed(context->origin, context->subtype, context->dir, dam, flg, context->obj))
4434 context->ident = true;
4435 return true;
4436 }
4437
4438 /**
4439 * Cast a bolt spell
4440 * Stop if we hit a monster, as a bolt
4441 * Affect monsters (not grids or objects)
4442 * The same as BOLT_STATUS, but done as a separate function to aid descriptions
4443 */
effect_handler_BOLT_STATUS_DAM(effect_handler_context_t * context)4444 bool effect_handler_BOLT_STATUS_DAM(effect_handler_context_t *context)
4445 {
4446 int dam = effect_calculate_value(context, true);
4447 int flg = PROJECT_STOP | PROJECT_KILL;
4448 if (project_aimed(context->origin, context->subtype, context->dir, dam, flg, context->obj))
4449 context->ident = true;
4450 return true;
4451 }
4452
4453 /**
4454 * Cast a bolt spell
4455 * Stop if we hit a monster, as a bolt
4456 * Affect monsters (not grids or objects)
4457 * Notice stuff based on awareness of the effect
4458 */
effect_handler_BOLT_AWARE(effect_handler_context_t * context)4459 bool effect_handler_BOLT_AWARE(effect_handler_context_t *context)
4460 {
4461 int dam = effect_calculate_value(context, true);
4462 int flg = PROJECT_STOP | PROJECT_KILL;
4463 if (context->aware) flg |= PROJECT_AWARE;
4464 if (project_aimed(context->origin, context->subtype, context->dir, dam, flg, context->obj))
4465 context->ident = true;
4466 return true;
4467 }
4468
4469 /**
4470 * Affect adjacent grids (radius 1 ball attack)
4471 */
effect_handler_TOUCH(effect_handler_context_t * context)4472 bool effect_handler_TOUCH(effect_handler_context_t *context)
4473 {
4474 int dam = effect_calculate_value(context, true);
4475 int rad = context->radius ? context->radius : 1;
4476
4477 if (context->origin.what == SRC_MONSTER) {
4478 struct monster *mon = cave_monster(cave, context->origin.which.monster);
4479 struct monster *t_mon = monster_target_monster(context);
4480 struct loc decoy = cave_find_decoy(cave);
4481
4482 /* Target decoy */
4483 if (decoy.y && decoy.x) {
4484 int flg = PROJECT_GRID | PROJECT_KILL | PROJECT_HIDE | PROJECT_ITEM | PROJECT_THRU;
4485 return (project(source_trap(square_trap(cave, decoy)),
4486 rad, decoy, dam, context->subtype,flg, 0, 0, context->obj));
4487 }
4488
4489 /* Monster cast at monster */
4490 if (t_mon) {
4491 int flg = PROJECT_GRID | PROJECT_KILL | PROJECT_HIDE | PROJECT_ITEM | PROJECT_THRU;
4492 return (project(source_monster(mon->target.midx), rad,
4493 t_mon->grid, dam, context->subtype,
4494 flg, 0, 0, context->obj));
4495 }
4496 }
4497
4498 if (project_touch(dam, rad, context->subtype, false, context->obj))
4499 context->ident = true;
4500 return true;
4501 }
4502
4503 /**
4504 * Affect adjacent grids (radius 1 ball attack)
4505 * Notice stuff based on awareness of the effect
4506 */
effect_handler_TOUCH_AWARE(effect_handler_context_t * context)4507 bool effect_handler_TOUCH_AWARE(effect_handler_context_t *context)
4508 {
4509 int dam = effect_calculate_value(context, true);
4510 int rad = context->radius ? context->radius : 1;
4511 if (project_touch(dam, rad, context->subtype, context->aware, context->obj))
4512 context->ident = true;
4513 return true;
4514 }
4515
4516 /**
4517 * Curse the player's armor
4518 */
effect_handler_CURSE_ARMOR(effect_handler_context_t * context)4519 bool effect_handler_CURSE_ARMOR(effect_handler_context_t *context)
4520 {
4521 struct object *obj;
4522
4523 char o_name[80];
4524
4525 /* Curse the body armor */
4526 obj = equipped_item_by_slot_name(player, "body");
4527
4528 /* Nothing to curse */
4529 if (!obj) return (true);
4530
4531 /* Describe */
4532 object_desc(o_name, sizeof(o_name), obj, ODESC_FULL);
4533
4534 /* Attempt a saving throw for artifacts */
4535 if (obj->artifact && (randint0(100) < 50)) {
4536 msg("A %s tries to %s, but your %s resists the effects!",
4537 "terrible black aura", "surround your armor", o_name);
4538 } else {
4539 int num = randint1(3);
4540 int max_tries = 20;
4541 msg("A terrible black aura blasts your %s!", o_name);
4542
4543 /* Take down bonus a wee bit */
4544 obj->to_a -= randint1(3);
4545
4546 /* Try to find enough appropriate curses */
4547 while (num && max_tries) {
4548 int pick = randint1(z_info->curse_max - 1);
4549 int power = 10 * m_bonus(9, player->depth);
4550 if (!curses[pick].poss[obj->tval]) {
4551 max_tries--;
4552 continue;
4553 }
4554 append_object_curse(obj, pick, power);
4555 num--;
4556 }
4557
4558 /* Recalculate bonuses */
4559 player->upkeep->update |= (PU_BONUS);
4560
4561 /* Recalculate mana */
4562 player->upkeep->update |= (PU_MANA);
4563
4564 /* Window stuff */
4565 player->upkeep->redraw |= (PR_INVEN | PR_EQUIP);
4566 }
4567
4568 context->ident = true;
4569
4570 return (true);
4571 }
4572
4573
4574 /**
4575 * Curse the player's weapon
4576 */
effect_handler_CURSE_WEAPON(effect_handler_context_t * context)4577 bool effect_handler_CURSE_WEAPON(effect_handler_context_t *context)
4578 {
4579 struct object *obj;
4580
4581 char o_name[80];
4582
4583 /* Curse the weapon */
4584 obj = equipped_item_by_slot_name(player, "weapon");
4585
4586 /* Nothing to curse */
4587 if (!obj) return (true);
4588
4589 /* Describe */
4590 object_desc(o_name, sizeof(o_name), obj, ODESC_FULL);
4591
4592 /* Attempt a saving throw */
4593 if (obj->artifact && (randint0(100) < 50)) {
4594 msg("A %s tries to %s, but your %s resists the effects!",
4595 "terrible black aura", "surround your weapon", o_name);
4596 } else {
4597 int num = randint1(3);
4598 int max_tries = 20;
4599 msg("A terrible black aura blasts your %s!", o_name);
4600
4601 /* Hurt it a bit */
4602 obj->to_h = 0 - randint1(3);
4603 obj->to_d = 0 - randint1(3);
4604
4605 /* Curse it */
4606 while (num) {
4607 int pick = randint1(z_info->curse_max - 1);
4608 int power = 10 * m_bonus(9, player->depth);
4609 if (!curses[pick].poss[obj->tval]) {
4610 max_tries--;
4611 continue;
4612 }
4613 append_object_curse(obj, pick, power);
4614 num--;
4615 }
4616
4617 /* Recalculate bonuses */
4618 player->upkeep->update |= (PU_BONUS);
4619
4620 /* Recalculate mana */
4621 player->upkeep->update |= (PU_MANA);
4622
4623 /* Window stuff */
4624 player->upkeep->redraw |= (PR_INVEN | PR_EQUIP);
4625 }
4626
4627 context->ident = true;
4628
4629 /* Notice */
4630 return (true);
4631 }
4632
4633
4634 /**
4635 * Brand the current weapon
4636 */
effect_handler_BRAND_WEAPON(effect_handler_context_t * context)4637 bool effect_handler_BRAND_WEAPON(effect_handler_context_t *context)
4638 {
4639 struct object *obj = equipped_item_by_slot_name(player, "weapon");
4640
4641 /* Select the brand */
4642 const char *brand = one_in_(2) ? "Flame" : "Frost";
4643
4644 /* Brand the weapon */
4645 brand_object(obj, brand);
4646
4647 context->ident = true;
4648 return true;
4649 }
4650
4651
4652 /**
4653 * Brand some (non-magical) ammo
4654 */
effect_handler_BRAND_AMMO(effect_handler_context_t * context)4655 bool effect_handler_BRAND_AMMO(effect_handler_context_t *context)
4656 {
4657 struct object *obj;
4658 const char *q, *s;
4659 int itemmode = (USE_INVEN | USE_QUIVER | USE_FLOOR);
4660 bool used = false;
4661
4662 /* Select the brand */
4663 const char *brand = one_in_(3) ? "Flame" : (one_in_(2) ? "Frost" : "Venom");
4664
4665 context->ident = true;
4666
4667 /* Get an item */
4668 q = "Brand which kind of ammunition? ";
4669 s = "You have nothing to brand.";
4670 if (context->cmd) {
4671 if (cmd_get_item(context->cmd, "tgtitem", &obj, q, s,
4672 item_tester_hook_ammo, itemmode)) {
4673 return used;
4674 }
4675 } else if (!get_item(&obj, q, s, 0, item_tester_hook_ammo, itemmode))
4676 return used;
4677
4678 /* Brand the ammo */
4679 brand_object(obj, brand);
4680
4681 /* Done */
4682 return (true);
4683 }
4684
4685 /**
4686 * Enchant some (non-magical) bolts
4687 */
effect_handler_BRAND_BOLTS(effect_handler_context_t * context)4688 bool effect_handler_BRAND_BOLTS(effect_handler_context_t *context)
4689 {
4690 struct object *obj;
4691 const char *q, *s;
4692 int itemmode = (USE_INVEN | USE_QUIVER | USE_FLOOR);
4693 bool used = false;
4694
4695 context->ident = true;
4696
4697 /* Get an item */
4698 q = "Brand which bolts? ";
4699 s = "You have no bolts to brand.";
4700 if (context->cmd) {
4701 if (cmd_get_item(context->cmd, "tgtitem", &obj, q, s,
4702 item_tester_hook_bolt, itemmode)) {
4703 return used;
4704 }
4705 } else if (!get_item(&obj, q, s, 0, item_tester_hook_bolt, itemmode))
4706 return used;
4707
4708 /* Brand the bolts */
4709 brand_object(obj, "Flame");
4710
4711 /* Done */
4712 return (true);
4713 }
4714
4715
4716 /**
4717 * Turn a staff into arrows
4718 */
effect_handler_CREATE_ARROWS(effect_handler_context_t * context)4719 bool effect_handler_CREATE_ARROWS(effect_handler_context_t *context)
4720 {
4721 int lev;
4722 struct object *obj, *staff, *arrows;
4723 const char *q, *s;
4724 int itemmode = (USE_INVEN | USE_FLOOR);
4725 bool good = false, great = false;
4726 bool none_left = false;
4727
4728 /* Get an item */
4729 q = "Make arrows from which staff? ";
4730 s = "You have no staff to use.";
4731 if (context->cmd) {
4732 if (cmd_get_item(context->cmd, "tgtitem", &obj, q, s,
4733 item_tester_hook_staff, itemmode)) {
4734 return false;
4735 }
4736 } else if (!get_item(&obj, q, s, 0, item_tester_hook_staff,
4737 itemmode)) {
4738 return false;
4739 }
4740
4741 /* Extract the object "level" */
4742 lev = obj->kind->level;
4743
4744 /* Roll for good */
4745 if (randint1(lev) > 25) {
4746 good = true;
4747 /* Roll for great */
4748 if (randint1(lev) > 50) {
4749 great = true;
4750 }
4751 }
4752
4753 /* Destroy the staff */
4754 if (object_is_carried(player, obj)) {
4755 staff = gear_object_for_use(obj, 1, true, &none_left);
4756 } else {
4757 staff = floor_object_for_use(obj, 1, true, &none_left);
4758 }
4759
4760 if (staff->known) {
4761 object_delete(&staff->known);
4762 }
4763 object_delete(&staff);
4764
4765 /* Make some arrows */
4766 arrows = make_object(cave, player->lev, good, great, false, NULL, TV_ARROW);
4767 drop_near(cave, &arrows, 0, player->grid, true, true);
4768
4769 return true;
4770 }
4771
4772 /**
4773 * Draw energy from a magical device
4774 */
effect_handler_TAP_DEVICE(effect_handler_context_t * context)4775 bool effect_handler_TAP_DEVICE(effect_handler_context_t *context)
4776 {
4777 int lev;
4778 int energy = 0;
4779 struct object *obj;
4780 bool used = false;
4781 int itemmode = (USE_INVEN | USE_FLOOR);
4782 const char *q, *s;
4783 char *item = "";
4784
4785 /* Get an item */
4786 q = "Drain charges from which item? ";
4787 s = "You have nothing to drain charges from.";
4788 if (context->cmd) {
4789 if (cmd_get_item(context->cmd, "tgtitem", &obj, q, s,
4790 item_tester_hook_recharge, itemmode)) {
4791 return used;
4792 }
4793 } else if (!get_item(&obj, q, s, 0, item_tester_hook_recharge,
4794 itemmode)) {
4795 return (used);
4796 }
4797
4798 /* Extract the object "level" */
4799 lev = obj->kind->level;
4800
4801 /* Extract the object's energy and get its generic name. */
4802 if (tval_is_staff(obj)) {
4803 energy = (5 + lev) * 3 * obj->pval / 2;
4804 item = "staff";
4805 } else if (tval_is_wand(obj)) {
4806 energy = (5 + lev) * 3 * obj->pval / 2;
4807 item = "wand";
4808 }
4809
4810 /* Turn energy into mana. */
4811 if (energy < 36) {
4812 /* Require a resonable amount of energy */
4813 msg("That %s had no useable energy", item);
4814 } else {
4815 /* If mana below maximum, increase mana and drain object. */
4816 if (player->csp < player->msp) {
4817 /* Drain the object. */
4818 obj->pval = 0;
4819
4820
4821 /* Combine / Reorder the pack (later) */
4822 player->upkeep->notice |= (PN_COMBINE);
4823
4824 /* Redraw stuff */
4825 player->upkeep->redraw |= (PR_INVEN);
4826
4827 /* Increase mana. */
4828 player->csp += energy / 6;
4829 player->csp_frac = 0;
4830 if (player->csp > player->msp) {
4831 (player->csp = player->msp);
4832 }
4833
4834 msg("You feel your head clear.");
4835 used = true;
4836
4837 player->upkeep->redraw |= (PR_MANA);
4838 } else {
4839 char *cap = string_make(item);
4840 my_strcap(cap);
4841 msg("Your mana was already at its maximum. %s not drained.", cap);
4842 string_free(cap);
4843 }
4844 }
4845
4846 return (used);
4847 }
4848
4849 /**
4850 * Draw energy from a nearby undead
4851 */
effect_handler_TAP_UNLIFE(effect_handler_context_t * context)4852 bool effect_handler_TAP_UNLIFE(effect_handler_context_t *context)
4853 {
4854 int amount = effect_calculate_value(context, false);
4855 struct loc target;
4856 struct monster *mon = NULL;
4857 char m_name[80];
4858 int drain = 0;
4859 bool fear = false;
4860 bool dead = false;
4861
4862 context->ident = true;
4863
4864 /* Closest living monster */
4865 if (!target_set_closest(TARGET_KILL, monster_is_undead)) {
4866 return false;
4867 }
4868 target_get(&target);
4869 mon = target_get_monster();
4870
4871 /* Hurt the monster */
4872 monster_desc(m_name, sizeof(m_name), mon, MDESC_TARG);
4873 msg("You draw power from the %s.", m_name);
4874 drain = MIN(mon->hp, amount) / 4;
4875 dead = mon_take_hit(mon, amount, &fear, " is destroyed!");
4876
4877 /* Gain mana */
4878 effect_simple(EF_RESTORE_MANA, context->origin, format("%d", drain), 0, 0,
4879 0, 0, 0, NULL);
4880
4881 /* Handle fear for surviving monsters */
4882 if (!dead && monster_is_visible(mon)) {
4883 message_pain(mon, amount);
4884 if (fear) {
4885 add_monster_message(mon, MON_MSG_FLEE_IN_TERROR, true);
4886 }
4887 }
4888
4889 return true;
4890 }
4891
4892 /**
4893 * Perform a player shapechange
4894 */
effect_handler_SHAPECHANGE(effect_handler_context_t * context)4895 bool effect_handler_SHAPECHANGE(effect_handler_context_t *context)
4896 {
4897 struct player_shape *shape = player_shape_by_idx(context->subtype);
4898 bool ident = false;
4899
4900 assert(shape);
4901
4902 /* Change shape */
4903 player->shape = lookup_player_shape(shape->name);
4904 msg("You assume the shape of a %s!", shape->name);
4905 msg("Your gear merges into your body.");
4906
4907 /* Do effect */
4908 if (shape->effect) {
4909 (void) effect_do(shape->effect, source_player(), NULL, &ident, true,
4910 0, 0, 0, NULL);
4911 }
4912
4913 /* Update */
4914 shape_learn_on_assume(player, shape->name);
4915 player->upkeep->update |= (PU_BONUS);
4916 player->upkeep->redraw |= (PR_TITLE | PR_MISC);
4917 handle_stuff(player);
4918
4919 return true;
4920 }
4921
4922 /**
4923 * Curse a monster for direct damage
4924 */
effect_handler_CURSE(effect_handler_context_t * context)4925 bool effect_handler_CURSE(effect_handler_context_t *context)
4926 {
4927 int dam = effect_calculate_value(context, false);
4928 struct monster *mon = target_get_monster();
4929 bool fear = false;
4930 bool dead = false;
4931
4932 context->ident = true;
4933
4934 /* Need to choose a monster, not just point */
4935 if (!mon) {
4936 msg("No monster selected!");
4937 return false;
4938 }
4939
4940 /* Hit it */
4941 dead = mon_take_hit(mon, dam, &fear, " dies!");
4942
4943 /* Handle fear for surviving monsters */
4944 if (!dead && monster_is_visible(mon)) {
4945 message_pain(mon, dam);
4946 if (fear) {
4947 add_monster_message(mon, MON_MSG_FLEE_IN_TERROR, true);
4948 }
4949 }
4950
4951 return true;
4952 }
4953
4954 /**
4955 * Take control of a monster
4956 */
effect_handler_COMMAND(effect_handler_context_t * context)4957 bool effect_handler_COMMAND(effect_handler_context_t *context)
4958 {
4959 int amount = effect_calculate_value(context, false);
4960 struct monster *mon = target_get_monster();
4961
4962 context->ident = true;
4963
4964 /* Need to choose a monster, not just point */
4965 if (!mon) {
4966 msg("No monster selected!");
4967 return false;
4968 }
4969
4970 /* Wake up, become aware */
4971 monster_wake(mon, false, 100);
4972
4973 /* Explicit saving throw */
4974 if (randint1(player->lev) < randint1(mon->race->level)) {
4975 char m_name[80];
4976 monster_desc(m_name, sizeof(m_name), mon, MDESC_STANDARD);
4977 msg("%s resists your command!", m_name);
4978 return false;
4979 }
4980
4981 /* Player is commanding */
4982 player_set_timed(player, TMD_COMMAND, MAX(amount, 0), false);
4983
4984 /* Monster is commanded */
4985 mon_inc_timed(mon, MON_TMD_COMMAND, MAX(amount, 0), 0);
4986
4987 return true;
4988 }
4989
4990 /**
4991 * Jump next to a living monster and draw hitpoints and nourishment from it
4992 */
effect_handler_JUMP_AND_BITE(effect_handler_context_t * context)4993 bool effect_handler_JUMP_AND_BITE(effect_handler_context_t *context)
4994 {
4995 int amount = effect_calculate_value(context, false);
4996 struct loc victim, grid;
4997 int d, first_d = randint0(8);
4998 struct monster *mon = NULL;
4999 char m_name[80];
5000 int drain = 0;
5001 bool fear = false;
5002 bool dead = false;
5003
5004 context->ident = true;
5005
5006 /* Closest living monster */
5007 if (!target_set_closest(TARGET_KILL, monster_is_living)) {
5008 return false;
5009 }
5010 target_get(&victim);
5011 mon = target_get_monster();
5012 monster_desc(m_name, sizeof(m_name), mon, MDESC_TARG);
5013
5014 /* Look next to the monster */
5015 for (d = first_d; d < first_d + 8; d++) {
5016 grid = loc_sum(victim, ddgrid_ddd[d % 8]);
5017 if (square_isplayertrap(cave, grid)) continue;
5018 if (square_iswebbed(cave, grid)) continue;
5019 if (square_isopen(cave, grid)) break;
5020 }
5021
5022 /* Needed to be adjacent */
5023 if (d == first_d + 8) {
5024 msg("Not enough room next to %s!", m_name);
5025 return false;
5026 }
5027
5028 /* Sound */
5029 sound(MSG_TELEPORT);
5030
5031 /* Move player */
5032 monster_swap(player->grid, grid);
5033
5034 /* Now bite it */
5035 drain = MIN(mon->hp, amount);
5036 if (drain == 0) return true;
5037 if (OPT(player, show_damage)) {
5038 msg("You bite %s. (%d)", m_name, drain);
5039 } else {
5040 msg("You bite %s.", m_name);
5041 }
5042 dead = mon_take_hit(mon, amount, &fear, " is drained dry!");
5043
5044 /* Heal and nourish */
5045 effect_simple(EF_HEAL_HP, context->origin, format("%d", drain), 0, 0, 0,
5046 0, 0, NULL);
5047 player_inc_timed(player, TMD_FOOD, MAX(drain, 0), false, false);
5048
5049 /* Handle fear for surviving monsters */
5050 if (!dead && monster_is_visible(mon)) {
5051 message_pain(mon, amount);
5052 if (fear) {
5053 add_monster_message(mon, MON_MSG_FLEE_IN_TERROR, true);
5054 }
5055 }
5056
5057 return true;
5058 }
5059
5060 /**
5061 * Move up to 4 spaces then do melee blows.
5062 * Could vary the length of the move without much work.
5063 */
effect_handler_MOVE_ATTACK(effect_handler_context_t * context)5064 bool effect_handler_MOVE_ATTACK(effect_handler_context_t *context)
5065 {
5066 int blows = effect_calculate_value(context, false);
5067 int moves = 4;
5068 int d, i;
5069 struct loc target = player->grid;
5070 struct loc next_grid, grid_diff;
5071 bool fear;
5072 struct monster *mon;
5073
5074 /* Ask for a target */
5075 if (context->dir == DIR_TARGET) {
5076 target_get(&target);
5077 } else {
5078 target = loc_sum(player->grid, ddgrid[context->dir]);
5079 }
5080
5081 mon = square_monster(cave, target);
5082 if (mon == NULL || !monster_is_obvious(mon)) {
5083 msg("This spell must target a monster.");
5084 return false;
5085 }
5086
5087 while (distance(player->grid, target) > 1 && moves > 0) {
5088 int choice[] = { 0, 1, -1 };
5089 bool attack = false;
5090 grid_diff = loc_diff(target, player->grid);
5091
5092 /* Choice of direction simplified by prioritizing diagonals */
5093 if (grid_diff.x == 0) {
5094 d = (grid_diff.y < 0) ? 0 : 4; /* up : down */
5095 } else if (grid_diff.y == 0) {
5096 d = (grid_diff.x < 0) ? 6 : 2; /* left : right */
5097 } else if (grid_diff.x < 0) {
5098 d = (grid_diff.y < 0) ? 7 : 5; /* up-left : down-left */
5099 } else {/* grid_diff.x > 0 */
5100 d = (grid_diff.y < 0) ? 1 : 3; /* up-right : down-right */
5101 }
5102
5103 /* We'll give up to 3 choices: d, d + 1, d - 1 */
5104 for (i = 0; i < 3; i++) {
5105 int d_test = (d + choice[i] + 8) % 8;
5106 next_grid = loc_sum(player->grid, clockwise_grid[d_test]);
5107 if (square_ispassable(cave, next_grid)) {
5108 d = d_test;
5109 if (square_monster(cave, next_grid)) attack = true;
5110 break;
5111 } else if (i == 2) {
5112 msg("The way is barred.");
5113 return moves != 4;
5114 }
5115 }
5116
5117 move_player(clockwise_ddd[d], false);
5118 moves--;
5119 if (attack) return false;
5120 }
5121
5122 /* Reduce blows based on distance traveled, round to nearest blow */
5123 blows = (blows * moves + 2) / 4;
5124
5125 /* Should return some energy if monster dies early */
5126 while (blows-- > 0) {
5127 if (py_attack_real(player, target, &fear)) break;
5128 }
5129
5130 return true;
5131 }
5132
5133 /**
5134 * Enter single combat with an enemy
5135 */
effect_handler_SINGLE_COMBAT(effect_handler_context_t * context)5136 bool effect_handler_SINGLE_COMBAT(effect_handler_context_t *context)
5137 {
5138 struct monster *mon = target_get_monster();
5139 context->ident = true;
5140
5141 /* Already in an arena */
5142 if (player->upkeep->arena_level) {
5143 msg("You are already in single combat!");
5144 return false;
5145 }
5146
5147 /* Need to choose a monster, not just point */
5148 if (mon) {
5149 int old_idx = mon->midx;
5150
5151 /* Monsters with high spell power can resist */
5152 if (randint0(mon->race->spell_power) > player->lev) {
5153 char m_name[80];
5154 monster_desc(m_name, sizeof(m_name), mon, MDESC_CAPITAL);
5155 msg("%s resists!", m_name);
5156 return true;
5157 }
5158
5159 /* Swap the targeted monster with the first in the monster list */
5160 if (old_idx == 1) {
5161 /* Do nothing */
5162 ;
5163 } else if (cave_monster(cave, 1)->race) {
5164 monster_index_move(old_idx, cave_monster_max(cave));
5165 monster_index_move(1, old_idx);
5166 monster_index_move(cave_monster_max(cave), 1);
5167 } else {
5168 monster_index_move(old_idx, 1);
5169 }
5170 target_set_monster(cave_monster(cave, 1));
5171 } else {
5172 msg("No monster selected!");
5173 return false;
5174 }
5175
5176 /* Head to the arena */
5177 player->upkeep->arena_level = true;
5178 dungeon_change_level(player, player->depth);
5179 return true;
5180 }
5181
5182
effect_handler_MELEE_BLOWS(effect_handler_context_t * context)5183 bool effect_handler_MELEE_BLOWS(effect_handler_context_t *context)
5184 {
5185 int blows = effect_calculate_value(context, false);
5186 int dam = context->radius;
5187 bool fear;
5188 int taim;
5189 struct loc target = loc(-1, -1);
5190 struct loc grid = player->grid;
5191 struct monster *mon = NULL;
5192
5193 /* players only for now */
5194 if (context->origin.what != SRC_PLAYER)
5195 return false;
5196
5197 /* Ask for a target if no direction given */
5198 if (context->dir == DIR_TARGET && target_okay()) {
5199 target_get(&target);
5200 } else {
5201 target = loc_sum(player->grid, ddgrid[context->dir]);
5202 }
5203
5204 /* Check target validity */
5205 taim = distance(grid, target);
5206 mon = square_monster(cave, target);
5207 if (taim > 1) {
5208 msgt(MSG_GENERIC, "Target too far away (%d).", taim);
5209 return false;
5210 } else if (!mon) {
5211 msg("You must attack a monster.");
5212 return false;
5213 }
5214
5215 while ((blows-- > 0) && mon) {
5216 /* Test for damaging the monster */
5217 int hp = mon->hp;
5218 if (py_attack_real(player, target, &fear)) return true;
5219 /*mon = square_monster(cave, target); */
5220 if (mon && (mon->hp == hp)) continue;
5221
5222 /* Apply side-effects */
5223 if (project(context->origin, 0, target, dam, context->subtype,
5224 PROJECT_KILL, 0, 0, context->obj)) {
5225 context->ident = true;
5226 }
5227 }
5228 return true;
5229 }
5230
effect_handler_SWEEP(effect_handler_context_t * context)5231 bool effect_handler_SWEEP(effect_handler_context_t *context)
5232 {
5233 int blows = effect_calculate_value(context, false);
5234 bool fear;
5235 int i;
5236 struct loc target;
5237
5238 /* Players only for now */
5239 if (context->origin.what != SRC_PLAYER) return false;
5240
5241 /* Doing these like >1 blows means spinning around multiple times. */
5242 while (blows-- > 0) {
5243 for (i = 0; i < 8; i++) {
5244 target = loc_sum(player->grid, clockwise_grid[i]);
5245 if (square_monster(cave, target) != NULL)
5246 py_attack_real(player, target, &fear);
5247 }
5248 }
5249
5250 /* Should return some energy if all enemies killed and blows remain? */
5251 return true;
5252 }
5253
5254
5255
5256 /**
5257 * One Ring activation
5258 */
effect_handler_BIZARRE(effect_handler_context_t * context)5259 bool effect_handler_BIZARRE(effect_handler_context_t *context)
5260 {
5261 context->ident = true;
5262
5263 /* Pick a random effect */
5264 switch (randint1(10))
5265 {
5266 case 1:
5267 case 2:
5268 {
5269 /* Message */
5270 msg("You are surrounded by a malignant aura.");
5271
5272 /* Decrease all stats (permanently) */
5273 player_stat_dec(player, STAT_STR, true);
5274 player_stat_dec(player, STAT_INT, true);
5275 player_stat_dec(player, STAT_WIS, true);
5276 player_stat_dec(player, STAT_DEX, true);
5277 player_stat_dec(player, STAT_CON, true);
5278
5279 /* Lose some experience (permanently) */
5280 player_exp_lose(player, player->exp / 4, true);
5281
5282 return true;
5283 }
5284
5285 case 3:
5286 {
5287 /* Message */
5288 msg("You are surrounded by a powerful aura.");
5289
5290 /* Dispel monsters */
5291 effect_simple(EF_PROJECT_LOS, context->origin, "1000", PROJ_DISP_ALL, 0, 0, 0, 0, NULL);
5292
5293 return true;
5294 }
5295
5296 case 4:
5297 case 5:
5298 case 6:
5299 {
5300 /* Mana Ball */
5301 int flg = PROJECT_THRU | PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;
5302 struct loc target = loc_sum(player->grid, ddgrid[context->dir]);
5303
5304 /* Ask for a target if no direction given */
5305 if ((context->dir == DIR_TARGET) && target_okay()) {
5306 flg &= ~(PROJECT_STOP | PROJECT_THRU);
5307
5308 target_get(&target);
5309 }
5310
5311 /* Aim at the target, explode */
5312 return (project(source_player(), 3, target, 300, PROJ_MANA, flg, 0,
5313 0, context->obj));
5314 }
5315
5316 case 7:
5317 case 8:
5318 case 9:
5319 case 10:
5320 {
5321 /* Mana Bolt */
5322 int flg = PROJECT_STOP | PROJECT_KILL | PROJECT_THRU;
5323 struct loc target = loc_sum(player->grid, ddgrid[context->dir]);
5324
5325 /* Use an actual target */
5326 if ((context->dir == DIR_TARGET) && target_okay())
5327 target_get(&target);
5328
5329 /* Aim at the target, do NOT explode */
5330 return project(source_player(), 0, target, 250, PROJ_MANA, flg, 0,
5331 0, context->obj);
5332 }
5333 }
5334
5335 return false;
5336 }
5337
5338 /**
5339 * The "wonder" effect.
5340 *
5341 * This spell should become more useful (more
5342 * controlled) as the player gains experience levels.
5343 * Thus, add 1/5 of the player's level to the die roll.
5344 * This eliminates the worst effects later on, while
5345 * keeping the results quite random. It also allows
5346 * some potent effects only at high level
5347 */
effect_handler_WONDER(effect_handler_context_t * context)5348 bool effect_handler_WONDER(effect_handler_context_t *context)
5349 {
5350 int plev = player->lev;
5351 int die = effect_calculate_value(context, false);
5352 int subtype = 0, radius = 0, other = 0, y = 0, x = 0;
5353 int beam = context->beam;
5354 effect_handler_f handler = NULL;
5355 random_value value = { 0, 0, 0, 0 };
5356
5357 context->ident = true;
5358
5359 if (die > 100)
5360 msg("You feel a surge of power!");
5361
5362 if (die < 8) {
5363 subtype = PROJ_MON_CLONE;
5364 handler = effect_handler_BOLT;
5365 } else if (die < 14) {
5366 subtype = PROJ_MON_SPEED;
5367 value.base = 100;
5368 handler = effect_handler_BOLT;
5369 } else if (die < 26) {
5370 subtype = PROJ_MON_HEAL;
5371 value.dice = 4;
5372 value.sides = 6;
5373 handler = effect_handler_BOLT;
5374 } else if (die < 31) {
5375 subtype = PROJ_MON_POLY;
5376 value.base = plev;
5377 handler = effect_handler_BOLT;
5378 } else if (die < 36) {
5379 beam -= 10;
5380 subtype = PROJ_MISSILE;
5381 value.dice = 3 + ((plev - 1) / 5);
5382 value.sides = 4;
5383 handler = effect_handler_BOLT_OR_BEAM;
5384 } else if (die < 41) {
5385 subtype = PROJ_MON_CONF;
5386 value.base = plev;
5387 handler = effect_handler_BOLT;
5388 } else if (die < 46) {
5389 subtype = PROJ_POIS;
5390 value.base = 20 + plev / 2;
5391 radius = 3;
5392 handler = effect_handler_BALL;
5393 } else if (die < 51) {
5394 subtype = PROJ_LIGHT_WEAK;
5395 value.dice = 6;
5396 value.sides = 8;
5397 handler = effect_handler_LINE;
5398 } else if (die < 56) {
5399 subtype = PROJ_ELEC;
5400 value.dice = 3 + ((plev - 5) / 6);
5401 value.sides = 6;
5402 handler = effect_handler_BEAM;
5403 } else if (die < 61) {
5404 beam -= 10;
5405 subtype = PROJ_COLD;
5406 value.dice = 5 + ((plev - 5) / 4);
5407 value.sides = 8;
5408 handler = effect_handler_BOLT_OR_BEAM;
5409 } else if (die < 66) {
5410 subtype = PROJ_ACID;
5411 value.dice = 6 + ((plev - 5) / 4);
5412 value.sides = 8;
5413 handler = effect_handler_BOLT_OR_BEAM;
5414 } else if (die < 71) {
5415 subtype = PROJ_FIRE;
5416 value.dice = 8 + ((plev - 5) / 4);
5417 value.sides = 8;
5418 handler = effect_handler_BOLT_OR_BEAM;
5419 } else if (die < 76) {
5420 subtype = PROJ_MON_DRAIN;
5421 value.base = 75;
5422 handler = effect_handler_BOLT;
5423 } else if (die < 81) {
5424 subtype = PROJ_ELEC;
5425 value.base = 30 + plev / 2;
5426 radius = 2;
5427 handler = effect_handler_BALL;
5428 } else if (die < 86) {
5429 subtype = PROJ_ACID;
5430 value.base = 40 + plev;
5431 radius = 2;
5432 handler = effect_handler_BALL;
5433 } else if (die < 91) {
5434 subtype = PROJ_ICE;
5435 value.base = 70 + plev;
5436 radius = 3;
5437 handler = effect_handler_BALL;
5438 } else if (die < 96) {
5439 subtype = PROJ_FIRE;
5440 value.base = 80 + plev;
5441 radius = 3;
5442 handler = effect_handler_BALL;
5443 } else if (die < 101) {
5444 subtype = PROJ_MON_DRAIN;
5445 value.base = 100 + plev;
5446 handler = effect_handler_BOLT;
5447 } else if (die < 104) {
5448 radius = 12;
5449 handler = effect_handler_EARTHQUAKE;
5450 } else if (die < 106) {
5451 radius = 15;
5452 handler = effect_handler_DESTRUCTION;
5453 } else if (die < 108) {
5454 handler = effect_handler_BANISH;
5455 } else if (die < 110) {
5456 subtype = PROJ_DISP_ALL;
5457 value.base = 120;
5458 handler = effect_handler_PROJECT_LOS;
5459 }
5460
5461 if (handler != NULL) {
5462 effect_handler_context_t new_context = {
5463 context->effect,
5464 context->origin,
5465 context->obj,
5466 context->aware,
5467 context->dir,
5468 beam,
5469 context->boost,
5470 value,
5471 subtype, radius, other, y, x,
5472 NULL,
5473 context->ident,
5474 context->cmd
5475 };
5476
5477 return handler(&new_context);
5478 } else {
5479 /* RARE */
5480 effect_simple(EF_PROJECT_LOS, context->origin, "150", PROJ_DISP_ALL, 0, 0, 0, 0, NULL);
5481 effect_simple(EF_PROJECT_LOS, context->origin, "20", PROJ_MON_SLOW, 0, 0, 0, 0, NULL);
5482 effect_simple(EF_PROJECT_LOS, context->origin, "40", PROJ_SLEEP_ALL, 0, 0, 0, 0, NULL);
5483 effect_simple(EF_HEAL_HP, context->origin, "300", 0, 0, 0, 0, 0, NULL);
5484
5485 return true;
5486 }
5487 }
5488
5489
5490 /**
5491 * ------------------------------------------------------------------------
5492 * Properties of effects
5493 * ------------------------------------------------------------------------ */
5494 /**
5495 * Useful things about effects.
5496 */
5497 static const struct effect_kind effects[] =
5498 {
5499 { EF_NONE, false, NULL, NULL, NULL },
5500 #define F(x) effect_handler_##x
5501 #define EFFECT(x, a, b, c, d, e) { EF_##x, a, b, F(x), e },
5502 #include "list-effects.h"
5503 #undef EFFECT
5504 #undef F
5505 { EF_MAX, false, NULL, NULL, NULL }
5506 };
5507
5508
5509 static const char *effect_names[] = {
5510 NULL,
5511 #define EFFECT(x, a, b, c, d, e) #x,
5512 #include "list-effects.h"
5513 #undef EFFECT
5514 };
5515
5516 /*
5517 * Utility functions
5518 */
5519
5520 /**
5521 * Free all the effects in a structure
5522 *
5523 * \param source the effects being freed
5524 */
free_effect(struct effect * source)5525 void free_effect(struct effect *source)
5526 {
5527 struct effect *e = source, *e_next;
5528 while (e) {
5529 e_next = e->next;
5530 dice_free(e->dice);
5531 if (e->msg) {
5532 string_free(e->msg);
5533 }
5534 mem_free(e);
5535 e = e_next;
5536 }
5537 }
5538
effect_valid(const struct effect * effect)5539 bool effect_valid(const struct effect *effect)
5540 {
5541 if (!effect) return false;
5542 return effect->index > EF_NONE && effect->index < EF_MAX;
5543 }
5544
effect_aim(const struct effect * effect)5545 bool effect_aim(const struct effect *effect)
5546 {
5547 const struct effect *e = effect;
5548
5549 if (!effect_valid(effect))
5550 return false;
5551
5552 while (e) {
5553 if (effects[e->index].aim) return true;
5554 e = e->next;
5555 }
5556
5557 return false;
5558 }
5559
effect_info(const struct effect * effect)5560 const char *effect_info(const struct effect *effect)
5561 {
5562 if (!effect_valid(effect))
5563 return NULL;
5564
5565 return effects[effect->index].info;
5566 }
5567
effect_desc(const struct effect * effect)5568 const char *effect_desc(const struct effect *effect)
5569 {
5570 if (!effect_valid(effect))
5571 return NULL;
5572
5573 return effects[effect->index].desc;
5574 }
5575
effect_lookup(const char * name)5576 effect_index effect_lookup(const char *name)
5577 {
5578 size_t i;
5579
5580 for (i = 0; i < N_ELEMENTS(effect_names); i++) {
5581 const char *effect_name = effect_names[i];
5582
5583 /* Test for equality */
5584 if (effect_name != NULL && streq(name, effect_name))
5585 return i;
5586 }
5587
5588 return EF_MAX;
5589 }
5590
5591 /**
5592 * Translate a string to an effect parameter subtype index
5593 */
effect_subtype(int index,const char * type)5594 int effect_subtype(int index, const char *type)
5595 {
5596 int val = -1;
5597
5598 /* If not a numerical value, assign according to effect index */
5599 if (sscanf(type, "%d", &val) != 1) {
5600 switch (index) {
5601 /* Projection name */
5602 case EF_PROJECT_LOS:
5603 case EF_PROJECT_LOS_AWARE:
5604 case EF_DESTRUCTION:
5605 case EF_SPOT:
5606 case EF_SPHERE:
5607 case EF_BALL:
5608 case EF_BREATH:
5609 case EF_ARC:
5610 case EF_SHORT_BEAM:
5611 case EF_LASH:
5612 case EF_SWARM:
5613 case EF_STRIKE:
5614 case EF_STAR:
5615 case EF_STAR_BALL:
5616 case EF_BOLT:
5617 case EF_BEAM:
5618 case EF_BOLT_OR_BEAM:
5619 case EF_LINE:
5620 case EF_ALTER:
5621 case EF_BOLT_STATUS:
5622 case EF_BOLT_STATUS_DAM:
5623 case EF_BOLT_AWARE:
5624 case EF_MELEE_BLOWS:
5625 case EF_TOUCH:
5626 case EF_TOUCH_AWARE: {
5627 val = proj_name_to_idx(type);
5628 break;
5629 }
5630
5631 /* Timed effect name */
5632 case EF_CURE:
5633 case EF_TIMED_SET:
5634 case EF_TIMED_INC:
5635 case EF_TIMED_INC_NO_RES:
5636 case EF_TIMED_DEC: {
5637 val = timed_name_to_idx(type);
5638 break;
5639 }
5640
5641 /* Nourishment types */
5642 case EF_NOURISH: {
5643 if (streq(type, "INC_BY"))
5644 val = 0;
5645 else if (streq(type, "DEC_BY"))
5646 val = 1;
5647 else if (streq(type, "SET_TO"))
5648 val = 2;
5649 else if (streq(type, "INC_TO"))
5650 val = 3;
5651 break;
5652 }
5653
5654 /* Monster timed effect name */
5655 case EF_MON_TIMED_INC: {
5656 val = mon_timed_name_to_idx(type);
5657 break;
5658 }
5659
5660 /* Summon name */
5661 case EF_SUMMON: {
5662 val = summon_name_to_idx(type);
5663 break;
5664 }
5665
5666 /* Stat name */
5667 case EF_RESTORE_STAT:
5668 case EF_DRAIN_STAT:
5669 case EF_LOSE_RANDOM_STAT:
5670 case EF_GAIN_STAT: {
5671 val = stat_name_to_idx(type);
5672 break;
5673 }
5674
5675 /* Enchant type name - not worth a separate function */
5676 case EF_ENCHANT: {
5677 if (streq(type, "TOBOTH"))
5678 val = ENCH_TOBOTH;
5679 else if (streq(type, "TOHIT"))
5680 val = ENCH_TOHIT;
5681 else if (streq(type, "TODAM"))
5682 val = ENCH_TODAM;
5683 else if (streq(type, "TOAC"))
5684 val = ENCH_TOAC;
5685 break;
5686 }
5687
5688 /* Player shape name */
5689 case EF_SHAPECHANGE: {
5690 val = shape_name_to_idx(type);
5691 break;
5692 }
5693
5694 /* Targeted earthquake */
5695 case EF_EARTHQUAKE: {
5696 if (streq(type, "TARGETED"))
5697 val = 1;
5698 else if (streq(type, "NONE"))
5699 val = 0;
5700 break;
5701 }
5702
5703 /* Inscribe a glyph */
5704 case EF_GLYPH: {
5705 if (streq(type, "WARDING"))
5706 val = GLYPH_WARDING;
5707 else if (streq(type, "DECOY"))
5708 val = GLYPH_DECOY;
5709 break;
5710 }
5711
5712 /* Allow teleport away */
5713 case EF_TELEPORT: {
5714 if (streq(type, "AWAY"))
5715 val = 1;
5716 break;
5717 }
5718
5719 /* Allow monster teleport toward */
5720 case EF_TELEPORT_TO: {
5721 if (streq(type, "SELF"))
5722 val = 1;
5723 break;
5724 }
5725
5726 /* Some effects only want a radius, so this is a dummy */
5727 default: {
5728 if (streq(type, "NONE"))
5729 val = 0;
5730 }
5731 }
5732 }
5733
5734 return val;
5735 }
5736
5737 /**
5738 * ------------------------------------------------------------------------
5739 * Execution of effects
5740 * ------------------------------------------------------------------------ */
5741 /**
5742 * Execute an effect chain.
5743 *
5744 * \param effect is the effect chain
5745 * \param origin is the origin of the effect (player, monster etc.)
5746 * \param obj is the object making the effect happen (or NULL)
5747 * \param ident will be updated if the effect is identifiable
5748 * (NB: no effect ever sets *ident to false)
5749 * \param aware indicates whether the player is aware of the effect already
5750 * \param dir is the direction the effect will go in
5751 * \param beam is the base chance out of 100 that a BOLT_OR_BEAM effect will beam
5752 * \param boost is the extent to which skill surpasses difficulty, used as % boost. It
5753 * ranges from 0 to 138.
5754 * \param cmd If the effect is invoked as part of a command, this is the
5755 * the command structure - used primarily so repeating the
5756 * command can use the same information without prompting the
5757 * player again. Use NULL for this if not invoked as part of
5758 * a command.
5759 */
effect_do(struct effect * effect,struct source origin,struct object * obj,bool * ident,bool aware,int dir,int beam,int boost,struct command * cmd)5760 bool effect_do(struct effect *effect,
5761 struct source origin,
5762 struct object *obj,
5763 bool *ident,
5764 bool aware,
5765 int dir,
5766 int beam,
5767 int boost,
5768 struct command *cmd)
5769 {
5770 bool completed = false;
5771 effect_handler_f handler;
5772 random_value value = { 0, 0, 0, 0 };
5773
5774 do {
5775 int random_choices = 0, leftover = 0;
5776
5777 if (!effect_valid(effect)) {
5778 msg("Bad effect passed to effect_do(). Please report this bug.");
5779 return false;
5780 }
5781
5782 if (effect->dice != NULL)
5783 random_choices = dice_roll(effect->dice, &value);
5784
5785 /* Deal with special random effect */
5786 if (effect->index == EF_RANDOM) {
5787 int choice = randint0(random_choices);
5788 leftover = random_choices - choice;
5789
5790 /* Skip to the chosen effect */
5791 effect = effect->next;
5792 while (choice--)
5793 effect = effect->next;
5794
5795 /* Roll the damage, if needed */
5796 if (effect->dice != NULL)
5797 (void) dice_roll(effect->dice, &value);
5798 }
5799
5800 /* Handle the effect */
5801 handler = effects[effect->index].handler;
5802 if (handler != NULL) {
5803 effect_handler_context_t context = {
5804 effect->index,
5805 origin,
5806 obj,
5807 aware,
5808 dir,
5809 beam,
5810 boost,
5811 value,
5812 effect->subtype,
5813 effect->radius,
5814 effect->other,
5815 effect->y,
5816 effect->x,
5817 effect->msg,
5818 *ident,
5819 cmd
5820 };
5821
5822 completed = handler(&context) || completed;
5823 *ident = context.ident;
5824 }
5825
5826 /* Get the next effect, if there is one */
5827 if (leftover)
5828 /* Skip the remaining non-chosen effects */
5829 while (leftover--)
5830 effect = effect->next;
5831 else
5832 effect = effect->next;
5833 } while (effect);
5834
5835 return completed;
5836 }
5837
5838 /**
5839 * Perform a single effect with a simple dice string and parameters
5840 * Calling with ident a valid pointer will (depending on effect) give success
5841 * information; ident = NULL will ignore this
5842 */
effect_simple(int index,struct source origin,const char * dice_string,int subtype,int radius,int other,int y,int x,bool * ident)5843 void effect_simple(int index,
5844 struct source origin,
5845 const char *dice_string,
5846 int subtype,
5847 int radius,
5848 int other,
5849 int y,
5850 int x,
5851 bool *ident)
5852 {
5853 struct effect effect;
5854 int dir = DIR_TARGET;
5855 bool dummy_ident = false;
5856
5857 /* Set all the values */
5858 memset(&effect, 0, sizeof(effect));
5859 effect.index = index;
5860 effect.dice = dice_new();
5861 dice_parse_string(effect.dice, dice_string);
5862 effect.subtype = subtype;
5863 effect.radius = radius;
5864 effect.other = other;
5865 effect.y = y;
5866 effect.x = x;
5867
5868 /* Direction if needed */
5869 if (effect_aim(&effect))
5870 get_aim_dir(&dir);
5871
5872 /* Do the effect */
5873 if (!ident) {
5874 ident = &dummy_ident;
5875 }
5876
5877 effect_do(&effect, origin, NULL, ident, true, dir, 0, 0, NULL);
5878 dice_free(effect.dice);
5879 }
5880