1 /**
2 * \file trap.c
3 * \brief The trap layer - player traps, runes and door locks
4 *
5 * Copyright (c) 1997 Ben Harrison, James E. Wilson, Robert A. Koeneke
6 *
7 * This work is free software; you can redistribute it and/or modify it
8 * under the terms of either:
9 *
10 * a) the GNU General Public License as published by the Free Software
11 * Foundation, version 2, or
12 *
13 * b) the "Angband licence":
14 * This software may be copied and distributed for educational, research,
15 * and not for profit purposes provided that this copyright and statement
16 * are included in all such copies. Other copyrights may also apply.
17 */
18
19 #include "angband.h"
20 #include "cave.h"
21 #include "effects.h"
22 #include "init.h"
23 #include "mon-util.h"
24 #include "obj-knowledge.h"
25 #include "player-attack.h"
26 #include "player-timed.h"
27 #include "player-util.h"
28 #include "trap.h"
29
30 /**
31 * ------------------------------------------------------------------------
32 * General trap routines
33 * ------------------------------------------------------------------------ */
34 struct trap_kind *trap_info;
35
36 /**
37 * Find a trap kind based on its short description
38 */
lookup_trap(const char * desc)39 struct trap_kind *lookup_trap(const char *desc)
40 {
41 int i;
42 struct trap_kind *closest = NULL;
43
44 /* Look for it */
45 for (i = 1; i < z_info->trap_max; i++) {
46 struct trap_kind *kind = &trap_info[i];
47 if (!kind->name)
48 continue;
49
50 /* Test for equality */
51 if (streq(desc, kind->desc))
52 return kind;
53
54 /* Test for close matches */
55 if (!closest && my_stristr(kind->desc, desc))
56 closest = kind;
57 }
58
59 /* Return our best match */
60 return closest;
61 }
62
63 /**
64 * Is there a specific kind of trap in this square?
65 */
square_trap_specific(struct chunk * c,struct loc grid,int t_idx)66 bool square_trap_specific(struct chunk *c, struct loc grid, int t_idx)
67 {
68 struct trap *trap = square_trap(c, grid);
69
70 /* First, check the trap marker */
71 if (!square_istrap(c, grid))
72 return false;
73
74 /* Scan the square trap list */
75 while (trap) {
76 /* We found a trap of the right kind */
77 if (trap->t_idx == t_idx)
78 return true;
79 trap = trap->next;
80 }
81
82 /* Report failure */
83 return false;
84 }
85
86 /**
87 * Is there a trap with a given flag in this square?
88 */
square_trap_flag(struct chunk * c,struct loc grid,int flag)89 bool square_trap_flag(struct chunk *c, struct loc grid, int flag)
90 {
91 struct trap *trap = square_trap(c, grid);
92
93 /* First, check the trap marker */
94 if (!square_istrap(c, grid))
95 return false;
96
97 /* Scan the square trap list */
98 while (trap) {
99 /* We found a trap with the right flag */
100 if (trf_has(trap->flags, flag))
101 return true;
102 trap = trap->next;
103 }
104
105 /* Report failure */
106 return false;
107 }
108
109 /**
110 * Determine if a trap actually exists in this square.
111 *
112 * Called with vis = 0 to accept any trap, = 1 to accept only visible
113 * traps, and = -1 to accept only invisible traps.
114 *
115 * Clear the SQUARE_TRAP flag if none exist.
116 */
square_verify_trap(struct chunk * c,struct loc grid,int vis)117 static bool square_verify_trap(struct chunk *c, struct loc grid, int vis)
118 {
119 struct trap *trap = square_trap(c, grid);
120 bool trap_exists = false;
121
122 /* Scan the square trap list */
123 while (trap) {
124 /* Accept any trap */
125 if (!vis)
126 return true;
127
128 /* Accept traps that match visibility requirements */
129 if ((vis == 1) && trf_has(trap->flags, TRF_VISIBLE))
130 return true;
131
132 if ((vis == -1) && !trf_has(trap->flags, TRF_VISIBLE))
133 return true;
134
135 /* Note that a trap does exist */
136 trap_exists = true;
137 }
138
139 /* No traps in this location. */
140 if (!trap_exists) {
141 /* No traps */
142 sqinfo_off(square(c, grid)->info, SQUARE_TRAP);
143
144 /* Take note */
145 square_note_spot(c, grid);
146 }
147
148 /* Report failure */
149 return false;
150 }
151
152 /**
153 * Free memory for all traps on a grid
154 */
square_free_trap(struct chunk * c,struct loc grid)155 void square_free_trap(struct chunk *c, struct loc grid)
156 {
157 struct trap *next, *trap = square_trap(c, grid);
158
159 while (trap) {
160 next = trap->next;
161 mem_free(trap);
162 trap = next;
163 }
164 }
165
166 /**
167 * Remove all traps from a grid.
168 *
169 * Return true if traps were removed.
170 */
square_remove_all_traps(struct chunk * c,struct loc grid)171 bool square_remove_all_traps(struct chunk *c, struct loc grid)
172 {
173 struct trap *trap = square(c, grid)->trap;
174 bool were_there_traps = trap == NULL ? false : true;
175
176 assert(square_in_bounds(c, grid));
177 while (trap) {
178 struct trap *next_trap = trap->next;
179 mem_free(trap);
180 trap = next_trap;
181 }
182
183 square_set_trap(c, grid, NULL);
184
185 /* Refresh grids that the character can see */
186 if (square_isseen(c, grid)) {
187 square_light_spot(c, grid);
188 }
189
190 (void)square_verify_trap(c, grid, 0);
191
192 return were_there_traps;
193 }
194
195 /**
196 * Remove all traps with the given index.
197 *
198 * Return true if traps were removed.
199 */
square_remove_trap(struct chunk * c,struct loc grid,int t_idx_remove)200 bool square_remove_trap(struct chunk *c, struct loc grid, int t_idx_remove)
201 {
202 bool removed = false;
203
204 /* Look at the traps in this grid */
205 struct trap *prev_trap = NULL;
206 struct trap *trap = square(c, grid)->trap;
207
208 assert(square_in_bounds(c, grid));
209 while (trap) {
210 struct trap *next_trap = trap->next;
211
212 if (t_idx_remove == trap->t_idx) {
213 mem_free(trap);
214 removed = true;
215
216 if (prev_trap) {
217 prev_trap->next = next_trap;
218 } else {
219 square_set_trap(c, grid, next_trap);
220 }
221
222 break;
223 }
224
225 prev_trap = trap;
226 trap = next_trap;
227 }
228
229 /* Refresh grids that the character can see */
230 if (square_isseen(c, grid))
231 square_light_spot(c, grid);
232
233 (void)square_verify_trap(c, grid, 0);
234
235 return removed;
236 }
237
238 /**
239 * ------------------------------------------------------------------------
240 * Player traps
241 * ------------------------------------------------------------------------ */
242 /**
243 * Determine if a cave grid is allowed to have player traps in it.
244 */
square_player_trap_allowed(struct chunk * c,struct loc grid)245 bool square_player_trap_allowed(struct chunk *c, struct loc grid)
246 {
247
248 /* We currently forbid multiple traps in a grid under normal conditions.
249 * If this changes, various bits of code elsewhere will have to change too.
250 */
251 if (square_istrap(c, grid))
252 return false;
253
254 /* We currently forbid traps in a grid with objects. */
255 if (square_object(c, grid))
256 return false;
257
258 /* Check it's a trappable square */
259 return (square_istrappable(c, grid));
260 }
261
262 /**
263 * Instantiate a player trap
264 */
pick_trap(struct chunk * c,int feat,int trap_level)265 static int pick_trap(struct chunk *c, int feat, int trap_level)
266 {
267 int i, pick;
268 int *trap_probs = NULL;
269 int trap_prob_max = 0;
270
271 /* Paranoia */
272 if (!feat_is_trap_holding(feat))
273 return -1;
274
275 /* No traps in town */
276 if (c->depth == 0)
277 return -1;
278
279 /* Get trap probabilities */
280 trap_probs = mem_zalloc(z_info->trap_max * sizeof(int));
281 for (i = 0; i < z_info->trap_max; i++) {
282 /* Get this trap */
283 struct trap_kind *kind = &trap_info[i];
284 trap_probs[i] = trap_prob_max;
285
286 /* Ensure that this is a valid player trap */
287 if (!kind->name) continue;
288 if (!kind->rarity) continue;
289 if (!trf_has(kind->flags, TRF_TRAP)) continue;
290
291 /* Require that trap_level not be too low */
292 if (kind->min_depth > trap_level) continue;
293
294 /* Floor? */
295 if (feat_is_floor(feat) && !trf_has(kind->flags, TRF_FLOOR))
296 continue;
297
298 /* Check legality of trapdoors. */
299 if (trf_has(kind->flags, TRF_DOWN)) {
300 /* No trap doors on quest levels */
301 if (is_quest(player->depth)) continue;
302
303 /* No trap doors on the deepest level */
304 if (player->depth >= z_info->max_depth - 1)
305 continue;
306
307 /* No trap doors with persistent levels (for now) */
308 if (OPT(player, birth_levels_persist))
309 continue;
310 }
311
312 /* Trap is okay, store the cumulative probability */
313 trap_probs[i] += (100 / kind->rarity);
314 trap_prob_max = trap_probs[i];
315 }
316
317 /* No valid trap */
318 if (trap_prob_max == 0) {
319 mem_free(trap_probs);
320 return -1;
321 }
322
323 /* Pick at random. */
324 pick = randint0(trap_prob_max);
325 for (i = 0; i < z_info->trap_max; i++) {
326 if (pick < trap_probs[i]) {
327 break;
328 }
329 }
330
331 mem_free(trap_probs);
332
333 /* Return our chosen trap */
334 return i < z_info->trap_max ? i : -1;
335 }
336
337 /**
338 * Make a new trap of the given type. Return true if successful.
339 *
340 * We choose a player trap at random if the index is not legal. This means that
341 * things which are not player traps must be picked by passing a valid index.
342 *
343 * This should be the only function that places traps in the dungeon
344 * except the savefile loading code.
345 */
place_trap(struct chunk * c,struct loc grid,int t_idx,int trap_level)346 void place_trap(struct chunk *c, struct loc grid, int t_idx, int trap_level)
347 {
348 struct trap *new_trap;
349
350 /* We've been called with an illegal index; choose a random trap */
351 if ((t_idx <= 0) || (t_idx >= z_info->trap_max)) {
352 /* Require the correct terrain */
353 if (!square_player_trap_allowed(c, grid)) return;
354
355 t_idx = pick_trap(c, square(c, grid)->feat, trap_level);
356 }
357
358 /* Failure */
359 if (t_idx < 0) return;
360
361 /* Allocate a new trap for this grid (at the front of the list) */
362 new_trap = mem_zalloc(sizeof(*new_trap));
363 new_trap->next = square_trap(c, grid);
364 square_set_trap(c, grid, new_trap);
365
366 /* Set the details */
367 new_trap->t_idx = t_idx;
368 new_trap->kind = &trap_info[t_idx];
369 new_trap->grid = grid;
370 new_trap->power = randcalc(new_trap->kind->power, trap_level, RANDOMISE);
371 trf_copy(new_trap->flags, trap_info[t_idx].flags);
372
373 /* Toggle on the trap marker */
374 sqinfo_on(square(c, grid)->info, SQUARE_TRAP);
375
376 /* Redraw the grid */
377 square_note_spot(c, grid);
378 square_light_spot(c, grid);
379 }
380
381 /**
382 * Reveal some of the player traps in a square
383 */
square_reveal_trap(struct chunk * c,struct loc grid,bool always,bool domsg)384 bool square_reveal_trap(struct chunk *c, struct loc grid, bool always,
385 bool domsg)
386 {
387 int found_trap = 0;
388 struct trap *trap = square_trap(c, grid);
389
390 /* Check there is a player trap */
391 if (!square_isplayertrap(c, grid))
392 return false;
393
394 /* Scan the grid */
395 while (trap) {
396 /* Skip non-player traps */
397 if (!trf_has(trap->flags, TRF_TRAP)) {
398 trap = trap->next;
399 continue;
400 }
401
402 /* Skip traps the player doesn't notice */
403 if (!always && player->state.skills[SKILL_SEARCH] < trap->power) {
404 trap = trap->next;
405 continue;
406 }
407
408 /* Trap is invisible */
409 if (!trf_has(trap->flags, TRF_VISIBLE)) {
410 /* See the trap (actually, see all the traps) */
411 trf_on(trap->flags, TRF_VISIBLE);
412 square_memorize_traps(c, grid);
413
414 /* We found a trap */
415 found_trap++;
416 }
417 trap = trap->next;
418 }
419
420 /* We found at least one trap */
421 if (found_trap) {
422 /* We want to talk about it */
423 if (domsg) {
424 if (found_trap == 1)
425 msg("You have found a trap.");
426 else
427 msg("You have found %d traps.", found_trap);
428 }
429
430 /* Memorize */
431 square_memorize(c, grid);
432
433 /* Redraw */
434 square_light_spot(c, grid);
435 }
436
437 /* Return true if we found any traps */
438 return (found_trap != 0);
439 }
440
441 /**
442 * Memorize all the visible traps on a square
443 */
square_memorize_traps(struct chunk * c,struct loc grid)444 void square_memorize_traps(struct chunk *c, struct loc grid)
445 {
446 struct trap *trap = square(c, grid)->trap;
447 struct trap *current = NULL;
448 if (c != cave) return;
449
450 /* Clear current knowledge */
451 square_remove_all_traps(player->cave, grid);
452
453 /* Copy all visible traps to the known cave */
454 while (trap) {
455 if (square_isvisibletrap(c, grid)) {
456 struct trap *next;
457 if (current) {
458 next = mem_zalloc(sizeof(*next));
459 current->next = next;
460 current = next;
461 } else {
462 current = mem_zalloc(sizeof(*current));
463 player->cave->squares[grid.y][grid.x].trap = current;
464 }
465 memcpy(current, trap, sizeof(*trap));
466 current->next = NULL;
467 }
468 trap = trap->next;
469 }
470 }
471
472 /**
473 * Determine if a trap affects the player.
474 * Always miss 5% of the time, Always hit 5% of the time.
475 * Otherwise, match trap power against player armor.
476 */
trap_check_hit(int power)477 bool trap_check_hit(int power)
478 {
479 return test_hit(power, player->state.ac + player->state.to_a, true);
480 }
481
482
483 /**
484 * Hit a trap.
485 */
hit_trap(struct loc grid,int delayed)486 extern void hit_trap(struct loc grid, int delayed)
487 {
488 bool ident = false;
489 struct trap *trap;
490 struct effect *effect;
491
492 /* The player is safe from all traps */
493 if (player_is_trapsafe(player)) return;
494
495 /* Look at the traps in this grid */
496 for (trap = square_trap(cave, grid); trap; trap = trap->next) {
497 int flag;
498 bool saved = false;
499
500 /* Require that trap be capable of affecting the character */
501 if (!trf_has(trap->kind->flags, TRF_TRAP)) continue;
502 if (trap->timeout) continue;
503
504 if (delayed != trf_has(trap->kind->flags, TRF_DELAY) &&
505 delayed != -1)
506 continue;
507
508 /* Disturb the player */
509 disturb(player);
510
511 /* Trap immune player learns the rune */
512 if (player_of_has(player, OF_TRAP_IMMUNE)) {
513 equip_learn_flag(player, OF_TRAP_IMMUNE);
514 break;
515 }
516
517 /* Give a message */
518 if (trap->kind->msg)
519 msg(trap->kind->msg);
520
521 /* Test for save due to flag */
522 for (flag = of_next(trap->kind->save_flags, FLAG_START);
523 flag != FLAG_END;
524 flag = of_next(trap->kind->save_flags, flag + 1))
525 if (player_of_has(player, flag)) {
526 saved = true;
527 equip_learn_flag(player, flag);
528 }
529
530 /* Test for save due to armor */
531 if (trf_has(trap->kind->flags, TRF_SAVE_ARMOR) && !trap_check_hit(125))
532 saved = true;
533
534 /* Test for save due to saving throw */
535 if (trf_has(trap->kind->flags, TRF_SAVE_THROW) &&
536 (randint0(100) < player->state.skills[SKILL_SAVE]))
537 saved = true;
538
539 /* Save, or fire off the trap */
540 if (saved) {
541 if (trap->kind->msg_good)
542 msg(trap->kind->msg_good);
543 } else {
544 if (trap->kind->msg_bad)
545 msg(trap->kind->msg_bad);
546 effect = trap->kind->effect;
547 effect_do(effect, source_trap(trap), NULL, &ident, false, 0, 0, 0, NULL);
548
549 /* Trap may have gone */
550 if (!square_trap(cave, grid)) break;
551
552 /* Do any extra effects */
553 if (trap->kind->effect_xtra && one_in_(2)) {
554 if (trap->kind->msg_xtra)
555 msg(trap->kind->msg_xtra);
556 effect = trap->kind->effect_xtra;
557 effect_do(effect, source_trap(trap), NULL, &ident, false,
558 0, 0, 0, NULL);
559
560 /* Trap may have gone */
561 if (!square_trap(cave, grid)) break;
562 }
563 }
564
565 /* Some traps drop you a dungeon level */
566 if (trf_has(trap->kind->flags, TRF_DOWN))
567 dungeon_change_level(player,
568 dungeon_get_next_level(player->depth, 1));
569
570 /* Some traps drop you onto them */
571 if (trf_has(trap->kind->flags, TRF_PIT))
572 monster_swap(player->grid, trap->grid);
573
574 /* Some traps disappear after activating, all have a chance to */
575 if (trf_has(trap->kind->flags, TRF_ONETIME) || one_in_(3)) {
576 square_destroy_trap(cave, grid);
577 square_forget(cave, grid);
578 }
579
580 /* Trap may have gone */
581 if (!square_trap(cave, grid)) break;
582
583 /* Trap becomes visible (always XXX) */
584 trf_on(trap->flags, TRF_VISIBLE);
585 square_memorize(cave, grid);
586 }
587
588 /* Verify traps (remove marker if appropriate) */
589 (void)square_verify_trap(cave, grid, 0);
590 }
591
592 /**
593 * Disable a trap for the specified number of turns
594 */
square_set_trap_timeout(struct chunk * c,struct loc grid,bool domsg,int t_idx,int time)595 bool square_set_trap_timeout(struct chunk *c, struct loc grid, bool domsg,
596 int t_idx, int time)
597 {
598 bool trap_exists;
599 struct trap *current_trap = NULL;
600
601 /* Bounds check */
602 assert(square_in_bounds(c, grid));
603
604 /* Look at the traps in this grid */
605 current_trap = square(c, grid)->trap;
606 while (current_trap) {
607 /* Get the next trap (may be NULL) */
608 struct trap *next_trap = current_trap->next;
609
610 /* If called with a specific index, skip others */
611 if ((t_idx >= 0) && (t_idx != current_trap->t_idx)) {
612 if (!next_trap) break;
613 current_trap = next_trap;
614 continue;
615 }
616
617 /* Set the timer */
618 current_trap->timeout = time;
619
620 /* Message if requested */
621 msg("You have disabled the %s.", current_trap->kind->name);
622
623 /* Replace with the next trap */
624 current_trap = next_trap;
625 }
626
627 /* Refresh grids that the character can see */
628 if (square_isseen(c, grid))
629 square_light_spot(c, grid);
630
631 /* Verify traps (remove marker if appropriate) */
632 trap_exists = square_verify_trap(c, grid, 0);
633
634 /* Report whether any traps exist in this grid */
635 return (!trap_exists);
636 }
637
638 /**
639 * Give the remaining time for a trap to be disabled; note it chooses the first
640 * appropriate trap on the grid
641 */
square_trap_timeout(struct chunk * c,struct loc grid,int t_idx)642 int square_trap_timeout(struct chunk *c, struct loc grid, int t_idx)
643 {
644 struct trap *current_trap = square(c, grid)->trap;
645 while (current_trap) {
646 /* Get the next trap (may be NULL) */
647 struct trap *next_trap = current_trap->next;
648
649 /* If called with a specific index, skip others */
650 if ((t_idx >= 0) && (t_idx != current_trap->t_idx)) {
651 if (!next_trap) break;
652 current_trap = next_trap;
653 continue;
654 }
655
656 /* If the timer is set, return the value */
657 if (current_trap->timeout)
658 return current_trap->timeout;
659
660 /* Replace with the next trap */
661 current_trap = next_trap;
662 }
663
664 return 0;
665 }
666
667 /**
668 * ------------------------------------------------------------------------
669 * Door locks
670 * ------------------------------------------------------------------------ */
671 /**
672 * Lock a closed door to a given power
673 */
square_set_door_lock(struct chunk * c,struct loc grid,int power)674 void square_set_door_lock(struct chunk *c, struct loc grid, int power)
675 {
676 struct trap_kind *lock = lookup_trap("door lock");
677 struct trap *trap;
678
679 /* Verify it's a closed door */
680 if (!square_iscloseddoor(c, grid))
681 return;
682
683 /* If there's no lock there, add one */
684 if (!square_trap_specific(c, grid, lock->tidx))
685 place_trap(c, grid, lock->tidx, 0);
686
687 /* Set the power (of all locks - there should be only one) */
688 trap = square_trap(c, grid);
689 while (trap) {
690 if (trap->kind == lock)
691 trap->power = power;
692 trap = trap->next;
693 }
694 }
695
696 /**
697 * Return the power of the lock on a door
698 */
square_door_power(struct chunk * c,struct loc grid)699 int square_door_power(struct chunk *c, struct loc grid)
700 {
701 struct trap_kind *lock = lookup_trap("door lock");
702 struct trap *trap;
703
704 /* Verify it's a closed door */
705 if (!square_iscloseddoor(c, grid))
706 return 0;
707
708 /* Is there a lock there? */
709 if (!square_trap_specific(c, grid, lock->tidx))
710 return 0;
711
712 /* Get the power and return it */
713 trap = square_trap(c, grid);
714 while (trap) {
715 if (trap->kind == lock)
716 return trap->power;
717 trap = trap->next;
718 }
719
720 return 0;
721 }
722
723