OSDN Git Service

patch artifact
[jnethack/source.git] / src / region.c
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. */
4
5 #include "hack.h"
6 #include "lev.h"
7
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.
12  */
13
14 static NhRegion **regions;
15 static int n_regions = 0;
16 static int max_regions = 0;
17
18 #define NO_CALLBACK (-1)
19
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 *));
29
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 *));
36
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
45
46 STATIC_DCL void FDECL(reset_region_mids, (NhRegion *));
47
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 };
54
55 /* Should be inlined. */
56 boolean
57 inside_rect(r, x, y)
58 NhRect *r;
59 int x, y;
60 {
61     return (boolean) (x >= r->lx && x <= r->hx && y >= r->ly && y <= r->hy);
62 }
63
64 /*
65  * Check if a point is inside a region.
66  */
67 boolean
68 inside_region(reg, x, y)
69 NhRegion *reg;
70 int x, y;
71 {
72     int i;
73
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;
80 }
81
82 /*
83  * Create a region. It does not activate it.
84  */
85 NhRegion *
86 create_region(rects, nrect)
87 NhRect *rects;
88 int nrect;
89 {
90     int i;
91     NhRegion *reg;
92
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 = 99;
99         reg->bounding_box.ly = 99;
100         reg->bounding_box.hx = 0;
101         reg->bounding_box.hy = 0;
102     }
103     reg->nrects = nrect;
104     reg->rects =
105         nrect > 0 ? (NhRect *) alloc((sizeof(NhRect)) * nrect) : (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];
116     }
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;
136 }
137
138 /*
139  * Add rectangle to region.
140  */
141 void
142 add_rect_to_reg(reg, rect)
143 NhRegion *reg;
144 NhRect *rect;
145 {
146     NhRect *tmp_rect;
147
148     tmp_rect = (NhRect *) alloc(sizeof(NhRect) * (reg->nrects + 1));
149     if (reg->nrects > 0) {
150         (void) memcpy((genericptr_t) tmp_rect, (genericptr_t) reg->rects,
151                       (sizeof(NhRect) * reg->nrects));
152         free((genericptr_t) reg->rects);
153     }
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;
166 }
167
168 /*
169  * Add a monster to the region
170  */
171 void
172 add_mon_to_reg(reg, mon)
173 NhRegion *reg;
174 struct monst *mon;
175 {
176     int i;
177     unsigned *tmp_m;
178
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);
186         }
187         reg->monsters = tmp_m;
188         reg->max_monst += MONST_INC;
189     }
190     reg->monsters[reg->n_monst++] = mon->m_id;
191 }
192
193 /*
194  * Remove a monster from the region list (it left or died...)
195  */
196 void
197 remove_mon_from_reg(reg, mon)
198 NhRegion *reg;
199 struct monst *mon;
200 {
201     register int i;
202
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;
208         }
209 }
210
211 /*
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.
215  */
216 boolean
217 mon_in_region(reg, mon)
218 NhRegion *reg;
219 struct monst *mon;
220 {
221     int i;
222
223     for (i = 0; i < reg->n_monst; i++)
224         if (reg->monsters[i] == mon->m_id)
225             return TRUE;
226     return FALSE;
227 }
228
229 #if 0
230 /* not yet used */
231
232 /*
233  * Clone (make a standalone copy) the region.
234  */
235 NhRegion *
236 clone_region(reg)
237 NhRegion *reg;
238 {
239     NhRegion *ret_reg;
240
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;
262 }
263
264 #endif /*0*/
265
266 /*
267  * Free mem from region.
268  */
269 void
270 free_region(reg)
271 NhRegion *reg;
272 {
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);
283     }
284 }
285
286 /*
287  * Add a region to the list.
288  * This actually activates the region.
289  */
290 void
291 add_region(reg)
292 NhRegion *reg;
293 {
294     NhRegion **tmp_reg;
295     int i, j;
296
297     if (max_regions <= n_regions) {
298         tmp_reg = regions;
299         regions =
300             (NhRegion **) alloc(sizeof(NhRegion *) * (max_regions + 10));
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);
305         }
306         max_regions += 10;
307     }
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);
320         }
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);
326 }
327
328 /*
329  * Remove a region from the list & free it.
330  */
331 void
332 remove_region(reg)
333 NhRegion *reg;
334 {
335     register int i, x, y;
336
337     for (i = 0; i < n_regions; i++)
338         if (regions[i] == reg)
339             break;
340     if (i == n_regions)
341         return;
342
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);
350
351     free_region(reg);
352     regions[i] = regions[n_regions - 1];
353     regions[n_regions - 1] = (NhRegion *) 0;
354     n_regions--;
355 }
356
357 /*
358  * Remove all regions and clear all related data (This must be down
359  * when changing level, for instance).
360  */
361 void
362 clear_regions()
363 {
364     register int i;
365
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;
373 }
374
375 /*
376  * This function is called every turn.
377  * It makes the regions age, if necessary and calls the appropriate
378  * callbacks when needed.
379  */
380 void
381 run_regions()
382 {
383     register int i, j, k;
384     int f_indx;
385
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]);
393         }
394     }
395
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);
410
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 */
418                 }
419             }
420         }
421     }
422 }
423
424 /*
425  * check whether player enters/leaves one or more regions.
426  */
427 boolean
428 in_out_region(x, y)
429 xchar x, y;
430 {
431     int i, f_indx;
432
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;
445         }
446     }
447
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);
457         }
458
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);
468         }
469     return TRUE;
470 }
471
472 /*
473  * check whether a monster enters/leaves one or more region.
474 */
475 boolean
476 m_in_out_region(mon, x, y)
477 struct monst *mon;
478 xchar x, y;
479 {
480     int i, f_indx;
481
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;
495         }
496     }
497
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);
506         }
507
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);
515         }
516     return TRUE;
517 }
518
519 /*
520  * Checks player's regions after a teleport for instance.
521  */
522 void
523 update_player_regions()
524 {
525     register int i;
526
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]);
532 }
533
534 /*
535  * Ditto for a specified monster.
536  */
537 void
538 update_monster_region(mon)
539 struct monst *mon;
540 {
541     register int i;
542
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);
550         }
551     }
552 }
553
554 #if 0
555 /* not yet used */
556
557 /*
558  * Change monster pointer in regions
559  * This happens, for instance, when a monster grows and
560  * need a new structure (internally that is).
561  */
562 void
563 replace_mon_regions(monold, monnew)
564 struct monst *monold, *monnew;
565 {
566     register int i;
567
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);
572         }
573 }
574
575 /*
576  * Remove monster from all regions it was in (ie monster just died)
577  */
578 void
579 remove_mon_from_regions(mon)
580 struct monst *mon;
581 {
582     register int i;
583
584     for (i = 0; i < n_regions; i++)
585         if (mon_in_region(regions[i], mon))
586             remove_mon_from_reg(regions[i], mon);
587 }
588
589 #endif /*0*/
590
591 /*
592  * Check if a spot is under a visible region (eg: gas cloud).
593  * Returns NULL if not, otherwise returns region.
594  */
595 NhRegion *
596 visible_region_at(x, y)
597 xchar x, y;
598 {
599     register int i;
600
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;
606 }
607
608 void
609 show_region(reg, x, y)
610 NhRegion *reg;
611 xchar x, y;
612 {
613     show_glyph(x, y, reg->glyph);
614 }
615
616 /**
617  * save_regions :
618  */
619 void
620 save_regions(fd, mode)
621 int fd;
622 int mode;
623 {
624     int i, j;
625     unsigned n;
626
627     if (!perform_bwrite(mode))
628         goto skip_lots;
629
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));
668     }
669
670 skip_lots:
671     if (release_data(mode))
672         clear_regions();
673 }
674
675 void
676 rest_regions(fd, ghostly)
677 int fd;
678 boolean ghostly; /* If a bones file restore */
679 {
680     int i, j;
681     unsigned n;
682     long tmstamp;
683     char *msg_buf;
684
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));
699
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));
707
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;
716
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;
725
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]);
742         }
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));
756     }
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]);
764 }
765
766 /* update monster IDs for region being loaded from bones; `ghostly' implied */
767 STATIC_OVL void
768 reset_region_mids(reg)
769 NhRegion *reg;
770 {
771     int i = 0, n = reg->n_monst;
772     unsigned *mid_list = reg->monsters;
773
774     while (i < n)
775         if (!lookup_id_mapping(mid_list[i], &mid_list[i])) {
776             /* shrink list to remove missing monster; order doesn't matter */
777             mid_list[i] = mid_list[--n];
778         } else {
779             /* move on to next monster */
780             ++i;
781         }
782     reg->n_monst = n;
783     return;
784 }
785
786 #if 0
787 /* not yet used */
788
789 /*--------------------------------------------------------------*
790  *                                                              *
791  *                      Create Region with just a message       *
792  *                                                              *
793  *--------------------------------------------------------------*/
794
795 NhRegion *
796 create_msg_region(x, y, w, h, msg_enter, msg_leave)
797 xchar x, y;
798 xchar w, h;
799 const char *msg_enter;
800 const char *msg_leave;
801 {
802     NhRect tmprect;
803     NhRegion *reg = create_region((NhRect *) 0, 0);
804
805     if (msg_enter)
806         reg->enter_msg = dupstr(msg_enter);
807     if (msg_leave)
808         reg->leave_msg = dupstr(msg_leave);
809     tmprect.lx = x;
810     tmprect.ly = y;
811     tmprect.hx = x + w;
812     tmprect.hy = y + h;
813     add_rect_to_reg(reg, &tmprect);
814     reg->ttl = -1L;
815     return reg;
816 }
817
818
819 /*--------------------------------------------------------------*
820  *                                                              *
821  *                      Force Field Related Cod                 *
822  *                      (unused yet)                            *
823  *--------------------------------------------------------------*/
824
825 boolean
826 enter_force_field(p1, p2)
827 genericptr_t p1;
828 genericptr_t p2;
829 {
830     struct monst *mtmp;
831
832     if (p2 == (genericptr_t) 0) { /* That means the player */
833         if (!Blind)
834             You("bump into %s. Ouch!",
835                 Hallucination ? "an invisible tree"
836                               : "some kind of invisible wall");
837         else
838             pline("Ouch!");
839     } else {
840         mtmp = (struct monst *) p2;
841         if (canseemon(mtmp))
842             pline("%s bumps into %s!", Monnam(mtmp), something);
843     }
844     return FALSE;
845 }
846
847 NhRegion *
848 create_force_field(x, y, radius, ttl)
849 xchar x, y;
850 int radius;
851 long ttl;
852 {
853     int i;
854     NhRegion *ff;
855     int nrect;
856     NhRect tmprect;
857
858     ff = create_region((NhRect *) 0, 0);
859     nrect = radius;
860     tmprect.lx = x;
861     tmprect.hx = x;
862     tmprect.ly = y - (radius - 1);
863     tmprect.hy = y + (radius - 1);
864     for (i = 0; i < nrect; i++) {
865         add_rect_to_reg(ff, &tmprect);
866         tmprect.lx--;
867         tmprect.hx++;
868         tmprect.ly++;
869         tmprect.hy--;
870     }
871     ff->ttl = ttl;
872     if (!in_mklev && !context.mon_moving)
873         set_heros_fault(ff); /* assume player has created it */
874  /* ff->can_enter_f = enter_force_field; */
875  /* ff->can_leave_f = enter_force_field; */
876     add_region(ff);
877     return ff;
878 }
879
880 #endif /*0*/
881
882 /*--------------------------------------------------------------*
883  *                                                              *
884  *                      Gas cloud related code                  *
885  *                                                              *
886  *--------------------------------------------------------------*/
887
888 /*
889  * Here is an example of an expire function that may prolong
890  * region life after some mods...
891  */
892 /*ARGSUSED*/
893 boolean
894 expire_gas_cloud(p1, p2)
895 genericptr_t p1;
896 genericptr_t p2 UNUSED;
897 {
898     NhRegion *reg;
899     int damage;
900
901     reg = (NhRegion *) p1;
902     damage = reg->arg.a_int;
903
904     /* If it was a thick cloud, it dissipates a little first */
905     if (damage >= 5) {
906         damage /= 2; /* It dissipates, let's do less damage */
907         reg->arg = zeroany;
908         reg->arg.a_int = damage;
909         reg->ttl = 2L; /* Here's the trick : reset ttl */
910         return FALSE;  /* THEN return FALSE, means "still there" */
911     }
912     return TRUE; /* OK, it's gone, you can free it! */
913 }
914
915 boolean
916 inside_gas_cloud(p1, p2)
917 genericptr_t p1;
918 genericptr_t p2;
919 {
920     NhRegion *reg;
921     struct monst *mtmp;
922     int dam;
923
924     reg = (NhRegion *) p1;
925     dam = reg->arg.a_int;
926     if (p2 == (genericptr_t) 0) { /* This means *YOU* Bozo! */
927         if (u.uinvulnerable || nonliving(youmonst.data) || Breathless)
928             return FALSE;
929         if (!Blind) {
930             Your("%s sting.", makeplural(body_part(EYE)));
931             make_blinded(1L, FALSE);
932         }
933         if (!Poison_resistance) {
934 #if 0 /*JP*/
935             pline("%s is burning your %s!", Something,
936                   makeplural(body_part(LUNG)));
937 #else
938             pline("\89½\82©\96­\82È\82à\82Ì\82ð\8bz\82¢\82±\82ñ\82¾\81I");
939 #endif
940 /*JP
941             You("cough and spit blood!");
942 */
943             You("\8aP\82«\82±\82Ý\81C\8c\8c\82ð\93f\82¢\82½\81I");
944 /*JP
945             losehp(Maybe_Half_Phys(rnd(dam) + 5), "gas cloud", KILLED_BY_AN);
946 */
947             losehp(Maybe_Half_Phys(rnd(dam) + 5), "\83K\83X\89_\82Å", KILLED_BY_AN);
948             return FALSE;
949         } else {
950 /*JP
951             You("cough!");
952 */
953             You("\8aP\82«\82±\82ñ\82¾\81I");
954             return FALSE;
955         }
956     } else { /* A monster is inside the cloud */
957         mtmp = (struct monst *) p2;
958
959         /* Non living and non breathing monsters are not concerned */
960         if (!(nonliving(mtmp->data) || is_vampshifter(mtmp))
961             && !breathless(mtmp->data)) {
962             if (cansee(mtmp->mx, mtmp->my))
963 /*JP
964                 pline("%s coughs!", Monnam(mtmp));
965 */
966                 pline("%s\82Í\8aP\82«\82±\82ñ\82¾\81I", Monnam(mtmp));
967             if (heros_fault(reg))
968                 setmangry(mtmp);
969             if (haseyes(mtmp->data) && mtmp->mcansee) {
970                 mtmp->mblinded = 1;
971                 mtmp->mcansee = 0;
972             }
973             if (resists_poison(mtmp))
974                 return FALSE;
975             mtmp->mhp -= rnd(dam) + 5;
976             if (mtmp->mhp <= 0) {
977                 if (heros_fault(reg))
978                     killed(mtmp);
979                 else
980 /*JP
981                     monkilled(mtmp, "gas cloud", AD_DRST);
982 */
983                     monkilled(mtmp, "\83K\83X\89_", AD_DRST);
984                 if (mtmp->mhp <= 0) { /* not lifesaved */
985                     return TRUE;
986                 }
987             }
988         }
989     }
990     return FALSE; /* Monster is still alive */
991 }
992
993 NhRegion *
994 create_gas_cloud(x, y, radius, damage)
995 xchar x, y;
996 int radius;
997 int damage;
998 {
999     NhRegion *cloud;
1000     int i, nrect;
1001     NhRect tmprect;
1002
1003     cloud = create_region((NhRect *) 0, 0);
1004     nrect = radius;
1005     tmprect.lx = x;
1006     tmprect.hx = x;
1007     tmprect.ly = y - (radius - 1);
1008     tmprect.hy = y + (radius - 1);
1009     for (i = 0; i < nrect; i++) {
1010         add_rect_to_reg(cloud, &tmprect);
1011         tmprect.lx--;
1012         tmprect.hx++;
1013         tmprect.ly++;
1014         tmprect.hy--;
1015     }
1016     cloud->ttl = rn1(3, 4);
1017     if (!in_mklev && !context.mon_moving)
1018         set_heros_fault(cloud); /* assume player has created it */
1019     cloud->inside_f = INSIDE_GAS_CLOUD;
1020     cloud->expire_f = EXPIRE_GAS_CLOUD;
1021     cloud->arg = zeroany;
1022     cloud->arg.a_int = damage;
1023     cloud->visible = TRUE;
1024     cloud->glyph = cmap_to_glyph(damage ? S_poisoncloud : S_cloud);
1025     add_region(cloud);
1026     return cloud;
1027 }
1028
1029 /* for checking troubles during prayer; is hero at risk? */
1030 boolean
1031 region_danger()
1032 {
1033     int i, f_indx, n = 0;
1034
1035     for (i = 0; i < n_regions; i++) {
1036         /* only care about regions that hero is in */
1037         if (!hero_inside(regions[i]))
1038             continue;
1039         f_indx = regions[i]->inside_f;
1040         /* the only type of region we understand is gas_cloud */
1041         if (f_indx == INSIDE_GAS_CLOUD) {
1042             /* completely harmless if you don't need to breathe */
1043             if (nonliving(youmonst.data) || Breathless)
1044                 continue;
1045             /* minor inconvenience if you're poison resistant;
1046                not harmful enough to be a prayer-level trouble */
1047             if (Poison_resistance)
1048                 continue;
1049             ++n;
1050         }
1051     }
1052     return n ? TRUE : FALSE;
1053 }
1054
1055 /* for fixing trouble at end of prayer;
1056    danger detected at start of prayer might have expired by now */
1057 void
1058 region_safety()
1059 {
1060     NhRegion *r = 0;
1061     int i, f_indx, n = 0;
1062
1063     for (i = 0; i < n_regions; i++) {
1064         /* only care about regions that hero is in */
1065         if (!hero_inside(regions[i]))
1066             continue;
1067         f_indx = regions[i]->inside_f;
1068         /* the only type of region we understand is gas_cloud */
1069         if (f_indx == INSIDE_GAS_CLOUD) {
1070             if (!n++ && regions[i]->ttl >= 0)
1071                 r = regions[i];
1072         }
1073     }
1074
1075     if (n > 1 || (n == 1 && !r)) {
1076         /* multiple overlapping cloud regions or non-expiring one */
1077         safe_teleds(FALSE);
1078     } else if (r) {
1079         remove_region(r);
1080         pline_The("gas cloud enveloping you dissipates.");
1081     } else {
1082         /* cloud dissipated on its own, so nothing needs to be done */
1083         pline_The("gas cloud has dissipated.");
1084     }
1085     /* maybe cure blindness too */
1086     if ((Blinded & TIMEOUT) == 1L)
1087         make_blinded(0L, TRUE);
1088 }
1089
1090 /*region.c*/