1 /**
2 * \file game-world.c
3 * \brief Game core management of the game world
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 "cmds.h"
21 #include "effects.h"
22 #include "game-world.h"
23 #include "generate.h"
24 #include "init.h"
25 #include "mon-make.h"
26 #include "mon-move.h"
27 #include "mon-util.h"
28 #include "obj-curse.h"
29 #include "obj-desc.h"
30 #include "obj-gear.h"
31 #include "obj-knowledge.h"
32 #include "obj-tval.h"
33 #include "obj-util.h"
34 #include "player-calcs.h"
35 #include "player-timed.h"
36 #include "player-util.h"
37 #include "source.h"
38 #include "target.h"
39 #include "trap.h"
40 #include "z-queue.h"
41
42 u16b daycount = 0;
43 u32b seed_randart; /* Hack -- consistent random artifacts */
44 u32b seed_flavor; /* Hack -- consistent object colors */
45 s32b turn; /* Current game turn */
46 bool character_generated; /* The character exists */
47 bool character_dungeon; /* The character has a dungeon */
48 struct level *world;
49
50 /**
51 * This table allows quick conversion from "speed" to "energy"
52 * The basic function WAS ((S>=110) ? (S-110) : (100 / (120-S)))
53 * Note that table access is *much* quicker than computation.
54 *
55 * Note that the table has been changed at high speeds. From
56 * "Slow (-40)" to "Fast (+30)" is pretty much unchanged, but
57 * at speeds above "Fast (+30)", one approaches an asymptotic
58 * effective limit of 50 energy per turn. This means that it
59 * is relatively easy to reach "Fast (+30)" and get about 40
60 * energy per turn, but then speed becomes very "expensive",
61 * and you must get all the way to "Fast (+50)" to reach the
62 * point of getting 45 energy per turn. After that point,
63 * furthur increases in speed are more or less pointless,
64 * except to balance out heavy inventory.
65 *
66 * Note that currently the fastest monster is "Fast (+30)".
67 */
68 const byte extract_energy[200] =
69 {
70 /* Slow */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
71 /* Slow */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
72 /* Slow */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
73 /* Slow */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
74 /* Slow */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
75 /* Slow */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
76 /* S-50 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
77 /* S-40 */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
78 /* S-30 */ 2, 2, 2, 2, 2, 2, 2, 3, 3, 3,
79 /* S-20 */ 3, 3, 3, 3, 3, 4, 4, 4, 4, 4,
80 /* S-10 */ 5, 5, 5, 5, 6, 6, 7, 7, 8, 9,
81 /* Norm */ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
82 /* F+10 */ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
83 /* F+20 */ 30, 31, 32, 33, 34, 35, 36, 36, 37, 37,
84 /* F+30 */ 38, 38, 39, 39, 40, 40, 40, 41, 41, 41,
85 /* F+40 */ 42, 42, 42, 43, 43, 43, 44, 44, 44, 44,
86 /* F+50 */ 45, 45, 45, 45, 45, 46, 46, 46, 46, 46,
87 /* F+60 */ 47, 47, 47, 47, 47, 48, 48, 48, 48, 48,
88 /* F+70 */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
89 /* Fast */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
90 };
91
92 /**
93 * Find a level by its name
94 */
level_by_name(char * name)95 struct level *level_by_name(char *name)
96 {
97 struct level *lev = world;
98 while (lev) {
99 if (streq(lev->name, name)) {
100 break;
101 }
102 lev = lev->next;
103 }
104 return lev;
105 }
106
107 /**
108 * Find a level by its depth
109 */
level_by_depth(int depth)110 struct level *level_by_depth(int depth)
111 {
112 struct level *lev = world;
113 while (lev) {
114 if (lev->depth == depth) {
115 break;
116 }
117 lev = lev->next;
118 }
119 return lev;
120 }
121
122 /**
123 * Say whether it's daytime or not
124 */
is_daytime(void)125 bool is_daytime(void)
126 {
127 if ((turn % (10L * z_info->day_length)) < ((10L * z_info->day_length) / 2))
128 return true;
129
130 return false;
131 }
132
133 /**
134 * The amount of energy gained in a turn by a player or monster
135 */
turn_energy(int speed)136 int turn_energy(int speed)
137 {
138 return extract_energy[speed] * z_info->move_energy / 100;
139 }
140
141 /**
142 * If player has inscribed the object with "!!", let him know when it's
143 * recharged. -LM-
144 * Also inform player when first item of a stack has recharged. -HK-
145 * Notify all recharges w/o inscription if notify_recharge option set -WP-
146 */
recharged_notice(const struct object * obj,bool all)147 static void recharged_notice(const struct object *obj, bool all)
148 {
149 char o_name[120];
150
151 const char *s;
152
153 bool notify = false;
154
155 if (OPT(player, notify_recharge)) {
156 notify = true;
157 } else if (obj->note) {
158 /* Find a '!' */
159 s = strchr(quark_str(obj->note), '!');
160
161 /* Process notification request */
162 while (s) {
163 /* Find another '!' */
164 if (s[1] == '!') {
165 notify = true;
166 break;
167 }
168
169 /* Keep looking for '!'s */
170 s = strchr(s + 1, '!');
171 }
172 }
173
174 if (!notify) return;
175
176 /* Describe (briefly) */
177 object_desc(o_name, sizeof(o_name), obj, ODESC_BASE);
178
179 /* Disturb the player */
180 disturb(player);
181
182 /* Notify the player */
183 if (obj->number > 1) {
184 if (all) msg("Your %s have recharged.", o_name);
185 else msg("One of your %s has recharged.", o_name);
186 } else if (obj->artifact)
187 msg("The %s has recharged.", o_name);
188 else
189 msg("Your %s has recharged.", o_name);
190 }
191
192
193 /**
194 * Recharge activatable objects in the player's equipment
195 * and rods in the inventory and on the ground.
196 */
recharge_objects(void)197 static void recharge_objects(void)
198 {
199 int i;
200 bool discharged_stack;
201 struct object *obj;
202
203 /* Recharge carried gear */
204 for (obj = player->gear; obj; obj = obj->next) {
205 /* Skip non-objects */
206 assert(obj->kind);
207
208 /* Recharge equipment */
209 if (object_is_equipped(player->body, obj)) {
210 /* Recharge activatable objects */
211 if (recharge_timeout(obj)) {
212 /* Message if an item recharged */
213 recharged_notice(obj, true);
214
215 /* Window stuff */
216 player->upkeep->redraw |= (PR_EQUIP);
217 }
218 } else {
219 /* Recharge the inventory */
220 discharged_stack =
221 (number_charging(obj) == obj->number) ? true : false;
222
223 /* Recharge rods, and update if any rods are recharged */
224 if (tval_can_have_timeout(obj) && recharge_timeout(obj)) {
225 /* Entire stack is recharged */
226 if (obj->timeout == 0)
227 recharged_notice(obj, true);
228
229 /* Previously exhausted stack has acquired a charge */
230 else if (discharged_stack)
231 recharged_notice(obj, false);
232
233 /* Combine pack */
234 player->upkeep->notice |= (PN_COMBINE);
235
236 /* Redraw stuff */
237 player->upkeep->redraw |= (PR_INVEN);
238 }
239 }
240 }
241
242 /* Recharge other level objects */
243 for (i = 1; i < cave->obj_max; i++) {
244 obj = cave->objects[i];
245 if (!obj) continue;
246
247 /* Recharge rods */
248 if (tval_can_have_timeout(obj))
249 recharge_timeout(obj);
250 }
251 }
252
253
254 /**
255 * Play an ambient sound dependent on dungeon level, and day or night in town
256 */
play_ambient_sound(void)257 void play_ambient_sound(void)
258 {
259 if (player->depth == 0) {
260 if (is_daytime())
261 sound(MSG_AMBIENT_DAY);
262 else
263 sound(MSG_AMBIENT_NITE);
264 } else if (player->depth <= 20) {
265 sound(MSG_AMBIENT_DNG1);
266 } else if (player->depth <= 40) {
267 sound(MSG_AMBIENT_DNG2);
268 } else if (player->depth <= 60) {
269 sound(MSG_AMBIENT_DNG3);
270 } else if (player->depth <= 80) {
271 sound(MSG_AMBIENT_DNG4);
272 } else {
273 sound(MSG_AMBIENT_DNG5);
274 }
275 }
276
277 /**
278 * Helper for process_world -- decrement player->timed[] and curse effect fields
279 */
decrease_timeouts(void)280 static void decrease_timeouts(void)
281 {
282 int adjust = (adj_con_fix[player->state.stat_ind[STAT_CON]] + 1);
283 int i;
284
285 /* Most timed effects decrement by 1 */
286 for (i = 0; i < TMD_MAX; i++) {
287 int decr = 1;
288 if (!player->timed[i])
289 continue;
290
291 /* Special cases */
292 switch (i) {
293 case TMD_FOOD:
294 {
295 /* Handled separately */
296 decr = 0;
297 break;
298 }
299
300 case TMD_CUT:
301 {
302 /* Check for truly "mortal" wound */
303 if (player_timed_grade_eq(player, i, "Mortal Wound")) {
304 decr = 0;
305 } else {
306 decr = adjust;
307 }
308
309 /* Rock players just maintain */
310 if (player_has(player, PF_ROCK)) {
311 decr = 0;
312 }
313
314 break;
315 }
316
317 case TMD_POISONED:
318 case TMD_STUN:
319 {
320 decr = adjust;
321 break;
322 }
323
324 case TMD_COMMAND:
325 {
326 struct monster *mon = get_commanded_monster();
327 if (!los(cave, player->grid, mon->grid)) {
328 /* Out of sight is out of mind */
329 mon_clear_timed(mon, MON_TMD_COMMAND, MON_TMD_FLG_NOTIFY);
330 player_clear_timed(player, TMD_COMMAND, true);
331 } else {
332 /* Keep monster timer aligned */
333 mon_dec_timed(mon, MON_TMD_COMMAND, decr, 0);
334 }
335 break;
336 }
337 }
338 /* Decrement the effect */
339 player_dec_timed(player, i, decr, false);
340 }
341
342 /* Curse effects always decrement by 1 */
343 for (i = 0; i < player->body.count; i++) {
344 struct curse_data *curse = NULL;
345 if (player->body.slots[i].obj == NULL) {
346 continue;
347 }
348 curse = player->body.slots[i].obj->curses;
349 if (curse) {
350 int j;
351 for (j = 0; j < z_info->curse_max; j++) {
352 if (curse[j].power) {
353 curse[j].timeout--;
354 if (!curse[j].timeout) {
355 struct curse *c = &curses[j];
356 if (do_curse_effect(j, player->body.slots[i].obj)) {
357 player_learn_curse(player, c);
358 }
359 curse[j].timeout = randcalc(c->obj->time, 0, RANDOMISE);
360 }
361 }
362 }
363 }
364 }
365
366 return;
367 }
368
369
370 /**
371 * Every turn, the character makes enough noise that nearby monsters can use
372 * it to home in.
373 *
374 * This function actually just computes distance from the player; this is
375 * used in combination with the player's stealth value to determine what
376 * monsters can hear. We mark the player's grid with 0, then fill in the noise
377 * field of every grid that the player can reach with that "noise"
378 * (actally distance) plus the number of steps needed to reach that grid
379 * - so higher values mean further from the player.
380 *
381 * Monsters use this information by moving to adjacent grids with lower noise
382 * values, thereby homing in on the player even though twisty tunnels and
383 * mazes. Monsters have a hearing value, which is the largest sound value
384 * they can detect.
385 */
make_noise(struct player * p)386 static void make_noise(struct player *p)
387 {
388 struct loc next = p->grid;
389 int y, x, d;
390 int noise = 0;
391 int noise_increment = p->timed[TMD_COVERTRACKS] ? 4 : 1;
392 struct queue *queue = q_new(cave->height * cave->width);
393 struct loc decoy = cave_find_decoy(cave);
394
395 /* Set all the grids to silence */
396 for (y = 1; y < cave->height - 1; y++) {
397 for (x = 1; x < cave->width - 1; x++) {
398 cave->noise.grids[y][x] = 0;
399 }
400 }
401
402 /* If there's a decoy, use that instead of the player */
403 if (!loc_is_zero(decoy)) {
404 next = decoy;
405 }
406
407 /* Player makes noise */
408 cave->noise.grids[next.y][next.x] = noise;
409 q_push_int(queue, grid_to_i(next, cave->width));
410 noise += noise_increment;
411
412 /* Propagate noise */
413 while (q_len(queue) > 0) {
414 /* Get the next grid */
415 i_to_grid(q_pop_int(queue), cave->width, &next);
416
417 /* If we've reached the current noise level, put it back and step */
418 if (cave->noise.grids[next.y][next.x] == noise) {
419 q_push_int(queue, grid_to_i(next, cave->width));
420 noise += noise_increment;
421 continue;
422 }
423
424 /* Assign noise to the children and enqueue them */
425 for (d = 0; d < 8; d++) {
426 /* Child location */
427 struct loc grid = loc_sum(next, ddgrid_ddd[d]);
428
429 if (!square_in_bounds(cave, grid)) continue;
430
431 /* Ignore features that don't transmit sound */
432 if (square_isnoflow(cave, grid)) continue;
433
434 /* Skip grids that already have noise */
435 if (cave->noise.grids[grid.y][grid.x] != 0) continue;
436
437 /* Skip the player grid */
438 if (loc_eq(p->grid, grid)) continue;
439
440 /* Save the noise */
441 cave->noise.grids[grid.y][grid.x] = noise;
442
443 /* Enqueue that entry */
444 q_push_int(queue, grid_to_i(grid, cave->width));
445 }
446 }
447
448 q_free(queue);
449 }
450
451 /**
452 * Characters leave scent trails for perceptive monsters to track.
453 *
454 * Scent is rather more limited than sound. Many creatures cannot use
455 * it at all, it doesn't extend very far outwards from the character's
456 * current position, and monsters can use it to home in the character,
457 * but not to run away.
458 *
459 * Scent is valued according to age. When a character takes his turn,
460 * scent is aged by one, and new scent is laid down. Monsters have a smell
461 * value which indicates the oldest scent they can detect. Grids where the
462 * player has never been will have scent 0. The player's grid will also have
463 * scent 0, but this is OK as no monster will ever be smelling it.
464 */
update_scent(void)465 static void update_scent(void)
466 {
467 int y, x;
468 int scent_strength[5][5] = {
469 {2, 2, 2, 2, 2},
470 {2, 1, 1, 1, 2},
471 {2, 1, 0, 1, 2},
472 {2, 1, 1, 1, 2},
473 {2, 2, 2, 2, 2},
474 };
475
476 /* Update scent for all grids */
477 for (y = 1; y < cave->height - 1; y++) {
478 for (x = 1; x < cave->width - 1; x++) {
479 if (cave->scent.grids[y][x] > 0) {
480 cave->scent.grids[y][x]++;
481 }
482 }
483 }
484
485 /* Scentless player */
486 if (player->timed[TMD_COVERTRACKS]) return;
487
488 /* Lay down new scent around the player */
489 for (y = 0; y < 5; y++) {
490 for (x = 0; x < 5; x++) {
491 struct loc scent;
492 int new_scent = scent_strength[y][x];
493 int d;
494 bool add_scent = false;
495
496 /* Initialize */
497 scent.y = y + player->grid.y - 2;
498 scent.x = x + player->grid.x - 2;
499
500 /* Ignore invalid or non-scent-carrying grids */
501 if (!square_in_bounds(cave, scent)) continue;
502 if (square_isnoscent(cave, scent)) continue;
503
504 /* Check scent is spreading on floors, not going through walls */
505 for (d = 0; d < 8; d++) {
506 struct loc adj = loc_sum(scent, ddgrid_ddd[d]);
507
508 if (!square_in_bounds(cave, adj)) {
509 continue;
510 }
511
512 /* Player grid is always valid */
513 if (x == 2 && y == 2) {
514 add_scent = true;
515 }
516
517 /* Adjacent to a closer grid, so valid */
518 if (cave->scent.grids[adj.y][adj.x] == new_scent - 1) {
519 add_scent = true;
520 }
521 }
522
523 /* Not valid */
524 if (!add_scent) {
525 continue;
526 }
527
528 /* Mark the scent */
529 cave->scent.grids[scent.y][scent.x] = new_scent;
530 }
531 }
532 }
533
534 /**
535 * Handle things that need updating once every 10 game turns
536 */
process_world(struct chunk * c)537 void process_world(struct chunk *c)
538 {
539 int i, y, x;
540
541 /* Compact the monster list if we're approaching the limit */
542 if (cave_monster_count(c) + 32 > z_info->level_monster_max)
543 compact_monsters(64);
544
545 /* Too many holes in the monster list - compress */
546 if (cave_monster_count(c) + 32 < cave_monster_max(c))
547 compact_monsters(0);
548
549 /*** Check the Time ***/
550
551 /* Play an ambient sound at regular intervals. */
552 if (!(turn % ((10L * z_info->day_length) / 4)))
553 play_ambient_sound();
554
555 /* Handle stores and sunshine */
556 if (!player->depth) {
557 /* Daybreak/Nighfall in town */
558 if (!(turn % ((10L * z_info->day_length) / 2))) {
559 /* Check for dawn */
560 bool dawn = (!(turn % (10L * z_info->day_length)));
561
562 if (dawn) {
563 /* Day breaks */
564 msg("The sun has risen.");
565 } else {
566 /* Night falls */
567 msg("The sun has fallen.");
568 }
569
570 /* Illuminate */
571 cave_illuminate(c, dawn);
572 }
573 } else {
574 /* Update the stores once a day (while in the dungeon).
575 The changes are not actually made until return to town,
576 to avoid giving details away in the knowledge menu. */
577 if (!(turn % (10L * z_info->store_turns))) daycount++;
578 }
579
580 /* Check for light change */
581 if (player_has(player, PF_UNLIGHT)) {
582 player->upkeep->update |= PU_BONUS;
583 }
584
585 /* Check for creature generation */
586 if (one_in_(z_info->alloc_monster_chance))
587 (void)pick_and_place_distant_monster(c, player, z_info->max_sight + 5,
588 true, player->depth);
589
590 /*** Damage (or healing) over Time ***/
591
592 /* Take damage from poison */
593 if (player->timed[TMD_POISONED])
594 take_hit(player, 1, "poison");
595
596 /* Take damage from cuts, worse from serious cuts */
597 if (player->timed[TMD_CUT]) {
598 if (player_has(player, PF_ROCK)) {
599 /* Rock players just maintain */
600 i = 0;
601 } else if (player_timed_grade_eq(player, TMD_CUT, "Mortal Wound") ||
602 player_timed_grade_eq(player, TMD_CUT, "Deep Gash")) {
603 i = 3;
604 } else if (player_timed_grade_eq(player, TMD_CUT, "Severe Cut")) {
605 i = 2;
606 } else {
607 i = 1;
608 }
609
610 /* Take damage */
611 take_hit(player, i, "a fatal wound");
612 }
613
614 /* Side effects of diminishing bloodlust */
615 if (player->timed[TMD_BLOODLUST]) {
616 player_over_exert(player, PY_EXERT_HP | PY_EXERT_CUT | PY_EXERT_SLOW,
617 MAX(0, 10 - player->timed[TMD_BLOODLUST]),
618 player->chp / 10);
619 }
620
621 /* Timed healing */
622 if (player->timed[TMD_HEAL]) {
623 bool ident = false;
624 effect_simple(EF_HEAL_HP, source_player(), "30", 0, 0, 0, 0, 0, &ident);
625 }
626
627 /* Effects of Black Breath */
628 if (player->timed[TMD_BLACKBREATH]) {
629 if (one_in_(2)) {
630 msg("The Black Breath sickens you.");
631 player_stat_dec(player, STAT_CON, false);
632 }
633 if (one_in_(2)) {
634 msg("The Black Breath saps your strength.");
635 player_stat_dec(player, STAT_STR, false);
636 }
637 if (one_in_(2)) {
638 /* Life draining */
639 int drain = 100 + (player->exp / 100) * z_info->life_drain_percent;
640 msg("The Black Breath dims your life force.");
641 player_exp_lose(player, drain, false);
642 }
643 }
644
645 /*** Check the Food, and Regenerate ***/
646
647 /* Digest */
648 if (!player_timed_grade_eq(player, TMD_FOOD, "Full")) {
649 /* Digest normally */
650 if (!(turn % 100)) {
651 /* Basic digestion rate based on speed */
652 i = turn_energy(player->state.speed);
653
654 /* Adjust for food value */
655 i = (i * 100) / z_info->food_value;
656
657 /* Regeneration takes more food */
658 if (player_of_has(player, OF_REGEN)) i *= 2;
659
660 /* Slow digestion takes less food */
661 if (player_of_has(player, OF_SLOW_DIGEST)) i /= 2;
662
663 /* Minimal digestion */
664 if (i < 1) i = 1;
665
666 /* Digest some food */
667 player_dec_timed(player, TMD_FOOD, i, false);
668 }
669
670 /* Fast metabolism */
671 if (player->timed[TMD_HEAL]) {
672 player_dec_timed(player, TMD_FOOD, 8 * z_info->food_value, false);
673 if (player->timed[TMD_FOOD] < PY_FOOD_HUNGRY) {
674 player_set_timed(player, TMD_HEAL, 0, true);
675 }
676 }
677 } else {
678 /* Digest quickly when gorged */
679 player_dec_timed(player, TMD_FOOD, 5000 / z_info->food_value, false);
680 player->upkeep->update |= PU_BONUS;
681 }
682
683 /* Faint or starving */
684 if (player_timed_grade_eq(player, TMD_FOOD, "Faint")) {
685 /* Faint occasionally */
686 if (!player->timed[TMD_PARALYZED] && one_in_(10)) {
687 /* Message */
688 msg("You faint from the lack of food.");
689 disturb(player);
690
691 /* Faint (bypass free action) */
692 (void)player_inc_timed(player, TMD_PARALYZED, 1 + randint0(5),
693 true, false);
694 }
695 } else if (player_timed_grade_eq(player, TMD_FOOD, "Starving")) {
696 /* Calculate damage */
697 i = (PY_FOOD_STARVE - player->timed[TMD_FOOD]) / 10;
698
699 /* Take damage */
700 take_hit(player, i, "starvation");
701 }
702
703 /* Regenerate Hit Points if needed */
704 if (player->chp < player->mhp)
705 player_regen_hp(player);
706
707 /* Regenerate or lose mana */
708 player_regen_mana(player);
709
710 /* Timeout various things */
711 decrease_timeouts();
712
713 /* Process light */
714 player_update_light(player);
715
716 /* Update noise and scent (not if resting) */
717 if (!player_is_resting(player)) {
718 make_noise(player);
719 update_scent();
720 }
721
722
723 /*** Process Inventory ***/
724
725 /* Handle experience draining */
726 if (player_of_has(player, OF_DRAIN_EXP)) {
727 if ((player->exp > 0) && one_in_(10)) {
728 s32b d = damroll(10, 6) +
729 (player->exp / 100) * z_info->life_drain_percent;
730 player_exp_lose(player, d / 10, false);
731 }
732
733 equip_learn_flag(player, OF_DRAIN_EXP);
734 }
735
736 /* Recharge activatable objects and rods */
737 recharge_objects();
738
739 /* Notice things after time */
740 if (!(turn % 100))
741 equip_learn_after_time(player);
742
743 /* Decrease trap timeouts */
744 for (y = 0; y < c->height; y++) {
745 for (x = 0; x < c->width; x++) {
746 struct loc grid = loc(x, y);
747 struct trap *trap = square(c, grid)->trap;
748 while (trap) {
749 if (trap->timeout) {
750 trap->timeout--;
751 if (!trap->timeout)
752 square_light_spot(c, grid);
753 }
754 trap = trap->next;
755 }
756 }
757 }
758
759
760 /*** Involuntary Movement ***/
761
762 /* Delayed Word-of-Recall */
763 if (player->word_recall) {
764 /* Count down towards recall */
765 player->word_recall--;
766
767 /* Activate the recall */
768 if (!player->word_recall) {
769 /* Disturbing! */
770 disturb(player);
771
772 /* Determine the level */
773 if (player->depth) {
774 msgt(MSG_TPLEVEL, "You feel yourself yanked upwards!");
775 dungeon_change_level(player, 0);
776 } else {
777 msgt(MSG_TPLEVEL, "You feel yourself yanked downwards!");
778 player_set_recall_depth(player);
779 dungeon_change_level(player, player->recall_depth);
780 }
781 }
782 }
783
784 /* Delayed Deep Descent */
785 if (player->deep_descent) {
786 /* Count down towards descent */
787 player->deep_descent--;
788
789 /* Activate the descent */
790 if (player->deep_descent == 0) {
791 /* Calculate target depth */
792 int target_increment = (4 / z_info->stair_skip) + 1;
793 int target_depth = dungeon_get_next_level(player->max_depth,
794 target_increment);
795 disturb(player);
796
797 /* Determine the level */
798 if (target_depth > player->depth) {
799 msgt(MSG_TPLEVEL, "The floor opens beneath you!");
800 dungeon_change_level(player, target_depth);
801 } else {
802 /* Otherwise do something disastrous */
803 msgt(MSG_TPLEVEL, "You are thrown back in an explosion!");
804 effect_simple(EF_DESTRUCTION, source_none(), "0", 0, 5, 0, 0, 0, NULL);
805 }
806 }
807 }
808 }
809
810
811 /**
812 * Housekeeping after the processing of a player command
813 */
process_player_cleanup(void)814 static void process_player_cleanup(void)
815 {
816 int i;
817
818 /* Significant */
819 if (player->upkeep->energy_use) {
820 /* Use some energy */
821 player->energy -= player->upkeep->energy_use;
822
823 /* Increment the total energy counter */
824 player->total_energy += player->upkeep->energy_use;
825
826 /* Player can be damaged by terrain */
827 player_take_terrain_damage(player, player->grid);
828
829 /* Do nothing else if player has auto-dropped stuff */
830 if (!player->upkeep->dropping) {
831 /* Hack -- constant hallucination */
832 if (player->timed[TMD_IMAGE])
833 player->upkeep->redraw |= (PR_MAP);
834
835 /* Shimmer multi-hued monsters */
836 for (i = 1; i < cave_monster_max(cave); i++) {
837 struct monster *mon = cave_monster(cave, i);
838 if (!mon->race)
839 continue;
840 if (!rf_has(mon->race->flags, RF_ATTR_MULTI))
841 continue;
842 square_light_spot(cave, mon->grid);
843 }
844
845 /* Clear NICE flag, and show marked monsters */
846 for (i = 1; i < cave_monster_max(cave); i++) {
847 struct monster *mon = cave_monster(cave, i);
848 mflag_off(mon->mflag, MFLAG_NICE);
849 if (mflag_has(mon->mflag, MFLAG_MARK)) {
850 if (!mflag_has(mon->mflag, MFLAG_SHOW)) {
851 mflag_off(mon->mflag, MFLAG_MARK);
852 update_mon(mon, cave, false);
853 }
854 }
855 }
856 }
857 }
858
859 /* Clear SHOW flag and player drop status */
860 for (i = 1; i < cave_monster_max(cave); i++) {
861 struct monster *mon = cave_monster(cave, i);
862 mflag_off(mon->mflag, MFLAG_SHOW);
863 }
864 player->upkeep->dropping = false;
865
866 /* Hack - update needed first because inventory may have changed */
867 update_stuff(player);
868 redraw_stuff(player);
869 }
870
871
872 /**
873 * Process player commands from the command queue, finishing when there is a
874 * command using energy (any regular game command), or we run out of commands
875 * and need another from the user, or the character changes level or dies, or
876 * the game is stopped.
877 *
878 * Notice the annoying code to handle "pack overflow", which
879 * must come first just in case somebody manages to corrupt
880 * the savefiles by clever use of menu commands or something. (Can go? NRM)
881 *
882 * Notice the annoying code to handle "monster memory" changes,
883 * which allows us to avoid having to update the window flags
884 * every time we change any internal monster memory field, and
885 * also reduces the number of times that the recall window must
886 * be redrawn.
887 */
process_player(void)888 void process_player(void)
889 {
890 /* Check for interrupts */
891 player_resting_complete_special(player);
892 event_signal(EVENT_CHECK_INTERRUPT);
893
894 /* Repeat until energy is reduced */
895 do {
896 /* Refresh */
897 notice_stuff(player);
898 handle_stuff(player);
899 event_signal(EVENT_REFRESH);
900
901 /* Hack -- Pack Overflow */
902 pack_overflow(NULL);
903
904 /* Assume free turn */
905 player->upkeep->energy_use = 0;
906
907 /* Dwarves detect treasure */
908 if (player_has(player, PF_SEE_ORE)) {
909 /* Only if they are in good shape */
910 if (!player->timed[TMD_IMAGE] &&
911 !player->timed[TMD_CONFUSED] &&
912 !player->timed[TMD_AMNESIA] &&
913 !player->timed[TMD_STUN] &&
914 !player->timed[TMD_PARALYZED] &&
915 !player->timed[TMD_TERROR] &&
916 !player->timed[TMD_AFRAID])
917 effect_simple(EF_DETECT_GOLD, source_none(), "0", 0, 0, 0, 3, 3, NULL);
918 }
919
920 /* Paralyzed or Knocked Out player gets no turn */
921 if (player->timed[TMD_PARALYZED] ||
922 player_timed_grade_eq(player, TMD_STUN, "Knocked Out")) {
923 cmdq_push(CMD_SLEEP);
924 }
925
926 /* Prepare for the next command */
927 if (cmd_get_nrepeats() > 0) {
928 event_signal(EVENT_COMMAND_REPEAT);
929 } else {
930 /* Check monster recall */
931 if (player->upkeep->monster_race)
932 player->upkeep->redraw |= (PR_MONSTER);
933
934 /* Place cursor on player/target */
935 event_signal(EVENT_REFRESH);
936 }
937
938 /* Get a command from the queue if there is one */
939 if (!cmdq_pop(CTX_GAME))
940 break;
941
942 if (!player->upkeep->playing)
943 break;
944
945 process_player_cleanup();
946 } while (!player->upkeep->energy_use &&
947 !player->is_dead &&
948 !player->upkeep->generate_level);
949
950 /* Notice stuff (if needed) */
951 notice_stuff(player);
952 }
953
954 /**
955 * Housekeeping on arriving on a new level
956 */
on_new_level(void)957 void on_new_level(void)
958 {
959 /* Arena levels are not really a level change */
960 if (!player->upkeep->arena_level) {
961 /* Play ambient sound on change of level. */
962 play_ambient_sound();
963
964 /* Cancel the target */
965 target_set_monster(0);
966
967 /* Cancel the health bar */
968 health_track(player->upkeep, NULL);
969 }
970
971 /* Disturb */
972 disturb(player);
973
974 /* Track maximum player level */
975 if (player->max_lev < player->lev)
976 player->max_lev = player->lev;
977
978 /* Track maximum dungeon level */
979 if (player->max_depth < player->depth)
980 player->max_depth = player->recall_depth = player->depth;
981
982 /* Flush messages */
983 event_signal(EVENT_MESSAGE_FLUSH);
984
985 /* Update display */
986 event_signal(EVENT_NEW_LEVEL_DISPLAY);
987
988 /* Update player */
989 player->upkeep->update |= (PU_BONUS | PU_HP | PU_SPELLS | PU_INVEN);
990 player->upkeep->notice |= (PN_COMBINE);
991 notice_stuff(player);
992 update_stuff(player);
993 redraw_stuff(player);
994
995 /* Refresh */
996 event_signal(EVENT_REFRESH);
997
998 if (player->upkeep->arena_level) {
999 return;
1000 }
1001
1002 /* Announce (or repeat) the feeling */
1003 if (player->depth)
1004 display_feeling(false);
1005
1006 /* Check the surroundings */
1007 search(player);
1008
1009 /* Give player minimum energy to start a new level, but do not reduce
1010 * higher value from savefile for level in progress */
1011 if (player->energy < z_info->move_energy)
1012 player->energy = z_info->move_energy;
1013 }
1014
1015 /**
1016 * Housekeeping on leaving a level
1017 */
on_leave_level(void)1018 static void on_leave_level(void) {
1019 /* Cancel any command */
1020 player_clear_timed(player, TMD_COMMAND, false);
1021
1022 /* Don't allow command repeat if moved away from item used. */
1023 if (cmdq_does_previous_use_floor_item()) {
1024 cmd_disable_repeat();
1025 }
1026
1027 /* Any pending processing */
1028 notice_stuff(player);
1029 update_stuff(player);
1030 redraw_stuff(player);
1031
1032 /* Flush messages */
1033 event_signal(EVENT_MESSAGE_FLUSH);
1034 }
1035
1036
1037 /**
1038 * The main game loop.
1039 *
1040 * This function will run until the player needs to enter a command, or closes
1041 * the game, or the character dies.
1042 */
run_game_loop(void)1043 void run_game_loop(void)
1044 {
1045 /* Tidy up after the player's command */
1046 process_player_cleanup();
1047
1048 /* Keep processing the player until they use some energy or
1049 * another command is needed */
1050 while (player->upkeep->playing) {
1051 process_player();
1052 if (player->upkeep->energy_use)
1053 break;
1054 else
1055 return;
1056 }
1057
1058 /* The player may still have enough energy to move, so we run another
1059 * player turn before processing the rest of the world */
1060 while (player->energy >= z_info->move_energy) {
1061 /* Do any necessary animations */
1062 event_signal(EVENT_ANIMATE);
1063
1064 /* Process monster with even more energy first */
1065 process_monsters(cave, player->energy + 1);
1066 if (player->is_dead || !player->upkeep->playing ||
1067 player->upkeep->generate_level)
1068 break;
1069
1070 /* Process the player until they use some energy */
1071 while (player->upkeep->playing) {
1072 process_player();
1073 if (player->upkeep->energy_use)
1074 break;
1075 else
1076 return;
1077 }
1078 }
1079
1080 /* Now that the player's turn is fully complete, we run the main loop
1081 * until player input is needed again */
1082 while (true) {
1083 notice_stuff(player);
1084 handle_stuff(player);
1085 event_signal(EVENT_REFRESH);
1086
1087 /* Process the rest of the world, give the player energy and
1088 * increment the turn counter unless we need to stop playing or
1089 * generate a new level */
1090 if (player->is_dead || !player->upkeep->playing)
1091 return;
1092 else if (!player->upkeep->generate_level) {
1093 /* Process the rest of the monsters */
1094 process_monsters(cave, 0);
1095
1096 /* Mark all monsters as ready to act when they have the energy */
1097 reset_monsters();
1098
1099 /* Refresh */
1100 notice_stuff(player);
1101 handle_stuff(player);
1102 event_signal(EVENT_REFRESH);
1103 if (player->is_dead || !player->upkeep->playing)
1104 return;
1105
1106 /* Process the world every ten turns */
1107 if (!(turn % 10) && !player->upkeep->generate_level) {
1108 process_world(cave);
1109
1110 /* Refresh */
1111 notice_stuff(player);
1112 handle_stuff(player);
1113 event_signal(EVENT_REFRESH);
1114 if (player->is_dead || !player->upkeep->playing)
1115 return;
1116 }
1117
1118 /* Give the player some energy */
1119 player->energy += turn_energy(player->state.speed);
1120
1121 /* Count game turns */
1122 turn++;
1123 }
1124
1125 /* Make a new level if requested */
1126 if (player->upkeep->generate_level) {
1127 bool arena = false;
1128 if (character_dungeon) {
1129 on_leave_level();
1130 if (cave->name && streq(cave->name, "arena")) {
1131 arena = true;
1132 }
1133 }
1134
1135 prepare_next_level(&cave, player);
1136 on_new_level();
1137
1138 player->upkeep->generate_level = false;
1139
1140 /* Kill arena monster */
1141 if (arena) {
1142 player->upkeep->arena_level = false;
1143 if (player->upkeep->health_who) {
1144 kill_arena_monster(player->upkeep->health_who);
1145 }
1146 }
1147 }
1148
1149 /* If the player has enough energy to move they now do so, after
1150 * any monsters with more energy take their turns */
1151 while (player->energy >= z_info->move_energy) {
1152 /* Do any necessary animations */
1153 event_signal(EVENT_ANIMATE);
1154
1155 /* Process monster with even more energy first */
1156 process_monsters(cave, player->energy + 1);
1157 if (player->is_dead || !player->upkeep->playing ||
1158 player->upkeep->generate_level)
1159 break;
1160
1161 /* Process the player until they use some energy */
1162 while (player->upkeep->playing) {
1163 process_player();
1164 if (player->upkeep->energy_use)
1165 break;
1166 else
1167 return;
1168 }
1169 }
1170 }
1171 }
1172