OSDN Git Service

update year to 2018
[jnethack/source.git] / src / region.c
1 /* NetHack 3.6  region.c        $NHDT-Date: 1496087244 2017/05/29 19:47:24 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.40 $ */
2 /* Copyright (c) 1996 by Jean-Christophe Collet  */
3 /* NetHack may be freely redistributed.  See license for details. */
4
5 /* JNetHack Copyright */
6 /* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000  */
7 /* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2018            */
8 /* JNetHack may be freely redistributed.  See license for details. */
9
10 #include "hack.h"
11 #include "lev.h"
12
13 /*
14  * This should really go into the level structure, but
15  * I'll start here for ease. It *WILL* move into the level
16  * structure eventually.
17  */
18
19 static NhRegion **regions;
20 static int n_regions = 0;
21 static int max_regions = 0;
22
23 #define NO_CALLBACK (-1)
24
25 boolean FDECL(inside_gas_cloud, (genericptr, genericptr));
26 boolean FDECL(expire_gas_cloud, (genericptr, genericptr));
27 boolean FDECL(inside_rect, (NhRect *, int, int));
28 boolean FDECL(inside_region, (NhRegion *, int, int));
29 NhRegion *FDECL(create_region, (NhRect *, int));
30 void FDECL(add_rect_to_reg, (NhRegion *, NhRect *));
31 void FDECL(add_mon_to_reg, (NhRegion *, struct monst *));
32 void FDECL(remove_mon_from_reg, (NhRegion *, struct monst *));
33 boolean FDECL(mon_in_region, (NhRegion *, struct monst *));
34
35 #if 0
36 NhRegion *FDECL(clone_region, (NhRegion *));
37 #endif
38 void FDECL(free_region, (NhRegion *));
39 void FDECL(add_region, (NhRegion *));
40 void FDECL(remove_region, (NhRegion *));
41
42 #if 0
43 void FDECL(replace_mon_regions, (struct monst *,struct monst *));
44 void FDECL(remove_mon_from_regions, (struct monst *));
45 NhRegion *FDECL(create_msg_region, (XCHAR_P,XCHAR_P,XCHAR_P,XCHAR_P,
46                                     const char *,const char *));
47 boolean FDECL(enter_force_field, (genericptr,genericptr));
48 NhRegion *FDECL(create_force_field, (XCHAR_P,XCHAR_P,int,long));
49 #endif
50
51 STATIC_DCL void FDECL(reset_region_mids, (NhRegion *));
52
53 static callback_proc callbacks[] = {
54 #define INSIDE_GAS_CLOUD 0
55     inside_gas_cloud,
56 #define EXPIRE_GAS_CLOUD 1
57     expire_gas_cloud
58 };
59
60 /* Should be inlined. */
61 boolean
62 inside_rect(r, x, y)
63 NhRect *r;
64 int x, y;
65 {
66     return (boolean) (x >= r->lx && x <= r->hx && y >= r->ly && y <= r->hy);
67 }
68
69 /*
70  * Check if a point is inside a region.
71  */
72 boolean
73 inside_region(reg, x, y)
74 NhRegion *reg;
75 int x, y;
76 {
77     int i;
78
79     if (reg == (NhRegion *) 0 || !inside_rect(&(reg->bounding_box), x, y))
80         return FALSE;
81     for (i = 0; i < reg->nrects; i++)
82         if (inside_rect(&(reg->rects[i]), x, y))
83             return TRUE;
84     return FALSE;
85 }
86
87 /*
88  * Create a region. It does not activate it.
89  */
90 NhRegion *
91 create_region(rects, nrect)
92 NhRect *rects;
93 int nrect;
94 {
95     int i;
96     NhRegion *reg;
97
98     reg = (NhRegion *) alloc(sizeof(NhRegion));
99     (void) memset((genericptr_t)reg, 0, sizeof(NhRegion));
100     /* Determines bounding box */
101     if (nrect > 0) {
102         reg->bounding_box = rects[0];
103     } else {
104         reg->bounding_box.lx = COLNO;
105         reg->bounding_box.ly = ROWNO;
106         reg->bounding_box.hx = 0; /* 1 */
107         reg->bounding_box.hy = 0;
108     }
109     reg->nrects = nrect;
110     reg->rects = (nrect > 0) ? (NhRect *) alloc(nrect * sizeof (NhRect)) : 0;
111     for (i = 0; i < nrect; i++) {
112         if (rects[i].lx < reg->bounding_box.lx)
113             reg->bounding_box.lx = rects[i].lx;
114         if (rects[i].ly < reg->bounding_box.ly)
115             reg->bounding_box.ly = rects[i].ly;
116         if (rects[i].hx > reg->bounding_box.hx)
117             reg->bounding_box.hx = rects[i].hx;
118         if (rects[i].hy > reg->bounding_box.hy)
119             reg->bounding_box.hy = rects[i].hy;
120         reg->rects[i] = rects[i];
121     }
122     reg->ttl = -1L; /* Defaults */
123     reg->attach_2_u = FALSE;
124     reg->attach_2_m = 0;
125     /* reg->attach_2_o = NULL; */
126     reg->enter_msg = (const char *) 0;
127     reg->leave_msg = (const char *) 0;
128     reg->expire_f = NO_CALLBACK;
129     reg->enter_f = NO_CALLBACK;
130     reg->can_enter_f = NO_CALLBACK;
131     reg->leave_f = NO_CALLBACK;
132     reg->can_leave_f = NO_CALLBACK;
133     reg->inside_f = NO_CALLBACK;
134     clear_hero_inside(reg);
135     clear_heros_fault(reg);
136     reg->n_monst = 0;
137     reg->max_monst = 0;
138     reg->monsters = (unsigned int *) 0;
139     reg->arg = zeroany;
140     return reg;
141 }
142
143 /*
144  * Add rectangle to region.
145  */
146 void
147 add_rect_to_reg(reg, rect)
148 NhRegion *reg;
149 NhRect *rect;
150 {
151     NhRect *tmp_rect;
152
153     tmp_rect = (NhRect *) alloc((reg->nrects + 1) * sizeof (NhRect));
154     if (reg->nrects > 0) {
155         (void) memcpy((genericptr_t) tmp_rect, (genericptr_t) reg->rects,
156                       reg->nrects * sizeof (NhRect));
157         free((genericptr_t) reg->rects);
158     }
159     tmp_rect[reg->nrects] = *rect;
160     reg->nrects++;
161     reg->rects = tmp_rect;
162     /* Update bounding box if needed */
163     if (reg->bounding_box.lx > rect->lx)
164         reg->bounding_box.lx = rect->lx;
165     if (reg->bounding_box.ly > rect->ly)
166         reg->bounding_box.ly = rect->ly;
167     if (reg->bounding_box.hx < rect->hx)
168         reg->bounding_box.hx = rect->hx;
169     if (reg->bounding_box.hy < rect->hy)
170         reg->bounding_box.hy = rect->hy;
171 }
172
173 /*
174  * Add a monster to the region
175  */
176 void
177 add_mon_to_reg(reg, mon)
178 NhRegion *reg;
179 struct monst *mon;
180 {
181     int i;
182     unsigned *tmp_m;
183
184     if (reg->max_monst <= reg->n_monst) {
185         tmp_m = (unsigned *) alloc(sizeof (unsigned)
186                                    * (reg->max_monst + MONST_INC));
187         if (reg->max_monst > 0) {
188             for (i = 0; i < reg->max_monst; i++)
189                 tmp_m[i] = reg->monsters[i];
190             free((genericptr_t) reg->monsters);
191         }
192         reg->monsters = tmp_m;
193         reg->max_monst += MONST_INC;
194     }
195     reg->monsters[reg->n_monst++] = mon->m_id;
196 }
197
198 /*
199  * Remove a monster from the region list (it left or died...)
200  */
201 void
202 remove_mon_from_reg(reg, mon)
203 NhRegion *reg;
204 struct monst *mon;
205 {
206     register int i;
207
208     for (i = 0; i < reg->n_monst; i++)
209         if (reg->monsters[i] == mon->m_id) {
210             reg->n_monst--;
211             reg->monsters[i] = reg->monsters[reg->n_monst];
212             return;
213         }
214 }
215
216 /*
217  * Check if a monster is inside the region.
218  * It's probably quicker to check with the region internal list
219  * than to check for coordinates.
220  */
221 boolean
222 mon_in_region(reg, mon)
223 NhRegion *reg;
224 struct monst *mon;
225 {
226     int i;
227
228     for (i = 0; i < reg->n_monst; i++)
229         if (reg->monsters[i] == mon->m_id)
230             return TRUE;
231     return FALSE;
232 }
233
234 #if 0
235 /* not yet used */
236
237 /*
238  * Clone (make a standalone copy) the region.
239  */
240 NhRegion *
241 clone_region(reg)
242 NhRegion *reg;
243 {
244     NhRegion *ret_reg;
245
246     ret_reg = create_region(reg->rects, reg->nrects);
247     ret_reg->ttl = reg->ttl;
248     ret_reg->attach_2_u = reg->attach_2_u;
249     ret_reg->attach_2_m = reg->attach_2_m;
250  /* ret_reg->attach_2_o = reg->attach_2_o; */
251     ret_reg->expire_f = reg->expire_f;
252     ret_reg->enter_f = reg->enter_f;
253     ret_reg->can_enter_f = reg->can_enter_f;
254     ret_reg->leave_f = reg->leave_f;
255     ret_reg->can_leave_f = reg->can_leave_f;
256     ret_reg->player_flags = reg->player_flags; /* set/clear_hero_inside,&c*/
257     ret_reg->n_monst = reg->n_monst;
258     if (reg->n_monst > 0) {
259         ret_reg->monsters = (unsigned int *)
260                                     alloc((sizeof (unsigned)) * reg->n_monst);
261         (void) memcpy((genericptr_t) ret_reg->monsters,
262                       (genericptr_t) reg->monsters,
263                       sizeof (unsigned) * reg->n_monst);
264     } else
265         ret_reg->monsters = (unsigned int *) 0;
266     return ret_reg;
267 }
268
269 #endif /*0*/
270
271 /*
272  * Free mem from region.
273  */
274 void
275 free_region(reg)
276 NhRegion *reg;
277 {
278     if (reg) {
279         if (reg->rects)
280             free((genericptr_t) reg->rects);
281         if (reg->monsters)
282             free((genericptr_t) reg->monsters);
283         if (reg->enter_msg)
284             free((genericptr_t) reg->enter_msg);
285         if (reg->leave_msg)
286             free((genericptr_t) reg->leave_msg);
287         free((genericptr_t) reg);
288     }
289 }
290
291 /*
292  * Add a region to the list.
293  * This actually activates the region.
294  */
295 void
296 add_region(reg)
297 NhRegion *reg;
298 {
299     NhRegion **tmp_reg;
300     int i, j;
301
302     if (max_regions <= n_regions) {
303         tmp_reg = regions;
304         regions =
305             (NhRegion **) alloc((max_regions + 10) * sizeof (NhRegion *));
306         if (max_regions > 0) {
307             (void) memcpy((genericptr_t) regions, (genericptr_t) tmp_reg,
308                           max_regions * sizeof (NhRegion *));
309             free((genericptr_t) tmp_reg);
310         }
311         max_regions += 10;
312     }
313     regions[n_regions] = reg;
314     n_regions++;
315     /* Check for monsters inside the region */
316     for (i = reg->bounding_box.lx; i <= reg->bounding_box.hx; i++)
317         for (j = reg->bounding_box.ly; j <= reg->bounding_box.hy; j++) {
318             /* Some regions can cross the level boundaries */
319             if (!isok(i, j))
320                 continue;
321             if (MON_AT(i, j) && inside_region(reg, i, j))
322                 add_mon_to_reg(reg, level.monsters[i][j]);
323             if (reg->visible && cansee(i, j))
324                 newsym(i, j);
325         }
326     /* Check for player now... */
327     if (inside_region(reg, u.ux, u.uy))
328         set_hero_inside(reg);
329     else
330         clear_hero_inside(reg);
331 }
332
333 /*
334  * Remove a region from the list & free it.
335  */
336 void
337 remove_region(reg)
338 NhRegion *reg;
339 {
340     register int i, x, y;
341
342     for (i = 0; i < n_regions; i++)
343         if (regions[i] == reg)
344             break;
345     if (i == n_regions)
346         return;
347
348     /* Update screen if necessary */
349     reg->ttl = -2L; /* for visible_region_at */
350     if (reg->visible)
351         for (x = reg->bounding_box.lx; x <= reg->bounding_box.hx; x++)
352             for (y = reg->bounding_box.ly; y <= reg->bounding_box.hy; y++)
353                 if (isok(x, y) && inside_region(reg, x, y) && cansee(x, y))
354                     newsym(x, y);
355
356     free_region(reg);
357     regions[i] = regions[n_regions - 1];
358     regions[n_regions - 1] = (NhRegion *) 0;
359     n_regions--;
360 }
361
362 /*
363  * Remove all regions and clear all related data (This must be down
364  * when changing level, for instance).
365  */
366 void
367 clear_regions()
368 {
369     register int i;
370
371     for (i = 0; i < n_regions; i++)
372         free_region(regions[i]);
373     n_regions = 0;
374     if (max_regions > 0)
375         free((genericptr_t) regions);
376     max_regions = 0;
377     regions = (NhRegion **) 0;
378 }
379
380 /*
381  * This function is called every turn.
382  * It makes the regions age, if necessary and calls the appropriate
383  * callbacks when needed.
384  */
385 void
386 run_regions()
387 {
388     register int i, j, k;
389     int f_indx;
390
391     /* End of life ? */
392     /* Do it backward because the array will be modified */
393     for (i = n_regions - 1; i >= 0; i--) {
394         if (regions[i]->ttl == 0L) {
395             if ((f_indx = regions[i]->expire_f) == NO_CALLBACK
396                 || (*callbacks[f_indx])(regions[i], (genericptr_t) 0))
397                 remove_region(regions[i]);
398         }
399     }
400
401     /* Process remaining regions */
402     for (i = 0; i < n_regions; i++) {
403         /* Make the region age */
404         if (regions[i]->ttl > 0L)
405             regions[i]->ttl--;
406         /* Check if player is inside region */
407         f_indx = regions[i]->inside_f;
408         if (f_indx != NO_CALLBACK && hero_inside(regions[i]))
409             (void) (*callbacks[f_indx])(regions[i], (genericptr_t) 0);
410         /* Check if any monster is inside region */
411         if (f_indx != NO_CALLBACK) {
412             for (j = 0; j < regions[i]->n_monst; j++) {
413                 struct monst *mtmp =
414                     find_mid(regions[i]->monsters[j], FM_FMON);
415
416                 if (!mtmp || mtmp->mhp <= 0
417                     || (*callbacks[f_indx])(regions[i], mtmp)) {
418                     /* The monster died, remove it from list */
419                     k = (regions[i]->n_monst -= 1);
420                     regions[i]->monsters[j] = regions[i]->monsters[k];
421                     regions[i]->monsters[k] = 0;
422                     --j; /* current slot has been reused; recheck it next */
423                 }
424             }
425         }
426     }
427 }
428
429 /*
430  * check whether player enters/leaves one or more regions.
431  */
432 boolean
433 in_out_region(x, y)
434 xchar x, y;
435 {
436     int i, f_indx;
437
438     /* First check if we can do the move */
439     for (i = 0; i < n_regions; i++) {
440         if (inside_region(regions[i], x, y) && !hero_inside(regions[i])
441             && !regions[i]->attach_2_u) {
442             if ((f_indx = regions[i]->can_enter_f) != NO_CALLBACK)
443                 if (!(*callbacks[f_indx])(regions[i], (genericptr_t) 0))
444                     return FALSE;
445         } else if (hero_inside(regions[i]) && !inside_region(regions[i], x, y)
446                    && !regions[i]->attach_2_u) {
447             if ((f_indx = regions[i]->can_leave_f) != NO_CALLBACK)
448                 if (!(*callbacks[f_indx])(regions[i], (genericptr_t) 0))
449                     return FALSE;
450         }
451     }
452
453     /* Callbacks for the regions we do leave */
454     for (i = 0; i < n_regions; i++)
455         if (hero_inside(regions[i]) && !regions[i]->attach_2_u
456             && !inside_region(regions[i], x, y)) {
457             clear_hero_inside(regions[i]);
458             if (regions[i]->leave_msg != (const char *) 0)
459                 pline1(regions[i]->leave_msg);
460             if ((f_indx = regions[i]->leave_f) != NO_CALLBACK)
461                 (void) (*callbacks[f_indx])(regions[i], (genericptr_t) 0);
462         }
463
464     /* Callbacks for the regions we do enter */
465     for (i = 0; i < n_regions; i++)
466         if (!hero_inside(regions[i]) && !regions[i]->attach_2_u
467             && inside_region(regions[i], x, y)) {
468             set_hero_inside(regions[i]);
469             if (regions[i]->enter_msg != (const char *) 0)
470                 pline1(regions[i]->enter_msg);
471             if ((f_indx = regions[i]->enter_f) != NO_CALLBACK)
472                 (void) (*callbacks[f_indx])(regions[i], (genericptr_t) 0);
473         }
474     return TRUE;
475 }
476
477 /*
478  * check whether a monster enters/leaves one or more region.
479 */
480 boolean
481 m_in_out_region(mon, x, y)
482 struct monst *mon;
483 xchar x, y;
484 {
485     int i, f_indx;
486
487     /* First check if we can do the move */
488     for (i = 0; i < n_regions; i++) {
489         if (inside_region(regions[i], x, y) && !mon_in_region(regions[i], mon)
490             && regions[i]->attach_2_m != mon->m_id) {
491             if ((f_indx = regions[i]->can_enter_f) != NO_CALLBACK)
492                 if (!(*callbacks[f_indx])(regions[i], mon))
493                     return FALSE;
494         } else if (mon_in_region(regions[i], mon)
495                    && !inside_region(regions[i], x, y)
496                    && regions[i]->attach_2_m != mon->m_id) {
497             if ((f_indx = regions[i]->can_leave_f) != NO_CALLBACK)
498                 if (!(*callbacks[f_indx])(regions[i], mon))
499                     return FALSE;
500         }
501     }
502
503     /* Callbacks for the regions we do leave */
504     for (i = 0; i < n_regions; i++)
505         if (mon_in_region(regions[i], mon)
506             && regions[i]->attach_2_m != mon->m_id
507             && !inside_region(regions[i], x, y)) {
508             remove_mon_from_reg(regions[i], mon);
509             if ((f_indx = regions[i]->leave_f) != NO_CALLBACK)
510                 (void) (*callbacks[f_indx])(regions[i], mon);
511         }
512
513     /* Callbacks for the regions we do enter */
514     for (i = 0; i < n_regions; i++)
515         if (!hero_inside(regions[i]) && !regions[i]->attach_2_u
516             && inside_region(regions[i], x, y)) {
517             add_mon_to_reg(regions[i], mon);
518             if ((f_indx = regions[i]->enter_f) != NO_CALLBACK)
519                 (void) (*callbacks[f_indx])(regions[i], mon);
520         }
521     return TRUE;
522 }
523
524 /*
525  * Checks player's regions after a teleport for instance.
526  */
527 void
528 update_player_regions()
529 {
530     register int i;
531
532     for (i = 0; i < n_regions; i++)
533         if (!regions[i]->attach_2_u && inside_region(regions[i], u.ux, u.uy))
534             set_hero_inside(regions[i]);
535         else
536             clear_hero_inside(regions[i]);
537 }
538
539 /*
540  * Ditto for a specified monster.
541  */
542 void
543 update_monster_region(mon)
544 struct monst *mon;
545 {
546     register int i;
547
548     for (i = 0; i < n_regions; i++) {
549         if (inside_region(regions[i], mon->mx, mon->my)) {
550             if (!mon_in_region(regions[i], mon))
551                 add_mon_to_reg(regions[i], mon);
552         } else {
553             if (mon_in_region(regions[i], mon))
554                 remove_mon_from_reg(regions[i], mon);
555         }
556     }
557 }
558
559 #if 0
560 /* not yet used */
561
562 /*
563  * Change monster pointer in regions
564  * This happens, for instance, when a monster grows and
565  * need a new structure (internally that is).
566  */
567 void
568 replace_mon_regions(monold, monnew)
569 struct monst *monold, *monnew;
570 {
571     register int i;
572
573     for (i = 0; i < n_regions; i++)
574         if (mon_in_region(regions[i], monold)) {
575             remove_mon_from_reg(regions[i], monold);
576             add_mon_to_reg(regions[i], monnew);
577         }
578 }
579
580 /*
581  * Remove monster from all regions it was in (ie monster just died)
582  */
583 void
584 remove_mon_from_regions(mon)
585 struct monst *mon;
586 {
587     register int i;
588
589     for (i = 0; i < n_regions; i++)
590         if (mon_in_region(regions[i], mon))
591             remove_mon_from_reg(regions[i], mon);
592 }
593
594 #endif /*0*/
595
596 /*
597  * Check if a spot is under a visible region (eg: gas cloud).
598  * Returns NULL if not, otherwise returns region.
599  */
600 NhRegion *
601 visible_region_at(x, y)
602 xchar x, y;
603 {
604     register int i;
605
606     for (i = 0; i < n_regions; i++)
607         if (inside_region(regions[i], x, y) && regions[i]->visible
608             && regions[i]->ttl != -2L)
609             return regions[i];
610     return (NhRegion *) 0;
611 }
612
613 void
614 show_region(reg, x, y)
615 NhRegion *reg;
616 xchar x, y;
617 {
618     show_glyph(x, y, reg->glyph);
619 }
620
621 /**
622  * save_regions :
623  */
624 void
625 save_regions(fd, mode)
626 int fd;
627 int mode;
628 {
629     int i, j;
630     unsigned n;
631
632     if (!perform_bwrite(mode))
633         goto skip_lots;
634
635     bwrite(fd, (genericptr_t) &moves, sizeof(moves)); /* timestamp */
636     bwrite(fd, (genericptr_t) &n_regions, sizeof(n_regions));
637     for (i = 0; i < n_regions; i++) {
638         bwrite(fd, (genericptr_t) &regions[i]->bounding_box, sizeof(NhRect));
639         bwrite(fd, (genericptr_t) &regions[i]->nrects, sizeof(short));
640         for (j = 0; j < regions[i]->nrects; j++)
641             bwrite(fd, (genericptr_t) &regions[i]->rects[j], sizeof(NhRect));
642         bwrite(fd, (genericptr_t) &regions[i]->attach_2_u, sizeof(boolean));
643         n = 0;
644         bwrite(fd, (genericptr_t) &regions[i]->attach_2_m, sizeof(unsigned));
645         n = regions[i]->enter_msg != (const char *) 0
646                 ? strlen(regions[i]->enter_msg)
647                 : 0;
648         bwrite(fd, (genericptr_t) &n, sizeof n);
649         if (n > 0)
650             bwrite(fd, (genericptr_t) regions[i]->enter_msg, n);
651         n = regions[i]->leave_msg != (const char *) 0
652                 ? strlen(regions[i]->leave_msg)
653                 : 0;
654         bwrite(fd, (genericptr_t) &n, sizeof n);
655         if (n > 0)
656             bwrite(fd, (genericptr_t) regions[i]->leave_msg, n);
657         bwrite(fd, (genericptr_t) &regions[i]->ttl, sizeof(long));
658         bwrite(fd, (genericptr_t) &regions[i]->expire_f, sizeof(short));
659         bwrite(fd, (genericptr_t) &regions[i]->can_enter_f, sizeof(short));
660         bwrite(fd, (genericptr_t) &regions[i]->enter_f, sizeof(short));
661         bwrite(fd, (genericptr_t) &regions[i]->can_leave_f, sizeof(short));
662         bwrite(fd, (genericptr_t) &regions[i]->leave_f, sizeof(short));
663         bwrite(fd, (genericptr_t) &regions[i]->inside_f, sizeof(short));
664         bwrite(fd, (genericptr_t) &regions[i]->player_flags,
665                sizeof(unsigned int));
666         bwrite(fd, (genericptr_t) &regions[i]->n_monst, sizeof(short));
667         for (j = 0; j < regions[i]->n_monst; j++)
668             bwrite(fd, (genericptr_t) &regions[i]->monsters[j],
669                    sizeof(unsigned));
670         bwrite(fd, (genericptr_t) &regions[i]->visible, sizeof(boolean));
671         bwrite(fd, (genericptr_t) &regions[i]->glyph, sizeof(int));
672         bwrite(fd, (genericptr_t) &regions[i]->arg, sizeof(anything));
673     }
674
675 skip_lots:
676     if (release_data(mode))
677         clear_regions();
678 }
679
680 void
681 rest_regions(fd, ghostly)
682 int fd;
683 boolean ghostly; /* If a bones file restore */
684 {
685     int i, j;
686     unsigned n;
687     long tmstamp;
688     char *msg_buf;
689
690     clear_regions(); /* Just for security */
691     mread(fd, (genericptr_t) &tmstamp, sizeof(tmstamp));
692     if (ghostly)
693         tmstamp = 0;
694     else
695         tmstamp = (moves - tmstamp);
696     mread(fd, (genericptr_t) &n_regions, sizeof(n_regions));
697     max_regions = n_regions;
698     if (n_regions > 0)
699         regions = (NhRegion **) alloc(sizeof(NhRegion *) * n_regions);
700     for (i = 0; i < n_regions; i++) {
701         regions[i] = (NhRegion *) alloc(sizeof(NhRegion));
702         mread(fd, (genericptr_t) &regions[i]->bounding_box, sizeof(NhRect));
703         mread(fd, (genericptr_t) &regions[i]->nrects, sizeof(short));
704
705         if (regions[i]->nrects > 0)
706             regions[i]->rects =
707                 (NhRect *) alloc(sizeof(NhRect) * regions[i]->nrects);
708         for (j = 0; j < regions[i]->nrects; j++)
709             mread(fd, (genericptr_t) &regions[i]->rects[j], sizeof(NhRect));
710         mread(fd, (genericptr_t) &regions[i]->attach_2_u, sizeof(boolean));
711         mread(fd, (genericptr_t) &regions[i]->attach_2_m, sizeof(unsigned));
712
713         mread(fd, (genericptr_t) &n, sizeof n);
714         if (n > 0) {
715             msg_buf = (char *) alloc(n + 1);
716             mread(fd, (genericptr_t) msg_buf, n);
717             msg_buf[n] = '\0';
718             regions[i]->enter_msg = (const char *) msg_buf;
719         } else
720             regions[i]->enter_msg = (const char *) 0;
721
722         mread(fd, (genericptr_t) &n, sizeof n);
723         if (n > 0) {
724             msg_buf = (char *) alloc(n + 1);
725             mread(fd, (genericptr_t) msg_buf, n);
726             msg_buf[n] = '\0';
727             regions[i]->leave_msg = (const char *) msg_buf;
728         } else
729             regions[i]->leave_msg = (const char *) 0;
730
731         mread(fd, (genericptr_t) &regions[i]->ttl, sizeof(long));
732         /* check for expired region */
733         if (regions[i]->ttl >= 0L)
734             regions[i]->ttl =
735                 (regions[i]->ttl > tmstamp) ? regions[i]->ttl - tmstamp : 0L;
736         mread(fd, (genericptr_t) &regions[i]->expire_f, sizeof(short));
737         mread(fd, (genericptr_t) &regions[i]->can_enter_f, sizeof(short));
738         mread(fd, (genericptr_t) &regions[i]->enter_f, sizeof(short));
739         mread(fd, (genericptr_t) &regions[i]->can_leave_f, sizeof(short));
740         mread(fd, (genericptr_t) &regions[i]->leave_f, sizeof(short));
741         mread(fd, (genericptr_t) &regions[i]->inside_f, sizeof(short));
742         mread(fd, (genericptr_t) &regions[i]->player_flags,
743               sizeof(unsigned int));
744         if (ghostly) { /* settings pertained to old player */
745             clear_hero_inside(regions[i]);
746             clear_heros_fault(regions[i]);
747         }
748         mread(fd, (genericptr_t) &regions[i]->n_monst, sizeof(short));
749         if (regions[i]->n_monst > 0)
750             regions[i]->monsters =
751                 (unsigned *) alloc(sizeof(unsigned) * regions[i]->n_monst);
752         else
753             regions[i]->monsters = (unsigned int *) 0;
754         regions[i]->max_monst = regions[i]->n_monst;
755         for (j = 0; j < regions[i]->n_monst; j++)
756             mread(fd, (genericptr_t) &regions[i]->monsters[j],
757                   sizeof(unsigned));
758         mread(fd, (genericptr_t) &regions[i]->visible, sizeof(boolean));
759         mread(fd, (genericptr_t) &regions[i]->glyph, sizeof(int));
760         mread(fd, (genericptr_t) &regions[i]->arg, sizeof(anything));
761     }
762     /* remove expired regions, do not trigger the expire_f callback (yet!);
763        also update monster lists if this data is coming from a bones file */
764     for (i = n_regions - 1; i >= 0; i--)
765         if (regions[i]->ttl == 0L)
766             remove_region(regions[i]);
767         else if (ghostly && regions[i]->n_monst > 0)
768             reset_region_mids(regions[i]);
769 }
770
771 /* to support '#stats' wizard-mode command */
772 void
773 region_stats(hdrfmt, hdrbuf, count, size)
774 const char *hdrfmt;
775 char *hdrbuf;
776 long *count, *size;
777 {
778     NhRegion *rg;
779     int i;
780
781     /* other stats formats take one parameter; this takes two */
782     Sprintf(hdrbuf, hdrfmt, (long) sizeof (NhRegion), (long) sizeof (NhRect));
783     *count = (long) n_regions; /* might be 0 even though max_regions isn't */
784     *size = (long) max_regions * (long) sizeof (NhRegion);
785     for (i = 0; i < n_regions; ++i) {
786         rg = regions[i];
787         *size += (long) rg->nrects * (long) sizeof (NhRect);
788         if (rg->enter_msg)
789             *size += (long) (strlen(rg->enter_msg) + 1);
790         if (rg->leave_msg)
791             *size += (long) (strlen(rg->leave_msg) + 1);
792         *size += (long) rg->max_monst * (long) sizeof *rg->monsters;
793     }
794     /* ? */
795 }
796
797 /* update monster IDs for region being loaded from bones; `ghostly' implied */
798 STATIC_OVL void
799 reset_region_mids(reg)
800 NhRegion *reg;
801 {
802     int i = 0, n = reg->n_monst;
803     unsigned *mid_list = reg->monsters;
804
805     while (i < n)
806         if (!lookup_id_mapping(mid_list[i], &mid_list[i])) {
807             /* shrink list to remove missing monster; order doesn't matter */
808             mid_list[i] = mid_list[--n];
809         } else {
810             /* move on to next monster */
811             ++i;
812         }
813     reg->n_monst = n;
814     return;
815 }
816
817 #if 0
818 /* not yet used */
819
820 /*--------------------------------------------------------------*
821  *                                                              *
822  *                      Create Region with just a message       *
823  *                                                              *
824  *--------------------------------------------------------------*/
825
826 NhRegion *
827 create_msg_region(x, y, w, h, msg_enter, msg_leave)
828 xchar x, y;
829 xchar w, h;
830 const char *msg_enter;
831 const char *msg_leave;
832 {
833     NhRect tmprect;
834     NhRegion *reg = create_region((NhRect *) 0, 0);
835
836     if (msg_enter)
837         reg->enter_msg = dupstr(msg_enter);
838     if (msg_leave)
839         reg->leave_msg = dupstr(msg_leave);
840     tmprect.lx = x;
841     tmprect.ly = y;
842     tmprect.hx = x + w;
843     tmprect.hy = y + h;
844     add_rect_to_reg(reg, &tmprect);
845     reg->ttl = -1L;
846     return reg;
847 }
848
849
850 /*--------------------------------------------------------------*
851  *                                                              *
852  *                      Force Field Related Cod                 *
853  *                      (unused yet)                            *
854  *--------------------------------------------------------------*/
855
856 boolean
857 enter_force_field(p1, p2)
858 genericptr_t p1;
859 genericptr_t p2;
860 {
861     struct monst *mtmp;
862
863     if (p2 == (genericptr_t) 0) { /* That means the player */
864         if (!Blind)
865             You("bump into %s. Ouch!",
866                 Hallucination ? "an invisible tree"
867                               : "some kind of invisible wall");
868         else
869             pline("Ouch!");
870     } else {
871         mtmp = (struct monst *) p2;
872         if (canseemon(mtmp))
873             pline("%s bumps into %s!", Monnam(mtmp), something);
874     }
875     return FALSE;
876 }
877
878 NhRegion *
879 create_force_field(x, y, radius, ttl)
880 xchar x, y;
881 int radius;
882 long ttl;
883 {
884     int i;
885     NhRegion *ff;
886     int nrect;
887     NhRect tmprect;
888
889     ff = create_region((NhRect *) 0, 0);
890     nrect = radius;
891     tmprect.lx = x;
892     tmprect.hx = x;
893     tmprect.ly = y - (radius - 1);
894     tmprect.hy = y + (radius - 1);
895     for (i = 0; i < nrect; i++) {
896         add_rect_to_reg(ff, &tmprect);
897         tmprect.lx--;
898         tmprect.hx++;
899         tmprect.ly++;
900         tmprect.hy--;
901     }
902     ff->ttl = ttl;
903     if (!in_mklev && !context.mon_moving)
904         set_heros_fault(ff); /* assume player has created it */
905  /* ff->can_enter_f = enter_force_field; */
906  /* ff->can_leave_f = enter_force_field; */
907     add_region(ff);
908     return ff;
909 }
910
911 #endif /*0*/
912
913 /*--------------------------------------------------------------*
914  *                                                              *
915  *                      Gas cloud related code                  *
916  *                                                              *
917  *--------------------------------------------------------------*/
918
919 /*
920  * Here is an example of an expire function that may prolong
921  * region life after some mods...
922  */
923 /*ARGSUSED*/
924 boolean
925 expire_gas_cloud(p1, p2)
926 genericptr_t p1;
927 genericptr_t p2 UNUSED;
928 {
929     NhRegion *reg;
930     int damage;
931
932     reg = (NhRegion *) p1;
933     damage = reg->arg.a_int;
934
935     /* If it was a thick cloud, it dissipates a little first */
936     if (damage >= 5) {
937         damage /= 2; /* It dissipates, let's do less damage */
938         reg->arg = zeroany;
939         reg->arg.a_int = damage;
940         reg->ttl = 2L; /* Here's the trick : reset ttl */
941         return FALSE;  /* THEN return FALSE, means "still there" */
942     }
943     return TRUE; /* OK, it's gone, you can free it! */
944 }
945
946 boolean
947 inside_gas_cloud(p1, p2)
948 genericptr_t p1;
949 genericptr_t p2;
950 {
951     NhRegion *reg;
952     struct monst *mtmp;
953     int dam;
954
955     reg = (NhRegion *) p1;
956     dam = reg->arg.a_int;
957     if (p2 == (genericptr_t) 0) { /* This means *YOU* Bozo! */
958         if (u.uinvulnerable || nonliving(youmonst.data) || Breathless)
959             return FALSE;
960         if (!Blind) {
961 /*JP
962             Your("%s sting.", makeplural(body_part(EYE)));
963 */
964             Your("%s\82ª\83`\83N\83`\83N\82µ\82½\81D", body_part(EYE));
965             make_blinded(1L, FALSE);
966         }
967         if (!Poison_resistance) {
968 #if 0 /*JP*/
969             pline("%s is burning your %s!", Something,
970                   makeplural(body_part(LUNG)));
971 #else
972             pline("\89½\82©\96­\82È\82à\82Ì\82ð\8bz\82¢\82±\82ñ\82¾\81I");
973 #endif
974 /*JP
975             You("cough and spit blood!");
976 */
977             You("\8aP\82«\82±\82Ý\81C\8c\8c\82ð\93f\82¢\82½\81I");
978 /*JP
979             losehp(Maybe_Half_Phys(rnd(dam) + 5), "gas cloud", KILLED_BY_AN);
980 */
981             losehp(Maybe_Half_Phys(rnd(dam) + 5), "\83K\83X\89_\82Å", KILLED_BY_AN);
982             return FALSE;
983         } else {
984 /*JP
985             You("cough!");
986 */
987             You("\8aP\82«\82±\82ñ\82¾\81I");
988             return FALSE;
989         }
990     } else { /* A monster is inside the cloud */
991         mtmp = (struct monst *) p2;
992
993         /* Non living and non breathing monsters are not concerned;
994            adult green dragon is not affected by gas cloud, baby one is */
995         if (!(nonliving(mtmp->data) || is_vampshifter(mtmp))
996             && !breathless(mtmp->data)
997             /* exclude monsters with poison gas breath attack:
998                adult green dragon and Chromatic Dragon (and iron golem,
999                but nonliving() and breathless() tests also catch that) */
1000             && !(attacktype_fordmg(mtmp->data, AT_BREA, AD_DRST)
1001                  || attacktype_fordmg(mtmp->data, AT_BREA, AD_RBRE))) {
1002             if (cansee(mtmp->mx, mtmp->my))
1003 /*JP
1004                 pline("%s coughs!", Monnam(mtmp));
1005 */
1006                 pline("%s\82Í\8aP\82«\82±\82ñ\82¾\81I", Monnam(mtmp));
1007             if (heros_fault(reg))
1008                 setmangry(mtmp, TRUE);
1009             if (haseyes(mtmp->data) && mtmp->mcansee) {
1010                 mtmp->mblinded = 1;
1011                 mtmp->mcansee = 0;
1012             }
1013             if (resists_poison(mtmp))
1014                 return FALSE;
1015             mtmp->mhp -= rnd(dam) + 5;
1016             if (mtmp->mhp <= 0) {
1017                 if (heros_fault(reg))
1018                     killed(mtmp);
1019                 else
1020 /*JP
1021                     monkilled(mtmp, "gas cloud", AD_DRST);
1022 */
1023                     monkilled(mtmp, "\83K\83X\89_", AD_DRST);
1024                 if (mtmp->mhp <= 0) { /* not lifesaved */
1025                     return TRUE;
1026                 }
1027             }
1028         }
1029     }
1030     return FALSE; /* Monster is still alive */
1031 }
1032
1033 NhRegion *
1034 create_gas_cloud(x, y, radius, damage)
1035 xchar x, y;
1036 int radius;
1037 int damage;
1038 {
1039     NhRegion *cloud;
1040     int i, nrect;
1041     NhRect tmprect;
1042
1043     cloud = create_region((NhRect *) 0, 0);
1044     nrect = radius;
1045     tmprect.lx = x;
1046     tmprect.hx = x;
1047     tmprect.ly = y - (radius - 1);
1048     tmprect.hy = y + (radius - 1);
1049     for (i = 0; i < nrect; i++) {
1050         add_rect_to_reg(cloud, &tmprect);
1051         tmprect.lx--;
1052         tmprect.hx++;
1053         tmprect.ly++;
1054         tmprect.hy--;
1055     }
1056     cloud->ttl = rn1(3, 4);
1057     if (!in_mklev && !context.mon_moving)
1058         set_heros_fault(cloud); /* assume player has created it */
1059     cloud->inside_f = INSIDE_GAS_CLOUD;
1060     cloud->expire_f = EXPIRE_GAS_CLOUD;
1061     cloud->arg = zeroany;
1062     cloud->arg.a_int = damage;
1063     cloud->visible = TRUE;
1064     cloud->glyph = cmap_to_glyph(damage ? S_poisoncloud : S_cloud);
1065     add_region(cloud);
1066     return cloud;
1067 }
1068
1069 /* for checking troubles during prayer; is hero at risk? */
1070 boolean
1071 region_danger()
1072 {
1073     int i, f_indx, n = 0;
1074
1075     for (i = 0; i < n_regions; i++) {
1076         /* only care about regions that hero is in */
1077         if (!hero_inside(regions[i]))
1078             continue;
1079         f_indx = regions[i]->inside_f;
1080         /* the only type of region we understand is gas_cloud */
1081         if (f_indx == INSIDE_GAS_CLOUD) {
1082             /* completely harmless if you don't need to breathe */
1083             if (nonliving(youmonst.data) || Breathless)
1084                 continue;
1085             /* minor inconvenience if you're poison resistant;
1086                not harmful enough to be a prayer-level trouble */
1087             if (Poison_resistance)
1088                 continue;
1089             ++n;
1090         }
1091     }
1092     return n ? TRUE : FALSE;
1093 }
1094
1095 /* for fixing trouble at end of prayer;
1096    danger detected at start of prayer might have expired by now */
1097 void
1098 region_safety()
1099 {
1100     NhRegion *r = 0;
1101     int i, f_indx, n = 0;
1102
1103     for (i = 0; i < n_regions; i++) {
1104         /* only care about regions that hero is in */
1105         if (!hero_inside(regions[i]))
1106             continue;
1107         f_indx = regions[i]->inside_f;
1108         /* the only type of region we understand is gas_cloud */
1109         if (f_indx == INSIDE_GAS_CLOUD) {
1110             if (!n++ && regions[i]->ttl >= 0)
1111                 r = regions[i];
1112         }
1113     }
1114
1115     if (n > 1 || (n == 1 && !r)) {
1116         /* multiple overlapping cloud regions or non-expiring one */
1117         safe_teleds(FALSE);
1118     } else if (r) {
1119         remove_region(r);
1120 /*JP
1121         pline_The("gas cloud enveloping you dissipates.");
1122 */
1123         pline("\82 \82È\82½\82ð\95ï\82ñ\82Å\82¢\82½\83K\83X\89_\82Í\8fÁ\82¦\82½\81D");
1124     } else {
1125         /* cloud dissipated on its own, so nothing needs to be done */
1126 /*JP
1127         pline_The("gas cloud has dissipated.");
1128 */
1129         pline("\83K\83X\89_\82Í\8fÁ\82¦\82½\81D");
1130     }
1131     /* maybe cure blindness too */
1132     if ((Blinded & TIMEOUT) == 1L)
1133         make_blinded(0L, TRUE);
1134 }
1135
1136 /*region.c*/