1 /* NetHack 3.6 region.c $NHDT-Date: 1446892454 2015/11/07 10:34:14 $ $NHDT-Branch: master $:$NHDT-Revision: 1.36 $ */
2 /* Copyright (c) 1996 by Jean-Christophe Collet */
3 /* NetHack may be freely redistributed. See license for details. */
9 * This should really go into the level structure, but
10 * I'll start here for ease. It *WILL* move into the level
11 * structure eventually.
14 static NhRegion
**regions
;
15 static int n_regions
= 0;
16 static int max_regions
= 0;
18 #define NO_CALLBACK (-1)
20 boolean
FDECL(inside_gas_cloud
, (genericptr
, genericptr
));
21 boolean
FDECL(expire_gas_cloud
, (genericptr
, genericptr
));
22 boolean
FDECL(inside_rect
, (NhRect
*, int, int));
23 boolean
FDECL(inside_region
, (NhRegion
*, int, int));
24 NhRegion
*FDECL(create_region
, (NhRect
*, int));
25 void FDECL(add_rect_to_reg
, (NhRegion
*, NhRect
*));
26 void FDECL(add_mon_to_reg
, (NhRegion
*, struct monst
*));
27 void FDECL(remove_mon_from_reg
, (NhRegion
*, struct monst
*));
28 boolean
FDECL(mon_in_region
, (NhRegion
*, struct monst
*));
31 NhRegion
*FDECL(clone_region
, (NhRegion
*));
33 void FDECL(free_region
, (NhRegion
*));
34 void FDECL(add_region
, (NhRegion
*));
35 void FDECL(remove_region
, (NhRegion
*));
38 void FDECL(replace_mon_regions
, (struct monst
*,struct monst
*));
39 void FDECL(remove_mon_from_regions
, (struct monst
*));
40 NhRegion
*FDECL(create_msg_region
, (XCHAR_P
,XCHAR_P
,XCHAR_P
,XCHAR_P
,
41 const char *,const char *));
42 boolean
FDECL(enter_force_field
, (genericptr
,genericptr
));
43 NhRegion
*FDECL(create_force_field
, (XCHAR_P
,XCHAR_P
,int,long));
46 STATIC_DCL
void FDECL(reset_region_mids
, (NhRegion
*));
48 static callback_proc callbacks
[] = {
49 #define INSIDE_GAS_CLOUD 0
51 #define EXPIRE_GAS_CLOUD 1
55 /* Should be inlined. */
61 return (boolean
) (x
>= r
->lx
&& x
<= r
->hx
&& y
>= r
->ly
&& y
<= r
->hy
);
65 * Check if a point is inside a region.
68 inside_region(reg
, x
, y
)
74 if (reg
== (NhRegion
*) 0 || !inside_rect(&(reg
->bounding_box
), x
, y
))
76 for (i
= 0; i
< reg
->nrects
; i
++)
77 if (inside_rect(&(reg
->rects
[i
]), x
, y
))
83 * Create a region. It does not activate it.
86 create_region(rects
, nrect
)
93 reg
= (NhRegion
*) alloc(sizeof(NhRegion
));
94 /* Determines bounding box */
96 reg
->bounding_box
= rects
[0];
98 reg
->bounding_box
.lx
= COLNO
;
99 reg
->bounding_box
.ly
= ROWNO
;
100 reg
->bounding_box
.hx
= 0; /* 1 */
101 reg
->bounding_box
.hy
= 0;
104 reg
->rects
= (nrect
> 0) ? (NhRect
*) alloc(nrect
* sizeof (NhRect
)) : 0;
105 for (i
= 0; i
< nrect
; i
++) {
106 if (rects
[i
].lx
< reg
->bounding_box
.lx
)
107 reg
->bounding_box
.lx
= rects
[i
].lx
;
108 if (rects
[i
].ly
< reg
->bounding_box
.ly
)
109 reg
->bounding_box
.ly
= rects
[i
].ly
;
110 if (rects
[i
].hx
> reg
->bounding_box
.hx
)
111 reg
->bounding_box
.hx
= rects
[i
].hx
;
112 if (rects
[i
].hy
> reg
->bounding_box
.hy
)
113 reg
->bounding_box
.hy
= rects
[i
].hy
;
114 reg
->rects
[i
] = rects
[i
];
116 reg
->ttl
= -1L; /* Defaults */
117 reg
->attach_2_u
= FALSE
;
119 /* reg->attach_2_o = NULL; */
120 reg
->enter_msg
= (const char *) 0;
121 reg
->leave_msg
= (const char *) 0;
122 reg
->expire_f
= NO_CALLBACK
;
123 reg
->enter_f
= NO_CALLBACK
;
124 reg
->can_enter_f
= NO_CALLBACK
;
125 reg
->leave_f
= NO_CALLBACK
;
126 reg
->can_leave_f
= NO_CALLBACK
;
127 reg
->inside_f
= NO_CALLBACK
;
128 clear_hero_inside(reg
);
129 clear_heros_fault(reg
);
132 reg
->monsters
= (unsigned int *) 0;
138 * Add rectangle to region.
141 add_rect_to_reg(reg
, rect
)
147 tmp_rect
= (NhRect
*) alloc((reg
->nrects
+ 1) * sizeof (NhRect
));
148 if (reg
->nrects
> 0) {
149 (void) memcpy((genericptr_t
) tmp_rect
, (genericptr_t
) reg
->rects
,
150 reg
->nrects
* sizeof (NhRect
));
151 free((genericptr_t
) reg
->rects
);
153 tmp_rect
[reg
->nrects
] = *rect
;
155 reg
->rects
= tmp_rect
;
156 /* Update bounding box if needed */
157 if (reg
->bounding_box
.lx
> rect
->lx
)
158 reg
->bounding_box
.lx
= rect
->lx
;
159 if (reg
->bounding_box
.ly
> rect
->ly
)
160 reg
->bounding_box
.ly
= rect
->ly
;
161 if (reg
->bounding_box
.hx
< rect
->hx
)
162 reg
->bounding_box
.hx
= rect
->hx
;
163 if (reg
->bounding_box
.hy
< rect
->hy
)
164 reg
->bounding_box
.hy
= rect
->hy
;
168 * Add a monster to the region
171 add_mon_to_reg(reg
, mon
)
178 if (reg
->max_monst
<= reg
->n_monst
) {
179 tmp_m
= (unsigned *) alloc(sizeof (unsigned)
180 * (reg
->max_monst
+ MONST_INC
));
181 if (reg
->max_monst
> 0) {
182 for (i
= 0; i
< reg
->max_monst
; i
++)
183 tmp_m
[i
] = reg
->monsters
[i
];
184 free((genericptr_t
) reg
->monsters
);
186 reg
->monsters
= tmp_m
;
187 reg
->max_monst
+= MONST_INC
;
189 reg
->monsters
[reg
->n_monst
++] = mon
->m_id
;
193 * Remove a monster from the region list (it left or died...)
196 remove_mon_from_reg(reg
, mon
)
202 for (i
= 0; i
< reg
->n_monst
; i
++)
203 if (reg
->monsters
[i
] == mon
->m_id
) {
205 reg
->monsters
[i
] = reg
->monsters
[reg
->n_monst
];
211 * Check if a monster is inside the region.
212 * It's probably quicker to check with the region internal list
213 * than to check for coordinates.
216 mon_in_region(reg
, mon
)
222 for (i
= 0; i
< reg
->n_monst
; i
++)
223 if (reg
->monsters
[i
] == mon
->m_id
)
232 * Clone (make a standalone copy) the region.
240 ret_reg
= create_region(reg
->rects
, reg
->nrects
);
241 ret_reg
->ttl
= reg
->ttl
;
242 ret_reg
->attach_2_u
= reg
->attach_2_u
;
243 ret_reg
->attach_2_m
= reg
->attach_2_m
;
244 /* ret_reg->attach_2_o = reg->attach_2_o; */
245 ret_reg
->expire_f
= reg
->expire_f
;
246 ret_reg
->enter_f
= reg
->enter_f
;
247 ret_reg
->can_enter_f
= reg
->can_enter_f
;
248 ret_reg
->leave_f
= reg
->leave_f
;
249 ret_reg
->can_leave_f
= reg
->can_leave_f
;
250 ret_reg
->player_flags
= reg
->player_flags
; /* set/clear_hero_inside,&c*/
251 ret_reg
->n_monst
= reg
->n_monst
;
252 if (reg
->n_monst
> 0) {
253 ret_reg
->monsters
= (unsigned int *)
254 alloc((sizeof (unsigned)) * reg
->n_monst
);
255 (void) memcpy((genericptr_t
) ret_reg
->monsters
,
256 (genericptr_t
) reg
->monsters
,
257 sizeof (unsigned) * reg
->n_monst
);
259 ret_reg
->monsters
= (unsigned int *) 0;
266 * Free mem from region.
274 free((genericptr_t
) reg
->rects
);
276 free((genericptr_t
) reg
->monsters
);
278 free((genericptr_t
) reg
->enter_msg
);
280 free((genericptr_t
) reg
->leave_msg
);
281 free((genericptr_t
) reg
);
286 * Add a region to the list.
287 * This actually activates the region.
296 if (max_regions
<= n_regions
) {
299 (NhRegion
**) alloc((max_regions
+ 10) * sizeof (NhRegion
*));
300 if (max_regions
> 0) {
301 (void) memcpy((genericptr_t
) regions
, (genericptr_t
) tmp_reg
,
302 max_regions
* sizeof (NhRegion
*));
303 free((genericptr_t
) tmp_reg
);
307 regions
[n_regions
] = reg
;
309 /* Check for monsters inside the region */
310 for (i
= reg
->bounding_box
.lx
; i
<= reg
->bounding_box
.hx
; i
++)
311 for (j
= reg
->bounding_box
.ly
; j
<= reg
->bounding_box
.hy
; j
++) {
312 /* Some regions can cross the level boundaries */
315 if (MON_AT(i
, j
) && inside_region(reg
, i
, j
))
316 add_mon_to_reg(reg
, level
.monsters
[i
][j
]);
317 if (reg
->visible
&& cansee(i
, j
))
320 /* Check for player now... */
321 if (inside_region(reg
, u
.ux
, u
.uy
))
322 set_hero_inside(reg
);
324 clear_hero_inside(reg
);
328 * Remove a region from the list & free it.
334 register int i
, x
, y
;
336 for (i
= 0; i
< n_regions
; i
++)
337 if (regions
[i
] == reg
)
342 /* Update screen if necessary */
343 reg
->ttl
= -2L; /* for visible_region_at */
345 for (x
= reg
->bounding_box
.lx
; x
<= reg
->bounding_box
.hx
; x
++)
346 for (y
= reg
->bounding_box
.ly
; y
<= reg
->bounding_box
.hy
; y
++)
347 if (isok(x
, y
) && inside_region(reg
, x
, y
) && cansee(x
, y
))
351 regions
[i
] = regions
[n_regions
- 1];
352 regions
[n_regions
- 1] = (NhRegion
*) 0;
357 * Remove all regions and clear all related data (This must be down
358 * when changing level, for instance).
365 for (i
= 0; i
< n_regions
; i
++)
366 free_region(regions
[i
]);
369 free((genericptr_t
) regions
);
371 regions
= (NhRegion
**) 0;
375 * This function is called every turn.
376 * It makes the regions age, if necessary and calls the appropriate
377 * callbacks when needed.
382 register int i
, j
, k
;
386 /* Do it backward because the array will be modified */
387 for (i
= n_regions
- 1; i
>= 0; i
--) {
388 if (regions
[i
]->ttl
== 0L) {
389 if ((f_indx
= regions
[i
]->expire_f
) == NO_CALLBACK
390 || (*callbacks
[f_indx
])(regions
[i
], (genericptr_t
) 0))
391 remove_region(regions
[i
]);
395 /* Process remaining regions */
396 for (i
= 0; i
< n_regions
; i
++) {
397 /* Make the region age */
398 if (regions
[i
]->ttl
> 0L)
400 /* Check if player is inside region */
401 f_indx
= regions
[i
]->inside_f
;
402 if (f_indx
!= NO_CALLBACK
&& hero_inside(regions
[i
]))
403 (void) (*callbacks
[f_indx
])(regions
[i
], (genericptr_t
) 0);
404 /* Check if any monster is inside region */
405 if (f_indx
!= NO_CALLBACK
) {
406 for (j
= 0; j
< regions
[i
]->n_monst
; j
++) {
408 find_mid(regions
[i
]->monsters
[j
], FM_FMON
);
410 if (!mtmp
|| mtmp
->mhp
<= 0
411 || (*callbacks
[f_indx
])(regions
[i
], mtmp
)) {
412 /* The monster died, remove it from list */
413 k
= (regions
[i
]->n_monst
-= 1);
414 regions
[i
]->monsters
[j
] = regions
[i
]->monsters
[k
];
415 regions
[i
]->monsters
[k
] = 0;
416 --j
; /* current slot has been reused; recheck it next */
424 * check whether player enters/leaves one or more regions.
432 /* First check if we can do the move */
433 for (i
= 0; i
< n_regions
; i
++) {
434 if (inside_region(regions
[i
], x
, y
) && !hero_inside(regions
[i
])
435 && !regions
[i
]->attach_2_u
) {
436 if ((f_indx
= regions
[i
]->can_enter_f
) != NO_CALLBACK
)
437 if (!(*callbacks
[f_indx
])(regions
[i
], (genericptr_t
) 0))
439 } else if (hero_inside(regions
[i
]) && !inside_region(regions
[i
], x
, y
)
440 && !regions
[i
]->attach_2_u
) {
441 if ((f_indx
= regions
[i
]->can_leave_f
) != NO_CALLBACK
)
442 if (!(*callbacks
[f_indx
])(regions
[i
], (genericptr_t
) 0))
447 /* Callbacks for the regions we do leave */
448 for (i
= 0; i
< n_regions
; i
++)
449 if (hero_inside(regions
[i
]) && !regions
[i
]->attach_2_u
450 && !inside_region(regions
[i
], x
, y
)) {
451 clear_hero_inside(regions
[i
]);
452 if (regions
[i
]->leave_msg
!= (const char *) 0)
453 pline1(regions
[i
]->leave_msg
);
454 if ((f_indx
= regions
[i
]->leave_f
) != NO_CALLBACK
)
455 (void) (*callbacks
[f_indx
])(regions
[i
], (genericptr_t
) 0);
458 /* Callbacks for the regions we do enter */
459 for (i
= 0; i
< n_regions
; i
++)
460 if (!hero_inside(regions
[i
]) && !regions
[i
]->attach_2_u
461 && inside_region(regions
[i
], x
, y
)) {
462 set_hero_inside(regions
[i
]);
463 if (regions
[i
]->enter_msg
!= (const char *) 0)
464 pline1(regions
[i
]->enter_msg
);
465 if ((f_indx
= regions
[i
]->enter_f
) != NO_CALLBACK
)
466 (void) (*callbacks
[f_indx
])(regions
[i
], (genericptr_t
) 0);
472 * check whether a monster enters/leaves one or more region.
475 m_in_out_region(mon
, x
, y
)
481 /* First check if we can do the move */
482 for (i
= 0; i
< n_regions
; i
++) {
483 if (inside_region(regions
[i
], x
, y
) && !mon_in_region(regions
[i
], mon
)
484 && regions
[i
]->attach_2_m
!= mon
->m_id
) {
485 if ((f_indx
= regions
[i
]->can_enter_f
) != NO_CALLBACK
)
486 if (!(*callbacks
[f_indx
])(regions
[i
], mon
))
488 } else if (mon_in_region(regions
[i
], mon
)
489 && !inside_region(regions
[i
], x
, y
)
490 && regions
[i
]->attach_2_m
!= mon
->m_id
) {
491 if ((f_indx
= regions
[i
]->can_leave_f
) != NO_CALLBACK
)
492 if (!(*callbacks
[f_indx
])(regions
[i
], mon
))
497 /* Callbacks for the regions we do leave */
498 for (i
= 0; i
< n_regions
; i
++)
499 if (mon_in_region(regions
[i
], mon
)
500 && regions
[i
]->attach_2_m
!= mon
->m_id
501 && !inside_region(regions
[i
], x
, y
)) {
502 remove_mon_from_reg(regions
[i
], mon
);
503 if ((f_indx
= regions
[i
]->leave_f
) != NO_CALLBACK
)
504 (void) (*callbacks
[f_indx
])(regions
[i
], mon
);
507 /* Callbacks for the regions we do enter */
508 for (i
= 0; i
< n_regions
; i
++)
509 if (!hero_inside(regions
[i
]) && !regions
[i
]->attach_2_u
510 && inside_region(regions
[i
], x
, y
)) {
511 add_mon_to_reg(regions
[i
], mon
);
512 if ((f_indx
= regions
[i
]->enter_f
) != NO_CALLBACK
)
513 (void) (*callbacks
[f_indx
])(regions
[i
], mon
);
519 * Checks player's regions after a teleport for instance.
522 update_player_regions()
526 for (i
= 0; i
< n_regions
; i
++)
527 if (!regions
[i
]->attach_2_u
&& inside_region(regions
[i
], u
.ux
, u
.uy
))
528 set_hero_inside(regions
[i
]);
530 clear_hero_inside(regions
[i
]);
534 * Ditto for a specified monster.
537 update_monster_region(mon
)
542 for (i
= 0; i
< n_regions
; i
++) {
543 if (inside_region(regions
[i
], mon
->mx
, mon
->my
)) {
544 if (!mon_in_region(regions
[i
], mon
))
545 add_mon_to_reg(regions
[i
], mon
);
547 if (mon_in_region(regions
[i
], mon
))
548 remove_mon_from_reg(regions
[i
], mon
);
557 * Change monster pointer in regions
558 * This happens, for instance, when a monster grows and
559 * need a new structure (internally that is).
562 replace_mon_regions(monold
, monnew
)
563 struct monst
*monold
, *monnew
;
567 for (i
= 0; i
< n_regions
; i
++)
568 if (mon_in_region(regions
[i
], monold
)) {
569 remove_mon_from_reg(regions
[i
], monold
);
570 add_mon_to_reg(regions
[i
], monnew
);
575 * Remove monster from all regions it was in (ie monster just died)
578 remove_mon_from_regions(mon
)
583 for (i
= 0; i
< n_regions
; i
++)
584 if (mon_in_region(regions
[i
], mon
))
585 remove_mon_from_reg(regions
[i
], mon
);
591 * Check if a spot is under a visible region (eg: gas cloud).
592 * Returns NULL if not, otherwise returns region.
595 visible_region_at(x
, y
)
600 for (i
= 0; i
< n_regions
; i
++)
601 if (inside_region(regions
[i
], x
, y
) && regions
[i
]->visible
602 && regions
[i
]->ttl
!= -2L)
604 return (NhRegion
*) 0;
608 show_region(reg
, x
, y
)
612 show_glyph(x
, y
, reg
->glyph
);
619 save_regions(fd
, mode
)
626 if (!perform_bwrite(mode
))
629 bwrite(fd
, (genericptr_t
) &moves
, sizeof(moves
)); /* timestamp */
630 bwrite(fd
, (genericptr_t
) &n_regions
, sizeof(n_regions
));
631 for (i
= 0; i
< n_regions
; i
++) {
632 bwrite(fd
, (genericptr_t
) ®ions
[i
]->bounding_box
, sizeof(NhRect
));
633 bwrite(fd
, (genericptr_t
) ®ions
[i
]->nrects
, sizeof(short));
634 for (j
= 0; j
< regions
[i
]->nrects
; j
++)
635 bwrite(fd
, (genericptr_t
) ®ions
[i
]->rects
[j
], sizeof(NhRect
));
636 bwrite(fd
, (genericptr_t
) ®ions
[i
]->attach_2_u
, sizeof(boolean
));
638 bwrite(fd
, (genericptr_t
) ®ions
[i
]->attach_2_m
, sizeof(unsigned));
639 n
= regions
[i
]->enter_msg
!= (const char *) 0
640 ? strlen(regions
[i
]->enter_msg
)
642 bwrite(fd
, (genericptr_t
) &n
, sizeof n
);
644 bwrite(fd
, (genericptr_t
) regions
[i
]->enter_msg
, n
);
645 n
= regions
[i
]->leave_msg
!= (const char *) 0
646 ? strlen(regions
[i
]->leave_msg
)
648 bwrite(fd
, (genericptr_t
) &n
, sizeof n
);
650 bwrite(fd
, (genericptr_t
) regions
[i
]->leave_msg
, n
);
651 bwrite(fd
, (genericptr_t
) ®ions
[i
]->ttl
, sizeof(long));
652 bwrite(fd
, (genericptr_t
) ®ions
[i
]->expire_f
, sizeof(short));
653 bwrite(fd
, (genericptr_t
) ®ions
[i
]->can_enter_f
, sizeof(short));
654 bwrite(fd
, (genericptr_t
) ®ions
[i
]->enter_f
, sizeof(short));
655 bwrite(fd
, (genericptr_t
) ®ions
[i
]->can_leave_f
, sizeof(short));
656 bwrite(fd
, (genericptr_t
) ®ions
[i
]->leave_f
, sizeof(short));
657 bwrite(fd
, (genericptr_t
) ®ions
[i
]->inside_f
, sizeof(short));
658 bwrite(fd
, (genericptr_t
) ®ions
[i
]->player_flags
,
659 sizeof(unsigned int));
660 bwrite(fd
, (genericptr_t
) ®ions
[i
]->n_monst
, sizeof(short));
661 for (j
= 0; j
< regions
[i
]->n_monst
; j
++)
662 bwrite(fd
, (genericptr_t
) ®ions
[i
]->monsters
[j
],
664 bwrite(fd
, (genericptr_t
) ®ions
[i
]->visible
, sizeof(boolean
));
665 bwrite(fd
, (genericptr_t
) ®ions
[i
]->glyph
, sizeof(int));
666 bwrite(fd
, (genericptr_t
) ®ions
[i
]->arg
, sizeof(anything
));
670 if (release_data(mode
))
675 rest_regions(fd
, ghostly
)
677 boolean ghostly
; /* If a bones file restore */
684 clear_regions(); /* Just for security */
685 mread(fd
, (genericptr_t
) &tmstamp
, sizeof(tmstamp
));
689 tmstamp
= (moves
- tmstamp
);
690 mread(fd
, (genericptr_t
) &n_regions
, sizeof(n_regions
));
691 max_regions
= n_regions
;
693 regions
= (NhRegion
**) alloc(sizeof(NhRegion
*) * n_regions
);
694 for (i
= 0; i
< n_regions
; i
++) {
695 regions
[i
] = (NhRegion
*) alloc(sizeof(NhRegion
));
696 mread(fd
, (genericptr_t
) ®ions
[i
]->bounding_box
, sizeof(NhRect
));
697 mread(fd
, (genericptr_t
) ®ions
[i
]->nrects
, sizeof(short));
699 if (regions
[i
]->nrects
> 0)
701 (NhRect
*) alloc(sizeof(NhRect
) * regions
[i
]->nrects
);
702 for (j
= 0; j
< regions
[i
]->nrects
; j
++)
703 mread(fd
, (genericptr_t
) ®ions
[i
]->rects
[j
], sizeof(NhRect
));
704 mread(fd
, (genericptr_t
) ®ions
[i
]->attach_2_u
, sizeof(boolean
));
705 mread(fd
, (genericptr_t
) ®ions
[i
]->attach_2_m
, sizeof(unsigned));
707 mread(fd
, (genericptr_t
) &n
, sizeof n
);
709 msg_buf
= (char *) alloc(n
+ 1);
710 mread(fd
, (genericptr_t
) msg_buf
, n
);
712 regions
[i
]->enter_msg
= (const char *) msg_buf
;
714 regions
[i
]->enter_msg
= (const char *) 0;
716 mread(fd
, (genericptr_t
) &n
, sizeof n
);
718 msg_buf
= (char *) alloc(n
+ 1);
719 mread(fd
, (genericptr_t
) msg_buf
, n
);
721 regions
[i
]->leave_msg
= (const char *) msg_buf
;
723 regions
[i
]->leave_msg
= (const char *) 0;
725 mread(fd
, (genericptr_t
) ®ions
[i
]->ttl
, sizeof(long));
726 /* check for expired region */
727 if (regions
[i
]->ttl
>= 0L)
729 (regions
[i
]->ttl
> tmstamp
) ? regions
[i
]->ttl
- tmstamp
: 0L;
730 mread(fd
, (genericptr_t
) ®ions
[i
]->expire_f
, sizeof(short));
731 mread(fd
, (genericptr_t
) ®ions
[i
]->can_enter_f
, sizeof(short));
732 mread(fd
, (genericptr_t
) ®ions
[i
]->enter_f
, sizeof(short));
733 mread(fd
, (genericptr_t
) ®ions
[i
]->can_leave_f
, sizeof(short));
734 mread(fd
, (genericptr_t
) ®ions
[i
]->leave_f
, sizeof(short));
735 mread(fd
, (genericptr_t
) ®ions
[i
]->inside_f
, sizeof(short));
736 mread(fd
, (genericptr_t
) ®ions
[i
]->player_flags
,
737 sizeof(unsigned int));
738 if (ghostly
) { /* settings pertained to old player */
739 clear_hero_inside(regions
[i
]);
740 clear_heros_fault(regions
[i
]);
742 mread(fd
, (genericptr_t
) ®ions
[i
]->n_monst
, sizeof(short));
743 if (regions
[i
]->n_monst
> 0)
744 regions
[i
]->monsters
=
745 (unsigned *) alloc(sizeof(unsigned) * regions
[i
]->n_monst
);
747 regions
[i
]->monsters
= (unsigned int *) 0;
748 regions
[i
]->max_monst
= regions
[i
]->n_monst
;
749 for (j
= 0; j
< regions
[i
]->n_monst
; j
++)
750 mread(fd
, (genericptr_t
) ®ions
[i
]->monsters
[j
],
752 mread(fd
, (genericptr_t
) ®ions
[i
]->visible
, sizeof(boolean
));
753 mread(fd
, (genericptr_t
) ®ions
[i
]->glyph
, sizeof(int));
754 mread(fd
, (genericptr_t
) ®ions
[i
]->arg
, sizeof(anything
));
756 /* remove expired regions, do not trigger the expire_f callback (yet!);
757 also update monster lists if this data is coming from a bones file */
758 for (i
= n_regions
- 1; i
>= 0; i
--)
759 if (regions
[i
]->ttl
== 0L)
760 remove_region(regions
[i
]);
761 else if (ghostly
&& regions
[i
]->n_monst
> 0)
762 reset_region_mids(regions
[i
]);
765 /* to support '#stats' wizard-mode command */
767 region_stats(hdrfmt
, hdrbuf
, count
, size
)
775 /* other stats formats take one parameter; this takes two */
776 Sprintf(hdrbuf
, hdrfmt
, (long) sizeof (NhRegion
), (long) sizeof (NhRect
));
777 *count
= (long) n_regions
; /* might be 0 even though max_regions isn't */
778 *size
= (long) max_regions
* (long) sizeof (NhRegion
);
779 for (i
= 0; i
< n_regions
; ++i
) {
781 *size
+= (long) rg
->nrects
* (long) sizeof (NhRect
);
783 *size
+= (long) (strlen(rg
->enter_msg
) + 1);
785 *size
+= (long) (strlen(rg
->leave_msg
) + 1);
786 *size
+= (long) rg
->max_monst
* (long) sizeof *rg
->monsters
;
791 /* update monster IDs for region being loaded from bones; `ghostly' implied */
793 reset_region_mids(reg
)
796 int i
= 0, n
= reg
->n_monst
;
797 unsigned *mid_list
= reg
->monsters
;
800 if (!lookup_id_mapping(mid_list
[i
], &mid_list
[i
])) {
801 /* shrink list to remove missing monster; order doesn't matter */
802 mid_list
[i
] = mid_list
[--n
];
804 /* move on to next monster */
814 /*--------------------------------------------------------------*
816 * Create Region with just a message *
818 *--------------------------------------------------------------*/
821 create_msg_region(x
, y
, w
, h
, msg_enter
, msg_leave
)
824 const char *msg_enter
;
825 const char *msg_leave
;
828 NhRegion
*reg
= create_region((NhRect
*) 0, 0);
831 reg
->enter_msg
= dupstr(msg_enter
);
833 reg
->leave_msg
= dupstr(msg_leave
);
838 add_rect_to_reg(reg
, &tmprect
);
844 /*--------------------------------------------------------------*
846 * Force Field Related Cod *
848 *--------------------------------------------------------------*/
851 enter_force_field(p1
, p2
)
857 if (p2
== (genericptr_t
) 0) { /* That means the player */
859 You("bump into %s. Ouch!",
860 Hallucination
? "an invisible tree"
861 : "some kind of invisible wall");
865 mtmp
= (struct monst
*) p2
;
867 pline("%s bumps into %s!", Monnam(mtmp
), something
);
873 create_force_field(x
, y
, radius
, ttl
)
883 ff
= create_region((NhRect
*) 0, 0);
887 tmprect
.ly
= y
- (radius
- 1);
888 tmprect
.hy
= y
+ (radius
- 1);
889 for (i
= 0; i
< nrect
; i
++) {
890 add_rect_to_reg(ff
, &tmprect
);
897 if (!in_mklev
&& !context
.mon_moving
)
898 set_heros_fault(ff
); /* assume player has created it */
899 /* ff->can_enter_f = enter_force_field; */
900 /* ff->can_leave_f = enter_force_field; */
907 /*--------------------------------------------------------------*
909 * Gas cloud related code *
911 *--------------------------------------------------------------*/
914 * Here is an example of an expire function that may prolong
915 * region life after some mods...
919 expire_gas_cloud(p1
, p2
)
921 genericptr_t p2 UNUSED
;
926 reg
= (NhRegion
*) p1
;
927 damage
= reg
->arg
.a_int
;
929 /* If it was a thick cloud, it dissipates a little first */
931 damage
/= 2; /* It dissipates, let's do less damage */
933 reg
->arg
.a_int
= damage
;
934 reg
->ttl
= 2L; /* Here's the trick : reset ttl */
935 return FALSE
; /* THEN return FALSE, means "still there" */
937 return TRUE
; /* OK, it's gone, you can free it! */
941 inside_gas_cloud(p1
, p2
)
949 reg
= (NhRegion
*) p1
;
950 dam
= reg
->arg
.a_int
;
951 if (p2
== (genericptr_t
) 0) { /* This means *YOU* Bozo! */
952 if (u
.uinvulnerable
|| nonliving(youmonst
.data
) || Breathless
)
955 Your("%s sting.", makeplural(body_part(EYE
)));
956 make_blinded(1L, FALSE
);
958 if (!Poison_resistance
) {
959 pline("%s is burning your %s!", Something
,
960 makeplural(body_part(LUNG
)));
961 You("cough and spit blood!");
962 losehp(Maybe_Half_Phys(rnd(dam
) + 5), "gas cloud", KILLED_BY_AN
);
968 } else { /* A monster is inside the cloud */
969 mtmp
= (struct monst
*) p2
;
971 /* Non living and non breathing monsters are not concerned */
972 if (!(nonliving(mtmp
->data
) || is_vampshifter(mtmp
))
973 && !breathless(mtmp
->data
)) {
974 if (cansee(mtmp
->mx
, mtmp
->my
))
975 pline("%s coughs!", Monnam(mtmp
));
976 if (heros_fault(reg
))
978 if (haseyes(mtmp
->data
) && mtmp
->mcansee
) {
982 if (resists_poison(mtmp
))
984 mtmp
->mhp
-= rnd(dam
) + 5;
985 if (mtmp
->mhp
<= 0) {
986 if (heros_fault(reg
))
989 monkilled(mtmp
, "gas cloud", AD_DRST
);
990 if (mtmp
->mhp
<= 0) { /* not lifesaved */
996 return FALSE
; /* Monster is still alive */
1000 create_gas_cloud(x
, y
, radius
, damage
)
1009 cloud
= create_region((NhRect
*) 0, 0);
1013 tmprect
.ly
= y
- (radius
- 1);
1014 tmprect
.hy
= y
+ (radius
- 1);
1015 for (i
= 0; i
< nrect
; i
++) {
1016 add_rect_to_reg(cloud
, &tmprect
);
1022 cloud
->ttl
= rn1(3, 4);
1023 if (!in_mklev
&& !context
.mon_moving
)
1024 set_heros_fault(cloud
); /* assume player has created it */
1025 cloud
->inside_f
= INSIDE_GAS_CLOUD
;
1026 cloud
->expire_f
= EXPIRE_GAS_CLOUD
;
1027 cloud
->arg
= zeroany
;
1028 cloud
->arg
.a_int
= damage
;
1029 cloud
->visible
= TRUE
;
1030 cloud
->glyph
= cmap_to_glyph(damage
? S_poisoncloud
: S_cloud
);
1035 /* for checking troubles during prayer; is hero at risk? */
1039 int i
, f_indx
, n
= 0;
1041 for (i
= 0; i
< n_regions
; i
++) {
1042 /* only care about regions that hero is in */
1043 if (!hero_inside(regions
[i
]))
1045 f_indx
= regions
[i
]->inside_f
;
1046 /* the only type of region we understand is gas_cloud */
1047 if (f_indx
== INSIDE_GAS_CLOUD
) {
1048 /* completely harmless if you don't need to breathe */
1049 if (nonliving(youmonst
.data
) || Breathless
)
1051 /* minor inconvenience if you're poison resistant;
1052 not harmful enough to be a prayer-level trouble */
1053 if (Poison_resistance
)
1058 return n
? TRUE
: FALSE
;
1061 /* for fixing trouble at end of prayer;
1062 danger detected at start of prayer might have expired by now */
1067 int i
, f_indx
, n
= 0;
1069 for (i
= 0; i
< n_regions
; i
++) {
1070 /* only care about regions that hero is in */
1071 if (!hero_inside(regions
[i
]))
1073 f_indx
= regions
[i
]->inside_f
;
1074 /* the only type of region we understand is gas_cloud */
1075 if (f_indx
== INSIDE_GAS_CLOUD
) {
1076 if (!n
++ && regions
[i
]->ttl
>= 0)
1081 if (n
> 1 || (n
== 1 && !r
)) {
1082 /* multiple overlapping cloud regions or non-expiring one */
1086 pline_The("gas cloud enveloping you dissipates.");
1088 /* cloud dissipated on its own, so nothing needs to be done */
1089 pline_The("gas cloud has dissipated.");
1091 /* maybe cure blindness too */
1092 if ((Blinded
& TIMEOUT
) == 1L)
1093 make_blinded(0L, TRUE
);