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 (void) memset((genericptr_t
)reg
, 0, sizeof(NhRegion
));
95 /* Determines bounding box */
97 reg
->bounding_box
= rects
[0];
99 reg
->bounding_box
.lx
= COLNO
;
100 reg
->bounding_box
.ly
= ROWNO
;
101 reg
->bounding_box
.hx
= 0; /* 1 */
102 reg
->bounding_box
.hy
= 0;
105 reg
->rects
= (nrect
> 0) ? (NhRect
*) alloc(nrect
* sizeof (NhRect
)) : 0;
106 for (i
= 0; i
< nrect
; i
++) {
107 if (rects
[i
].lx
< reg
->bounding_box
.lx
)
108 reg
->bounding_box
.lx
= rects
[i
].lx
;
109 if (rects
[i
].ly
< reg
->bounding_box
.ly
)
110 reg
->bounding_box
.ly
= rects
[i
].ly
;
111 if (rects
[i
].hx
> reg
->bounding_box
.hx
)
112 reg
->bounding_box
.hx
= rects
[i
].hx
;
113 if (rects
[i
].hy
> reg
->bounding_box
.hy
)
114 reg
->bounding_box
.hy
= rects
[i
].hy
;
115 reg
->rects
[i
] = rects
[i
];
117 reg
->ttl
= -1L; /* Defaults */
118 reg
->attach_2_u
= FALSE
;
120 /* reg->attach_2_o = NULL; */
121 reg
->enter_msg
= (const char *) 0;
122 reg
->leave_msg
= (const char *) 0;
123 reg
->expire_f
= NO_CALLBACK
;
124 reg
->enter_f
= NO_CALLBACK
;
125 reg
->can_enter_f
= NO_CALLBACK
;
126 reg
->leave_f
= NO_CALLBACK
;
127 reg
->can_leave_f
= NO_CALLBACK
;
128 reg
->inside_f
= NO_CALLBACK
;
129 clear_hero_inside(reg
);
130 clear_heros_fault(reg
);
133 reg
->monsters
= (unsigned int *) 0;
139 * Add rectangle to region.
142 add_rect_to_reg(reg
, rect
)
148 tmp_rect
= (NhRect
*) alloc((reg
->nrects
+ 1) * sizeof (NhRect
));
149 if (reg
->nrects
> 0) {
150 (void) memcpy((genericptr_t
) tmp_rect
, (genericptr_t
) reg
->rects
,
151 reg
->nrects
* sizeof (NhRect
));
152 free((genericptr_t
) reg
->rects
);
154 tmp_rect
[reg
->nrects
] = *rect
;
156 reg
->rects
= tmp_rect
;
157 /* Update bounding box if needed */
158 if (reg
->bounding_box
.lx
> rect
->lx
)
159 reg
->bounding_box
.lx
= rect
->lx
;
160 if (reg
->bounding_box
.ly
> rect
->ly
)
161 reg
->bounding_box
.ly
= rect
->ly
;
162 if (reg
->bounding_box
.hx
< rect
->hx
)
163 reg
->bounding_box
.hx
= rect
->hx
;
164 if (reg
->bounding_box
.hy
< rect
->hy
)
165 reg
->bounding_box
.hy
= rect
->hy
;
169 * Add a monster to the region
172 add_mon_to_reg(reg
, mon
)
179 if (reg
->max_monst
<= reg
->n_monst
) {
180 tmp_m
= (unsigned *) alloc(sizeof (unsigned)
181 * (reg
->max_monst
+ MONST_INC
));
182 if (reg
->max_monst
> 0) {
183 for (i
= 0; i
< reg
->max_monst
; i
++)
184 tmp_m
[i
] = reg
->monsters
[i
];
185 free((genericptr_t
) reg
->monsters
);
187 reg
->monsters
= tmp_m
;
188 reg
->max_monst
+= MONST_INC
;
190 reg
->monsters
[reg
->n_monst
++] = mon
->m_id
;
194 * Remove a monster from the region list (it left or died...)
197 remove_mon_from_reg(reg
, mon
)
203 for (i
= 0; i
< reg
->n_monst
; i
++)
204 if (reg
->monsters
[i
] == mon
->m_id
) {
206 reg
->monsters
[i
] = reg
->monsters
[reg
->n_monst
];
212 * Check if a monster is inside the region.
213 * It's probably quicker to check with the region internal list
214 * than to check for coordinates.
217 mon_in_region(reg
, mon
)
223 for (i
= 0; i
< reg
->n_monst
; i
++)
224 if (reg
->monsters
[i
] == mon
->m_id
)
233 * Clone (make a standalone copy) the region.
241 ret_reg
= create_region(reg
->rects
, reg
->nrects
);
242 ret_reg
->ttl
= reg
->ttl
;
243 ret_reg
->attach_2_u
= reg
->attach_2_u
;
244 ret_reg
->attach_2_m
= reg
->attach_2_m
;
245 /* ret_reg->attach_2_o = reg->attach_2_o; */
246 ret_reg
->expire_f
= reg
->expire_f
;
247 ret_reg
->enter_f
= reg
->enter_f
;
248 ret_reg
->can_enter_f
= reg
->can_enter_f
;
249 ret_reg
->leave_f
= reg
->leave_f
;
250 ret_reg
->can_leave_f
= reg
->can_leave_f
;
251 ret_reg
->player_flags
= reg
->player_flags
; /* set/clear_hero_inside,&c*/
252 ret_reg
->n_monst
= reg
->n_monst
;
253 if (reg
->n_monst
> 0) {
254 ret_reg
->monsters
= (unsigned int *)
255 alloc((sizeof (unsigned)) * reg
->n_monst
);
256 (void) memcpy((genericptr_t
) ret_reg
->monsters
,
257 (genericptr_t
) reg
->monsters
,
258 sizeof (unsigned) * reg
->n_monst
);
260 ret_reg
->monsters
= (unsigned int *) 0;
267 * Free mem from region.
275 free((genericptr_t
) reg
->rects
);
277 free((genericptr_t
) reg
->monsters
);
279 free((genericptr_t
) reg
->enter_msg
);
281 free((genericptr_t
) reg
->leave_msg
);
282 free((genericptr_t
) reg
);
287 * Add a region to the list.
288 * This actually activates the region.
297 if (max_regions
<= n_regions
) {
300 (NhRegion
**) alloc((max_regions
+ 10) * sizeof (NhRegion
*));
301 if (max_regions
> 0) {
302 (void) memcpy((genericptr_t
) regions
, (genericptr_t
) tmp_reg
,
303 max_regions
* sizeof (NhRegion
*));
304 free((genericptr_t
) tmp_reg
);
308 regions
[n_regions
] = reg
;
310 /* Check for monsters inside the region */
311 for (i
= reg
->bounding_box
.lx
; i
<= reg
->bounding_box
.hx
; i
++)
312 for (j
= reg
->bounding_box
.ly
; j
<= reg
->bounding_box
.hy
; j
++) {
313 /* Some regions can cross the level boundaries */
316 if (MON_AT(i
, j
) && inside_region(reg
, i
, j
))
317 add_mon_to_reg(reg
, level
.monsters
[i
][j
]);
318 if (reg
->visible
&& cansee(i
, j
))
321 /* Check for player now... */
322 if (inside_region(reg
, u
.ux
, u
.uy
))
323 set_hero_inside(reg
);
325 clear_hero_inside(reg
);
329 * Remove a region from the list & free it.
335 register int i
, x
, y
;
337 for (i
= 0; i
< n_regions
; i
++)
338 if (regions
[i
] == reg
)
343 /* Update screen if necessary */
344 reg
->ttl
= -2L; /* for visible_region_at */
346 for (x
= reg
->bounding_box
.lx
; x
<= reg
->bounding_box
.hx
; x
++)
347 for (y
= reg
->bounding_box
.ly
; y
<= reg
->bounding_box
.hy
; y
++)
348 if (isok(x
, y
) && inside_region(reg
, x
, y
) && cansee(x
, y
))
352 regions
[i
] = regions
[n_regions
- 1];
353 regions
[n_regions
- 1] = (NhRegion
*) 0;
358 * Remove all regions and clear all related data (This must be down
359 * when changing level, for instance).
366 for (i
= 0; i
< n_regions
; i
++)
367 free_region(regions
[i
]);
370 free((genericptr_t
) regions
);
372 regions
= (NhRegion
**) 0;
376 * This function is called every turn.
377 * It makes the regions age, if necessary and calls the appropriate
378 * callbacks when needed.
383 register int i
, j
, k
;
387 /* Do it backward because the array will be modified */
388 for (i
= n_regions
- 1; i
>= 0; i
--) {
389 if (regions
[i
]->ttl
== 0L) {
390 if ((f_indx
= regions
[i
]->expire_f
) == NO_CALLBACK
391 || (*callbacks
[f_indx
])(regions
[i
], (genericptr_t
) 0))
392 remove_region(regions
[i
]);
396 /* Process remaining regions */
397 for (i
= 0; i
< n_regions
; i
++) {
398 /* Make the region age */
399 if (regions
[i
]->ttl
> 0L)
401 /* Check if player is inside region */
402 f_indx
= regions
[i
]->inside_f
;
403 if (f_indx
!= NO_CALLBACK
&& hero_inside(regions
[i
]))
404 (void) (*callbacks
[f_indx
])(regions
[i
], (genericptr_t
) 0);
405 /* Check if any monster is inside region */
406 if (f_indx
!= NO_CALLBACK
) {
407 for (j
= 0; j
< regions
[i
]->n_monst
; j
++) {
409 find_mid(regions
[i
]->monsters
[j
], FM_FMON
);
411 if (!mtmp
|| mtmp
->mhp
<= 0
412 || (*callbacks
[f_indx
])(regions
[i
], mtmp
)) {
413 /* The monster died, remove it from list */
414 k
= (regions
[i
]->n_monst
-= 1);
415 regions
[i
]->monsters
[j
] = regions
[i
]->monsters
[k
];
416 regions
[i
]->monsters
[k
] = 0;
417 --j
; /* current slot has been reused; recheck it next */
425 * check whether player enters/leaves one or more regions.
433 /* First check if we can do the move */
434 for (i
= 0; i
< n_regions
; i
++) {
435 if (inside_region(regions
[i
], x
, y
) && !hero_inside(regions
[i
])
436 && !regions
[i
]->attach_2_u
) {
437 if ((f_indx
= regions
[i
]->can_enter_f
) != NO_CALLBACK
)
438 if (!(*callbacks
[f_indx
])(regions
[i
], (genericptr_t
) 0))
440 } else if (hero_inside(regions
[i
]) && !inside_region(regions
[i
], x
, y
)
441 && !regions
[i
]->attach_2_u
) {
442 if ((f_indx
= regions
[i
]->can_leave_f
) != NO_CALLBACK
)
443 if (!(*callbacks
[f_indx
])(regions
[i
], (genericptr_t
) 0))
448 /* Callbacks for the regions we do leave */
449 for (i
= 0; i
< n_regions
; i
++)
450 if (hero_inside(regions
[i
]) && !regions
[i
]->attach_2_u
451 && !inside_region(regions
[i
], x
, y
)) {
452 clear_hero_inside(regions
[i
]);
453 if (regions
[i
]->leave_msg
!= (const char *) 0)
454 pline1(regions
[i
]->leave_msg
);
455 if ((f_indx
= regions
[i
]->leave_f
) != NO_CALLBACK
)
456 (void) (*callbacks
[f_indx
])(regions
[i
], (genericptr_t
) 0);
459 /* Callbacks for the regions we do enter */
460 for (i
= 0; i
< n_regions
; i
++)
461 if (!hero_inside(regions
[i
]) && !regions
[i
]->attach_2_u
462 && inside_region(regions
[i
], x
, y
)) {
463 set_hero_inside(regions
[i
]);
464 if (regions
[i
]->enter_msg
!= (const char *) 0)
465 pline1(regions
[i
]->enter_msg
);
466 if ((f_indx
= regions
[i
]->enter_f
) != NO_CALLBACK
)
467 (void) (*callbacks
[f_indx
])(regions
[i
], (genericptr_t
) 0);
473 * check whether a monster enters/leaves one or more region.
476 m_in_out_region(mon
, x
, y
)
482 /* First check if we can do the move */
483 for (i
= 0; i
< n_regions
; i
++) {
484 if (inside_region(regions
[i
], x
, y
) && !mon_in_region(regions
[i
], mon
)
485 && regions
[i
]->attach_2_m
!= mon
->m_id
) {
486 if ((f_indx
= regions
[i
]->can_enter_f
) != NO_CALLBACK
)
487 if (!(*callbacks
[f_indx
])(regions
[i
], mon
))
489 } else if (mon_in_region(regions
[i
], mon
)
490 && !inside_region(regions
[i
], x
, y
)
491 && regions
[i
]->attach_2_m
!= mon
->m_id
) {
492 if ((f_indx
= regions
[i
]->can_leave_f
) != NO_CALLBACK
)
493 if (!(*callbacks
[f_indx
])(regions
[i
], mon
))
498 /* Callbacks for the regions we do leave */
499 for (i
= 0; i
< n_regions
; i
++)
500 if (mon_in_region(regions
[i
], mon
)
501 && regions
[i
]->attach_2_m
!= mon
->m_id
502 && !inside_region(regions
[i
], x
, y
)) {
503 remove_mon_from_reg(regions
[i
], mon
);
504 if ((f_indx
= regions
[i
]->leave_f
) != NO_CALLBACK
)
505 (void) (*callbacks
[f_indx
])(regions
[i
], mon
);
508 /* Callbacks for the regions we do enter */
509 for (i
= 0; i
< n_regions
; i
++)
510 if (!hero_inside(regions
[i
]) && !regions
[i
]->attach_2_u
511 && inside_region(regions
[i
], x
, y
)) {
512 add_mon_to_reg(regions
[i
], mon
);
513 if ((f_indx
= regions
[i
]->enter_f
) != NO_CALLBACK
)
514 (void) (*callbacks
[f_indx
])(regions
[i
], mon
);
520 * Checks player's regions after a teleport for instance.
523 update_player_regions()
527 for (i
= 0; i
< n_regions
; i
++)
528 if (!regions
[i
]->attach_2_u
&& inside_region(regions
[i
], u
.ux
, u
.uy
))
529 set_hero_inside(regions
[i
]);
531 clear_hero_inside(regions
[i
]);
535 * Ditto for a specified monster.
538 update_monster_region(mon
)
543 for (i
= 0; i
< n_regions
; i
++) {
544 if (inside_region(regions
[i
], mon
->mx
, mon
->my
)) {
545 if (!mon_in_region(regions
[i
], mon
))
546 add_mon_to_reg(regions
[i
], mon
);
548 if (mon_in_region(regions
[i
], mon
))
549 remove_mon_from_reg(regions
[i
], mon
);
558 * Change monster pointer in regions
559 * This happens, for instance, when a monster grows and
560 * need a new structure (internally that is).
563 replace_mon_regions(monold
, monnew
)
564 struct monst
*monold
, *monnew
;
568 for (i
= 0; i
< n_regions
; i
++)
569 if (mon_in_region(regions
[i
], monold
)) {
570 remove_mon_from_reg(regions
[i
], monold
);
571 add_mon_to_reg(regions
[i
], monnew
);
576 * Remove monster from all regions it was in (ie monster just died)
579 remove_mon_from_regions(mon
)
584 for (i
= 0; i
< n_regions
; i
++)
585 if (mon_in_region(regions
[i
], mon
))
586 remove_mon_from_reg(regions
[i
], mon
);
592 * Check if a spot is under a visible region (eg: gas cloud).
593 * Returns NULL if not, otherwise returns region.
596 visible_region_at(x
, y
)
601 for (i
= 0; i
< n_regions
; i
++)
602 if (inside_region(regions
[i
], x
, y
) && regions
[i
]->visible
603 && regions
[i
]->ttl
!= -2L)
605 return (NhRegion
*) 0;
609 show_region(reg
, x
, y
)
613 show_glyph(x
, y
, reg
->glyph
);
620 save_regions(fd
, mode
)
627 if (!perform_bwrite(mode
))
630 bwrite(fd
, (genericptr_t
) &moves
, sizeof(moves
)); /* timestamp */
631 bwrite(fd
, (genericptr_t
) &n_regions
, sizeof(n_regions
));
632 for (i
= 0; i
< n_regions
; i
++) {
633 bwrite(fd
, (genericptr_t
) ®ions
[i
]->bounding_box
, sizeof(NhRect
));
634 bwrite(fd
, (genericptr_t
) ®ions
[i
]->nrects
, sizeof(short));
635 for (j
= 0; j
< regions
[i
]->nrects
; j
++)
636 bwrite(fd
, (genericptr_t
) ®ions
[i
]->rects
[j
], sizeof(NhRect
));
637 bwrite(fd
, (genericptr_t
) ®ions
[i
]->attach_2_u
, sizeof(boolean
));
639 bwrite(fd
, (genericptr_t
) ®ions
[i
]->attach_2_m
, sizeof(unsigned));
640 n
= regions
[i
]->enter_msg
!= (const char *) 0
641 ? strlen(regions
[i
]->enter_msg
)
643 bwrite(fd
, (genericptr_t
) &n
, sizeof n
);
645 bwrite(fd
, (genericptr_t
) regions
[i
]->enter_msg
, n
);
646 n
= regions
[i
]->leave_msg
!= (const char *) 0
647 ? strlen(regions
[i
]->leave_msg
)
649 bwrite(fd
, (genericptr_t
) &n
, sizeof n
);
651 bwrite(fd
, (genericptr_t
) regions
[i
]->leave_msg
, n
);
652 bwrite(fd
, (genericptr_t
) ®ions
[i
]->ttl
, sizeof(long));
653 bwrite(fd
, (genericptr_t
) ®ions
[i
]->expire_f
, sizeof(short));
654 bwrite(fd
, (genericptr_t
) ®ions
[i
]->can_enter_f
, sizeof(short));
655 bwrite(fd
, (genericptr_t
) ®ions
[i
]->enter_f
, sizeof(short));
656 bwrite(fd
, (genericptr_t
) ®ions
[i
]->can_leave_f
, sizeof(short));
657 bwrite(fd
, (genericptr_t
) ®ions
[i
]->leave_f
, sizeof(short));
658 bwrite(fd
, (genericptr_t
) ®ions
[i
]->inside_f
, sizeof(short));
659 bwrite(fd
, (genericptr_t
) ®ions
[i
]->player_flags
,
660 sizeof(unsigned int));
661 bwrite(fd
, (genericptr_t
) ®ions
[i
]->n_monst
, sizeof(short));
662 for (j
= 0; j
< regions
[i
]->n_monst
; j
++)
663 bwrite(fd
, (genericptr_t
) ®ions
[i
]->monsters
[j
],
665 bwrite(fd
, (genericptr_t
) ®ions
[i
]->visible
, sizeof(boolean
));
666 bwrite(fd
, (genericptr_t
) ®ions
[i
]->glyph
, sizeof(int));
667 bwrite(fd
, (genericptr_t
) ®ions
[i
]->arg
, sizeof(anything
));
671 if (release_data(mode
))
676 rest_regions(fd
, ghostly
)
678 boolean ghostly
; /* If a bones file restore */
685 clear_regions(); /* Just for security */
686 mread(fd
, (genericptr_t
) &tmstamp
, sizeof(tmstamp
));
690 tmstamp
= (moves
- tmstamp
);
691 mread(fd
, (genericptr_t
) &n_regions
, sizeof(n_regions
));
692 max_regions
= n_regions
;
694 regions
= (NhRegion
**) alloc(sizeof(NhRegion
*) * n_regions
);
695 for (i
= 0; i
< n_regions
; i
++) {
696 regions
[i
] = (NhRegion
*) alloc(sizeof(NhRegion
));
697 mread(fd
, (genericptr_t
) ®ions
[i
]->bounding_box
, sizeof(NhRect
));
698 mread(fd
, (genericptr_t
) ®ions
[i
]->nrects
, sizeof(short));
700 if (regions
[i
]->nrects
> 0)
702 (NhRect
*) alloc(sizeof(NhRect
) * regions
[i
]->nrects
);
703 for (j
= 0; j
< regions
[i
]->nrects
; j
++)
704 mread(fd
, (genericptr_t
) ®ions
[i
]->rects
[j
], sizeof(NhRect
));
705 mread(fd
, (genericptr_t
) ®ions
[i
]->attach_2_u
, sizeof(boolean
));
706 mread(fd
, (genericptr_t
) ®ions
[i
]->attach_2_m
, sizeof(unsigned));
708 mread(fd
, (genericptr_t
) &n
, sizeof n
);
710 msg_buf
= (char *) alloc(n
+ 1);
711 mread(fd
, (genericptr_t
) msg_buf
, n
);
713 regions
[i
]->enter_msg
= (const char *) msg_buf
;
715 regions
[i
]->enter_msg
= (const char *) 0;
717 mread(fd
, (genericptr_t
) &n
, sizeof n
);
719 msg_buf
= (char *) alloc(n
+ 1);
720 mread(fd
, (genericptr_t
) msg_buf
, n
);
722 regions
[i
]->leave_msg
= (const char *) msg_buf
;
724 regions
[i
]->leave_msg
= (const char *) 0;
726 mread(fd
, (genericptr_t
) ®ions
[i
]->ttl
, sizeof(long));
727 /* check for expired region */
728 if (regions
[i
]->ttl
>= 0L)
730 (regions
[i
]->ttl
> tmstamp
) ? regions
[i
]->ttl
- tmstamp
: 0L;
731 mread(fd
, (genericptr_t
) ®ions
[i
]->expire_f
, sizeof(short));
732 mread(fd
, (genericptr_t
) ®ions
[i
]->can_enter_f
, sizeof(short));
733 mread(fd
, (genericptr_t
) ®ions
[i
]->enter_f
, sizeof(short));
734 mread(fd
, (genericptr_t
) ®ions
[i
]->can_leave_f
, sizeof(short));
735 mread(fd
, (genericptr_t
) ®ions
[i
]->leave_f
, sizeof(short));
736 mread(fd
, (genericptr_t
) ®ions
[i
]->inside_f
, sizeof(short));
737 mread(fd
, (genericptr_t
) ®ions
[i
]->player_flags
,
738 sizeof(unsigned int));
739 if (ghostly
) { /* settings pertained to old player */
740 clear_hero_inside(regions
[i
]);
741 clear_heros_fault(regions
[i
]);
743 mread(fd
, (genericptr_t
) ®ions
[i
]->n_monst
, sizeof(short));
744 if (regions
[i
]->n_monst
> 0)
745 regions
[i
]->monsters
=
746 (unsigned *) alloc(sizeof(unsigned) * regions
[i
]->n_monst
);
748 regions
[i
]->monsters
= (unsigned int *) 0;
749 regions
[i
]->max_monst
= regions
[i
]->n_monst
;
750 for (j
= 0; j
< regions
[i
]->n_monst
; j
++)
751 mread(fd
, (genericptr_t
) ®ions
[i
]->monsters
[j
],
753 mread(fd
, (genericptr_t
) ®ions
[i
]->visible
, sizeof(boolean
));
754 mread(fd
, (genericptr_t
) ®ions
[i
]->glyph
, sizeof(int));
755 mread(fd
, (genericptr_t
) ®ions
[i
]->arg
, sizeof(anything
));
757 /* remove expired regions, do not trigger the expire_f callback (yet!);
758 also update monster lists if this data is coming from a bones file */
759 for (i
= n_regions
- 1; i
>= 0; i
--)
760 if (regions
[i
]->ttl
== 0L)
761 remove_region(regions
[i
]);
762 else if (ghostly
&& regions
[i
]->n_monst
> 0)
763 reset_region_mids(regions
[i
]);
766 /* to support '#stats' wizard-mode command */
768 region_stats(hdrfmt
, hdrbuf
, count
, size
)
776 /* other stats formats take one parameter; this takes two */
777 Sprintf(hdrbuf
, hdrfmt
, (long) sizeof (NhRegion
), (long) sizeof (NhRect
));
778 *count
= (long) n_regions
; /* might be 0 even though max_regions isn't */
779 *size
= (long) max_regions
* (long) sizeof (NhRegion
);
780 for (i
= 0; i
< n_regions
; ++i
) {
782 *size
+= (long) rg
->nrects
* (long) sizeof (NhRect
);
784 *size
+= (long) (strlen(rg
->enter_msg
) + 1);
786 *size
+= (long) (strlen(rg
->leave_msg
) + 1);
787 *size
+= (long) rg
->max_monst
* (long) sizeof *rg
->monsters
;
792 /* update monster IDs for region being loaded from bones; `ghostly' implied */
794 reset_region_mids(reg
)
797 int i
= 0, n
= reg
->n_monst
;
798 unsigned *mid_list
= reg
->monsters
;
801 if (!lookup_id_mapping(mid_list
[i
], &mid_list
[i
])) {
802 /* shrink list to remove missing monster; order doesn't matter */
803 mid_list
[i
] = mid_list
[--n
];
805 /* move on to next monster */
815 /*--------------------------------------------------------------*
817 * Create Region with just a message *
819 *--------------------------------------------------------------*/
822 create_msg_region(x
, y
, w
, h
, msg_enter
, msg_leave
)
825 const char *msg_enter
;
826 const char *msg_leave
;
829 NhRegion
*reg
= create_region((NhRect
*) 0, 0);
832 reg
->enter_msg
= dupstr(msg_enter
);
834 reg
->leave_msg
= dupstr(msg_leave
);
839 add_rect_to_reg(reg
, &tmprect
);
845 /*--------------------------------------------------------------*
847 * Force Field Related Cod *
849 *--------------------------------------------------------------*/
852 enter_force_field(p1
, p2
)
858 if (p2
== (genericptr_t
) 0) { /* That means the player */
860 You("bump into %s. Ouch!",
861 Hallucination
? "an invisible tree"
862 : "some kind of invisible wall");
866 mtmp
= (struct monst
*) p2
;
868 pline("%s bumps into %s!", Monnam(mtmp
), something
);
874 create_force_field(x
, y
, radius
, ttl
)
884 ff
= create_region((NhRect
*) 0, 0);
888 tmprect
.ly
= y
- (radius
- 1);
889 tmprect
.hy
= y
+ (radius
- 1);
890 for (i
= 0; i
< nrect
; i
++) {
891 add_rect_to_reg(ff
, &tmprect
);
898 if (!in_mklev
&& !context
.mon_moving
)
899 set_heros_fault(ff
); /* assume player has created it */
900 /* ff->can_enter_f = enter_force_field; */
901 /* ff->can_leave_f = enter_force_field; */
908 /*--------------------------------------------------------------*
910 * Gas cloud related code *
912 *--------------------------------------------------------------*/
915 * Here is an example of an expire function that may prolong
916 * region life after some mods...
920 expire_gas_cloud(p1
, p2
)
922 genericptr_t p2 UNUSED
;
927 reg
= (NhRegion
*) p1
;
928 damage
= reg
->arg
.a_int
;
930 /* If it was a thick cloud, it dissipates a little first */
932 damage
/= 2; /* It dissipates, let's do less damage */
934 reg
->arg
.a_int
= damage
;
935 reg
->ttl
= 2L; /* Here's the trick : reset ttl */
936 return FALSE
; /* THEN return FALSE, means "still there" */
938 return TRUE
; /* OK, it's gone, you can free it! */
942 inside_gas_cloud(p1
, p2
)
950 reg
= (NhRegion
*) p1
;
951 dam
= reg
->arg
.a_int
;
952 if (p2
== (genericptr_t
) 0) { /* This means *YOU* Bozo! */
953 if (u
.uinvulnerable
|| nonliving(youmonst
.data
) || Breathless
)
956 Your("%s sting.", makeplural(body_part(EYE
)));
957 make_blinded(1L, FALSE
);
959 if (!Poison_resistance
) {
960 pline("%s is burning your %s!", Something
,
961 makeplural(body_part(LUNG
)));
962 You("cough and spit blood!");
963 losehp(Maybe_Half_Phys(rnd(dam
) + 5), "gas cloud", KILLED_BY_AN
);
969 } else { /* A monster is inside the cloud */
970 mtmp
= (struct monst
*) p2
;
972 /* Non living and non breathing monsters are not concerned */
973 if (!(nonliving(mtmp
->data
) || is_vampshifter(mtmp
))
974 && !breathless(mtmp
->data
)) {
975 if (cansee(mtmp
->mx
, mtmp
->my
))
976 pline("%s coughs!", Monnam(mtmp
));
977 if (heros_fault(reg
))
978 setmangry(mtmp
, TRUE
);
979 if (haseyes(mtmp
->data
) && mtmp
->mcansee
) {
983 if (resists_poison(mtmp
))
985 mtmp
->mhp
-= rnd(dam
) + 5;
986 if (mtmp
->mhp
<= 0) {
987 if (heros_fault(reg
))
990 monkilled(mtmp
, "gas cloud", AD_DRST
);
991 if (mtmp
->mhp
<= 0) { /* not lifesaved */
997 return FALSE
; /* Monster is still alive */
1001 create_gas_cloud(x
, y
, radius
, damage
)
1010 cloud
= create_region((NhRect
*) 0, 0);
1014 tmprect
.ly
= y
- (radius
- 1);
1015 tmprect
.hy
= y
+ (radius
- 1);
1016 for (i
= 0; i
< nrect
; i
++) {
1017 add_rect_to_reg(cloud
, &tmprect
);
1023 cloud
->ttl
= rn1(3, 4);
1024 if (!in_mklev
&& !context
.mon_moving
)
1025 set_heros_fault(cloud
); /* assume player has created it */
1026 cloud
->inside_f
= INSIDE_GAS_CLOUD
;
1027 cloud
->expire_f
= EXPIRE_GAS_CLOUD
;
1028 cloud
->arg
= zeroany
;
1029 cloud
->arg
.a_int
= damage
;
1030 cloud
->visible
= TRUE
;
1031 cloud
->glyph
= cmap_to_glyph(damage
? S_poisoncloud
: S_cloud
);
1036 /* for checking troubles during prayer; is hero at risk? */
1040 int i
, f_indx
, n
= 0;
1042 for (i
= 0; i
< n_regions
; i
++) {
1043 /* only care about regions that hero is in */
1044 if (!hero_inside(regions
[i
]))
1046 f_indx
= regions
[i
]->inside_f
;
1047 /* the only type of region we understand is gas_cloud */
1048 if (f_indx
== INSIDE_GAS_CLOUD
) {
1049 /* completely harmless if you don't need to breathe */
1050 if (nonliving(youmonst
.data
) || Breathless
)
1052 /* minor inconvenience if you're poison resistant;
1053 not harmful enough to be a prayer-level trouble */
1054 if (Poison_resistance
)
1059 return n
? TRUE
: FALSE
;
1062 /* for fixing trouble at end of prayer;
1063 danger detected at start of prayer might have expired by now */
1068 int i
, f_indx
, n
= 0;
1070 for (i
= 0; i
< n_regions
; i
++) {
1071 /* only care about regions that hero is in */
1072 if (!hero_inside(regions
[i
]))
1074 f_indx
= regions
[i
]->inside_f
;
1075 /* the only type of region we understand is gas_cloud */
1076 if (f_indx
== INSIDE_GAS_CLOUD
) {
1077 if (!n
++ && regions
[i
]->ttl
>= 0)
1082 if (n
> 1 || (n
== 1 && !r
)) {
1083 /* multiple overlapping cloud regions or non-expiring one */
1087 pline_The("gas cloud enveloping you dissipates.");
1089 /* cloud dissipated on its own, so nothing needs to be done */
1090 pline_The("gas cloud has dissipated.");
1092 /* maybe cure blindness too */
1093 if ((Blinded
& TIMEOUT
) == 1L)
1094 make_blinded(0L, TRUE
);