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