use of #terrain while underwater
[aNetHack.git] / src / region.c
blob240f7270c92bf357d6c1be19eabf49af5168a102
1 /* NetHack 3.6 region.c $NHDT-Date: 1446892454 2015/11/07 10:34:14 $ $NHDT-Branch: master $:$NHDT-Revision: 1.36 $ */
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 FDECL(inside_gas_cloud, (genericptr, genericptr));
21 boolean FDECL(expire_gas_cloud, (genericptr, genericptr));
22 boolean FDECL(inside_rect, (NhRect *, int, int));
23 boolean FDECL(inside_region, (NhRegion *, int, int));
24 NhRegion *FDECL(create_region, (NhRect *, int));
25 void FDECL(add_rect_to_reg, (NhRegion *, NhRect *));
26 void FDECL(add_mon_to_reg, (NhRegion *, struct monst *));
27 void FDECL(remove_mon_from_reg, (NhRegion *, struct monst *));
28 boolean FDECL(mon_in_region, (NhRegion *, struct monst *));
30 #if 0
31 NhRegion *FDECL(clone_region, (NhRegion *));
32 #endif
33 void FDECL(free_region, (NhRegion *));
34 void FDECL(add_region, (NhRegion *));
35 void FDECL(remove_region, (NhRegion *));
37 #if 0
38 void FDECL(replace_mon_regions, (struct monst *,struct monst *));
39 void FDECL(remove_mon_from_regions, (struct monst *));
40 NhRegion *FDECL(create_msg_region, (XCHAR_P,XCHAR_P,XCHAR_P,XCHAR_P,
41 const char *,const char *));
42 boolean FDECL(enter_force_field, (genericptr,genericptr));
43 NhRegion *FDECL(create_force_field, (XCHAR_P,XCHAR_P,int,long));
44 #endif
46 STATIC_DCL void FDECL(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
55 /* Should be inlined. */
56 boolean
57 inside_rect(r, x, y)
58 NhRect *r;
59 int x, y;
61 return (boolean) (x >= r->lx && x <= r->hx && y >= r->ly && y <= r->hy);
65 * Check if a point is inside a region.
67 boolean
68 inside_region(reg, x, y)
69 NhRegion *reg;
70 int x, y;
72 int i;
74 if (reg == (NhRegion *) 0 || !inside_rect(&(reg->bounding_box), x, y))
75 return FALSE;
76 for (i = 0; i < reg->nrects; i++)
77 if (inside_rect(&(reg->rects[i]), x, y))
78 return TRUE;
79 return FALSE;
83 * Create a region. It does not activate it.
85 NhRegion *
86 create_region(rects, nrect)
87 NhRect *rects;
88 int nrect;
90 int i;
91 NhRegion *reg;
93 reg = (NhRegion *) alloc(sizeof(NhRegion));
94 /* Determines bounding box */
95 if (nrect > 0) {
96 reg->bounding_box = rects[0];
97 } else {
98 reg->bounding_box.lx = COLNO;
99 reg->bounding_box.ly = ROWNO;
100 reg->bounding_box.hx = 0; /* 1 */
101 reg->bounding_box.hy = 0;
103 reg->nrects = nrect;
104 reg->rects = (nrect > 0) ? (NhRect *) alloc(nrect * sizeof (NhRect)) : 0;
105 for (i = 0; i < nrect; i++) {
106 if (rects[i].lx < reg->bounding_box.lx)
107 reg->bounding_box.lx = rects[i].lx;
108 if (rects[i].ly < reg->bounding_box.ly)
109 reg->bounding_box.ly = rects[i].ly;
110 if (rects[i].hx > reg->bounding_box.hx)
111 reg->bounding_box.hx = rects[i].hx;
112 if (rects[i].hy > reg->bounding_box.hy)
113 reg->bounding_box.hy = rects[i].hy;
114 reg->rects[i] = rects[i];
116 reg->ttl = -1L; /* Defaults */
117 reg->attach_2_u = FALSE;
118 reg->attach_2_m = 0;
119 /* reg->attach_2_o = NULL; */
120 reg->enter_msg = (const char *) 0;
121 reg->leave_msg = (const char *) 0;
122 reg->expire_f = NO_CALLBACK;
123 reg->enter_f = NO_CALLBACK;
124 reg->can_enter_f = NO_CALLBACK;
125 reg->leave_f = NO_CALLBACK;
126 reg->can_leave_f = NO_CALLBACK;
127 reg->inside_f = NO_CALLBACK;
128 clear_hero_inside(reg);
129 clear_heros_fault(reg);
130 reg->n_monst = 0;
131 reg->max_monst = 0;
132 reg->monsters = (unsigned int *) 0;
133 reg->arg = zeroany;
134 return reg;
138 * Add rectangle to region.
140 void
141 add_rect_to_reg(reg, rect)
142 NhRegion *reg;
143 NhRect *rect;
145 NhRect *tmp_rect;
147 tmp_rect = (NhRect *) alloc((reg->nrects + 1) * sizeof (NhRect));
148 if (reg->nrects > 0) {
149 (void) memcpy((genericptr_t) tmp_rect, (genericptr_t) reg->rects,
150 reg->nrects * sizeof (NhRect));
151 free((genericptr_t) reg->rects);
153 tmp_rect[reg->nrects] = *rect;
154 reg->nrects++;
155 reg->rects = tmp_rect;
156 /* Update bounding box if needed */
157 if (reg->bounding_box.lx > rect->lx)
158 reg->bounding_box.lx = rect->lx;
159 if (reg->bounding_box.ly > rect->ly)
160 reg->bounding_box.ly = rect->ly;
161 if (reg->bounding_box.hx < rect->hx)
162 reg->bounding_box.hx = rect->hx;
163 if (reg->bounding_box.hy < rect->hy)
164 reg->bounding_box.hy = rect->hy;
168 * Add a monster to the region
170 void
171 add_mon_to_reg(reg, mon)
172 NhRegion *reg;
173 struct monst *mon;
175 int i;
176 unsigned *tmp_m;
178 if (reg->max_monst <= reg->n_monst) {
179 tmp_m = (unsigned *) alloc(sizeof (unsigned)
180 * (reg->max_monst + MONST_INC));
181 if (reg->max_monst > 0) {
182 for (i = 0; i < reg->max_monst; i++)
183 tmp_m[i] = reg->monsters[i];
184 free((genericptr_t) reg->monsters);
186 reg->monsters = tmp_m;
187 reg->max_monst += MONST_INC;
189 reg->monsters[reg->n_monst++] = mon->m_id;
193 * Remove a monster from the region list (it left or died...)
195 void
196 remove_mon_from_reg(reg, mon)
197 NhRegion *reg;
198 struct monst *mon;
200 register int i;
202 for (i = 0; i < reg->n_monst; i++)
203 if (reg->monsters[i] == mon->m_id) {
204 reg->n_monst--;
205 reg->monsters[i] = reg->monsters[reg->n_monst];
206 return;
211 * Check if a monster is inside the region.
212 * It's probably quicker to check with the region internal list
213 * than to check for coordinates.
215 boolean
216 mon_in_region(reg, mon)
217 NhRegion *reg;
218 struct monst *mon;
220 int i;
222 for (i = 0; i < reg->n_monst; i++)
223 if (reg->monsters[i] == mon->m_id)
224 return TRUE;
225 return FALSE;
228 #if 0
229 /* not yet used */
232 * Clone (make a standalone copy) the region.
234 NhRegion *
235 clone_region(reg)
236 NhRegion *reg;
238 NhRegion *ret_reg;
240 ret_reg = create_region(reg->rects, reg->nrects);
241 ret_reg->ttl = reg->ttl;
242 ret_reg->attach_2_u = reg->attach_2_u;
243 ret_reg->attach_2_m = reg->attach_2_m;
244 /* ret_reg->attach_2_o = reg->attach_2_o; */
245 ret_reg->expire_f = reg->expire_f;
246 ret_reg->enter_f = reg->enter_f;
247 ret_reg->can_enter_f = reg->can_enter_f;
248 ret_reg->leave_f = reg->leave_f;
249 ret_reg->can_leave_f = reg->can_leave_f;
250 ret_reg->player_flags = reg->player_flags; /* set/clear_hero_inside,&c*/
251 ret_reg->n_monst = reg->n_monst;
252 if (reg->n_monst > 0) {
253 ret_reg->monsters = (unsigned int *)
254 alloc((sizeof (unsigned)) * reg->n_monst);
255 (void) memcpy((genericptr_t) ret_reg->monsters,
256 (genericptr_t) reg->monsters,
257 sizeof (unsigned) * reg->n_monst);
258 } else
259 ret_reg->monsters = (unsigned int *) 0;
260 return ret_reg;
263 #endif /*0*/
266 * Free mem from region.
268 void
269 free_region(reg)
270 NhRegion *reg;
272 if (reg) {
273 if (reg->rects)
274 free((genericptr_t) reg->rects);
275 if (reg->monsters)
276 free((genericptr_t) reg->monsters);
277 if (reg->enter_msg)
278 free((genericptr_t) reg->enter_msg);
279 if (reg->leave_msg)
280 free((genericptr_t) reg->leave_msg);
281 free((genericptr_t) reg);
286 * Add a region to the list.
287 * This actually activates the region.
289 void
290 add_region(reg)
291 NhRegion *reg;
293 NhRegion **tmp_reg;
294 int i, j;
296 if (max_regions <= n_regions) {
297 tmp_reg = regions;
298 regions =
299 (NhRegion **) alloc((max_regions + 10) * sizeof (NhRegion *));
300 if (max_regions > 0) {
301 (void) memcpy((genericptr_t) regions, (genericptr_t) tmp_reg,
302 max_regions * sizeof (NhRegion *));
303 free((genericptr_t) tmp_reg);
305 max_regions += 10;
307 regions[n_regions] = reg;
308 n_regions++;
309 /* Check for monsters inside the region */
310 for (i = reg->bounding_box.lx; i <= reg->bounding_box.hx; i++)
311 for (j = reg->bounding_box.ly; j <= reg->bounding_box.hy; j++) {
312 /* Some regions can cross the level boundaries */
313 if (!isok(i, j))
314 continue;
315 if (MON_AT(i, j) && inside_region(reg, i, j))
316 add_mon_to_reg(reg, level.monsters[i][j]);
317 if (reg->visible && cansee(i, j))
318 newsym(i, j);
320 /* Check for player now... */
321 if (inside_region(reg, u.ux, u.uy))
322 set_hero_inside(reg);
323 else
324 clear_hero_inside(reg);
328 * Remove a region from the list & free it.
330 void
331 remove_region(reg)
332 NhRegion *reg;
334 register int i, x, y;
336 for (i = 0; i < n_regions; i++)
337 if (regions[i] == reg)
338 break;
339 if (i == n_regions)
340 return;
342 /* Update screen if necessary */
343 reg->ttl = -2L; /* for visible_region_at */
344 if (reg->visible)
345 for (x = reg->bounding_box.lx; x <= reg->bounding_box.hx; x++)
346 for (y = reg->bounding_box.ly; y <= reg->bounding_box.hy; y++)
347 if (isok(x, y) && inside_region(reg, x, y) && cansee(x, y))
348 newsym(x, y);
350 free_region(reg);
351 regions[i] = regions[n_regions - 1];
352 regions[n_regions - 1] = (NhRegion *) 0;
353 n_regions--;
357 * Remove all regions and clear all related data (This must be down
358 * when changing level, for instance).
360 void
361 clear_regions()
363 register int i;
365 for (i = 0; i < n_regions; i++)
366 free_region(regions[i]);
367 n_regions = 0;
368 if (max_regions > 0)
369 free((genericptr_t) regions);
370 max_regions = 0;
371 regions = (NhRegion **) 0;
375 * This function is called every turn.
376 * It makes the regions age, if necessary and calls the appropriate
377 * callbacks when needed.
379 void
380 run_regions()
382 register int i, j, k;
383 int f_indx;
385 /* End of life ? */
386 /* Do it backward because the array will be modified */
387 for (i = n_regions - 1; i >= 0; i--) {
388 if (regions[i]->ttl == 0L) {
389 if ((f_indx = regions[i]->expire_f) == NO_CALLBACK
390 || (*callbacks[f_indx])(regions[i], (genericptr_t) 0))
391 remove_region(regions[i]);
395 /* Process remaining regions */
396 for (i = 0; i < n_regions; i++) {
397 /* Make the region age */
398 if (regions[i]->ttl > 0L)
399 regions[i]->ttl--;
400 /* Check if player is inside region */
401 f_indx = regions[i]->inside_f;
402 if (f_indx != NO_CALLBACK && hero_inside(regions[i]))
403 (void) (*callbacks[f_indx])(regions[i], (genericptr_t) 0);
404 /* Check if any monster is inside region */
405 if (f_indx != NO_CALLBACK) {
406 for (j = 0; j < regions[i]->n_monst; j++) {
407 struct monst *mtmp =
408 find_mid(regions[i]->monsters[j], FM_FMON);
410 if (!mtmp || mtmp->mhp <= 0
411 || (*callbacks[f_indx])(regions[i], mtmp)) {
412 /* The monster died, remove it from list */
413 k = (regions[i]->n_monst -= 1);
414 regions[i]->monsters[j] = regions[i]->monsters[k];
415 regions[i]->monsters[k] = 0;
416 --j; /* current slot has been reused; recheck it next */
424 * check whether player enters/leaves one or more regions.
426 boolean
427 in_out_region(x, y)
428 xchar x, y;
430 int i, f_indx;
432 /* First check if we can do the move */
433 for (i = 0; i < n_regions; i++) {
434 if (inside_region(regions[i], x, y) && !hero_inside(regions[i])
435 && !regions[i]->attach_2_u) {
436 if ((f_indx = regions[i]->can_enter_f) != NO_CALLBACK)
437 if (!(*callbacks[f_indx])(regions[i], (genericptr_t) 0))
438 return FALSE;
439 } else if (hero_inside(regions[i]) && !inside_region(regions[i], x, y)
440 && !regions[i]->attach_2_u) {
441 if ((f_indx = regions[i]->can_leave_f) != NO_CALLBACK)
442 if (!(*callbacks[f_indx])(regions[i], (genericptr_t) 0))
443 return FALSE;
447 /* Callbacks for the regions we do leave */
448 for (i = 0; i < n_regions; i++)
449 if (hero_inside(regions[i]) && !regions[i]->attach_2_u
450 && !inside_region(regions[i], x, y)) {
451 clear_hero_inside(regions[i]);
452 if (regions[i]->leave_msg != (const char *) 0)
453 pline1(regions[i]->leave_msg);
454 if ((f_indx = regions[i]->leave_f) != NO_CALLBACK)
455 (void) (*callbacks[f_indx])(regions[i], (genericptr_t) 0);
458 /* Callbacks for the regions we do enter */
459 for (i = 0; i < n_regions; i++)
460 if (!hero_inside(regions[i]) && !regions[i]->attach_2_u
461 && inside_region(regions[i], x, y)) {
462 set_hero_inside(regions[i]);
463 if (regions[i]->enter_msg != (const char *) 0)
464 pline1(regions[i]->enter_msg);
465 if ((f_indx = regions[i]->enter_f) != NO_CALLBACK)
466 (void) (*callbacks[f_indx])(regions[i], (genericptr_t) 0);
468 return TRUE;
472 * check whether a monster enters/leaves one or more region.
474 boolean
475 m_in_out_region(mon, x, y)
476 struct monst *mon;
477 xchar x, y;
479 int i, f_indx;
481 /* First check if we can do the move */
482 for (i = 0; i < n_regions; i++) {
483 if (inside_region(regions[i], x, y) && !mon_in_region(regions[i], mon)
484 && regions[i]->attach_2_m != mon->m_id) {
485 if ((f_indx = regions[i]->can_enter_f) != NO_CALLBACK)
486 if (!(*callbacks[f_indx])(regions[i], mon))
487 return FALSE;
488 } else if (mon_in_region(regions[i], mon)
489 && !inside_region(regions[i], x, y)
490 && regions[i]->attach_2_m != mon->m_id) {
491 if ((f_indx = regions[i]->can_leave_f) != NO_CALLBACK)
492 if (!(*callbacks[f_indx])(regions[i], mon))
493 return FALSE;
497 /* Callbacks for the regions we do leave */
498 for (i = 0; i < n_regions; i++)
499 if (mon_in_region(regions[i], mon)
500 && regions[i]->attach_2_m != mon->m_id
501 && !inside_region(regions[i], x, y)) {
502 remove_mon_from_reg(regions[i], mon);
503 if ((f_indx = regions[i]->leave_f) != NO_CALLBACK)
504 (void) (*callbacks[f_indx])(regions[i], mon);
507 /* Callbacks for the regions we do enter */
508 for (i = 0; i < n_regions; i++)
509 if (!hero_inside(regions[i]) && !regions[i]->attach_2_u
510 && inside_region(regions[i], x, y)) {
511 add_mon_to_reg(regions[i], mon);
512 if ((f_indx = regions[i]->enter_f) != NO_CALLBACK)
513 (void) (*callbacks[f_indx])(regions[i], mon);
515 return TRUE;
519 * Checks player's regions after a teleport for instance.
521 void
522 update_player_regions()
524 register int i;
526 for (i = 0; i < n_regions; i++)
527 if (!regions[i]->attach_2_u && inside_region(regions[i], u.ux, u.uy))
528 set_hero_inside(regions[i]);
529 else
530 clear_hero_inside(regions[i]);
534 * Ditto for a specified monster.
536 void
537 update_monster_region(mon)
538 struct monst *mon;
540 register int i;
542 for (i = 0; i < n_regions; i++) {
543 if (inside_region(regions[i], mon->mx, mon->my)) {
544 if (!mon_in_region(regions[i], mon))
545 add_mon_to_reg(regions[i], mon);
546 } else {
547 if (mon_in_region(regions[i], mon))
548 remove_mon_from_reg(regions[i], mon);
553 #if 0
554 /* not yet used */
557 * Change monster pointer in regions
558 * This happens, for instance, when a monster grows and
559 * need a new structure (internally that is).
561 void
562 replace_mon_regions(monold, monnew)
563 struct monst *monold, *monnew;
565 register int i;
567 for (i = 0; i < n_regions; i++)
568 if (mon_in_region(regions[i], monold)) {
569 remove_mon_from_reg(regions[i], monold);
570 add_mon_to_reg(regions[i], monnew);
575 * Remove monster from all regions it was in (ie monster just died)
577 void
578 remove_mon_from_regions(mon)
579 struct monst *mon;
581 register int i;
583 for (i = 0; i < n_regions; i++)
584 if (mon_in_region(regions[i], mon))
585 remove_mon_from_reg(regions[i], mon);
588 #endif /*0*/
591 * Check if a spot is under a visible region (eg: gas cloud).
592 * Returns NULL if not, otherwise returns region.
594 NhRegion *
595 visible_region_at(x, y)
596 xchar x, y;
598 register int i;
600 for (i = 0; i < n_regions; i++)
601 if (inside_region(regions[i], x, y) && regions[i]->visible
602 && regions[i]->ttl != -2L)
603 return regions[i];
604 return (NhRegion *) 0;
607 void
608 show_region(reg, x, y)
609 NhRegion *reg;
610 xchar x, y;
612 show_glyph(x, y, reg->glyph);
616 * save_regions :
618 void
619 save_regions(fd, mode)
620 int fd;
621 int mode;
623 int i, j;
624 unsigned n;
626 if (!perform_bwrite(mode))
627 goto skip_lots;
629 bwrite(fd, (genericptr_t) &moves, sizeof(moves)); /* timestamp */
630 bwrite(fd, (genericptr_t) &n_regions, sizeof(n_regions));
631 for (i = 0; i < n_regions; i++) {
632 bwrite(fd, (genericptr_t) &regions[i]->bounding_box, sizeof(NhRect));
633 bwrite(fd, (genericptr_t) &regions[i]->nrects, sizeof(short));
634 for (j = 0; j < regions[i]->nrects; j++)
635 bwrite(fd, (genericptr_t) &regions[i]->rects[j], sizeof(NhRect));
636 bwrite(fd, (genericptr_t) &regions[i]->attach_2_u, sizeof(boolean));
637 n = 0;
638 bwrite(fd, (genericptr_t) &regions[i]->attach_2_m, sizeof(unsigned));
639 n = regions[i]->enter_msg != (const char *) 0
640 ? strlen(regions[i]->enter_msg)
641 : 0;
642 bwrite(fd, (genericptr_t) &n, sizeof n);
643 if (n > 0)
644 bwrite(fd, (genericptr_t) regions[i]->enter_msg, n);
645 n = regions[i]->leave_msg != (const char *) 0
646 ? strlen(regions[i]->leave_msg)
647 : 0;
648 bwrite(fd, (genericptr_t) &n, sizeof n);
649 if (n > 0)
650 bwrite(fd, (genericptr_t) regions[i]->leave_msg, n);
651 bwrite(fd, (genericptr_t) &regions[i]->ttl, sizeof(long));
652 bwrite(fd, (genericptr_t) &regions[i]->expire_f, sizeof(short));
653 bwrite(fd, (genericptr_t) &regions[i]->can_enter_f, sizeof(short));
654 bwrite(fd, (genericptr_t) &regions[i]->enter_f, sizeof(short));
655 bwrite(fd, (genericptr_t) &regions[i]->can_leave_f, sizeof(short));
656 bwrite(fd, (genericptr_t) &regions[i]->leave_f, sizeof(short));
657 bwrite(fd, (genericptr_t) &regions[i]->inside_f, sizeof(short));
658 bwrite(fd, (genericptr_t) &regions[i]->player_flags,
659 sizeof(unsigned int));
660 bwrite(fd, (genericptr_t) &regions[i]->n_monst, sizeof(short));
661 for (j = 0; j < regions[i]->n_monst; j++)
662 bwrite(fd, (genericptr_t) &regions[i]->monsters[j],
663 sizeof(unsigned));
664 bwrite(fd, (genericptr_t) &regions[i]->visible, sizeof(boolean));
665 bwrite(fd, (genericptr_t) &regions[i]->glyph, sizeof(int));
666 bwrite(fd, (genericptr_t) &regions[i]->arg, sizeof(anything));
669 skip_lots:
670 if (release_data(mode))
671 clear_regions();
674 void
675 rest_regions(fd, ghostly)
676 int fd;
677 boolean ghostly; /* If a bones file restore */
679 int i, j;
680 unsigned n;
681 long tmstamp;
682 char *msg_buf;
684 clear_regions(); /* Just for security */
685 mread(fd, (genericptr_t) &tmstamp, sizeof(tmstamp));
686 if (ghostly)
687 tmstamp = 0;
688 else
689 tmstamp = (moves - tmstamp);
690 mread(fd, (genericptr_t) &n_regions, sizeof(n_regions));
691 max_regions = n_regions;
692 if (n_regions > 0)
693 regions = (NhRegion **) alloc(sizeof(NhRegion *) * n_regions);
694 for (i = 0; i < n_regions; i++) {
695 regions[i] = (NhRegion *) alloc(sizeof(NhRegion));
696 mread(fd, (genericptr_t) &regions[i]->bounding_box, sizeof(NhRect));
697 mread(fd, (genericptr_t) &regions[i]->nrects, sizeof(short));
699 if (regions[i]->nrects > 0)
700 regions[i]->rects =
701 (NhRect *) alloc(sizeof(NhRect) * regions[i]->nrects);
702 for (j = 0; j < regions[i]->nrects; j++)
703 mread(fd, (genericptr_t) &regions[i]->rects[j], sizeof(NhRect));
704 mread(fd, (genericptr_t) &regions[i]->attach_2_u, sizeof(boolean));
705 mread(fd, (genericptr_t) &regions[i]->attach_2_m, sizeof(unsigned));
707 mread(fd, (genericptr_t) &n, sizeof n);
708 if (n > 0) {
709 msg_buf = (char *) alloc(n + 1);
710 mread(fd, (genericptr_t) msg_buf, n);
711 msg_buf[n] = '\0';
712 regions[i]->enter_msg = (const char *) msg_buf;
713 } else
714 regions[i]->enter_msg = (const char *) 0;
716 mread(fd, (genericptr_t) &n, sizeof n);
717 if (n > 0) {
718 msg_buf = (char *) alloc(n + 1);
719 mread(fd, (genericptr_t) msg_buf, n);
720 msg_buf[n] = '\0';
721 regions[i]->leave_msg = (const char *) msg_buf;
722 } else
723 regions[i]->leave_msg = (const char *) 0;
725 mread(fd, (genericptr_t) &regions[i]->ttl, sizeof(long));
726 /* check for expired region */
727 if (regions[i]->ttl >= 0L)
728 regions[i]->ttl =
729 (regions[i]->ttl > tmstamp) ? regions[i]->ttl - tmstamp : 0L;
730 mread(fd, (genericptr_t) &regions[i]->expire_f, sizeof(short));
731 mread(fd, (genericptr_t) &regions[i]->can_enter_f, sizeof(short));
732 mread(fd, (genericptr_t) &regions[i]->enter_f, sizeof(short));
733 mread(fd, (genericptr_t) &regions[i]->can_leave_f, sizeof(short));
734 mread(fd, (genericptr_t) &regions[i]->leave_f, sizeof(short));
735 mread(fd, (genericptr_t) &regions[i]->inside_f, sizeof(short));
736 mread(fd, (genericptr_t) &regions[i]->player_flags,
737 sizeof(unsigned int));
738 if (ghostly) { /* settings pertained to old player */
739 clear_hero_inside(regions[i]);
740 clear_heros_fault(regions[i]);
742 mread(fd, (genericptr_t) &regions[i]->n_monst, sizeof(short));
743 if (regions[i]->n_monst > 0)
744 regions[i]->monsters =
745 (unsigned *) alloc(sizeof(unsigned) * regions[i]->n_monst);
746 else
747 regions[i]->monsters = (unsigned int *) 0;
748 regions[i]->max_monst = regions[i]->n_monst;
749 for (j = 0; j < regions[i]->n_monst; j++)
750 mread(fd, (genericptr_t) &regions[i]->monsters[j],
751 sizeof(unsigned));
752 mread(fd, (genericptr_t) &regions[i]->visible, sizeof(boolean));
753 mread(fd, (genericptr_t) &regions[i]->glyph, sizeof(int));
754 mread(fd, (genericptr_t) &regions[i]->arg, sizeof(anything));
756 /* remove expired regions, do not trigger the expire_f callback (yet!);
757 also update monster lists if this data is coming from a bones file */
758 for (i = n_regions - 1; i >= 0; i--)
759 if (regions[i]->ttl == 0L)
760 remove_region(regions[i]);
761 else if (ghostly && regions[i]->n_monst > 0)
762 reset_region_mids(regions[i]);
765 /* to support '#stats' wizard-mode command */
766 void
767 region_stats(hdrfmt, hdrbuf, count, size)
768 const char *hdrfmt;
769 char *hdrbuf;
770 long *count, *size;
772 NhRegion *rg;
773 int i;
775 /* other stats formats take one parameter; this takes two */
776 Sprintf(hdrbuf, hdrfmt, (long) sizeof (NhRegion), (long) sizeof (NhRect));
777 *count = (long) n_regions; /* might be 0 even though max_regions isn't */
778 *size = (long) max_regions * (long) sizeof (NhRegion);
779 for (i = 0; i < n_regions; ++i) {
780 rg = regions[i];
781 *size += (long) rg->nrects * (long) sizeof (NhRect);
782 if (rg->enter_msg)
783 *size += (long) (strlen(rg->enter_msg) + 1);
784 if (rg->leave_msg)
785 *size += (long) (strlen(rg->leave_msg) + 1);
786 *size += (long) rg->max_monst * (long) sizeof *rg->monsters;
788 /* ? */
791 /* update monster IDs for region being loaded from bones; `ghostly' implied */
792 STATIC_OVL void
793 reset_region_mids(reg)
794 NhRegion *reg;
796 int i = 0, n = reg->n_monst;
797 unsigned *mid_list = reg->monsters;
799 while (i < n)
800 if (!lookup_id_mapping(mid_list[i], &mid_list[i])) {
801 /* shrink list to remove missing monster; order doesn't matter */
802 mid_list[i] = mid_list[--n];
803 } else {
804 /* move on to next monster */
805 ++i;
807 reg->n_monst = n;
808 return;
811 #if 0
812 /* not yet used */
814 /*--------------------------------------------------------------*
816 * Create Region with just a message *
818 *--------------------------------------------------------------*/
820 NhRegion *
821 create_msg_region(x, y, w, h, msg_enter, msg_leave)
822 xchar x, y;
823 xchar w, h;
824 const char *msg_enter;
825 const char *msg_leave;
827 NhRect tmprect;
828 NhRegion *reg = create_region((NhRect *) 0, 0);
830 if (msg_enter)
831 reg->enter_msg = dupstr(msg_enter);
832 if (msg_leave)
833 reg->leave_msg = dupstr(msg_leave);
834 tmprect.lx = x;
835 tmprect.ly = y;
836 tmprect.hx = x + w;
837 tmprect.hy = y + h;
838 add_rect_to_reg(reg, &tmprect);
839 reg->ttl = -1L;
840 return reg;
844 /*--------------------------------------------------------------*
846 * Force Field Related Cod *
847 * (unused yet) *
848 *--------------------------------------------------------------*/
850 boolean
851 enter_force_field(p1, p2)
852 genericptr_t p1;
853 genericptr_t p2;
855 struct monst *mtmp;
857 if (p2 == (genericptr_t) 0) { /* That means the player */
858 if (!Blind)
859 You("bump into %s. Ouch!",
860 Hallucination ? "an invisible tree"
861 : "some kind of invisible wall");
862 else
863 pline("Ouch!");
864 } else {
865 mtmp = (struct monst *) p2;
866 if (canseemon(mtmp))
867 pline("%s bumps into %s!", Monnam(mtmp), something);
869 return FALSE;
872 NhRegion *
873 create_force_field(x, y, radius, ttl)
874 xchar x, y;
875 int radius;
876 long ttl;
878 int i;
879 NhRegion *ff;
880 int nrect;
881 NhRect tmprect;
883 ff = create_region((NhRect *) 0, 0);
884 nrect = radius;
885 tmprect.lx = x;
886 tmprect.hx = x;
887 tmprect.ly = y - (radius - 1);
888 tmprect.hy = y + (radius - 1);
889 for (i = 0; i < nrect; i++) {
890 add_rect_to_reg(ff, &tmprect);
891 tmprect.lx--;
892 tmprect.hx++;
893 tmprect.ly++;
894 tmprect.hy--;
896 ff->ttl = ttl;
897 if (!in_mklev && !context.mon_moving)
898 set_heros_fault(ff); /* assume player has created it */
899 /* ff->can_enter_f = enter_force_field; */
900 /* ff->can_leave_f = enter_force_field; */
901 add_region(ff);
902 return ff;
905 #endif /*0*/
907 /*--------------------------------------------------------------*
909 * Gas cloud related code *
911 *--------------------------------------------------------------*/
914 * Here is an example of an expire function that may prolong
915 * region life after some mods...
917 /*ARGSUSED*/
918 boolean
919 expire_gas_cloud(p1, p2)
920 genericptr_t p1;
921 genericptr_t p2 UNUSED;
923 NhRegion *reg;
924 int damage;
926 reg = (NhRegion *) p1;
927 damage = reg->arg.a_int;
929 /* If it was a thick cloud, it dissipates a little first */
930 if (damage >= 5) {
931 damage /= 2; /* It dissipates, let's do less damage */
932 reg->arg = zeroany;
933 reg->arg.a_int = damage;
934 reg->ttl = 2L; /* Here's the trick : reset ttl */
935 return FALSE; /* THEN return FALSE, means "still there" */
937 return TRUE; /* OK, it's gone, you can free it! */
940 boolean
941 inside_gas_cloud(p1, p2)
942 genericptr_t p1;
943 genericptr_t p2;
945 NhRegion *reg;
946 struct monst *mtmp;
947 int dam;
949 reg = (NhRegion *) p1;
950 dam = reg->arg.a_int;
951 if (p2 == (genericptr_t) 0) { /* This means *YOU* Bozo! */
952 if (u.uinvulnerable || nonliving(youmonst.data) || Breathless)
953 return FALSE;
954 if (!Blind) {
955 Your("%s sting.", makeplural(body_part(EYE)));
956 make_blinded(1L, FALSE);
958 if (!Poison_resistance) {
959 pline("%s is burning your %s!", Something,
960 makeplural(body_part(LUNG)));
961 You("cough and spit blood!");
962 losehp(Maybe_Half_Phys(rnd(dam) + 5), "gas cloud", KILLED_BY_AN);
963 return FALSE;
964 } else {
965 You("cough!");
966 return FALSE;
968 } else { /* A monster is inside the cloud */
969 mtmp = (struct monst *) p2;
971 /* Non living and non breathing monsters are not concerned */
972 if (!(nonliving(mtmp->data) || is_vampshifter(mtmp))
973 && !breathless(mtmp->data)) {
974 if (cansee(mtmp->mx, mtmp->my))
975 pline("%s coughs!", Monnam(mtmp));
976 if (heros_fault(reg))
977 setmangry(mtmp);
978 if (haseyes(mtmp->data) && mtmp->mcansee) {
979 mtmp->mblinded = 1;
980 mtmp->mcansee = 0;
982 if (resists_poison(mtmp))
983 return FALSE;
984 mtmp->mhp -= rnd(dam) + 5;
985 if (mtmp->mhp <= 0) {
986 if (heros_fault(reg))
987 killed(mtmp);
988 else
989 monkilled(mtmp, "gas cloud", AD_DRST);
990 if (mtmp->mhp <= 0) { /* not lifesaved */
991 return TRUE;
996 return FALSE; /* Monster is still alive */
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 && !context.mon_moving)
1024 set_heros_fault(cloud); /* assume player has created it */
1025 cloud->inside_f = INSIDE_GAS_CLOUD;
1026 cloud->expire_f = EXPIRE_GAS_CLOUD;
1027 cloud->arg = zeroany;
1028 cloud->arg.a_int = damage;
1029 cloud->visible = TRUE;
1030 cloud->glyph = cmap_to_glyph(damage ? S_poisoncloud : S_cloud);
1031 add_region(cloud);
1032 return cloud;
1035 /* for checking troubles during prayer; is hero at risk? */
1036 boolean
1037 region_danger()
1039 int i, f_indx, n = 0;
1041 for (i = 0; i < n_regions; i++) {
1042 /* only care about regions that hero is in */
1043 if (!hero_inside(regions[i]))
1044 continue;
1045 f_indx = regions[i]->inside_f;
1046 /* the only type of region we understand is gas_cloud */
1047 if (f_indx == INSIDE_GAS_CLOUD) {
1048 /* completely harmless if you don't need to breathe */
1049 if (nonliving(youmonst.data) || Breathless)
1050 continue;
1051 /* minor inconvenience if you're poison resistant;
1052 not harmful enough to be a prayer-level trouble */
1053 if (Poison_resistance)
1054 continue;
1055 ++n;
1058 return n ? TRUE : FALSE;
1061 /* for fixing trouble at end of prayer;
1062 danger detected at start of prayer might have expired by now */
1063 void
1064 region_safety()
1066 NhRegion *r = 0;
1067 int i, f_indx, n = 0;
1069 for (i = 0; i < n_regions; i++) {
1070 /* only care about regions that hero is in */
1071 if (!hero_inside(regions[i]))
1072 continue;
1073 f_indx = regions[i]->inside_f;
1074 /* the only type of region we understand is gas_cloud */
1075 if (f_indx == INSIDE_GAS_CLOUD) {
1076 if (!n++ && regions[i]->ttl >= 0)
1077 r = regions[i];
1081 if (n > 1 || (n == 1 && !r)) {
1082 /* multiple overlapping cloud regions or non-expiring one */
1083 safe_teleds(FALSE);
1084 } else if (r) {
1085 remove_region(r);
1086 pline_The("gas cloud enveloping you dissipates.");
1087 } else {
1088 /* cloud dissipated on its own, so nothing needs to be done */
1089 pline_The("gas cloud has dissipated.");
1091 /* maybe cure blindness too */
1092 if ((Blinded & TIMEOUT) == 1L)
1093 make_blinded(0L, TRUE);
1096 /*region.c*/