1 /* SCCS Id: @(#)region.c 3.4 2002/10/15 */
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
inside_gas_cloud(void *,void *);
21 boolean
expire_gas_cloud(void *,void *);
22 boolean
revive_cthulhu(void *, void *);
23 boolean
inside_rect(NhRect
*,int,int);
24 boolean
inside_region(NhRegion
*,int,int);
25 NhRegion
*create_region(NhRect
*,int);
26 void add_rect_to_reg(NhRegion
*,NhRect
*);
27 void add_mon_to_reg(NhRegion
*,struct monst
*);
28 void remove_mon_from_reg(NhRegion
*,struct monst
*);
29 boolean
mon_in_region(NhRegion
*,struct monst
*);
32 NhRegion
*clone_region(NhRegion
*);
34 void free_region(NhRegion
*);
35 void add_region(NhRegion
*);
36 void remove_region(NhRegion
*);
39 void replace_mon_regions(struct monst
*,struct monst
*);
40 void remove_mon_from_regions(struct monst
*);
41 NhRegion
*create_msg_region(XCHAR_P
,XCHAR_P
,XCHAR_P
,XCHAR_P
, const char *,const char *);
42 boolean
enter_force_field(void *,void *);
43 NhRegion
*create_force_field(XCHAR_P
,XCHAR_P
,int,int);
46 static void reset_region_mids(NhRegion
*);
48 static callback_proc callbacks
[] = {
49 #define INSIDE_GAS_CLOUD 0
51 #define EXPIRE_GAS_CLOUD 1
53 #define REVIVE_CTHULHU 2 /* Cthulhu comes back... */
57 /* Should be inlined. */
63 return (x
>= r
->lx
&& x
<= r
->hx
&& y
>= r
->ly
&& y
<= r
->hy
);
67 * Check if a point is inside a region.
70 inside_region(reg
, x
, y
)
76 if (reg
== NULL
|| !inside_rect(&(reg
->bounding_box
), x
, y
))
78 for (i
= 0; i
< reg
->nrects
; i
++)
79 if (inside_rect(&(reg
->rects
[i
]), x
, y
))
85 * Create a region. It does not activate it.
88 create_region(rects
, nrect
)
95 reg
= (NhRegion
*) alloc(sizeof (NhRegion
));
96 /* Determines bounding box */
98 reg
->bounding_box
= rects
[0];
100 reg
->bounding_box
.lx
= 99;
101 reg
->bounding_box
.ly
= 99;
102 reg
->bounding_box
.hx
= 0;
103 reg
->bounding_box
.hy
= 0;
106 reg
->rects
= nrect
> 0 ? (NhRect
*)alloc((sizeof (NhRect
)) * nrect
) : NULL
;
107 for (i
= 0; i
< nrect
; i
++) {
108 if (rects
[i
].lx
< reg
->bounding_box
.lx
)
109 reg
->bounding_box
.lx
= rects
[i
].lx
;
110 if (rects
[i
].ly
< reg
->bounding_box
.ly
)
111 reg
->bounding_box
.ly
= rects
[i
].ly
;
112 if (rects
[i
].hx
> reg
->bounding_box
.hx
)
113 reg
->bounding_box
.hx
= rects
[i
].hx
;
114 if (rects
[i
].hy
> reg
->bounding_box
.hy
)
115 reg
->bounding_box
.hy
= rects
[i
].hy
;
116 reg
->rects
[i
] = rects
[i
];
118 reg
->ttl
= -1; /* Defaults */
119 reg
->attach_2_u
= FALSE
;
121 /* reg->attach_2_o = NULL; */
122 reg
->enter_msg
= NULL
;
123 reg
->leave_msg
= NULL
;
124 reg
->expire_f
= NO_CALLBACK
;
125 reg
->enter_f
= NO_CALLBACK
;
126 reg
->can_enter_f
= NO_CALLBACK
;
127 reg
->leave_f
= NO_CALLBACK
;
128 reg
->can_leave_f
= NO_CALLBACK
;
129 reg
->inside_f
= NO_CALLBACK
;
130 clear_hero_inside(reg
);
131 clear_heros_fault(reg
);
134 reg
->monsters
= NULL
;
140 * Add rectangle to region.
143 add_rect_to_reg(reg
, rect
)
149 tmp_rect
= (NhRect
*) alloc(sizeof (NhRect
) * (reg
->nrects
+ 1));
150 if (reg
->nrects
> 0) {
151 (void) memcpy((void *) tmp_rect
, (void *) reg
->rects
,
152 (sizeof (NhRect
) * reg
->nrects
));
153 free((void *) reg
->rects
);
155 tmp_rect
[reg
->nrects
] = *rect
;
157 reg
->rects
= tmp_rect
;
158 /* Update bounding box if needed */
159 if (reg
->bounding_box
.lx
> rect
->lx
)
160 reg
->bounding_box
.lx
= rect
->lx
;
161 if (reg
->bounding_box
.ly
> rect
->ly
)
162 reg
->bounding_box
.ly
= rect
->ly
;
163 if (reg
->bounding_box
.hx
< rect
->hx
)
164 reg
->bounding_box
.hx
= rect
->hx
;
165 if (reg
->bounding_box
.hy
< rect
->hy
)
166 reg
->bounding_box
.hy
= rect
->hy
;
170 * Add a monster to the region
173 add_mon_to_reg(reg
, mon
)
180 if (reg
->max_monst
<= reg
->n_monst
) {
182 alloc(sizeof (unsigned) * (reg
->max_monst
+ MONST_INC
));
183 if (reg
->max_monst
> 0) {
184 for (i
= 0; i
< reg
->max_monst
; i
++)
185 tmp_m
[i
] = reg
->monsters
[i
];
186 free((void *) reg
->monsters
);
188 reg
->monsters
= tmp_m
;
189 reg
->max_monst
+= MONST_INC
;
191 reg
->monsters
[reg
->n_monst
++] = mon
->m_id
;
195 * Remove a monster from the region list (it left or died...)
198 remove_mon_from_reg(reg
, mon
)
204 for (i
= 0; i
< reg
->n_monst
; i
++)
205 if (reg
->monsters
[i
] == mon
->m_id
) {
207 reg
->monsters
[i
] = reg
->monsters
[reg
->n_monst
];
213 * Check if a monster is inside the region.
214 * It's probably quicker to check with the region internal list
215 * than to check for coordinates.
218 mon_in_region(reg
, mon
)
224 for (i
= 0; i
< reg
->n_monst
; i
++)
225 if (reg
->monsters
[i
] == mon
->m_id
)
234 * Clone (make a standalone copy) the region.
242 ret_reg
= create_region(reg
->rects
, reg
->nrects
);
243 ret_reg
->ttl
= reg
->ttl
;
244 ret_reg
->attach_2_u
= reg
->attach_2_u
;
245 ret_reg
->attach_2_m
= reg
->attach_2_m
;
246 /* ret_reg->attach_2_o = reg->attach_2_o; */
247 ret_reg
->expire_f
= reg
->expire_f
;
248 ret_reg
->enter_f
= reg
->enter_f
;
249 ret_reg
->can_enter_f
= reg
->can_enter_f
;
250 ret_reg
->leave_f
= reg
->leave_f
;
251 ret_reg
->can_leave_f
= reg
->can_leave_f
;
252 ret_reg
->player_flags
= reg
->player_flags
; /* set/clear_hero_inside,&c*/
253 ret_reg
->n_monst
= reg
->n_monst
;
254 if (reg
->n_monst
> 0) {
255 ret_reg
->monsters
= (unsigned *)
256 alloc((sizeof (unsigned)) * reg
->n_monst
);
257 (void) memcpy((void *) ret_reg
->monsters
, (void *) reg
->monsters
,
258 sizeof (unsigned) * reg
->n_monst
);
260 ret_reg
->monsters
= NULL
;
267 * Free mem from region.
275 free((void *) reg
->rects
);
277 free((void *) reg
->monsters
);
283 * Add a region to the list.
284 * This actually activates the region.
293 if (max_regions
<= n_regions
) {
295 regions
= (NhRegion
**)alloc(sizeof (NhRegion
*) * (max_regions
+ 10));
296 if (max_regions
> 0) {
297 (void) memcpy((void *) regions
, (void *) tmp_reg
,
298 max_regions
* sizeof (NhRegion
*));
299 free((void *) tmp_reg
);
303 regions
[n_regions
] = reg
;
305 /* Check for monsters inside the region */
306 for (i
= reg
->bounding_box
.lx
; i
<= reg
->bounding_box
.hx
; i
++)
307 for (j
= reg
->bounding_box
.ly
; j
<= reg
->bounding_box
.hy
; j
++) {
308 /* Some regions can cross the level boundaries */
311 if (MON_AT(i
, j
) && inside_region(reg
, i
, j
))
312 add_mon_to_reg(reg
, level
.monsters
[i
][j
]);
313 if (reg
->visible
&& cansee(i
, j
))
316 /* Check for player now... */
317 if (inside_region(reg
, u
.ux
, u
.uy
))
318 set_hero_inside(reg
);
320 clear_hero_inside(reg
);
324 * Remove a region from the list & free it.
330 register int i
, x
, y
;
332 for (i
= 0; i
< n_regions
; i
++)
333 if (regions
[i
] == reg
)
338 /* Update screen if necessary */
340 for (x
= reg
->bounding_box
.lx
; x
<= reg
->bounding_box
.hx
; x
++)
341 for (y
= reg
->bounding_box
.ly
; y
<= reg
->bounding_box
.hy
; y
++)
342 if (isok(x
,y
) && inside_region(reg
, x
, y
) && cansee(x
, y
))
346 regions
[i
] = regions
[n_regions
- 1];
347 regions
[n_regions
- 1] = (NhRegion
*) 0;
352 * Remove all regions and clear all related data (This must be down
353 * when changing level, for instance).
360 for (i
= 0; i
< n_regions
; i
++)
361 free_region(regions
[i
]);
364 free((void *) regions
);
370 * This function is called every turn.
371 * It makes the regions age, if necessary and calls the appropriate
372 * callbacks when needed.
377 register int i
, j
, k
;
381 /* Do it backward because the array will be modified */
382 for (i
= n_regions
- 1; i
>= 0; i
--) {
383 if (regions
[i
]->ttl
== 0) {
384 if ((f_indx
= regions
[i
]->expire_f
) == NO_CALLBACK
||
385 (*callbacks
[f_indx
])(regions
[i
], (void *) 0))
386 remove_region(regions
[i
]);
390 /* Process remaining regions */
391 for (i
= 0; i
< n_regions
; i
++) {
392 /* Make the region age */
393 if (regions
[i
]->ttl
> 0)
395 /* Check if player is inside region */
396 f_indx
= regions
[i
]->inside_f
;
397 if (f_indx
!= NO_CALLBACK
&& hero_inside(regions
[i
]))
398 (void) (*callbacks
[f_indx
])(regions
[i
], (void *) 0);
399 /* Check if any monster is inside region */
400 if (f_indx
!= NO_CALLBACK
) {
401 for (j
= 0; j
< regions
[i
]->n_monst
; j
++) {
402 struct monst
*mtmp
= find_mid(regions
[i
]->monsters
[j
], FM_FMON
);
404 if (!mtmp
|| mtmp
->mhp
<= 0 ||
405 (*callbacks
[f_indx
])(regions
[i
], mtmp
)) {
406 /* The monster died, remove it from list */
407 k
= (regions
[i
]->n_monst
-= 1);
408 regions
[i
]->monsters
[j
] = regions
[i
]->monsters
[k
];
409 regions
[i
]->monsters
[k
] = 0;
410 --j
; /* current slot has been reused; recheck it next */
418 * check whether player enters/leaves one or more regions.
427 /* First check if we can do the move */
428 for (i
= 0; i
< n_regions
; i
++) {
429 if (inside_region(regions
[i
], x
, y
)
430 && !hero_inside(regions
[i
]) && !regions
[i
]->attach_2_u
) {
431 if ((f_indx
= regions
[i
]->can_enter_f
) != NO_CALLBACK
)
432 if (!(*callbacks
[f_indx
])(regions
[i
], (void *) 0))
435 if (hero_inside(regions
[i
])
436 && !inside_region(regions
[i
], x
, y
)
437 && !regions
[i
]->attach_2_u
) {
438 if ((f_indx
= regions
[i
]->can_leave_f
) != NO_CALLBACK
)
439 if (!(*callbacks
[f_indx
])(regions
[i
], (void *) 0))
444 /* Callbacks for the regions we do leave */
445 for (i
= 0; i
< n_regions
; i
++)
446 if (hero_inside(regions
[i
]) &&
447 !regions
[i
]->attach_2_u
&& !inside_region(regions
[i
], x
, y
)) {
448 clear_hero_inside(regions
[i
]);
449 if (regions
[i
]->leave_msg
!= NULL
)
450 pline("%s", regions
[i
]->leave_msg
);
451 if ((f_indx
= regions
[i
]->leave_f
) != NO_CALLBACK
)
452 (void) (*callbacks
[f_indx
])(regions
[i
], (void *) 0);
455 /* Callbacks for the regions we do enter */
456 for (i
= 0; i
< n_regions
; i
++)
457 if (!hero_inside(regions
[i
]) &&
458 !regions
[i
]->attach_2_u
&& inside_region(regions
[i
], x
, y
)) {
459 set_hero_inside(regions
[i
]);
460 if (regions
[i
]->enter_msg
!= NULL
)
461 pline("%s", regions
[i
]->enter_msg
);
462 if ((f_indx
= regions
[i
]->enter_f
) != NO_CALLBACK
)
463 (void) (*callbacks
[f_indx
])(regions
[i
], (void *) 0);
469 * check wether a monster enters/leaves one or more region.
472 m_in_out_region(mon
, x
, y
)
478 /* First check if we can do the move */
479 for (i
= 0; i
< n_regions
; i
++) {
480 if (inside_region(regions
[i
], x
, y
) &&
481 !mon_in_region(regions
[i
], mon
) &&
482 regions
[i
]->attach_2_m
!= mon
->m_id
) {
483 if ((f_indx
= regions
[i
]->can_enter_f
) != NO_CALLBACK
)
484 if (!(*callbacks
[f_indx
])(regions
[i
], mon
))
486 } else if (mon_in_region(regions
[i
], mon
) &&
487 !inside_region(regions
[i
], x
, y
) &&
488 regions
[i
]->attach_2_m
!= mon
->m_id
) {
489 if ((f_indx
= regions
[i
]->can_leave_f
) != NO_CALLBACK
)
490 if (!(*callbacks
[f_indx
])(regions
[i
], mon
))
495 /* Callbacks for the regions we do leave */
496 for (i
= 0; i
< n_regions
; i
++)
497 if (mon_in_region(regions
[i
], mon
) &&
498 regions
[i
]->attach_2_m
!= mon
->m_id
&&
499 !inside_region(regions
[i
], x
, y
)) {
500 remove_mon_from_reg(regions
[i
], mon
);
501 if ((f_indx
= regions
[i
]->leave_f
) != NO_CALLBACK
)
502 (void) (*callbacks
[f_indx
])(regions
[i
], mon
);
505 /* Callbacks for the regions we do enter */
506 for (i
= 0; i
< n_regions
; i
++)
507 if (!hero_inside(regions
[i
]) &&
508 !regions
[i
]->attach_2_u
&& inside_region(regions
[i
], x
, y
)) {
509 add_mon_to_reg(regions
[i
], mon
);
510 if ((f_indx
= regions
[i
]->enter_f
) != NO_CALLBACK
)
511 (void) (*callbacks
[f_indx
])(regions
[i
], mon
);
517 * Checks player's regions after a teleport for instance.
520 update_player_regions()
524 for (i
= 0; i
< n_regions
; i
++)
525 if (!regions
[i
]->attach_2_u
&& inside_region(regions
[i
], u
.ux
, u
.uy
))
526 set_hero_inside(regions
[i
]);
528 clear_hero_inside(regions
[i
]);
532 * Ditto for a specified monster.
535 update_monster_region(mon
)
540 for (i
= 0; i
< n_regions
; i
++) {
541 if (inside_region(regions
[i
], mon
->mx
, mon
->my
)) {
542 if (!mon_in_region(regions
[i
], mon
))
543 add_mon_to_reg(regions
[i
], mon
);
545 if (mon_in_region(regions
[i
], mon
))
546 remove_mon_from_reg(regions
[i
], mon
);
555 * Change monster pointer in regions
556 * This happens, for instance, when a monster grows and
557 * need a new structure (internally that is).
560 replace_mon_regions(monold
, monnew
)
561 struct monst
*monold
, *monnew
;
565 for (i
= 0; i
< n_regions
; i
++)
566 if (mon_in_region(regions
[i
], monold
)) {
567 remove_mon_from_reg(regions
[i
], monold
);
568 add_mon_to_reg(regions
[i
], monnew
);
573 * Remove monster from all regions it was in (ie monster just died)
576 remove_mon_from_regions(mon
)
581 for (i
= 0; i
< n_regions
; i
++)
582 if (mon_in_region(regions
[i
], mon
))
583 remove_mon_from_reg(regions
[i
], mon
);
589 * Check if a spot is under a visible region (eg: gas cloud).
590 * Returns NULL if not, otherwise returns region.
593 visible_region_at(x
, y
)
598 for (i
= 0; i
< n_regions
; i
++)
599 if (inside_region(regions
[i
], x
, y
) && regions
[i
]->visible
&&
600 regions
[i
]->ttl
!= 0)
602 return (NhRegion
*) 0;
606 show_region(reg
, x
, y
)
610 show_glyph(x
, y
, reg
->glyph
);
617 save_regions(fd
, mode
)
624 if (!perform_bwrite(mode
)) goto skip_lots
;
626 bwrite(fd
, (void *) &moves
, sizeof (moves
)); /* timestamp */
627 bwrite(fd
, (void *) &n_regions
, sizeof (n_regions
));
628 for (i
= 0; i
< n_regions
; i
++) {
629 bwrite(fd
, (void *) ®ions
[i
]->bounding_box
, sizeof (NhRect
));
630 bwrite(fd
, (void *) ®ions
[i
]->nrects
, sizeof (short));
631 for (j
= 0; j
< regions
[i
]->nrects
; j
++)
632 bwrite(fd
, (void *) ®ions
[i
]->rects
[j
], sizeof (NhRect
));
633 bwrite(fd
, (void *) ®ions
[i
]->attach_2_u
, sizeof (boolean
));
635 bwrite(fd
, (void *) ®ions
[i
]->attach_2_m
, sizeof (unsigned));
636 n
= regions
[i
]->enter_msg
!= NULL
? strlen(regions
[i
]->enter_msg
) : 0;
637 bwrite(fd
, (void *) &n
, sizeof n
);
639 bwrite(fd
, (void *) regions
[i
]->enter_msg
, n
);
640 n
= regions
[i
]->leave_msg
!= NULL
? strlen(regions
[i
]->leave_msg
) : 0;
641 bwrite(fd
, (void *) &n
, sizeof n
);
643 bwrite(fd
, (void *) regions
[i
]->leave_msg
, n
);
644 bwrite(fd
, (void *) ®ions
[i
]->ttl
, sizeof (short));
645 bwrite(fd
, (void *) ®ions
[i
]->expire_f
, sizeof (short));
646 bwrite(fd
, (void *) ®ions
[i
]->can_enter_f
, sizeof (short));
647 bwrite(fd
, (void *) ®ions
[i
]->enter_f
, sizeof (short));
648 bwrite(fd
, (void *) ®ions
[i
]->can_leave_f
, sizeof (short));
649 bwrite(fd
, (void *) ®ions
[i
]->leave_f
, sizeof (short));
650 bwrite(fd
, (void *) ®ions
[i
]->inside_f
, sizeof (short));
651 bwrite(fd
, (void *) ®ions
[i
]->player_flags
, sizeof (boolean
));
652 bwrite(fd
, (void *) ®ions
[i
]->n_monst
, sizeof (short));
653 for (j
= 0; j
< regions
[i
]->n_monst
; j
++)
654 bwrite(fd
, (void *) ®ions
[i
]->monsters
[j
],
656 bwrite(fd
, (void *) ®ions
[i
]->visible
, sizeof (boolean
));
657 bwrite(fd
, (void *) ®ions
[i
]->glyph
, sizeof (int));
658 bwrite(fd
, (void *) ®ions
[i
]->arg
, sizeof (void *));
662 if (release_data(mode
))
667 rest_regions(fd
, ghostly
)
669 boolean ghostly
; /* If a bones file restore */
676 clear_regions(); /* Just for security */
677 mread(fd
, (void *) &tmstamp
, sizeof (tmstamp
));
678 if (ghostly
) tmstamp
= 0;
679 else tmstamp
= (moves
- tmstamp
);
680 mread(fd
, (void *) &n_regions
, sizeof (n_regions
));
681 max_regions
= n_regions
;
683 regions
= (NhRegion
**) alloc(sizeof (NhRegion
*) * n_regions
);
684 for (i
= 0; i
< n_regions
; i
++) {
685 regions
[i
] = (NhRegion
*) alloc(sizeof (NhRegion
));
686 mread(fd
, (void *) ®ions
[i
]->bounding_box
, sizeof (NhRect
));
687 mread(fd
, (void *) ®ions
[i
]->nrects
, sizeof (short));
689 if (regions
[i
]->nrects
> 0)
690 regions
[i
]->rects
= (NhRect
*)
691 alloc(sizeof (NhRect
) * regions
[i
]->nrects
);
692 for (j
= 0; j
< regions
[i
]->nrects
; j
++)
693 mread(fd
, (void *) ®ions
[i
]->rects
[j
], sizeof (NhRect
));
694 mread(fd
, (void *) ®ions
[i
]->attach_2_u
, sizeof (boolean
));
695 mread(fd
, (void *) ®ions
[i
]->attach_2_m
, sizeof (unsigned));
697 mread(fd
, (void *) &n
, sizeof n
);
699 msg_buf
= (char *) alloc(n
+ 1);
700 mread(fd
, (void *) msg_buf
, n
);
702 regions
[i
]->enter_msg
= (const char *) msg_buf
;
704 regions
[i
]->enter_msg
= NULL
;
706 mread(fd
, (void *) &n
, sizeof n
);
708 msg_buf
= (char *) alloc(n
+ 1);
709 mread(fd
, (void *) msg_buf
, n
);
711 regions
[i
]->leave_msg
= (const char *) msg_buf
;
713 regions
[i
]->leave_msg
= NULL
;
715 mread(fd
, (void *) ®ions
[i
]->ttl
, sizeof (short));
716 /* check for expired region */
717 if (regions
[i
]->ttl
>= 0)
719 (regions
[i
]->ttl
> tmstamp
) ? regions
[i
]->ttl
- tmstamp
: 0;
720 mread(fd
, (void *) ®ions
[i
]->expire_f
, sizeof (short));
721 mread(fd
, (void *) ®ions
[i
]->can_enter_f
, sizeof (short));
722 mread(fd
, (void *) ®ions
[i
]->enter_f
, sizeof (short));
723 mread(fd
, (void *) ®ions
[i
]->can_leave_f
, sizeof (short));
724 mread(fd
, (void *) ®ions
[i
]->leave_f
, sizeof (short));
725 mread(fd
, (void *) ®ions
[i
]->inside_f
, sizeof (short));
726 mread(fd
, (void *) ®ions
[i
]->player_flags
, sizeof (boolean
));
727 if (ghostly
) { /* settings pertained to old player */
728 clear_hero_inside(regions
[i
]);
729 clear_heros_fault(regions
[i
]);
731 mread(fd
, (void *) ®ions
[i
]->n_monst
, sizeof (short));
732 if (regions
[i
]->n_monst
> 0)
733 regions
[i
]->monsters
=
734 (unsigned *) alloc(sizeof (unsigned) * regions
[i
]->n_monst
);
736 regions
[i
]->monsters
= NULL
;
737 regions
[i
]->max_monst
= regions
[i
]->n_monst
;
738 for (j
= 0; j
< regions
[i
]->n_monst
; j
++)
739 mread(fd
, (void *) ®ions
[i
]->monsters
[j
],
741 mread(fd
, (void *) ®ions
[i
]->visible
, sizeof (boolean
));
742 mread(fd
, (void *) ®ions
[i
]->glyph
, sizeof (int));
743 mread(fd
, (void *) ®ions
[i
]->arg
, sizeof (void *));
745 /* remove expired regions, do not trigger the expire_f callback (yet!);
746 also update monster lists if this data is coming from a bones file */
747 for (i
= n_regions
- 1; i
>= 0; i
--)
748 if (regions
[i
]->ttl
== 0)
749 remove_region(regions
[i
]);
750 else if (ghostly
&& regions
[i
]->n_monst
> 0)
751 reset_region_mids(regions
[i
]);
754 /* update monster IDs for region being loaded from bones; `ghostly' implied */
756 reset_region_mids(reg
)
759 int i
= 0, n
= reg
->n_monst
;
760 unsigned *mid_list
= reg
->monsters
;
763 if (!lookup_id_mapping(mid_list
[i
], &mid_list
[i
])) {
764 /* shrink list to remove missing monster; order doesn't matter */
765 mid_list
[i
] = mid_list
[--n
];
767 /* move on to next monster */
777 /*--------------------------------------------------------------*
779 * Create Region with just a message *
781 *--------------------------------------------------------------*/
784 create_msg_region(x
, y
, w
, h
, msg_enter
, msg_leave
)
787 const char *msg_enter
;
788 const char *msg_leave
;
791 NhRegion
*reg
= create_region((NhRect
*) 0, 0);
793 reg
->enter_msg
= msg_enter
;
794 reg
->leave_msg
= msg_leave
;
799 add_rect_to_reg(reg
, &tmprect
);
805 /*--------------------------------------------------------------*
807 * Force Field Related Code *
809 *--------------------------------------------------------------*/
812 enter_force_field(p1
, p2
)
818 if (p2
== NULL
) { /* That means the player */
820 You("bump into %s. Ouch!",
821 FunnyHallu
? "an invisible tree" :
822 "some kind of invisible wall");
826 mtmp
= (struct monst
*) p2
;
828 pline("%s bumps into %s!", Monnam(mtmp
), something
);
834 create_force_field(x
, y
, radius
, ttl
)
843 ff
= create_region((NhRect
*) 0, 0);
847 tmprect
.ly
= y
- (radius
- 1);
848 tmprect
.hy
= y
+ (radius
- 1);
849 for (i
= 0; i
< nrect
; i
++) {
850 add_rect_to_reg(ff
, &tmprect
);
857 if (!in_mklev
&& !flags
.mon_moving
)
858 set_heros_fault(ff
); /* assume player has created it */
859 /* ff->can_enter_f = enter_force_field; */
860 /* ff->can_leave_f = enter_force_field; */
867 /*--------------------------------------------------------------*
869 * Gas cloud related code *
871 *--------------------------------------------------------------*/
874 * Here is an example of an expire function that may prolong
875 * region life after some mods...
878 expire_gas_cloud(p1
, p2
)
885 reg
= (NhRegion
*) p1
;
886 damage
= (int) reg
->arg
;
888 /* If it was a thick cloud, it dissipates a little first */
890 damage
/= 2; /* It dissipates, let's do less damage */
891 reg
->arg
= (void *) damage
;
892 reg
->ttl
= 2; /* Here's the trick : reset ttl */
893 return FALSE
; /* THEN return FALSE, means "still there" */
895 return TRUE
; /* OK, it's gone, you can free it! */
899 revive_cthulhu(p1
, p2
)
903 boolean ret
= expire_gas_cloud(p1
, p2
);
905 /* Bring back Cthulhu! */
907 NhRegion
*reg
= (NhRegion
*) p1
;
908 struct monst
*cthulhu
= NULL
;
911 cx
= (reg
->bounding_box
.lx
+ reg
->bounding_box
.hx
) / 2;
912 cy
= (reg
->bounding_box
.ly
+ reg
->bounding_box
.hy
) / 2;
914 if (enexto(&cc
, cx
, cy
, &mons
[PM_CTHULHU
])) {
918 cx
= cy
= 0; /* Place Cthulhu randomly */
921 /* Make sure Cthulhu doesn't get the Amulet again! :-) */
922 cthulhu
= makemon(&mons
[PM_CTHULHU
], cx
, cy
,
923 MM_NOCOUNTBIRTH
| NO_MINVENT
);
924 if (cthulhu
&& canseemon(cthulhu
))
925 pline("%s reforms!", Monnam(cthulhu
));
931 inside_gas_cloud(p1
, p2
)
939 reg
= (NhRegion
*) p1
;
940 dam
= (int) reg
->arg
;
941 if (p2
== NULL
) { /* This means *YOU* Bozo! */
942 if (nonliving(youmonst
.data
) || Breathless
)
945 make_blinded(1L, FALSE
);
946 if (!Poison_resistance
|| (!rn2(10) && !StrongPoison_resistance
) ) {
947 pline("%s is burning your %s!", Something
, makeplural(body_part(LUNG
)));
948 You("cough and spit blood!");
949 losehp(rnd(dam
) + 5, "gas cloud", KILLED_BY_AN
);
955 } else { /* A monster is inside the cloud */
956 mtmp
= (struct monst
*) p2
;
958 /* Non living and non breathing monsters are not concerned */
959 if (!nonliving(mtmp
->data
) && !breathless(mtmp
->data
) && (!mtmp
->egotype_undead
) ) {
960 if (cansee(mtmp
->mx
, mtmp
->my
))
961 pline("%s coughs!", Monnam(mtmp
));
962 if (heros_fault(reg
))
964 if (haseyes(mtmp
->data
) && mtmp
->mcansee
) {
968 if (resists_poison(mtmp
) && !player_will_pierce_resistance())
970 mtmp
->mhp
-= rnd(dam
) + 5;
971 if (mtmp
->mhp
<= 0) {
972 if (heros_fault(reg
))
975 monkilled(mtmp
, "gas cloud", AD_DRST
);
976 if (mtmp
->mhp
<= 0) { /* not lifesaved */
982 return FALSE
; /* Monster is still alive */
986 create_cthulhu_death_cloud(x
, y
, radius
, damage
)
993 cloud
= create_gas_cloud(x
, y
, radius
, damage
);
994 if (cloud
) cloud
->expire_f
= REVIVE_CTHULHU
;
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
&& !flags
.mon_moving
) {
1024 set_heros_fault(cloud
); /* assume player has created it */
1025 if (practicantterror
) {
1026 pline("%s booms: 'You're causing a smelling nuisance! That's very definitely not allowed, so you pay 2000 zorkmids now and then stop stinking around in my lab!'", noroelaname());
1027 fineforpracticant(2000, 0, 0);
1030 cloud
->inside_f
= INSIDE_GAS_CLOUD
;
1031 cloud
->expire_f
= EXPIRE_GAS_CLOUD
;
1032 cloud
->arg
= (void *) damage
;
1033 cloud
->visible
= TRUE
;
1034 cloud
->glyph
= cmap_to_glyph(S_cloud
);