dumplog lint and formatting
[aNetHack.git] / src / region.c
blobb38936c81d13d7034377613f717ad7450c1c1f29
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 (void) memset((genericptr_t)reg, 0, sizeof(NhRegion));
95 /* Determines bounding box */
96 if (nrect > 0) {
97 reg->bounding_box = rects[0];
98 } else {
99 reg->bounding_box.lx = COLNO;
100 reg->bounding_box.ly = ROWNO;
101 reg->bounding_box.hx = 0; /* 1 */
102 reg->bounding_box.hy = 0;
104 reg->nrects = nrect;
105 reg->rects = (nrect > 0) ? (NhRect *) alloc(nrect * sizeof (NhRect)) : 0;
106 for (i = 0; i < nrect; i++) {
107 if (rects[i].lx < reg->bounding_box.lx)
108 reg->bounding_box.lx = rects[i].lx;
109 if (rects[i].ly < reg->bounding_box.ly)
110 reg->bounding_box.ly = rects[i].ly;
111 if (rects[i].hx > reg->bounding_box.hx)
112 reg->bounding_box.hx = rects[i].hx;
113 if (rects[i].hy > reg->bounding_box.hy)
114 reg->bounding_box.hy = rects[i].hy;
115 reg->rects[i] = rects[i];
117 reg->ttl = -1L; /* Defaults */
118 reg->attach_2_u = FALSE;
119 reg->attach_2_m = 0;
120 /* reg->attach_2_o = NULL; */
121 reg->enter_msg = (const char *) 0;
122 reg->leave_msg = (const char *) 0;
123 reg->expire_f = NO_CALLBACK;
124 reg->enter_f = NO_CALLBACK;
125 reg->can_enter_f = NO_CALLBACK;
126 reg->leave_f = NO_CALLBACK;
127 reg->can_leave_f = NO_CALLBACK;
128 reg->inside_f = NO_CALLBACK;
129 clear_hero_inside(reg);
130 clear_heros_fault(reg);
131 reg->n_monst = 0;
132 reg->max_monst = 0;
133 reg->monsters = (unsigned int *) 0;
134 reg->arg = zeroany;
135 return reg;
139 * Add rectangle to region.
141 void
142 add_rect_to_reg(reg, rect)
143 NhRegion *reg;
144 NhRect *rect;
146 NhRect *tmp_rect;
148 tmp_rect = (NhRect *) alloc((reg->nrects + 1) * sizeof (NhRect));
149 if (reg->nrects > 0) {
150 (void) memcpy((genericptr_t) tmp_rect, (genericptr_t) reg->rects,
151 reg->nrects * sizeof (NhRect));
152 free((genericptr_t) reg->rects);
154 tmp_rect[reg->nrects] = *rect;
155 reg->nrects++;
156 reg->rects = tmp_rect;
157 /* Update bounding box if needed */
158 if (reg->bounding_box.lx > rect->lx)
159 reg->bounding_box.lx = rect->lx;
160 if (reg->bounding_box.ly > rect->ly)
161 reg->bounding_box.ly = rect->ly;
162 if (reg->bounding_box.hx < rect->hx)
163 reg->bounding_box.hx = rect->hx;
164 if (reg->bounding_box.hy < rect->hy)
165 reg->bounding_box.hy = rect->hy;
169 * Add a monster to the region
171 void
172 add_mon_to_reg(reg, mon)
173 NhRegion *reg;
174 struct monst *mon;
176 int i;
177 unsigned *tmp_m;
179 if (reg->max_monst <= reg->n_monst) {
180 tmp_m = (unsigned *) alloc(sizeof (unsigned)
181 * (reg->max_monst + MONST_INC));
182 if (reg->max_monst > 0) {
183 for (i = 0; i < reg->max_monst; i++)
184 tmp_m[i] = reg->monsters[i];
185 free((genericptr_t) reg->monsters);
187 reg->monsters = tmp_m;
188 reg->max_monst += MONST_INC;
190 reg->monsters[reg->n_monst++] = mon->m_id;
194 * Remove a monster from the region list (it left or died...)
196 void
197 remove_mon_from_reg(reg, mon)
198 NhRegion *reg;
199 struct monst *mon;
201 register int i;
203 for (i = 0; i < reg->n_monst; i++)
204 if (reg->monsters[i] == mon->m_id) {
205 reg->n_monst--;
206 reg->monsters[i] = reg->monsters[reg->n_monst];
207 return;
212 * Check if a monster is inside the region.
213 * It's probably quicker to check with the region internal list
214 * than to check for coordinates.
216 boolean
217 mon_in_region(reg, mon)
218 NhRegion *reg;
219 struct monst *mon;
221 int i;
223 for (i = 0; i < reg->n_monst; i++)
224 if (reg->monsters[i] == mon->m_id)
225 return TRUE;
226 return FALSE;
229 #if 0
230 /* not yet used */
233 * Clone (make a standalone copy) the region.
235 NhRegion *
236 clone_region(reg)
237 NhRegion *reg;
239 NhRegion *ret_reg;
241 ret_reg = create_region(reg->rects, reg->nrects);
242 ret_reg->ttl = reg->ttl;
243 ret_reg->attach_2_u = reg->attach_2_u;
244 ret_reg->attach_2_m = reg->attach_2_m;
245 /* ret_reg->attach_2_o = reg->attach_2_o; */
246 ret_reg->expire_f = reg->expire_f;
247 ret_reg->enter_f = reg->enter_f;
248 ret_reg->can_enter_f = reg->can_enter_f;
249 ret_reg->leave_f = reg->leave_f;
250 ret_reg->can_leave_f = reg->can_leave_f;
251 ret_reg->player_flags = reg->player_flags; /* set/clear_hero_inside,&c*/
252 ret_reg->n_monst = reg->n_monst;
253 if (reg->n_monst > 0) {
254 ret_reg->monsters = (unsigned int *)
255 alloc((sizeof (unsigned)) * reg->n_monst);
256 (void) memcpy((genericptr_t) ret_reg->monsters,
257 (genericptr_t) reg->monsters,
258 sizeof (unsigned) * reg->n_monst);
259 } else
260 ret_reg->monsters = (unsigned int *) 0;
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((genericptr_t) reg->rects);
276 if (reg->monsters)
277 free((genericptr_t) reg->monsters);
278 if (reg->enter_msg)
279 free((genericptr_t) reg->enter_msg);
280 if (reg->leave_msg)
281 free((genericptr_t) reg->leave_msg);
282 free((genericptr_t) reg);
287 * Add a region to the list.
288 * This actually activates the region.
290 void
291 add_region(reg)
292 NhRegion *reg;
294 NhRegion **tmp_reg;
295 int i, j;
297 if (max_regions <= n_regions) {
298 tmp_reg = regions;
299 regions =
300 (NhRegion **) alloc((max_regions + 10) * sizeof (NhRegion *));
301 if (max_regions > 0) {
302 (void) memcpy((genericptr_t) regions, (genericptr_t) tmp_reg,
303 max_regions * sizeof (NhRegion *));
304 free((genericptr_t) tmp_reg);
306 max_regions += 10;
308 regions[n_regions] = reg;
309 n_regions++;
310 /* Check for monsters inside the region */
311 for (i = reg->bounding_box.lx; i <= reg->bounding_box.hx; i++)
312 for (j = reg->bounding_box.ly; j <= reg->bounding_box.hy; j++) {
313 /* Some regions can cross the level boundaries */
314 if (!isok(i, j))
315 continue;
316 if (MON_AT(i, j) && inside_region(reg, i, j))
317 add_mon_to_reg(reg, level.monsters[i][j]);
318 if (reg->visible && cansee(i, j))
319 newsym(i, j);
321 /* Check for player now... */
322 if (inside_region(reg, u.ux, u.uy))
323 set_hero_inside(reg);
324 else
325 clear_hero_inside(reg);
329 * Remove a region from the list & free it.
331 void
332 remove_region(reg)
333 NhRegion *reg;
335 register int i, x, y;
337 for (i = 0; i < n_regions; i++)
338 if (regions[i] == reg)
339 break;
340 if (i == n_regions)
341 return;
343 /* Update screen if necessary */
344 reg->ttl = -2L; /* for visible_region_at */
345 if (reg->visible)
346 for (x = reg->bounding_box.lx; x <= reg->bounding_box.hx; x++)
347 for (y = reg->bounding_box.ly; y <= reg->bounding_box.hy; y++)
348 if (isok(x, y) && inside_region(reg, x, y) && cansee(x, y))
349 newsym(x, y);
351 free_region(reg);
352 regions[i] = regions[n_regions - 1];
353 regions[n_regions - 1] = (NhRegion *) 0;
354 n_regions--;
358 * Remove all regions and clear all related data (This must be down
359 * when changing level, for instance).
361 void
362 clear_regions()
364 register int i;
366 for (i = 0; i < n_regions; i++)
367 free_region(regions[i]);
368 n_regions = 0;
369 if (max_regions > 0)
370 free((genericptr_t) regions);
371 max_regions = 0;
372 regions = (NhRegion **) 0;
376 * This function is called every turn.
377 * It makes the regions age, if necessary and calls the appropriate
378 * callbacks when needed.
380 void
381 run_regions()
383 register int i, j, k;
384 int f_indx;
386 /* End of life ? */
387 /* Do it backward because the array will be modified */
388 for (i = n_regions - 1; i >= 0; i--) {
389 if (regions[i]->ttl == 0L) {
390 if ((f_indx = regions[i]->expire_f) == NO_CALLBACK
391 || (*callbacks[f_indx])(regions[i], (genericptr_t) 0))
392 remove_region(regions[i]);
396 /* Process remaining regions */
397 for (i = 0; i < n_regions; i++) {
398 /* Make the region age */
399 if (regions[i]->ttl > 0L)
400 regions[i]->ttl--;
401 /* Check if player is inside region */
402 f_indx = regions[i]->inside_f;
403 if (f_indx != NO_CALLBACK && hero_inside(regions[i]))
404 (void) (*callbacks[f_indx])(regions[i], (genericptr_t) 0);
405 /* Check if any monster is inside region */
406 if (f_indx != NO_CALLBACK) {
407 for (j = 0; j < regions[i]->n_monst; j++) {
408 struct monst *mtmp =
409 find_mid(regions[i]->monsters[j], FM_FMON);
411 if (!mtmp || mtmp->mhp <= 0
412 || (*callbacks[f_indx])(regions[i], mtmp)) {
413 /* The monster died, remove it from list */
414 k = (regions[i]->n_monst -= 1);
415 regions[i]->monsters[j] = regions[i]->monsters[k];
416 regions[i]->monsters[k] = 0;
417 --j; /* current slot has been reused; recheck it next */
425 * check whether player enters/leaves one or more regions.
427 boolean
428 in_out_region(x, y)
429 xchar x, y;
431 int i, f_indx;
433 /* First check if we can do the move */
434 for (i = 0; i < n_regions; i++) {
435 if (inside_region(regions[i], x, y) && !hero_inside(regions[i])
436 && !regions[i]->attach_2_u) {
437 if ((f_indx = regions[i]->can_enter_f) != NO_CALLBACK)
438 if (!(*callbacks[f_indx])(regions[i], (genericptr_t) 0))
439 return FALSE;
440 } else if (hero_inside(regions[i]) && !inside_region(regions[i], x, y)
441 && !regions[i]->attach_2_u) {
442 if ((f_indx = regions[i]->can_leave_f) != NO_CALLBACK)
443 if (!(*callbacks[f_indx])(regions[i], (genericptr_t) 0))
444 return FALSE;
448 /* Callbacks for the regions we do leave */
449 for (i = 0; i < n_regions; i++)
450 if (hero_inside(regions[i]) && !regions[i]->attach_2_u
451 && !inside_region(regions[i], x, y)) {
452 clear_hero_inside(regions[i]);
453 if (regions[i]->leave_msg != (const char *) 0)
454 pline1(regions[i]->leave_msg);
455 if ((f_indx = regions[i]->leave_f) != NO_CALLBACK)
456 (void) (*callbacks[f_indx])(regions[i], (genericptr_t) 0);
459 /* Callbacks for the regions we do enter */
460 for (i = 0; i < n_regions; i++)
461 if (!hero_inside(regions[i]) && !regions[i]->attach_2_u
462 && inside_region(regions[i], x, y)) {
463 set_hero_inside(regions[i]);
464 if (regions[i]->enter_msg != (const char *) 0)
465 pline1(regions[i]->enter_msg);
466 if ((f_indx = regions[i]->enter_f) != NO_CALLBACK)
467 (void) (*callbacks[f_indx])(regions[i], (genericptr_t) 0);
469 return TRUE;
473 * check whether a monster enters/leaves one or more region.
475 boolean
476 m_in_out_region(mon, x, y)
477 struct monst *mon;
478 xchar x, y;
480 int i, f_indx;
482 /* First check if we can do the move */
483 for (i = 0; i < n_regions; i++) {
484 if (inside_region(regions[i], x, y) && !mon_in_region(regions[i], mon)
485 && regions[i]->attach_2_m != mon->m_id) {
486 if ((f_indx = regions[i]->can_enter_f) != NO_CALLBACK)
487 if (!(*callbacks[f_indx])(regions[i], mon))
488 return FALSE;
489 } else if (mon_in_region(regions[i], mon)
490 && !inside_region(regions[i], x, y)
491 && regions[i]->attach_2_m != mon->m_id) {
492 if ((f_indx = regions[i]->can_leave_f) != NO_CALLBACK)
493 if (!(*callbacks[f_indx])(regions[i], mon))
494 return FALSE;
498 /* Callbacks for the regions we do leave */
499 for (i = 0; i < n_regions; i++)
500 if (mon_in_region(regions[i], mon)
501 && regions[i]->attach_2_m != mon->m_id
502 && !inside_region(regions[i], x, y)) {
503 remove_mon_from_reg(regions[i], mon);
504 if ((f_indx = regions[i]->leave_f) != NO_CALLBACK)
505 (void) (*callbacks[f_indx])(regions[i], mon);
508 /* Callbacks for the regions we do enter */
509 for (i = 0; i < n_regions; i++)
510 if (!hero_inside(regions[i]) && !regions[i]->attach_2_u
511 && inside_region(regions[i], x, y)) {
512 add_mon_to_reg(regions[i], mon);
513 if ((f_indx = regions[i]->enter_f) != NO_CALLBACK)
514 (void) (*callbacks[f_indx])(regions[i], mon);
516 return TRUE;
520 * Checks player's regions after a teleport for instance.
522 void
523 update_player_regions()
525 register int i;
527 for (i = 0; i < n_regions; i++)
528 if (!regions[i]->attach_2_u && inside_region(regions[i], u.ux, u.uy))
529 set_hero_inside(regions[i]);
530 else
531 clear_hero_inside(regions[i]);
535 * Ditto for a specified monster.
537 void
538 update_monster_region(mon)
539 struct monst *mon;
541 register int i;
543 for (i = 0; i < n_regions; i++) {
544 if (inside_region(regions[i], mon->mx, mon->my)) {
545 if (!mon_in_region(regions[i], mon))
546 add_mon_to_reg(regions[i], mon);
547 } else {
548 if (mon_in_region(regions[i], mon))
549 remove_mon_from_reg(regions[i], mon);
554 #if 0
555 /* not yet used */
558 * Change monster pointer in regions
559 * This happens, for instance, when a monster grows and
560 * need a new structure (internally that is).
562 void
563 replace_mon_regions(monold, monnew)
564 struct monst *monold, *monnew;
566 register int i;
568 for (i = 0; i < n_regions; i++)
569 if (mon_in_region(regions[i], monold)) {
570 remove_mon_from_reg(regions[i], monold);
571 add_mon_to_reg(regions[i], monnew);
576 * Remove monster from all regions it was in (ie monster just died)
578 void
579 remove_mon_from_regions(mon)
580 struct monst *mon;
582 register int i;
584 for (i = 0; i < n_regions; i++)
585 if (mon_in_region(regions[i], mon))
586 remove_mon_from_reg(regions[i], mon);
589 #endif /*0*/
592 * Check if a spot is under a visible region (eg: gas cloud).
593 * Returns NULL if not, otherwise returns region.
595 NhRegion *
596 visible_region_at(x, y)
597 xchar x, y;
599 register int i;
601 for (i = 0; i < n_regions; i++)
602 if (inside_region(regions[i], x, y) && regions[i]->visible
603 && regions[i]->ttl != -2L)
604 return regions[i];
605 return (NhRegion *) 0;
608 void
609 show_region(reg, x, y)
610 NhRegion *reg;
611 xchar x, y;
613 show_glyph(x, y, reg->glyph);
617 * save_regions :
619 void
620 save_regions(fd, mode)
621 int fd;
622 int mode;
624 int i, j;
625 unsigned n;
627 if (!perform_bwrite(mode))
628 goto skip_lots;
630 bwrite(fd, (genericptr_t) &moves, sizeof(moves)); /* timestamp */
631 bwrite(fd, (genericptr_t) &n_regions, sizeof(n_regions));
632 for (i = 0; i < n_regions; i++) {
633 bwrite(fd, (genericptr_t) &regions[i]->bounding_box, sizeof(NhRect));
634 bwrite(fd, (genericptr_t) &regions[i]->nrects, sizeof(short));
635 for (j = 0; j < regions[i]->nrects; j++)
636 bwrite(fd, (genericptr_t) &regions[i]->rects[j], sizeof(NhRect));
637 bwrite(fd, (genericptr_t) &regions[i]->attach_2_u, sizeof(boolean));
638 n = 0;
639 bwrite(fd, (genericptr_t) &regions[i]->attach_2_m, sizeof(unsigned));
640 n = regions[i]->enter_msg != (const char *) 0
641 ? strlen(regions[i]->enter_msg)
642 : 0;
643 bwrite(fd, (genericptr_t) &n, sizeof n);
644 if (n > 0)
645 bwrite(fd, (genericptr_t) regions[i]->enter_msg, n);
646 n = regions[i]->leave_msg != (const char *) 0
647 ? strlen(regions[i]->leave_msg)
648 : 0;
649 bwrite(fd, (genericptr_t) &n, sizeof n);
650 if (n > 0)
651 bwrite(fd, (genericptr_t) regions[i]->leave_msg, n);
652 bwrite(fd, (genericptr_t) &regions[i]->ttl, sizeof(long));
653 bwrite(fd, (genericptr_t) &regions[i]->expire_f, sizeof(short));
654 bwrite(fd, (genericptr_t) &regions[i]->can_enter_f, sizeof(short));
655 bwrite(fd, (genericptr_t) &regions[i]->enter_f, sizeof(short));
656 bwrite(fd, (genericptr_t) &regions[i]->can_leave_f, sizeof(short));
657 bwrite(fd, (genericptr_t) &regions[i]->leave_f, sizeof(short));
658 bwrite(fd, (genericptr_t) &regions[i]->inside_f, sizeof(short));
659 bwrite(fd, (genericptr_t) &regions[i]->player_flags,
660 sizeof(unsigned int));
661 bwrite(fd, (genericptr_t) &regions[i]->n_monst, sizeof(short));
662 for (j = 0; j < regions[i]->n_monst; j++)
663 bwrite(fd, (genericptr_t) &regions[i]->monsters[j],
664 sizeof(unsigned));
665 bwrite(fd, (genericptr_t) &regions[i]->visible, sizeof(boolean));
666 bwrite(fd, (genericptr_t) &regions[i]->glyph, sizeof(int));
667 bwrite(fd, (genericptr_t) &regions[i]->arg, sizeof(anything));
670 skip_lots:
671 if (release_data(mode))
672 clear_regions();
675 void
676 rest_regions(fd, ghostly)
677 int fd;
678 boolean ghostly; /* If a bones file restore */
680 int i, j;
681 unsigned n;
682 long tmstamp;
683 char *msg_buf;
685 clear_regions(); /* Just for security */
686 mread(fd, (genericptr_t) &tmstamp, sizeof(tmstamp));
687 if (ghostly)
688 tmstamp = 0;
689 else
690 tmstamp = (moves - tmstamp);
691 mread(fd, (genericptr_t) &n_regions, sizeof(n_regions));
692 max_regions = n_regions;
693 if (n_regions > 0)
694 regions = (NhRegion **) alloc(sizeof(NhRegion *) * n_regions);
695 for (i = 0; i < n_regions; i++) {
696 regions[i] = (NhRegion *) alloc(sizeof(NhRegion));
697 mread(fd, (genericptr_t) &regions[i]->bounding_box, sizeof(NhRect));
698 mread(fd, (genericptr_t) &regions[i]->nrects, sizeof(short));
700 if (regions[i]->nrects > 0)
701 regions[i]->rects =
702 (NhRect *) alloc(sizeof(NhRect) * regions[i]->nrects);
703 for (j = 0; j < regions[i]->nrects; j++)
704 mread(fd, (genericptr_t) &regions[i]->rects[j], sizeof(NhRect));
705 mread(fd, (genericptr_t) &regions[i]->attach_2_u, sizeof(boolean));
706 mread(fd, (genericptr_t) &regions[i]->attach_2_m, sizeof(unsigned));
708 mread(fd, (genericptr_t) &n, sizeof n);
709 if (n > 0) {
710 msg_buf = (char *) alloc(n + 1);
711 mread(fd, (genericptr_t) msg_buf, n);
712 msg_buf[n] = '\0';
713 regions[i]->enter_msg = (const char *) msg_buf;
714 } else
715 regions[i]->enter_msg = (const char *) 0;
717 mread(fd, (genericptr_t) &n, sizeof n);
718 if (n > 0) {
719 msg_buf = (char *) alloc(n + 1);
720 mread(fd, (genericptr_t) msg_buf, n);
721 msg_buf[n] = '\0';
722 regions[i]->leave_msg = (const char *) msg_buf;
723 } else
724 regions[i]->leave_msg = (const char *) 0;
726 mread(fd, (genericptr_t) &regions[i]->ttl, sizeof(long));
727 /* check for expired region */
728 if (regions[i]->ttl >= 0L)
729 regions[i]->ttl =
730 (regions[i]->ttl > tmstamp) ? regions[i]->ttl - tmstamp : 0L;
731 mread(fd, (genericptr_t) &regions[i]->expire_f, sizeof(short));
732 mread(fd, (genericptr_t) &regions[i]->can_enter_f, sizeof(short));
733 mread(fd, (genericptr_t) &regions[i]->enter_f, sizeof(short));
734 mread(fd, (genericptr_t) &regions[i]->can_leave_f, sizeof(short));
735 mread(fd, (genericptr_t) &regions[i]->leave_f, sizeof(short));
736 mread(fd, (genericptr_t) &regions[i]->inside_f, sizeof(short));
737 mread(fd, (genericptr_t) &regions[i]->player_flags,
738 sizeof(unsigned int));
739 if (ghostly) { /* settings pertained to old player */
740 clear_hero_inside(regions[i]);
741 clear_heros_fault(regions[i]);
743 mread(fd, (genericptr_t) &regions[i]->n_monst, sizeof(short));
744 if (regions[i]->n_monst > 0)
745 regions[i]->monsters =
746 (unsigned *) alloc(sizeof(unsigned) * regions[i]->n_monst);
747 else
748 regions[i]->monsters = (unsigned int *) 0;
749 regions[i]->max_monst = regions[i]->n_monst;
750 for (j = 0; j < regions[i]->n_monst; j++)
751 mread(fd, (genericptr_t) &regions[i]->monsters[j],
752 sizeof(unsigned));
753 mread(fd, (genericptr_t) &regions[i]->visible, sizeof(boolean));
754 mread(fd, (genericptr_t) &regions[i]->glyph, sizeof(int));
755 mread(fd, (genericptr_t) &regions[i]->arg, sizeof(anything));
757 /* remove expired regions, do not trigger the expire_f callback (yet!);
758 also update monster lists if this data is coming from a bones file */
759 for (i = n_regions - 1; i >= 0; i--)
760 if (regions[i]->ttl == 0L)
761 remove_region(regions[i]);
762 else if (ghostly && regions[i]->n_monst > 0)
763 reset_region_mids(regions[i]);
766 /* to support '#stats' wizard-mode command */
767 void
768 region_stats(hdrfmt, hdrbuf, count, size)
769 const char *hdrfmt;
770 char *hdrbuf;
771 long *count, *size;
773 NhRegion *rg;
774 int i;
776 /* other stats formats take one parameter; this takes two */
777 Sprintf(hdrbuf, hdrfmt, (long) sizeof (NhRegion), (long) sizeof (NhRect));
778 *count = (long) n_regions; /* might be 0 even though max_regions isn't */
779 *size = (long) max_regions * (long) sizeof (NhRegion);
780 for (i = 0; i < n_regions; ++i) {
781 rg = regions[i];
782 *size += (long) rg->nrects * (long) sizeof (NhRect);
783 if (rg->enter_msg)
784 *size += (long) (strlen(rg->enter_msg) + 1);
785 if (rg->leave_msg)
786 *size += (long) (strlen(rg->leave_msg) + 1);
787 *size += (long) rg->max_monst * (long) sizeof *rg->monsters;
789 /* ? */
792 /* update monster IDs for region being loaded from bones; `ghostly' implied */
793 STATIC_OVL void
794 reset_region_mids(reg)
795 NhRegion *reg;
797 int i = 0, n = reg->n_monst;
798 unsigned *mid_list = reg->monsters;
800 while (i < n)
801 if (!lookup_id_mapping(mid_list[i], &mid_list[i])) {
802 /* shrink list to remove missing monster; order doesn't matter */
803 mid_list[i] = mid_list[--n];
804 } else {
805 /* move on to next monster */
806 ++i;
808 reg->n_monst = n;
809 return;
812 #if 0
813 /* not yet used */
815 /*--------------------------------------------------------------*
817 * Create Region with just a message *
819 *--------------------------------------------------------------*/
821 NhRegion *
822 create_msg_region(x, y, w, h, msg_enter, msg_leave)
823 xchar x, y;
824 xchar w, h;
825 const char *msg_enter;
826 const char *msg_leave;
828 NhRect tmprect;
829 NhRegion *reg = create_region((NhRect *) 0, 0);
831 if (msg_enter)
832 reg->enter_msg = dupstr(msg_enter);
833 if (msg_leave)
834 reg->leave_msg = dupstr(msg_leave);
835 tmprect.lx = x;
836 tmprect.ly = y;
837 tmprect.hx = x + w;
838 tmprect.hy = y + h;
839 add_rect_to_reg(reg, &tmprect);
840 reg->ttl = -1L;
841 return reg;
845 /*--------------------------------------------------------------*
847 * Force Field Related Cod *
848 * (unused yet) *
849 *--------------------------------------------------------------*/
851 boolean
852 enter_force_field(p1, p2)
853 genericptr_t p1;
854 genericptr_t p2;
856 struct monst *mtmp;
858 if (p2 == (genericptr_t) 0) { /* That means the player */
859 if (!Blind)
860 You("bump into %s. Ouch!",
861 Hallucination ? "an invisible tree"
862 : "some kind of invisible wall");
863 else
864 pline("Ouch!");
865 } else {
866 mtmp = (struct monst *) p2;
867 if (canseemon(mtmp))
868 pline("%s bumps into %s!", Monnam(mtmp), something);
870 return FALSE;
873 NhRegion *
874 create_force_field(x, y, radius, ttl)
875 xchar x, y;
876 int radius;
877 long ttl;
879 int i;
880 NhRegion *ff;
881 int nrect;
882 NhRect tmprect;
884 ff = create_region((NhRect *) 0, 0);
885 nrect = radius;
886 tmprect.lx = x;
887 tmprect.hx = x;
888 tmprect.ly = y - (radius - 1);
889 tmprect.hy = y + (radius - 1);
890 for (i = 0; i < nrect; i++) {
891 add_rect_to_reg(ff, &tmprect);
892 tmprect.lx--;
893 tmprect.hx++;
894 tmprect.ly++;
895 tmprect.hy--;
897 ff->ttl = ttl;
898 if (!in_mklev && !context.mon_moving)
899 set_heros_fault(ff); /* assume player has created it */
900 /* ff->can_enter_f = enter_force_field; */
901 /* ff->can_leave_f = enter_force_field; */
902 add_region(ff);
903 return ff;
906 #endif /*0*/
908 /*--------------------------------------------------------------*
910 * Gas cloud related code *
912 *--------------------------------------------------------------*/
915 * Here is an example of an expire function that may prolong
916 * region life after some mods...
918 /*ARGSUSED*/
919 boolean
920 expire_gas_cloud(p1, p2)
921 genericptr_t p1;
922 genericptr_t p2 UNUSED;
924 NhRegion *reg;
925 int damage;
927 reg = (NhRegion *) p1;
928 damage = reg->arg.a_int;
930 /* If it was a thick cloud, it dissipates a little first */
931 if (damage >= 5) {
932 damage /= 2; /* It dissipates, let's do less damage */
933 reg->arg = zeroany;
934 reg->arg.a_int = damage;
935 reg->ttl = 2L; /* Here's the trick : reset ttl */
936 return FALSE; /* THEN return FALSE, means "still there" */
938 return TRUE; /* OK, it's gone, you can free it! */
941 boolean
942 inside_gas_cloud(p1, p2)
943 genericptr_t p1;
944 genericptr_t p2;
946 NhRegion *reg;
947 struct monst *mtmp;
948 int dam;
950 reg = (NhRegion *) p1;
951 dam = reg->arg.a_int;
952 if (p2 == (genericptr_t) 0) { /* This means *YOU* Bozo! */
953 if (u.uinvulnerable || nonliving(youmonst.data) || Breathless)
954 return FALSE;
955 if (!Blind) {
956 Your("%s sting.", makeplural(body_part(EYE)));
957 make_blinded(1L, FALSE);
959 if (!Poison_resistance) {
960 pline("%s is burning your %s!", Something,
961 makeplural(body_part(LUNG)));
962 You("cough and spit blood!");
963 losehp(Maybe_Half_Phys(rnd(dam) + 5), "gas cloud", KILLED_BY_AN);
964 return FALSE;
965 } else {
966 You("cough!");
967 return FALSE;
969 } else { /* A monster is inside the cloud */
970 mtmp = (struct monst *) p2;
972 /* Non living and non breathing monsters are not concerned */
973 if (!(nonliving(mtmp->data) || is_vampshifter(mtmp))
974 && !breathless(mtmp->data)) {
975 if (cansee(mtmp->mx, mtmp->my))
976 pline("%s coughs!", Monnam(mtmp));
977 if (heros_fault(reg))
978 setmangry(mtmp, TRUE);
979 if (haseyes(mtmp->data) && mtmp->mcansee) {
980 mtmp->mblinded = 1;
981 mtmp->mcansee = 0;
983 if (resists_poison(mtmp))
984 return FALSE;
985 mtmp->mhp -= rnd(dam) + 5;
986 if (mtmp->mhp <= 0) {
987 if (heros_fault(reg))
988 killed(mtmp);
989 else
990 monkilled(mtmp, "gas cloud", AD_DRST);
991 if (mtmp->mhp <= 0) { /* not lifesaved */
992 return TRUE;
997 return FALSE; /* Monster is still alive */
1000 NhRegion *
1001 create_gas_cloud(x, y, radius, damage)
1002 xchar x, y;
1003 int radius;
1004 int damage;
1006 NhRegion *cloud;
1007 int i, nrect;
1008 NhRect tmprect;
1010 cloud = create_region((NhRect *) 0, 0);
1011 nrect = radius;
1012 tmprect.lx = x;
1013 tmprect.hx = x;
1014 tmprect.ly = y - (radius - 1);
1015 tmprect.hy = y + (radius - 1);
1016 for (i = 0; i < nrect; i++) {
1017 add_rect_to_reg(cloud, &tmprect);
1018 tmprect.lx--;
1019 tmprect.hx++;
1020 tmprect.ly++;
1021 tmprect.hy--;
1023 cloud->ttl = rn1(3, 4);
1024 if (!in_mklev && !context.mon_moving)
1025 set_heros_fault(cloud); /* assume player has created it */
1026 cloud->inside_f = INSIDE_GAS_CLOUD;
1027 cloud->expire_f = EXPIRE_GAS_CLOUD;
1028 cloud->arg = zeroany;
1029 cloud->arg.a_int = damage;
1030 cloud->visible = TRUE;
1031 cloud->glyph = cmap_to_glyph(damage ? S_poisoncloud : S_cloud);
1032 add_region(cloud);
1033 return cloud;
1036 /* for checking troubles during prayer; is hero at risk? */
1037 boolean
1038 region_danger()
1040 int i, f_indx, n = 0;
1042 for (i = 0; i < n_regions; i++) {
1043 /* only care about regions that hero is in */
1044 if (!hero_inside(regions[i]))
1045 continue;
1046 f_indx = regions[i]->inside_f;
1047 /* the only type of region we understand is gas_cloud */
1048 if (f_indx == INSIDE_GAS_CLOUD) {
1049 /* completely harmless if you don't need to breathe */
1050 if (nonliving(youmonst.data) || Breathless)
1051 continue;
1052 /* minor inconvenience if you're poison resistant;
1053 not harmful enough to be a prayer-level trouble */
1054 if (Poison_resistance)
1055 continue;
1056 ++n;
1059 return n ? TRUE : FALSE;
1062 /* for fixing trouble at end of prayer;
1063 danger detected at start of prayer might have expired by now */
1064 void
1065 region_safety()
1067 NhRegion *r = 0;
1068 int i, f_indx, n = 0;
1070 for (i = 0; i < n_regions; i++) {
1071 /* only care about regions that hero is in */
1072 if (!hero_inside(regions[i]))
1073 continue;
1074 f_indx = regions[i]->inside_f;
1075 /* the only type of region we understand is gas_cloud */
1076 if (f_indx == INSIDE_GAS_CLOUD) {
1077 if (!n++ && regions[i]->ttl >= 0)
1078 r = regions[i];
1082 if (n > 1 || (n == 1 && !r)) {
1083 /* multiple overlapping cloud regions or non-expiring one */
1084 safe_teleds(FALSE);
1085 } else if (r) {
1086 remove_region(r);
1087 pline_The("gas cloud enveloping you dissipates.");
1088 } else {
1089 /* cloud dissipated on its own, so nothing needs to be done */
1090 pline_The("gas cloud has dissipated.");
1092 /* maybe cure blindness too */
1093 if ((Blinded & TIMEOUT) == 1L)
1094 make_blinded(0L, TRUE);
1097 /*region.c*/