OSDN Git Service

remove task for packaging
[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-2019            */
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             You("bump into %s.  Ouch!",
880                 Hallucination ? "an invisible tree"
881                               : "some kind of invisible wall");
882         else
883             pline("Ouch!");
884     } else {
885         mtmp = (struct monst *) p2;
886         if (canseemon(mtmp))
887             pline("%s bumps into %s!", Monnam(mtmp), something);
888     }
889     return FALSE;
890 }
891
892 NhRegion *
893 create_force_field(x, y, radius, ttl)
894 xchar x, y;
895 int radius;
896 long ttl;
897 {
898     int i;
899     NhRegion *ff;
900     int nrect;
901     NhRect tmprect;
902
903     ff = create_region((NhRect *) 0, 0);
904     nrect = radius;
905     tmprect.lx = x;
906     tmprect.hx = x;
907     tmprect.ly = y - (radius - 1);
908     tmprect.hy = y + (radius - 1);
909     for (i = 0; i < nrect; i++) {
910         add_rect_to_reg(ff, &tmprect);
911         tmprect.lx--;
912         tmprect.hx++;
913         tmprect.ly++;
914         tmprect.hy--;
915     }
916     ff->ttl = ttl;
917     if (!in_mklev && !context.mon_moving)
918         set_heros_fault(ff); /* assume player has created it */
919  /* ff->can_enter_f = enter_force_field; */
920  /* ff->can_leave_f = enter_force_field; */
921     add_region(ff);
922     return ff;
923 }
924
925 #endif /*0*/
926
927 /*--------------------------------------------------------------*
928  *                                                              *
929  *                      Gas cloud related code                  *
930  *                                                              *
931  *--------------------------------------------------------------*/
932
933 /*
934  * Here is an example of an expire function that may prolong
935  * region life after some mods...
936  */
937 /*ARGSUSED*/
938 boolean
939 expire_gas_cloud(p1, p2)
940 genericptr_t p1;
941 genericptr_t p2 UNUSED;
942 {
943     NhRegion *reg;
944     int damage;
945
946     reg = (NhRegion *) p1;
947     damage = reg->arg.a_int;
948
949     /* If it was a thick cloud, it dissipates a little first */
950     if (damage >= 5) {
951         damage /= 2; /* It dissipates, let's do less damage */
952         reg->arg = zeroany;
953         reg->arg.a_int = damage;
954         reg->ttl = 2L; /* Here's the trick : reset ttl */
955         return FALSE;  /* THEN return FALSE, means "still there" */
956     }
957     return TRUE; /* OK, it's gone, you can free it! */
958 }
959
960 boolean
961 inside_gas_cloud(p1, p2)
962 genericptr_t p1;
963 genericptr_t p2;
964 {
965     NhRegion *reg;
966     struct monst *mtmp;
967     int dam;
968
969     /*
970      * Gas clouds can't be targetted at water locations, but they can
971      * start next to water and spread over it.
972      */
973
974     reg = (NhRegion *) p1;
975     dam = reg->arg.a_int;
976     if (p2 == (genericptr_t) 0) { /* This means *YOU* Bozo! */
977         if (u.uinvulnerable || nonliving(youmonst.data) || Breathless
978             || Underwater)
979             return FALSE;
980         if (!Blind) {
981 /*JP
982             Your("%s sting.", makeplural(body_part(EYE)));
983 */
984             Your("%s\82ª\83`\83N\83`\83N\82µ\82½\81D", body_part(EYE));
985             make_blinded(1L, FALSE);
986         }
987         if (!Poison_resistance) {
988 #if 0 /*JP:T*/
989             pline("%s is burning your %s!", Something,
990                   makeplural(body_part(LUNG)));
991 #else
992             pline("\89½\82©\96­\82È\82à\82Ì\82ð\8bz\82¢\82±\82ñ\82¾\81I");
993 #endif
994 /*JP
995             You("cough and spit blood!");
996 */
997             You("\8aP\82«\82±\82Ý\81C\8c\8c\82ð\93f\82¢\82½\81I");
998 /*JP
999             losehp(Maybe_Half_Phys(rnd(dam) + 5), "gas cloud", KILLED_BY_AN);
1000 */
1001             losehp(Maybe_Half_Phys(rnd(dam) + 5), "\83K\83X\89_\82Å", KILLED_BY_AN);
1002             return FALSE;
1003         } else {
1004 /*JP
1005             You("cough!");
1006 */
1007             You("\8aP\82«\82±\82ñ\82¾\81I");
1008             return FALSE;
1009         }
1010     } else { /* A monster is inside the cloud */
1011         mtmp = (struct monst *) p2;
1012
1013         /* Non living and non breathing monsters are not concerned;
1014            adult green dragon is not affected by gas cloud, baby one is */
1015         if (!(nonliving(mtmp->data) || is_vampshifter(mtmp))
1016             && !breathless(mtmp->data)
1017             /* not is_swimmer(); assume that non-fish are swimming on
1018                the surface and breathing the air above it periodically
1019                unless located at water spot on plane of water */
1020             && !((mtmp->data->mlet == S_EEL || Is_waterlevel(&u.uz))
1021                  && is_pool(mtmp->mx, mtmp->my))
1022             /* exclude monsters with poison gas breath attack:
1023                adult green dragon and Chromatic Dragon (and iron golem,
1024                but nonliving() and breathless() tests also catch that) */
1025             && !(attacktype_fordmg(mtmp->data, AT_BREA, AD_DRST)
1026                  || attacktype_fordmg(mtmp->data, AT_BREA, AD_RBRE))) {
1027             if (cansee(mtmp->mx, mtmp->my))
1028 /*JP
1029                 pline("%s coughs!", Monnam(mtmp));
1030 */
1031                 pline("%s\82Í\8aP\82«\82±\82ñ\82¾\81I", Monnam(mtmp));
1032             if (heros_fault(reg))
1033                 setmangry(mtmp, TRUE);
1034             if (haseyes(mtmp->data) && mtmp->mcansee) {
1035                 mtmp->mblinded = 1;
1036                 mtmp->mcansee = 0;
1037             }
1038             if (resists_poison(mtmp))
1039                 return FALSE;
1040             mtmp->mhp -= rnd(dam) + 5;
1041             if (DEADMONSTER(mtmp)) {
1042                 if (heros_fault(reg))
1043                     killed(mtmp);
1044                 else
1045 /*JP
1046                     monkilled(mtmp, "gas cloud", AD_DRST);
1047 */
1048                     monkilled(mtmp, "\83K\83X\89_", AD_DRST);
1049                 if (DEADMONSTER(mtmp)) { /* not lifesaved */
1050                     return TRUE;
1051                 }
1052             }
1053         }
1054     }
1055     return FALSE; /* Monster is still alive */
1056 }
1057
1058 NhRegion *
1059 create_gas_cloud(x, y, radius, damage)
1060 xchar x, y;
1061 int radius;
1062 int damage;
1063 {
1064     NhRegion *cloud;
1065     int i, nrect;
1066     NhRect tmprect;
1067
1068     cloud = create_region((NhRect *) 0, 0);
1069     nrect = radius;
1070     tmprect.lx = x;
1071     tmprect.hx = x;
1072     tmprect.ly = y - (radius - 1);
1073     tmprect.hy = y + (radius - 1);
1074     for (i = 0; i < nrect; i++) {
1075         add_rect_to_reg(cloud, &tmprect);
1076         tmprect.lx--;
1077         tmprect.hx++;
1078         tmprect.ly++;
1079         tmprect.hy--;
1080     }
1081     cloud->ttl = rn1(3, 4);
1082     if (!in_mklev && !context.mon_moving)
1083         set_heros_fault(cloud); /* assume player has created it */
1084     cloud->inside_f = INSIDE_GAS_CLOUD;
1085     cloud->expire_f = EXPIRE_GAS_CLOUD;
1086     cloud->arg = zeroany;
1087     cloud->arg.a_int = damage;
1088     cloud->visible = TRUE;
1089     cloud->glyph = cmap_to_glyph(damage ? S_poisoncloud : S_cloud);
1090     add_region(cloud);
1091     return cloud;
1092 }
1093
1094 /* for checking troubles during prayer; is hero at risk? */
1095 boolean
1096 region_danger()
1097 {
1098     int i, f_indx, n = 0;
1099
1100     for (i = 0; i < n_regions; i++) {
1101         /* only care about regions that hero is in */
1102         if (!hero_inside(regions[i]))
1103             continue;
1104         f_indx = regions[i]->inside_f;
1105         /* the only type of region we understand is gas_cloud */
1106         if (f_indx == INSIDE_GAS_CLOUD) {
1107             /* completely harmless if you don't need to breathe */
1108             if (nonliving(youmonst.data) || Breathless)
1109                 continue;
1110             /* minor inconvenience if you're poison resistant;
1111                not harmful enough to be a prayer-level trouble */
1112             if (Poison_resistance)
1113                 continue;
1114             ++n;
1115         }
1116     }
1117     return n ? TRUE : FALSE;
1118 }
1119
1120 /* for fixing trouble at end of prayer;
1121    danger detected at start of prayer might have expired by now */
1122 void
1123 region_safety()
1124 {
1125     NhRegion *r = 0;
1126     int i, f_indx, n = 0;
1127
1128     for (i = 0; i < n_regions; i++) {
1129         /* only care about regions that hero is in */
1130         if (!hero_inside(regions[i]))
1131             continue;
1132         f_indx = regions[i]->inside_f;
1133         /* the only type of region we understand is gas_cloud */
1134         if (f_indx == INSIDE_GAS_CLOUD) {
1135             if (!n++ && regions[i]->ttl >= 0)
1136                 r = regions[i];
1137         }
1138     }
1139
1140     if (n > 1 || (n == 1 && !r)) {
1141         /* multiple overlapping cloud regions or non-expiring one */
1142         safe_teleds(FALSE);
1143     } else if (r) {
1144         remove_region(r);
1145 /*JP
1146         pline_The("gas cloud enveloping you dissipates.");
1147 */
1148         pline("\82 \82È\82½\82ð\95ï\82ñ\82Å\82¢\82½\83K\83X\89_\82Í\8fÁ\82¦\82½\81D");
1149     } else {
1150         /* cloud dissipated on its own, so nothing needs to be done */
1151 /*JP
1152         pline_The("gas cloud has dissipated.");
1153 */
1154         pline("\83K\83X\89_\82Í\8fÁ\82¦\82½\81D");
1155     }
1156     /* maybe cure blindness too */
1157     if ((Blinded & TIMEOUT) == 1L)
1158         make_blinded(0L, TRUE);
1159 }
1160
1161 /*region.c*/