OSDN Git Service

update year to 2020
[jnethack/source.git] / src / region.c
1 /* NetHack 3.6  region.c        $NHDT-Date: 1573957877 2019/11/17 02:31:17 $  $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.45 $ */
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-2020            */
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     /* remove region before potential newsym() calls, but don't free it yet */
349     if (--n_regions != i)
350         regions[i] = regions[n_regions];
351     regions[n_regions] = (NhRegion *) 0;
352
353     /* Update screen if necessary */
354     reg->ttl = -2L; /* for visible_region_at */
355     if (reg->visible)
356         for (x = reg->bounding_box.lx; x <= reg->bounding_box.hx; x++)
357             for (y = reg->bounding_box.ly; y <= reg->bounding_box.hy; y++)
358                 if (isok(x, y) && inside_region(reg, x, y) && cansee(x, y))
359                     newsym(x, y);
360
361     free_region(reg);
362 }
363
364 /*
365  * Remove all regions and clear all related data (This must be down
366  * when changing level, for instance).
367  */
368 void
369 clear_regions()
370 {
371     register int i;
372
373     for (i = 0; i < n_regions; i++)
374         free_region(regions[i]);
375     n_regions = 0;
376     if (max_regions > 0)
377         free((genericptr_t) regions);
378     max_regions = 0;
379     regions = (NhRegion **) 0;
380 }
381
382 /*
383  * This function is called every turn.
384  * It makes the regions age, if necessary and calls the appropriate
385  * callbacks when needed.
386  */
387 void
388 run_regions()
389 {
390     register int i, j, k;
391     int f_indx;
392
393     /* End of life ? */
394     /* Do it backward because the array will be modified */
395     for (i = n_regions - 1; i >= 0; i--) {
396         if (regions[i]->ttl == 0L) {
397             if ((f_indx = regions[i]->expire_f) == NO_CALLBACK
398                 || (*callbacks[f_indx])(regions[i], (genericptr_t) 0))
399                 remove_region(regions[i]);
400         }
401     }
402
403     /* Process remaining regions */
404     for (i = 0; i < n_regions; i++) {
405         /* Make the region age */
406         if (regions[i]->ttl > 0L)
407             regions[i]->ttl--;
408         /* Check if player is inside region */
409         f_indx = regions[i]->inside_f;
410         if (f_indx != NO_CALLBACK && hero_inside(regions[i]))
411             (void) (*callbacks[f_indx])(regions[i], (genericptr_t) 0);
412         /* Check if any monster is inside region */
413         if (f_indx != NO_CALLBACK) {
414             for (j = 0; j < regions[i]->n_monst; j++) {
415                 struct monst *mtmp =
416                     find_mid(regions[i]->monsters[j], FM_FMON);
417
418                 if (!mtmp || DEADMONSTER(mtmp)
419                     || (*callbacks[f_indx])(regions[i], mtmp)) {
420                     /* The monster died, remove it from list */
421                     k = (regions[i]->n_monst -= 1);
422                     regions[i]->monsters[j] = regions[i]->monsters[k];
423                     regions[i]->monsters[k] = 0;
424                     --j; /* current slot has been reused; recheck it next */
425                 }
426             }
427         }
428     }
429 }
430
431 /*
432  * check whether player enters/leaves one or more regions.
433  */
434 boolean
435 in_out_region(x, y)
436 xchar x, y;
437 {
438     int i, f_indx = 0;
439
440     /* First check if hero can do the move */
441     for (i = 0; i < n_regions; i++) {
442         if (regions[i]->attach_2_u)
443             continue;
444         if (inside_region(regions[i], x, y)
445             ? (!hero_inside(regions[i])
446                && (f_indx = regions[i]->can_enter_f) != NO_CALLBACK)
447             : (hero_inside(regions[i])
448                && (f_indx = regions[i]->can_leave_f) != NO_CALLBACK)) {
449             if (!(*callbacks[f_indx])(regions[i], (genericptr_t) 0))
450                 return FALSE;
451         }
452     }
453
454     /* Callbacks for the regions hero does leave */
455     for (i = 0; i < n_regions; i++) {
456         if (regions[i]->attach_2_u)
457             continue;
458         if (hero_inside(regions[i])
459             && !inside_region(regions[i], x, y)) {
460             clear_hero_inside(regions[i]);
461             if (regions[i]->leave_msg != (const char *) 0)
462                 pline1(regions[i]->leave_msg);
463             if ((f_indx = regions[i]->leave_f) != NO_CALLBACK)
464                 (void) (*callbacks[f_indx])(regions[i], (genericptr_t) 0);
465         }
466     }
467
468     /* Callbacks for the regions hero does enter */
469     for (i = 0; i < n_regions; i++) {
470         if (regions[i]->attach_2_u)
471             continue;
472         if (!hero_inside(regions[i])
473             && inside_region(regions[i], x, y)) {
474             set_hero_inside(regions[i]);
475             if (regions[i]->enter_msg != (const char *) 0)
476                 pline1(regions[i]->enter_msg);
477             if ((f_indx = regions[i]->enter_f) != NO_CALLBACK)
478                 (void) (*callbacks[f_indx])(regions[i], (genericptr_t) 0);
479         }
480     }
481
482     return TRUE;
483 }
484
485 /*
486  * check whether a monster enters/leaves one or more regions.
487  */
488 boolean
489 m_in_out_region(mon, x, y)
490 struct monst *mon;
491 xchar x, y;
492 {
493     int i, f_indx = 0;
494
495     /* First check if mon can do the move */
496     for (i = 0; i < n_regions; i++) {
497         if (regions[i]->attach_2_m == mon->m_id)
498             continue;
499         if (inside_region(regions[i], x, y)
500             ? (!mon_in_region(regions[i], mon)
501                && (f_indx = regions[i]->can_enter_f) != NO_CALLBACK)
502             : (mon_in_region(regions[i], mon)
503                && (f_indx = regions[i]->can_leave_f) != NO_CALLBACK)) {
504             if (!(*callbacks[f_indx])(regions[i], mon))
505                 return FALSE;
506         }
507     }
508
509     /* Callbacks for the regions mon does leave */
510     for (i = 0; i < n_regions; i++) {
511         if (regions[i]->attach_2_m == mon->m_id)
512             continue;
513         if (mon_in_region(regions[i], mon)
514             && !inside_region(regions[i], x, y)) {
515             remove_mon_from_reg(regions[i], mon);
516             if ((f_indx = regions[i]->leave_f) != NO_CALLBACK)
517                 (void) (*callbacks[f_indx])(regions[i], mon);
518         }
519     }
520
521     /* Callbacks for the regions mon does enter */
522     for (i = 0; i < n_regions; i++) {
523         if (regions[i]->attach_2_m == mon->m_id)
524             continue;
525         if (!mon_in_region(regions[i], mon)
526             && inside_region(regions[i], x, y)) {
527             add_mon_to_reg(regions[i], mon);
528             if ((f_indx = regions[i]->enter_f) != NO_CALLBACK)
529                 (void) (*callbacks[f_indx])(regions[i], mon);
530         }
531     }
532
533     return TRUE;
534 }
535
536 /*
537  * Checks player's regions after a teleport for instance.
538  */
539 void
540 update_player_regions()
541 {
542     register int i;
543
544     for (i = 0; i < n_regions; i++)
545         if (!regions[i]->attach_2_u && inside_region(regions[i], u.ux, u.uy))
546             set_hero_inside(regions[i]);
547         else
548             clear_hero_inside(regions[i]);
549 }
550
551 /*
552  * Ditto for a specified monster.
553  */
554 void
555 update_monster_region(mon)
556 struct monst *mon;
557 {
558     register int i;
559
560     for (i = 0; i < n_regions; i++) {
561         if (inside_region(regions[i], mon->mx, mon->my)) {
562             if (!mon_in_region(regions[i], mon))
563                 add_mon_to_reg(regions[i], mon);
564         } else {
565             if (mon_in_region(regions[i], mon))
566                 remove_mon_from_reg(regions[i], mon);
567         }
568     }
569 }
570
571 #if 0
572 /* not yet used */
573
574 /*
575  * Change monster pointer in regions
576  * This happens, for instance, when a monster grows and
577  * need a new structure (internally that is).
578  */
579 void
580 replace_mon_regions(monold, monnew)
581 struct monst *monold, *monnew;
582 {
583     register int i;
584
585     for (i = 0; i < n_regions; i++)
586         if (mon_in_region(regions[i], monold)) {
587             remove_mon_from_reg(regions[i], monold);
588             add_mon_to_reg(regions[i], monnew);
589         }
590 }
591
592 /*
593  * Remove monster from all regions it was in (ie monster just died)
594  */
595 void
596 remove_mon_from_regions(mon)
597 struct monst *mon;
598 {
599     register int i;
600
601     for (i = 0; i < n_regions; i++)
602         if (mon_in_region(regions[i], mon))
603             remove_mon_from_reg(regions[i], mon);
604 }
605
606 #endif /*0*/
607
608 /*
609  * Check if a spot is under a visible region (eg: gas cloud).
610  * Returns NULL if not, otherwise returns region.
611  */
612 NhRegion *
613 visible_region_at(x, y)
614 xchar x, y;
615 {
616     register int i;
617
618     for (i = 0; i < n_regions; i++) {
619         if (!regions[i]->visible || regions[i]->ttl == -2L)
620             continue;
621         if (inside_region(regions[i], x, y))
622             return regions[i];
623     }
624     return (NhRegion *) 0;
625 }
626
627 void
628 show_region(reg, x, y)
629 NhRegion *reg;
630 xchar x, y;
631 {
632     show_glyph(x, y, reg->glyph);
633 }
634
635 /**
636  * save_regions :
637  */
638 void
639 save_regions(fd, mode)
640 int fd;
641 int mode;
642 {
643     int i, j;
644     unsigned n;
645
646     if (!perform_bwrite(mode))
647         goto skip_lots;
648
649     bwrite(fd, (genericptr_t) &moves, sizeof(moves)); /* timestamp */
650     bwrite(fd, (genericptr_t) &n_regions, sizeof(n_regions));
651     for (i = 0; i < n_regions; i++) {
652         bwrite(fd, (genericptr_t) &regions[i]->bounding_box, sizeof(NhRect));
653         bwrite(fd, (genericptr_t) &regions[i]->nrects, sizeof(short));
654         for (j = 0; j < regions[i]->nrects; j++)
655             bwrite(fd, (genericptr_t) &regions[i]->rects[j], sizeof(NhRect));
656         bwrite(fd, (genericptr_t) &regions[i]->attach_2_u, sizeof(boolean));
657         n = 0;
658         bwrite(fd, (genericptr_t) &regions[i]->attach_2_m, sizeof(unsigned));
659         n = regions[i]->enter_msg != (const char *) 0
660                 ? strlen(regions[i]->enter_msg)
661                 : 0;
662         bwrite(fd, (genericptr_t) &n, sizeof n);
663         if (n > 0)
664             bwrite(fd, (genericptr_t) regions[i]->enter_msg, n);
665         n = regions[i]->leave_msg != (const char *) 0
666                 ? strlen(regions[i]->leave_msg)
667                 : 0;
668         bwrite(fd, (genericptr_t) &n, sizeof n);
669         if (n > 0)
670             bwrite(fd, (genericptr_t) regions[i]->leave_msg, n);
671         bwrite(fd, (genericptr_t) &regions[i]->ttl, sizeof(long));
672         bwrite(fd, (genericptr_t) &regions[i]->expire_f, sizeof(short));
673         bwrite(fd, (genericptr_t) &regions[i]->can_enter_f, sizeof(short));
674         bwrite(fd, (genericptr_t) &regions[i]->enter_f, sizeof(short));
675         bwrite(fd, (genericptr_t) &regions[i]->can_leave_f, sizeof(short));
676         bwrite(fd, (genericptr_t) &regions[i]->leave_f, sizeof(short));
677         bwrite(fd, (genericptr_t) &regions[i]->inside_f, sizeof(short));
678         bwrite(fd, (genericptr_t) &regions[i]->player_flags,
679                sizeof(unsigned int));
680         bwrite(fd, (genericptr_t) &regions[i]->n_monst, sizeof(short));
681         for (j = 0; j < regions[i]->n_monst; j++)
682             bwrite(fd, (genericptr_t) &regions[i]->monsters[j],
683                    sizeof(unsigned));
684         bwrite(fd, (genericptr_t) &regions[i]->visible, sizeof(boolean));
685         bwrite(fd, (genericptr_t) &regions[i]->glyph, sizeof(int));
686         bwrite(fd, (genericptr_t) &regions[i]->arg, sizeof(anything));
687     }
688
689 skip_lots:
690     if (release_data(mode))
691         clear_regions();
692 }
693
694 void
695 rest_regions(fd, ghostly)
696 int fd;
697 boolean ghostly; /* If a bones file restore */
698 {
699     int i, j;
700     unsigned n;
701     long tmstamp;
702     char *msg_buf;
703
704     clear_regions(); /* Just for security */
705     mread(fd, (genericptr_t) &tmstamp, sizeof(tmstamp));
706     if (ghostly)
707         tmstamp = 0;
708     else
709         tmstamp = (moves - tmstamp);
710     mread(fd, (genericptr_t) &n_regions, sizeof(n_regions));
711     max_regions = n_regions;
712     if (n_regions > 0)
713         regions = (NhRegion **) alloc(sizeof(NhRegion *) * n_regions);
714     for (i = 0; i < n_regions; i++) {
715         regions[i] = (NhRegion *) alloc(sizeof(NhRegion));
716         mread(fd, (genericptr_t) &regions[i]->bounding_box, sizeof(NhRect));
717         mread(fd, (genericptr_t) &regions[i]->nrects, sizeof(short));
718
719         if (regions[i]->nrects > 0)
720             regions[i]->rects =
721                 (NhRect *) alloc(sizeof(NhRect) * regions[i]->nrects);
722         for (j = 0; j < regions[i]->nrects; j++)
723             mread(fd, (genericptr_t) &regions[i]->rects[j], sizeof(NhRect));
724         mread(fd, (genericptr_t) &regions[i]->attach_2_u, sizeof(boolean));
725         mread(fd, (genericptr_t) &regions[i]->attach_2_m, sizeof(unsigned));
726
727         mread(fd, (genericptr_t) &n, sizeof n);
728         if (n > 0) {
729             msg_buf = (char *) alloc(n + 1);
730             mread(fd, (genericptr_t) msg_buf, n);
731             msg_buf[n] = '\0';
732             regions[i]->enter_msg = (const char *) msg_buf;
733         } else
734             regions[i]->enter_msg = (const char *) 0;
735
736         mread(fd, (genericptr_t) &n, sizeof n);
737         if (n > 0) {
738             msg_buf = (char *) alloc(n + 1);
739             mread(fd, (genericptr_t) msg_buf, n);
740             msg_buf[n] = '\0';
741             regions[i]->leave_msg = (const char *) msg_buf;
742         } else
743             regions[i]->leave_msg = (const char *) 0;
744
745         mread(fd, (genericptr_t) &regions[i]->ttl, sizeof(long));
746         /* check for expired region */
747         if (regions[i]->ttl >= 0L)
748             regions[i]->ttl =
749                 (regions[i]->ttl > tmstamp) ? regions[i]->ttl - tmstamp : 0L;
750         mread(fd, (genericptr_t) &regions[i]->expire_f, sizeof(short));
751         mread(fd, (genericptr_t) &regions[i]->can_enter_f, sizeof(short));
752         mread(fd, (genericptr_t) &regions[i]->enter_f, sizeof(short));
753         mread(fd, (genericptr_t) &regions[i]->can_leave_f, sizeof(short));
754         mread(fd, (genericptr_t) &regions[i]->leave_f, sizeof(short));
755         mread(fd, (genericptr_t) &regions[i]->inside_f, sizeof(short));
756         mread(fd, (genericptr_t) &regions[i]->player_flags,
757               sizeof(unsigned int));
758         if (ghostly) { /* settings pertained to old player */
759             clear_hero_inside(regions[i]);
760             clear_heros_fault(regions[i]);
761         }
762         mread(fd, (genericptr_t) &regions[i]->n_monst, sizeof(short));
763         if (regions[i]->n_monst > 0)
764             regions[i]->monsters =
765                 (unsigned *) alloc(sizeof(unsigned) * regions[i]->n_monst);
766         else
767             regions[i]->monsters = (unsigned int *) 0;
768         regions[i]->max_monst = regions[i]->n_monst;
769         for (j = 0; j < regions[i]->n_monst; j++)
770             mread(fd, (genericptr_t) &regions[i]->monsters[j],
771                   sizeof(unsigned));
772         mread(fd, (genericptr_t) &regions[i]->visible, sizeof(boolean));
773         mread(fd, (genericptr_t) &regions[i]->glyph, sizeof(int));
774         mread(fd, (genericptr_t) &regions[i]->arg, sizeof(anything));
775     }
776     /* remove expired regions, do not trigger the expire_f callback (yet!);
777        also update monster lists if this data is coming from a bones file */
778     for (i = n_regions - 1; i >= 0; i--)
779         if (regions[i]->ttl == 0L)
780             remove_region(regions[i]);
781         else if (ghostly && regions[i]->n_monst > 0)
782             reset_region_mids(regions[i]);
783 }
784
785 /* to support '#stats' wizard-mode command */
786 void
787 region_stats(hdrfmt, hdrbuf, count, size)
788 const char *hdrfmt;
789 char *hdrbuf;
790 long *count, *size;
791 {
792     NhRegion *rg;
793     int i;
794
795     /* other stats formats take one parameter; this takes two */
796     Sprintf(hdrbuf, hdrfmt, (long) sizeof (NhRegion), (long) sizeof (NhRect));
797     *count = (long) n_regions; /* might be 0 even though max_regions isn't */
798     *size = (long) max_regions * (long) sizeof (NhRegion);
799     for (i = 0; i < n_regions; ++i) {
800         rg = regions[i];
801         *size += (long) rg->nrects * (long) sizeof (NhRect);
802         if (rg->enter_msg)
803             *size += (long) (strlen(rg->enter_msg) + 1);
804         if (rg->leave_msg)
805             *size += (long) (strlen(rg->leave_msg) + 1);
806         *size += (long) rg->max_monst * (long) sizeof *rg->monsters;
807     }
808     /* ? */
809 }
810
811 /* update monster IDs for region being loaded from bones; `ghostly' implied */
812 STATIC_OVL void
813 reset_region_mids(reg)
814 NhRegion *reg;
815 {
816     int i = 0, n = reg->n_monst;
817     unsigned *mid_list = reg->monsters;
818
819     while (i < n)
820         if (!lookup_id_mapping(mid_list[i], &mid_list[i])) {
821             /* shrink list to remove missing monster; order doesn't matter */
822             mid_list[i] = mid_list[--n];
823         } else {
824             /* move on to next monster */
825             ++i;
826         }
827     reg->n_monst = n;
828     return;
829 }
830
831 #if 0
832 /* not yet used */
833
834 /*--------------------------------------------------------------*
835  *                                                              *
836  *                      Create Region with just a message       *
837  *                                                              *
838  *--------------------------------------------------------------*/
839
840 NhRegion *
841 create_msg_region(x, y, w, h, msg_enter, msg_leave)
842 xchar x, y;
843 xchar w, h;
844 const char *msg_enter;
845 const char *msg_leave;
846 {
847     NhRect tmprect;
848     NhRegion *reg = create_region((NhRect *) 0, 0);
849
850     if (msg_enter)
851         reg->enter_msg = dupstr(msg_enter);
852     if (msg_leave)
853         reg->leave_msg = dupstr(msg_leave);
854     tmprect.lx = x;
855     tmprect.ly = y;
856     tmprect.hx = x + w;
857     tmprect.hy = y + h;
858     add_rect_to_reg(reg, &tmprect);
859     reg->ttl = -1L;
860     return reg;
861 }
862
863
864 /*--------------------------------------------------------------*
865  *                                                              *
866  *                      Force Field Related Cod                 *
867  *                      (unused yet)                            *
868  *--------------------------------------------------------------*/
869
870 boolean
871 enter_force_field(p1, p2)
872 genericptr_t p1;
873 genericptr_t p2;
874 {
875     struct monst *mtmp;
876
877     if (p2 == (genericptr_t) 0) { /* That means the player */
878         if (!Blind)
879 #if 0 /*JP*/
880             You("bump into %s.  Ouch!",
881                 Hallucination ? "an invisible tree"
882                               : "some kind of invisible wall");
883 #else
884             You("%s\82É\82Ô\82¿\82 \82½\82Á\82½\81D\82¢\82Ä\82Á\81I",
885                 Hallucination ? "\96Ú\82É\8c©\82¦\82È\82¢\96Ø"
886                               : "\82È\82ñ\82ç\82©\82Ì\96Ú\82É\8c©\82¦\82È\82¢\95Ç");
887 #endif
888         else
889 /*JP
890             pline("Ouch!");
891 */
892             pline("\82¢\82Ä\82Á\81I");
893     } else {
894         mtmp = (struct monst *) p2;
895         if (canseemon(mtmp))
896 /*JP
897             pline("%s bumps into %s!", Monnam(mtmp), something);
898 */
899             pline("%s\82Í%s\82É\82Ô\82¿\82 \82½\82Á\82½\81I", Monnam(mtmp), something);
900     }
901     return FALSE;
902 }
903
904 NhRegion *
905 create_force_field(x, y, radius, ttl)
906 xchar x, y;
907 int radius;
908 long ttl;
909 {
910     int i;
911     NhRegion *ff;
912     int nrect;
913     NhRect tmprect;
914
915     ff = create_region((NhRect *) 0, 0);
916     nrect = radius;
917     tmprect.lx = x;
918     tmprect.hx = x;
919     tmprect.ly = y - (radius - 1);
920     tmprect.hy = y + (radius - 1);
921     for (i = 0; i < nrect; i++) {
922         add_rect_to_reg(ff, &tmprect);
923         tmprect.lx--;
924         tmprect.hx++;
925         tmprect.ly++;
926         tmprect.hy--;
927     }
928     ff->ttl = ttl;
929     if (!in_mklev && !context.mon_moving)
930         set_heros_fault(ff); /* assume player has created it */
931  /* ff->can_enter_f = enter_force_field; */
932  /* ff->can_leave_f = enter_force_field; */
933     add_region(ff);
934     return ff;
935 }
936
937 #endif /*0*/
938
939 /*--------------------------------------------------------------*
940  *                                                              *
941  *                      Gas cloud related code                  *
942  *                                                              *
943  *--------------------------------------------------------------*/
944
945 /*
946  * Here is an example of an expire function that may prolong
947  * region life after some mods...
948  */
949 /*ARGSUSED*/
950 boolean
951 expire_gas_cloud(p1, p2)
952 genericptr_t p1;
953 genericptr_t p2 UNUSED;
954 {
955     NhRegion *reg;
956     int damage;
957
958     reg = (NhRegion *) p1;
959     damage = reg->arg.a_int;
960
961     /* If it was a thick cloud, it dissipates a little first */
962     if (damage >= 5) {
963         damage /= 2; /* It dissipates, let's do less damage */
964         reg->arg = zeroany;
965         reg->arg.a_int = damage;
966         reg->ttl = 2L; /* Here's the trick : reset ttl */
967         return FALSE;  /* THEN return FALSE, means "still there" */
968     }
969     return TRUE; /* OK, it's gone, you can free it! */
970 }
971
972 boolean
973 inside_gas_cloud(p1, p2)
974 genericptr_t p1;
975 genericptr_t p2;
976 {
977     NhRegion *reg;
978     struct monst *mtmp;
979     int dam;
980
981     /*
982      * Gas clouds can't be targetted at water locations, but they can
983      * start next to water and spread over it.
984      */
985
986     reg = (NhRegion *) p1;
987     dam = reg->arg.a_int;
988     if (p2 == (genericptr_t) 0) { /* This means *YOU* Bozo! */
989         if (u.uinvulnerable || nonliving(youmonst.data) || Breathless
990             || Underwater)
991             return FALSE;
992         if (!Blind) {
993 /*JP
994             Your("%s sting.", makeplural(body_part(EYE)));
995 */
996             Your("%s\82ª\83`\83N\83`\83N\82µ\82½\81D", body_part(EYE));
997             make_blinded(1L, FALSE);
998         }
999         if (!Poison_resistance) {
1000 #if 0 /*JP:T*/
1001             pline("%s is burning your %s!", Something,
1002                   makeplural(body_part(LUNG)));
1003 #else
1004             pline("\89½\82©\96­\82È\82à\82Ì\82ð\8bz\82¢\82±\82ñ\82¾\81I");
1005 #endif
1006 /*JP
1007             You("cough and spit blood!");
1008 */
1009             You("\8aP\82«\82±\82Ý\81C\8c\8c\82ð\93f\82¢\82½\81I");
1010 /*JP
1011             losehp(Maybe_Half_Phys(rnd(dam) + 5), "gas cloud", KILLED_BY_AN);
1012 */
1013             losehp(Maybe_Half_Phys(rnd(dam) + 5), "\83K\83X\89_\82Å", KILLED_BY_AN);
1014             return FALSE;
1015         } else {
1016 /*JP
1017             You("cough!");
1018 */
1019             You("\8aP\82«\82±\82ñ\82¾\81I");
1020             return FALSE;
1021         }
1022     } else { /* A monster is inside the cloud */
1023         mtmp = (struct monst *) p2;
1024
1025         /* Non living and non breathing monsters are not concerned;
1026            adult green dragon is not affected by gas cloud, baby one is */
1027         if (!(nonliving(mtmp->data) || is_vampshifter(mtmp))
1028             && !breathless(mtmp->data)
1029             /* not is_swimmer(); assume that non-fish are swimming on
1030                the surface and breathing the air above it periodically
1031                unless located at water spot on plane of water */
1032             && !((mtmp->data->mlet == S_EEL || Is_waterlevel(&u.uz))
1033                  && is_pool(mtmp->mx, mtmp->my))
1034             /* exclude monsters with poison gas breath attack:
1035                adult green dragon and Chromatic Dragon (and iron golem,
1036                but nonliving() and breathless() tests also catch that) */
1037             && !(attacktype_fordmg(mtmp->data, AT_BREA, AD_DRST)
1038                  || attacktype_fordmg(mtmp->data, AT_BREA, AD_RBRE))) {
1039             if (cansee(mtmp->mx, mtmp->my))
1040 /*JP
1041                 pline("%s coughs!", Monnam(mtmp));
1042 */
1043                 pline("%s\82Í\8aP\82«\82±\82ñ\82¾\81I", Monnam(mtmp));
1044             if (heros_fault(reg))
1045                 setmangry(mtmp, TRUE);
1046             if (haseyes(mtmp->data) && mtmp->mcansee) {
1047                 mtmp->mblinded = 1;
1048                 mtmp->mcansee = 0;
1049             }
1050             if (resists_poison(mtmp))
1051                 return FALSE;
1052             mtmp->mhp -= rnd(dam) + 5;
1053             if (DEADMONSTER(mtmp)) {
1054                 if (heros_fault(reg))
1055                     killed(mtmp);
1056                 else
1057 /*JP
1058                     monkilled(mtmp, "gas cloud", AD_DRST);
1059 */
1060                     monkilled(mtmp, "\83K\83X\89_", AD_DRST);
1061                 if (DEADMONSTER(mtmp)) { /* not lifesaved */
1062                     return TRUE;
1063                 }
1064             }
1065         }
1066     }
1067     return FALSE; /* Monster is still alive */
1068 }
1069
1070 NhRegion *
1071 create_gas_cloud(x, y, radius, damage)
1072 xchar x, y;
1073 int radius;
1074 int damage;
1075 {
1076     NhRegion *cloud;
1077     int i, nrect;
1078     NhRect tmprect;
1079
1080     cloud = create_region((NhRect *) 0, 0);
1081     nrect = radius;
1082     tmprect.lx = x;
1083     tmprect.hx = x;
1084     tmprect.ly = y - (radius - 1);
1085     tmprect.hy = y + (radius - 1);
1086     for (i = 0; i < nrect; i++) {
1087         add_rect_to_reg(cloud, &tmprect);
1088         tmprect.lx--;
1089         tmprect.hx++;
1090         tmprect.ly++;
1091         tmprect.hy--;
1092     }
1093     cloud->ttl = rn1(3, 4);
1094     if (!in_mklev && !context.mon_moving)
1095         set_heros_fault(cloud); /* assume player has created it */
1096     cloud->inside_f = INSIDE_GAS_CLOUD;
1097     cloud->expire_f = EXPIRE_GAS_CLOUD;
1098     cloud->arg = zeroany;
1099     cloud->arg.a_int = damage;
1100     cloud->visible = TRUE;
1101     cloud->glyph = cmap_to_glyph(damage ? S_poisoncloud : S_cloud);
1102     add_region(cloud);
1103     return cloud;
1104 }
1105
1106 /* for checking troubles during prayer; is hero at risk? */
1107 boolean
1108 region_danger()
1109 {
1110     int i, f_indx, n = 0;
1111
1112     for (i = 0; i < n_regions; i++) {
1113         /* only care about regions that hero is in */
1114         if (!hero_inside(regions[i]))
1115             continue;
1116         f_indx = regions[i]->inside_f;
1117         /* the only type of region we understand is gas_cloud */
1118         if (f_indx == INSIDE_GAS_CLOUD) {
1119             /* completely harmless if you don't need to breathe */
1120             if (nonliving(youmonst.data) || Breathless)
1121                 continue;
1122             /* minor inconvenience if you're poison resistant;
1123                not harmful enough to be a prayer-level trouble */
1124             if (Poison_resistance)
1125                 continue;
1126             ++n;
1127         }
1128     }
1129     return n ? TRUE : FALSE;
1130 }
1131
1132 /* for fixing trouble at end of prayer;
1133    danger detected at start of prayer might have expired by now */
1134 void
1135 region_safety()
1136 {
1137     NhRegion *r = 0;
1138     int i, f_indx, n = 0;
1139
1140     for (i = 0; i < n_regions; i++) {
1141         /* only care about regions that hero is in */
1142         if (!hero_inside(regions[i]))
1143             continue;
1144         f_indx = regions[i]->inside_f;
1145         /* the only type of region we understand is gas_cloud */
1146         if (f_indx == INSIDE_GAS_CLOUD) {
1147             if (!n++ && regions[i]->ttl >= 0)
1148                 r = regions[i];
1149         }
1150     }
1151
1152     if (n > 1 || (n == 1 && !r)) {
1153         /* multiple overlapping cloud regions or non-expiring one */
1154         safe_teleds(FALSE);
1155     } else if (r) {
1156         remove_region(r);
1157 /*JP
1158         pline_The("gas cloud enveloping you dissipates.");
1159 */
1160         pline("\82 \82È\82½\82ð\95ï\82ñ\82Å\82¢\82½\83K\83X\89_\82Í\8fÁ\82¦\82½\81D");
1161     } else {
1162         /* cloud dissipated on its own, so nothing needs to be done */
1163 /*JP
1164         pline_The("gas cloud has dissipated.");
1165 */
1166         pline("\83K\83X\89_\82Í\8fÁ\82¦\82½\81D");
1167     }
1168     /* maybe cure blindness too */
1169     if ((Blinded & TIMEOUT) == 1L)
1170         make_blinded(0L, TRUE);
1171 }
1172
1173 /*region.c*/