Blindfold removal fix
[slashemextended.git] / src / region.c
blob48d5ca9644b40bd1447df433cee836eecbf1a9a7
1 /* SCCS Id: @(#)region.c 3.4 2002/10/15 */
2 /* Copyright (c) 1996 by Jean-Christophe Collet */
3 /* NetHack may be freely redistributed. See license for details. */
5 #include "hack.h"
6 #include "lev.h"
8 /*
9 * This should really go into the level structure, but
10 * I'll start here for ease. It *WILL* move into the level
11 * structure eventually.
14 static NhRegion **regions;
15 static int n_regions = 0;
16 static int max_regions = 0;
18 #define NO_CALLBACK (-1)
20 boolean inside_gas_cloud(void *,void *);
21 boolean expire_gas_cloud(void *,void *);
22 boolean revive_cthulhu(void *, void *);
23 boolean inside_rect(NhRect *,int,int);
24 boolean inside_region(NhRegion *,int,int);
25 NhRegion *create_region(NhRect *,int);
26 void add_rect_to_reg(NhRegion *,NhRect *);
27 void add_mon_to_reg(NhRegion *,struct monst *);
28 void remove_mon_from_reg(NhRegion *,struct monst *);
29 boolean mon_in_region(NhRegion *,struct monst *);
31 #if 0
32 NhRegion *clone_region(NhRegion *);
33 #endif
34 void free_region(NhRegion *);
35 void add_region(NhRegion *);
36 void remove_region(NhRegion *);
38 #if 0
39 void replace_mon_regions(struct monst *,struct monst *);
40 void remove_mon_from_regions(struct monst *);
41 NhRegion *create_msg_region(XCHAR_P,XCHAR_P,XCHAR_P,XCHAR_P, const char *,const char *);
42 boolean enter_force_field(void *,void *);
43 NhRegion *create_force_field(XCHAR_P,XCHAR_P,int,int);
44 #endif
46 static void reset_region_mids(NhRegion *);
48 static callback_proc callbacks[] = {
49 #define INSIDE_GAS_CLOUD 0
50 inside_gas_cloud,
51 #define EXPIRE_GAS_CLOUD 1
52 expire_gas_cloud,
53 #define REVIVE_CTHULHU 2 /* Cthulhu comes back... */
54 revive_cthulhu
57 /* Should be inlined. */
58 boolean
59 inside_rect(r, x, y)
60 NhRect *r;
61 int x, y;
63 return (x >= r->lx && x <= r->hx && y >= r->ly && y <= r->hy);
67 * Check if a point is inside a region.
69 boolean
70 inside_region(reg, x, y)
71 NhRegion *reg;
72 int x, y;
74 int i;
76 if (reg == NULL || !inside_rect(&(reg->bounding_box), x, y))
77 return FALSE;
78 for (i = 0; i < reg->nrects; i++)
79 if (inside_rect(&(reg->rects[i]), x, y))
80 return TRUE;
81 return FALSE;
85 * Create a region. It does not activate it.
87 NhRegion *
88 create_region(rects, nrect)
89 NhRect *rects;
90 int nrect;
92 int i;
93 NhRegion *reg;
95 reg = (NhRegion *) alloc(sizeof (NhRegion));
96 /* Determines bounding box */
97 if (nrect > 0) {
98 reg->bounding_box = rects[0];
99 } else {
100 reg->bounding_box.lx = 99;
101 reg->bounding_box.ly = 99;
102 reg->bounding_box.hx = 0;
103 reg->bounding_box.hy = 0;
105 reg->nrects = nrect;
106 reg->rects = nrect > 0 ? (NhRect *)alloc((sizeof (NhRect)) * nrect) : NULL;
107 for (i = 0; i < nrect; i++) {
108 if (rects[i].lx < reg->bounding_box.lx)
109 reg->bounding_box.lx = rects[i].lx;
110 if (rects[i].ly < reg->bounding_box.ly)
111 reg->bounding_box.ly = rects[i].ly;
112 if (rects[i].hx > reg->bounding_box.hx)
113 reg->bounding_box.hx = rects[i].hx;
114 if (rects[i].hy > reg->bounding_box.hy)
115 reg->bounding_box.hy = rects[i].hy;
116 reg->rects[i] = rects[i];
118 reg->ttl = -1; /* Defaults */
119 reg->attach_2_u = FALSE;
120 reg->attach_2_m = 0;
121 /* reg->attach_2_o = NULL; */
122 reg->enter_msg = NULL;
123 reg->leave_msg = NULL;
124 reg->expire_f = NO_CALLBACK;
125 reg->enter_f = NO_CALLBACK;
126 reg->can_enter_f = NO_CALLBACK;
127 reg->leave_f = NO_CALLBACK;
128 reg->can_leave_f = NO_CALLBACK;
129 reg->inside_f = NO_CALLBACK;
130 clear_hero_inside(reg);
131 clear_heros_fault(reg);
132 reg->n_monst = 0;
133 reg->max_monst = 0;
134 reg->monsters = NULL;
135 reg->arg = NULL;
136 return reg;
140 * Add rectangle to region.
142 void
143 add_rect_to_reg(reg, rect)
144 NhRegion *reg;
145 NhRect *rect;
147 NhRect *tmp_rect;
149 tmp_rect = (NhRect *) alloc(sizeof (NhRect) * (reg->nrects + 1));
150 if (reg->nrects > 0) {
151 (void) memcpy((void *) tmp_rect, (void *) reg->rects,
152 (sizeof (NhRect) * reg->nrects));
153 free((void *) reg->rects);
155 tmp_rect[reg->nrects] = *rect;
156 reg->nrects++;
157 reg->rects = tmp_rect;
158 /* Update bounding box if needed */
159 if (reg->bounding_box.lx > rect->lx)
160 reg->bounding_box.lx = rect->lx;
161 if (reg->bounding_box.ly > rect->ly)
162 reg->bounding_box.ly = rect->ly;
163 if (reg->bounding_box.hx < rect->hx)
164 reg->bounding_box.hx = rect->hx;
165 if (reg->bounding_box.hy < rect->hy)
166 reg->bounding_box.hy = rect->hy;
170 * Add a monster to the region
172 void
173 add_mon_to_reg(reg, mon)
174 NhRegion *reg;
175 struct monst *mon;
177 int i;
178 unsigned *tmp_m;
180 if (reg->max_monst <= reg->n_monst) {
181 tmp_m = (unsigned *)
182 alloc(sizeof (unsigned) * (reg->max_monst + MONST_INC));
183 if (reg->max_monst > 0) {
184 for (i = 0; i < reg->max_monst; i++)
185 tmp_m[i] = reg->monsters[i];
186 free((void *) reg->monsters);
188 reg->monsters = tmp_m;
189 reg->max_monst += MONST_INC;
191 reg->monsters[reg->n_monst++] = mon->m_id;
195 * Remove a monster from the region list (it left or died...)
197 void
198 remove_mon_from_reg(reg, mon)
199 NhRegion *reg;
200 struct monst *mon;
202 register int i;
204 for (i = 0; i < reg->n_monst; i++)
205 if (reg->monsters[i] == mon->m_id) {
206 reg->n_monst--;
207 reg->monsters[i] = reg->monsters[reg->n_monst];
208 return;
213 * Check if a monster is inside the region.
214 * It's probably quicker to check with the region internal list
215 * than to check for coordinates.
217 boolean
218 mon_in_region(reg, mon)
219 NhRegion *reg;
220 struct monst *mon;
222 int i;
224 for (i = 0; i < reg->n_monst; i++)
225 if (reg->monsters[i] == mon->m_id)
226 return TRUE;
227 return FALSE;
230 #if 0
231 /* not yet used */
234 * Clone (make a standalone copy) the region.
236 NhRegion *
237 clone_region(reg)
238 NhRegion *reg;
240 NhRegion *ret_reg;
242 ret_reg = create_region(reg->rects, reg->nrects);
243 ret_reg->ttl = reg->ttl;
244 ret_reg->attach_2_u = reg->attach_2_u;
245 ret_reg->attach_2_m = reg->attach_2_m;
246 /* ret_reg->attach_2_o = reg->attach_2_o; */
247 ret_reg->expire_f = reg->expire_f;
248 ret_reg->enter_f = reg->enter_f;
249 ret_reg->can_enter_f = reg->can_enter_f;
250 ret_reg->leave_f = reg->leave_f;
251 ret_reg->can_leave_f = reg->can_leave_f;
252 ret_reg->player_flags = reg->player_flags; /* set/clear_hero_inside,&c*/
253 ret_reg->n_monst = reg->n_monst;
254 if (reg->n_monst > 0) {
255 ret_reg->monsters = (unsigned *)
256 alloc((sizeof (unsigned)) * reg->n_monst);
257 (void) memcpy((void *) ret_reg->monsters, (void *) reg->monsters,
258 sizeof (unsigned) * reg->n_monst);
259 } else
260 ret_reg->monsters = NULL;
261 return ret_reg;
264 #endif /*0*/
267 * Free mem from region.
269 void
270 free_region(reg)
271 NhRegion *reg;
273 if (reg) {
274 if (reg->rects)
275 free((void *) reg->rects);
276 if (reg->monsters)
277 free((void *) reg->monsters);
278 free((void *) reg);
283 * Add a region to the list.
284 * This actually activates the region.
286 void
287 add_region(reg)
288 NhRegion *reg;
290 NhRegion **tmp_reg;
291 int i, j;
293 if (max_regions <= n_regions) {
294 tmp_reg = regions;
295 regions = (NhRegion **)alloc(sizeof (NhRegion *) * (max_regions + 10));
296 if (max_regions > 0) {
297 (void) memcpy((void *) regions, (void *) tmp_reg,
298 max_regions * sizeof (NhRegion *));
299 free((void *) tmp_reg);
301 max_regions += 10;
303 regions[n_regions] = reg;
304 n_regions++;
305 /* Check for monsters inside the region */
306 for (i = reg->bounding_box.lx; i <= reg->bounding_box.hx; i++)
307 for (j = reg->bounding_box.ly; j <= reg->bounding_box.hy; j++) {
308 /* Some regions can cross the level boundaries */
309 if (!isok(i,j))
310 continue;
311 if (MON_AT(i, j) && inside_region(reg, i, j))
312 add_mon_to_reg(reg, level.monsters[i][j]);
313 if (reg->visible && cansee(i, j))
314 newsym(i, j);
316 /* Check for player now... */
317 if (inside_region(reg, u.ux, u.uy))
318 set_hero_inside(reg);
319 else
320 clear_hero_inside(reg);
324 * Remove a region from the list & free it.
326 void
327 remove_region(reg)
328 NhRegion *reg;
330 register int i, x, y;
332 for (i = 0; i < n_regions; i++)
333 if (regions[i] == reg)
334 break;
335 if (i == n_regions)
336 return;
338 /* Update screen if necessary */
339 if (reg->visible)
340 for (x = reg->bounding_box.lx; x <= reg->bounding_box.hx; x++)
341 for (y = reg->bounding_box.ly; y <= reg->bounding_box.hy; y++)
342 if (isok(x,y) && inside_region(reg, x, y) && cansee(x, y))
343 newsym(x, y);
345 free_region(reg);
346 regions[i] = regions[n_regions - 1];
347 regions[n_regions - 1] = (NhRegion *) 0;
348 n_regions--;
352 * Remove all regions and clear all related data (This must be down
353 * when changing level, for instance).
355 void
356 clear_regions()
358 register int i;
360 for (i = 0; i < n_regions; i++)
361 free_region(regions[i]);
362 n_regions = 0;
363 if (max_regions > 0)
364 free((void *) regions);
365 max_regions = 0;
366 regions = NULL;
370 * This function is called every turn.
371 * It makes the regions age, if necessary and calls the appropriate
372 * callbacks when needed.
374 void
375 run_regions()
377 register int i, j, k;
378 int f_indx;
380 /* End of life ? */
381 /* Do it backward because the array will be modified */
382 for (i = n_regions - 1; i >= 0; i--) {
383 if (regions[i]->ttl == 0) {
384 if ((f_indx = regions[i]->expire_f) == NO_CALLBACK ||
385 (*callbacks[f_indx])(regions[i], (void *) 0))
386 remove_region(regions[i]);
390 /* Process remaining regions */
391 for (i = 0; i < n_regions; i++) {
392 /* Make the region age */
393 if (regions[i]->ttl > 0)
394 regions[i]->ttl--;
395 /* Check if player is inside region */
396 f_indx = regions[i]->inside_f;
397 if (f_indx != NO_CALLBACK && hero_inside(regions[i]))
398 (void) (*callbacks[f_indx])(regions[i], (void *) 0);
399 /* Check if any monster is inside region */
400 if (f_indx != NO_CALLBACK) {
401 for (j = 0; j < regions[i]->n_monst; j++) {
402 struct monst *mtmp = find_mid(regions[i]->monsters[j], FM_FMON);
404 if (!mtmp || mtmp->mhp <= 0 ||
405 (*callbacks[f_indx])(regions[i], mtmp)) {
406 /* The monster died, remove it from list */
407 k = (regions[i]->n_monst -= 1);
408 regions[i]->monsters[j] = regions[i]->monsters[k];
409 regions[i]->monsters[k] = 0;
410 --j; /* current slot has been reused; recheck it next */
418 * check whether player enters/leaves one or more regions.
420 boolean
421 in_out_region(x, y)
422 xchar
423 x, y;
425 int i, f_indx;
427 /* First check if we can do the move */
428 for (i = 0; i < n_regions; i++) {
429 if (inside_region(regions[i], x, y)
430 && !hero_inside(regions[i]) && !regions[i]->attach_2_u) {
431 if ((f_indx = regions[i]->can_enter_f) != NO_CALLBACK)
432 if (!(*callbacks[f_indx])(regions[i], (void *) 0))
433 return FALSE;
434 } else
435 if (hero_inside(regions[i])
436 && !inside_region(regions[i], x, y)
437 && !regions[i]->attach_2_u) {
438 if ((f_indx = regions[i]->can_leave_f) != NO_CALLBACK)
439 if (!(*callbacks[f_indx])(regions[i], (void *) 0))
440 return FALSE;
444 /* Callbacks for the regions we do leave */
445 for (i = 0; i < n_regions; i++)
446 if (hero_inside(regions[i]) &&
447 !regions[i]->attach_2_u && !inside_region(regions[i], x, y)) {
448 clear_hero_inside(regions[i]);
449 if (regions[i]->leave_msg != NULL)
450 pline("%s", regions[i]->leave_msg);
451 if ((f_indx = regions[i]->leave_f) != NO_CALLBACK)
452 (void) (*callbacks[f_indx])(regions[i], (void *) 0);
455 /* Callbacks for the regions we do enter */
456 for (i = 0; i < n_regions; i++)
457 if (!hero_inside(regions[i]) &&
458 !regions[i]->attach_2_u && inside_region(regions[i], x, y)) {
459 set_hero_inside(regions[i]);
460 if (regions[i]->enter_msg != NULL)
461 pline("%s", regions[i]->enter_msg);
462 if ((f_indx = regions[i]->enter_f) != NO_CALLBACK)
463 (void) (*callbacks[f_indx])(regions[i], (void *) 0);
465 return TRUE;
469 * check wether a monster enters/leaves one or more region.
471 boolean
472 m_in_out_region(mon, x, y)
473 struct monst *mon;
474 xchar x, y;
476 int i, f_indx;
478 /* First check if we can do the move */
479 for (i = 0; i < n_regions; i++) {
480 if (inside_region(regions[i], x, y) &&
481 !mon_in_region(regions[i], mon) &&
482 regions[i]->attach_2_m != mon->m_id) {
483 if ((f_indx = regions[i]->can_enter_f) != NO_CALLBACK)
484 if (!(*callbacks[f_indx])(regions[i], mon))
485 return FALSE;
486 } else if (mon_in_region(regions[i], mon) &&
487 !inside_region(regions[i], x, y) &&
488 regions[i]->attach_2_m != mon->m_id) {
489 if ((f_indx = regions[i]->can_leave_f) != NO_CALLBACK)
490 if (!(*callbacks[f_indx])(regions[i], mon))
491 return FALSE;
495 /* Callbacks for the regions we do leave */
496 for (i = 0; i < n_regions; i++)
497 if (mon_in_region(regions[i], mon) &&
498 regions[i]->attach_2_m != mon->m_id &&
499 !inside_region(regions[i], x, y)) {
500 remove_mon_from_reg(regions[i], mon);
501 if ((f_indx = regions[i]->leave_f) != NO_CALLBACK)
502 (void) (*callbacks[f_indx])(regions[i], mon);
505 /* Callbacks for the regions we do enter */
506 for (i = 0; i < n_regions; i++)
507 if (!hero_inside(regions[i]) &&
508 !regions[i]->attach_2_u && inside_region(regions[i], x, y)) {
509 add_mon_to_reg(regions[i], mon);
510 if ((f_indx = regions[i]->enter_f) != NO_CALLBACK)
511 (void) (*callbacks[f_indx])(regions[i], mon);
513 return TRUE;
517 * Checks player's regions after a teleport for instance.
519 void
520 update_player_regions()
522 register int i;
524 for (i = 0; i < n_regions; i++)
525 if (!regions[i]->attach_2_u && inside_region(regions[i], u.ux, u.uy))
526 set_hero_inside(regions[i]);
527 else
528 clear_hero_inside(regions[i]);
532 * Ditto for a specified monster.
534 void
535 update_monster_region(mon)
536 struct monst *mon;
538 register int i;
540 for (i = 0; i < n_regions; i++) {
541 if (inside_region(regions[i], mon->mx, mon->my)) {
542 if (!mon_in_region(regions[i], mon))
543 add_mon_to_reg(regions[i], mon);
544 } else {
545 if (mon_in_region(regions[i], mon))
546 remove_mon_from_reg(regions[i], mon);
551 #if 0
552 /* not yet used */
555 * Change monster pointer in regions
556 * This happens, for instance, when a monster grows and
557 * need a new structure (internally that is).
559 void
560 replace_mon_regions(monold, monnew)
561 struct monst *monold, *monnew;
563 register int i;
565 for (i = 0; i < n_regions; i++)
566 if (mon_in_region(regions[i], monold)) {
567 remove_mon_from_reg(regions[i], monold);
568 add_mon_to_reg(regions[i], monnew);
573 * Remove monster from all regions it was in (ie monster just died)
575 void
576 remove_mon_from_regions(mon)
577 struct monst *mon;
579 register int i;
581 for (i = 0; i < n_regions; i++)
582 if (mon_in_region(regions[i], mon))
583 remove_mon_from_reg(regions[i], mon);
586 #endif /*0*/
589 * Check if a spot is under a visible region (eg: gas cloud).
590 * Returns NULL if not, otherwise returns region.
592 NhRegion *
593 visible_region_at(x, y)
594 xchar x, y;
596 register int i;
598 for (i = 0; i < n_regions; i++)
599 if (inside_region(regions[i], x, y) && regions[i]->visible &&
600 regions[i]->ttl != 0)
601 return regions[i];
602 return (NhRegion *) 0;
605 void
606 show_region(reg, x, y)
607 NhRegion *reg;
608 xchar x, y;
610 show_glyph(x, y, reg->glyph);
614 * save_regions :
616 void
617 save_regions(fd, mode)
618 int fd;
619 int mode;
621 int i, j;
622 unsigned n;
624 if (!perform_bwrite(mode)) goto skip_lots;
626 bwrite(fd, (void *) &moves, sizeof (moves)); /* timestamp */
627 bwrite(fd, (void *) &n_regions, sizeof (n_regions));
628 for (i = 0; i < n_regions; i++) {
629 bwrite(fd, (void *) &regions[i]->bounding_box, sizeof (NhRect));
630 bwrite(fd, (void *) &regions[i]->nrects, sizeof (short));
631 for (j = 0; j < regions[i]->nrects; j++)
632 bwrite(fd, (void *) &regions[i]->rects[j], sizeof (NhRect));
633 bwrite(fd, (void *) &regions[i]->attach_2_u, sizeof (boolean));
634 n = 0;
635 bwrite(fd, (void *) &regions[i]->attach_2_m, sizeof (unsigned));
636 n = regions[i]->enter_msg != NULL ? strlen(regions[i]->enter_msg) : 0;
637 bwrite(fd, (void *) &n, sizeof n);
638 if (n > 0)
639 bwrite(fd, (void *) regions[i]->enter_msg, n);
640 n = regions[i]->leave_msg != NULL ? strlen(regions[i]->leave_msg) : 0;
641 bwrite(fd, (void *) &n, sizeof n);
642 if (n > 0)
643 bwrite(fd, (void *) regions[i]->leave_msg, n);
644 bwrite(fd, (void *) &regions[i]->ttl, sizeof (short));
645 bwrite(fd, (void *) &regions[i]->expire_f, sizeof (short));
646 bwrite(fd, (void *) &regions[i]->can_enter_f, sizeof (short));
647 bwrite(fd, (void *) &regions[i]->enter_f, sizeof (short));
648 bwrite(fd, (void *) &regions[i]->can_leave_f, sizeof (short));
649 bwrite(fd, (void *) &regions[i]->leave_f, sizeof (short));
650 bwrite(fd, (void *) &regions[i]->inside_f, sizeof (short));
651 bwrite(fd, (void *) &regions[i]->player_flags, sizeof (boolean));
652 bwrite(fd, (void *) &regions[i]->n_monst, sizeof (short));
653 for (j = 0; j < regions[i]->n_monst; j++)
654 bwrite(fd, (void *) &regions[i]->monsters[j],
655 sizeof (unsigned));
656 bwrite(fd, (void *) &regions[i]->visible, sizeof (boolean));
657 bwrite(fd, (void *) &regions[i]->glyph, sizeof (int));
658 bwrite(fd, (void *) &regions[i]->arg, sizeof (void *));
661 skip_lots:
662 if (release_data(mode))
663 clear_regions();
666 void
667 rest_regions(fd, ghostly)
668 int fd;
669 boolean ghostly; /* If a bones file restore */
671 int i, j;
672 unsigned n;
673 long tmstamp;
674 char *msg_buf;
676 clear_regions(); /* Just for security */
677 mread(fd, (void *) &tmstamp, sizeof (tmstamp));
678 if (ghostly) tmstamp = 0;
679 else tmstamp = (moves - tmstamp);
680 mread(fd, (void *) &n_regions, sizeof (n_regions));
681 max_regions = n_regions;
682 if (n_regions > 0)
683 regions = (NhRegion **) alloc(sizeof (NhRegion *) * n_regions);
684 for (i = 0; i < n_regions; i++) {
685 regions[i] = (NhRegion *) alloc(sizeof (NhRegion));
686 mread(fd, (void *) &regions[i]->bounding_box, sizeof (NhRect));
687 mread(fd, (void *) &regions[i]->nrects, sizeof (short));
689 if (regions[i]->nrects > 0)
690 regions[i]->rects = (NhRect *)
691 alloc(sizeof (NhRect) * regions[i]->nrects);
692 for (j = 0; j < regions[i]->nrects; j++)
693 mread(fd, (void *) &regions[i]->rects[j], sizeof (NhRect));
694 mread(fd, (void *) &regions[i]->attach_2_u, sizeof (boolean));
695 mread(fd, (void *) &regions[i]->attach_2_m, sizeof (unsigned));
697 mread(fd, (void *) &n, sizeof n);
698 if (n > 0) {
699 msg_buf = (char *) alloc(n + 1);
700 mread(fd, (void *) msg_buf, n);
701 msg_buf[n] = '\0';
702 regions[i]->enter_msg = (const char *) msg_buf;
703 } else
704 regions[i]->enter_msg = NULL;
706 mread(fd, (void *) &n, sizeof n);
707 if (n > 0) {
708 msg_buf = (char *) alloc(n + 1);
709 mread(fd, (void *) msg_buf, n);
710 msg_buf[n] = '\0';
711 regions[i]->leave_msg = (const char *) msg_buf;
712 } else
713 regions[i]->leave_msg = NULL;
715 mread(fd, (void *) &regions[i]->ttl, sizeof (short));
716 /* check for expired region */
717 if (regions[i]->ttl >= 0)
718 regions[i]->ttl =
719 (regions[i]->ttl > tmstamp) ? regions[i]->ttl - tmstamp : 0;
720 mread(fd, (void *) &regions[i]->expire_f, sizeof (short));
721 mread(fd, (void *) &regions[i]->can_enter_f, sizeof (short));
722 mread(fd, (void *) &regions[i]->enter_f, sizeof (short));
723 mread(fd, (void *) &regions[i]->can_leave_f, sizeof (short));
724 mread(fd, (void *) &regions[i]->leave_f, sizeof (short));
725 mread(fd, (void *) &regions[i]->inside_f, sizeof (short));
726 mread(fd, (void *) &regions[i]->player_flags, sizeof (boolean));
727 if (ghostly) { /* settings pertained to old player */
728 clear_hero_inside(regions[i]);
729 clear_heros_fault(regions[i]);
731 mread(fd, (void *) &regions[i]->n_monst, sizeof (short));
732 if (regions[i]->n_monst > 0)
733 regions[i]->monsters =
734 (unsigned *) alloc(sizeof (unsigned) * regions[i]->n_monst);
735 else
736 regions[i]->monsters = NULL;
737 regions[i]->max_monst = regions[i]->n_monst;
738 for (j = 0; j < regions[i]->n_monst; j++)
739 mread(fd, (void *) &regions[i]->monsters[j],
740 sizeof (unsigned));
741 mread(fd, (void *) &regions[i]->visible, sizeof (boolean));
742 mread(fd, (void *) &regions[i]->glyph, sizeof (int));
743 mread(fd, (void *) &regions[i]->arg, sizeof (void *));
745 /* remove expired regions, do not trigger the expire_f callback (yet!);
746 also update monster lists if this data is coming from a bones file */
747 for (i = n_regions - 1; i >= 0; i--)
748 if (regions[i]->ttl == 0)
749 remove_region(regions[i]);
750 else if (ghostly && regions[i]->n_monst > 0)
751 reset_region_mids(regions[i]);
754 /* update monster IDs for region being loaded from bones; `ghostly' implied */
755 static void
756 reset_region_mids(reg)
757 NhRegion *reg;
759 int i = 0, n = reg->n_monst;
760 unsigned *mid_list = reg->monsters;
762 while (i < n)
763 if (!lookup_id_mapping(mid_list[i], &mid_list[i])) {
764 /* shrink list to remove missing monster; order doesn't matter */
765 mid_list[i] = mid_list[--n];
766 } else {
767 /* move on to next monster */
768 ++i;
770 reg->n_monst = n;
771 return;
774 #if 0
775 /* not yet used */
777 /*--------------------------------------------------------------*
779 * Create Region with just a message *
781 *--------------------------------------------------------------*/
783 NhRegion *
784 create_msg_region(x, y, w, h, msg_enter, msg_leave)
785 xchar x, y;
786 xchar w, h;
787 const char *msg_enter;
788 const char *msg_leave;
790 NhRect tmprect;
791 NhRegion *reg = create_region((NhRect *) 0, 0);
793 reg->enter_msg = msg_enter;
794 reg->leave_msg = msg_leave;
795 tmprect.lx = x;
796 tmprect.ly = y;
797 tmprect.hx = x + w;
798 tmprect.hy = y + h;
799 add_rect_to_reg(reg, &tmprect);
800 reg->ttl = -1;
801 return reg;
805 /*--------------------------------------------------------------*
807 * Force Field Related Code *
808 * (unused yet) *
809 *--------------------------------------------------------------*/
811 boolean
812 enter_force_field(p1, p2)
813 void * p1;
814 void * p2;
816 struct monst *mtmp;
818 if (p2 == NULL) { /* That means the player */
819 if (!Blind)
820 You("bump into %s. Ouch!",
821 FunnyHallu ? "an invisible tree" :
822 "some kind of invisible wall");
823 else
824 pline("Ouch!");
825 } else {
826 mtmp = (struct monst *) p2;
827 if (canseemon(mtmp))
828 pline("%s bumps into %s!", Monnam(mtmp), something);
830 return FALSE;
833 NhRegion *
834 create_force_field(x, y, radius, ttl)
835 xchar x, y;
836 int radius, ttl;
838 int i;
839 NhRegion *ff;
840 int nrect;
841 NhRect tmprect;
843 ff = create_region((NhRect *) 0, 0);
844 nrect = radius;
845 tmprect.lx = x;
846 tmprect.hx = x;
847 tmprect.ly = y - (radius - 1);
848 tmprect.hy = y + (radius - 1);
849 for (i = 0; i < nrect; i++) {
850 add_rect_to_reg(ff, &tmprect);
851 tmprect.lx--;
852 tmprect.hx++;
853 tmprect.ly++;
854 tmprect.hy--;
856 ff->ttl = ttl;
857 if (!in_mklev && !flags.mon_moving)
858 set_heros_fault(ff); /* assume player has created it */
859 /* ff->can_enter_f = enter_force_field; */
860 /* ff->can_leave_f = enter_force_field; */
861 add_region(ff);
862 return ff;
865 #endif /*0*/
867 /*--------------------------------------------------------------*
869 * Gas cloud related code *
871 *--------------------------------------------------------------*/
874 * Here is an example of an expire function that may prolong
875 * region life after some mods...
877 boolean
878 expire_gas_cloud(p1, p2)
879 void * p1;
880 void * p2;
882 NhRegion *reg;
883 int damage;
885 reg = (NhRegion *) p1;
886 damage = (int) reg->arg;
888 /* If it was a thick cloud, it dissipates a little first */
889 if (damage >= 5) {
890 damage /= 2; /* It dissipates, let's do less damage */
891 reg->arg = (void *) damage;
892 reg->ttl = 2; /* Here's the trick : reset ttl */
893 return FALSE; /* THEN return FALSE, means "still there" */
895 return TRUE; /* OK, it's gone, you can free it! */
898 boolean
899 revive_cthulhu(p1, p2)
900 void * p1;
901 void * p2;
903 boolean ret = expire_gas_cloud(p1, p2);
904 if (ret) {
905 /* Bring back Cthulhu! */
906 int cx, cy;
907 NhRegion *reg = (NhRegion *) p1;
908 struct monst *cthulhu = NULL;
909 coord cc;
911 cx = (reg->bounding_box.lx + reg->bounding_box.hx) / 2;
912 cy = (reg->bounding_box.ly + reg->bounding_box.hy) / 2;
914 if (enexto(&cc, cx, cy, &mons[PM_CTHULHU])) {
915 cx = cc.x;
916 cy = cc.y;
917 } else {
918 cx = cy = 0; /* Place Cthulhu randomly */
921 /* Make sure Cthulhu doesn't get the Amulet again! :-) */
922 cthulhu = makemon(&mons[PM_CTHULHU], cx, cy,
923 MM_NOCOUNTBIRTH | NO_MINVENT);
924 if (cthulhu && canseemon(cthulhu))
925 pline("%s reforms!", Monnam(cthulhu));
927 return ret;
930 boolean
931 inside_gas_cloud(p1, p2)
932 void * p1;
933 void * p2;
935 NhRegion *reg;
936 struct monst *mtmp;
937 int dam;
939 reg = (NhRegion *) p1;
940 dam = (int) reg->arg;
941 if (p2 == NULL) { /* This means *YOU* Bozo! */
942 if (nonliving(youmonst.data) || Breathless)
943 return FALSE;
944 if (!Blind)
945 make_blinded(1L, FALSE);
946 if (!Poison_resistance || (!rn2(10) && !StrongPoison_resistance) ) {
947 pline("%s is burning your %s!", Something, makeplural(body_part(LUNG)));
948 You("cough and spit blood!");
949 losehp(rnd(dam) + 5, "gas cloud", KILLED_BY_AN);
950 return FALSE;
951 } else {
952 You("cough!");
953 return FALSE;
955 } else { /* A monster is inside the cloud */
956 mtmp = (struct monst *) p2;
958 /* Non living and non breathing monsters are not concerned */
959 if (!nonliving(mtmp->data) && !breathless(mtmp->data) && (!mtmp->egotype_undead) ) {
960 if (cansee(mtmp->mx, mtmp->my))
961 pline("%s coughs!", Monnam(mtmp));
962 if (heros_fault(reg))
963 setmangry(mtmp);
964 if (haseyes(mtmp->data) && mtmp->mcansee) {
965 mtmp->mblinded = 1;
966 mtmp->mcansee = 0;
968 if (resists_poison(mtmp) && !player_will_pierce_resistance())
969 return FALSE;
970 mtmp->mhp -= rnd(dam) + 5;
971 if (mtmp->mhp <= 0) {
972 if (heros_fault(reg))
973 killed(mtmp);
974 else
975 monkilled(mtmp, "gas cloud", AD_DRST);
976 if (mtmp->mhp <= 0) { /* not lifesaved */
977 return TRUE;
982 return FALSE; /* Monster is still alive */
985 NhRegion *
986 create_cthulhu_death_cloud(x, y, radius, damage)
987 xchar x, y;
988 int radius;
989 int damage;
991 NhRegion *cloud;
993 cloud = create_gas_cloud(x, y, radius, damage);
994 if (cloud) cloud->expire_f = REVIVE_CTHULHU;
996 return cloud;
999 NhRegion *
1000 create_gas_cloud(x, y, radius, damage)
1001 xchar x, y;
1002 int radius;
1003 int damage;
1005 NhRegion *cloud;
1006 int i, nrect;
1007 NhRect tmprect;
1009 cloud = create_region((NhRect *) 0, 0);
1010 nrect = radius;
1011 tmprect.lx = x;
1012 tmprect.hx = x;
1013 tmprect.ly = y - (radius - 1);
1014 tmprect.hy = y + (radius - 1);
1015 for (i = 0; i < nrect; i++) {
1016 add_rect_to_reg(cloud, &tmprect);
1017 tmprect.lx--;
1018 tmprect.hx++;
1019 tmprect.ly++;
1020 tmprect.hy--;
1022 cloud->ttl = rn1(3,4);
1023 if (!in_mklev && !flags.mon_moving) {
1024 set_heros_fault(cloud); /* assume player has created it */
1025 if (practicantterror) {
1026 pline("%s booms: 'You're causing a smelling nuisance! That's very definitely not allowed, so you pay 2000 zorkmids now and then stop stinking around in my lab!'", noroelaname());
1027 fineforpracticant(2000, 0, 0);
1030 cloud->inside_f = INSIDE_GAS_CLOUD;
1031 cloud->expire_f = EXPIRE_GAS_CLOUD;
1032 cloud->arg = (void *) damage;
1033 cloud->visible = TRUE;
1034 cloud->glyph = cmap_to_glyph(S_cloud);
1035 add_region(cloud);
1036 return cloud;
1039 /*region.c*/