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. */
5 /* JNetHack Copyright */
6 /* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000 */
7 /* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2020 */
8 /* JNetHack may be freely redistributed. See license for details. */
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.
19 static NhRegion **regions;
20 static int n_regions = 0;
21 static int max_regions = 0;
23 #define NO_CALLBACK (-1)
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 *));
36 NhRegion *FDECL(clone_region, (NhRegion *));
38 void FDECL(free_region, (NhRegion *));
39 void FDECL(add_region, (NhRegion *));
40 void FDECL(remove_region, (NhRegion *));
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));
51 STATIC_DCL void FDECL(reset_region_mids, (NhRegion *));
53 static callback_proc callbacks[] = {
54 #define INSIDE_GAS_CLOUD 0
56 #define EXPIRE_GAS_CLOUD 1
60 /* Should be inlined. */
66 return (boolean) (x >= r->lx && x <= r->hx && y >= r->ly && y <= r->hy);
70 * Check if a point is inside a region.
73 inside_region(reg, x, y)
79 if (reg == (NhRegion *) 0 || !inside_rect(&(reg->bounding_box), x, y))
81 for (i = 0; i < reg->nrects; i++)
82 if (inside_rect(&(reg->rects[i]), x, y))
88 * Create a region. It does not activate it.
91 create_region(rects, nrect)
98 reg = (NhRegion *) alloc(sizeof(NhRegion));
99 (void) memset((genericptr_t)reg, 0, sizeof(NhRegion));
100 /* Determines bounding box */
102 reg->bounding_box = rects[0];
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;
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];
122 reg->ttl = -1L; /* Defaults */
123 reg->attach_2_u = FALSE;
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);
138 reg->monsters = (unsigned int *) 0;
144 * Add rectangle to region.
147 add_rect_to_reg(reg, rect)
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);
159 tmp_rect[reg->nrects] = *rect;
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;
174 * Add a monster to the region
177 add_mon_to_reg(reg, mon)
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);
192 reg->monsters = tmp_m;
193 reg->max_monst += MONST_INC;
195 reg->monsters[reg->n_monst++] = mon->m_id;
199 * Remove a monster from the region list (it left or died...)
202 remove_mon_from_reg(reg, mon)
208 for (i = 0; i < reg->n_monst; i++)
209 if (reg->monsters[i] == mon->m_id) {
211 reg->monsters[i] = reg->monsters[reg->n_monst];
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.
222 mon_in_region(reg, mon)
228 for (i = 0; i < reg->n_monst; i++)
229 if (reg->monsters[i] == mon->m_id)
238 * Clone (make a standalone copy) the region.
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);
265 ret_reg->monsters = (unsigned int *) 0;
272 * Free mem from region.
280 free((genericptr_t) reg->rects);
282 free((genericptr_t) reg->monsters);
284 free((genericptr_t) reg->enter_msg);
286 free((genericptr_t) reg->leave_msg);
287 free((genericptr_t) reg);
292 * Add a region to the list.
293 * This actually activates the region.
302 if (max_regions <= n_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);
313 regions[n_regions] = reg;
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 */
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))
326 /* Check for player now... */
327 if (inside_region(reg, u.ux, u.uy))
328 set_hero_inside(reg);
330 clear_hero_inside(reg);
334 * Remove a region from the list & free it.
340 register int i, x, y;
342 for (i = 0; i < n_regions; i++)
343 if (regions[i] == reg)
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;
353 /* Update screen if necessary */
354 reg->ttl = -2L; /* for visible_region_at */
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))
365 * Remove all regions and clear all related data (This must be down
366 * when changing level, for instance).
373 for (i = 0; i < n_regions; i++)
374 free_region(regions[i]);
377 free((genericptr_t) regions);
379 regions = (NhRegion **) 0;
383 * This function is called every turn.
384 * It makes the regions age, if necessary and calls the appropriate
385 * callbacks when needed.
390 register int i, j, k;
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]);
403 /* Process remaining regions */
404 for (i = 0; i < n_regions; i++) {
405 /* Make the region age */
406 if (regions[i]->ttl > 0L)
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++) {
416 find_mid(regions[i]->monsters[j], FM_FMON);
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 */
432 * check whether player enters/leaves one or more regions.
440 /* First check if hero can do the move */
441 for (i = 0; i < n_regions; i++) {
442 if (regions[i]->attach_2_u)
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))
454 /* Callbacks for the regions hero does leave */
455 for (i = 0; i < n_regions; i++) {
456 if (regions[i]->attach_2_u)
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);
468 /* Callbacks for the regions hero does enter */
469 for (i = 0; i < n_regions; i++) {
470 if (regions[i]->attach_2_u)
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);
486 * check whether a monster enters/leaves one or more regions.
489 m_in_out_region(mon, x, y)
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)
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))
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)
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);
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)
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);
537 * Checks player's regions after a teleport for instance.
540 update_player_regions()
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]);
548 clear_hero_inside(regions[i]);
552 * Ditto for a specified monster.
555 update_monster_region(mon)
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);
565 if (mon_in_region(regions[i], mon))
566 remove_mon_from_reg(regions[i], mon);
575 * Change monster pointer in regions
576 * This happens, for instance, when a monster grows and
577 * need a new structure (internally that is).
580 replace_mon_regions(monold, monnew)
581 struct monst *monold, *monnew;
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);
593 * Remove monster from all regions it was in (ie monster just died)
596 remove_mon_from_regions(mon)
601 for (i = 0; i < n_regions; i++)
602 if (mon_in_region(regions[i], mon))
603 remove_mon_from_reg(regions[i], mon);
609 * Check if a spot is under a visible region (eg: gas cloud).
610 * Returns NULL if not, otherwise returns region.
613 visible_region_at(x, y)
618 for (i = 0; i < n_regions; i++) {
619 if (!regions[i]->visible || regions[i]->ttl == -2L)
621 if (inside_region(regions[i], x, y))
624 return (NhRegion *) 0;
628 show_region(reg, x, y)
632 show_glyph(x, y, reg->glyph);
639 save_regions(fd, mode)
646 if (!perform_bwrite(mode))
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) ®ions[i]->bounding_box, sizeof(NhRect));
653 bwrite(fd, (genericptr_t) ®ions[i]->nrects, sizeof(short));
654 for (j = 0; j < regions[i]->nrects; j++)
655 bwrite(fd, (genericptr_t) ®ions[i]->rects[j], sizeof(NhRect));
656 bwrite(fd, (genericptr_t) ®ions[i]->attach_2_u, sizeof(boolean));
658 bwrite(fd, (genericptr_t) ®ions[i]->attach_2_m, sizeof(unsigned));
659 n = regions[i]->enter_msg != (const char *) 0
660 ? strlen(regions[i]->enter_msg)
662 bwrite(fd, (genericptr_t) &n, sizeof n);
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)
668 bwrite(fd, (genericptr_t) &n, sizeof n);
670 bwrite(fd, (genericptr_t) regions[i]->leave_msg, n);
671 bwrite(fd, (genericptr_t) ®ions[i]->ttl, sizeof(long));
672 bwrite(fd, (genericptr_t) ®ions[i]->expire_f, sizeof(short));
673 bwrite(fd, (genericptr_t) ®ions[i]->can_enter_f, sizeof(short));
674 bwrite(fd, (genericptr_t) ®ions[i]->enter_f, sizeof(short));
675 bwrite(fd, (genericptr_t) ®ions[i]->can_leave_f, sizeof(short));
676 bwrite(fd, (genericptr_t) ®ions[i]->leave_f, sizeof(short));
677 bwrite(fd, (genericptr_t) ®ions[i]->inside_f, sizeof(short));
678 bwrite(fd, (genericptr_t) ®ions[i]->player_flags,
679 sizeof(unsigned int));
680 bwrite(fd, (genericptr_t) ®ions[i]->n_monst, sizeof(short));
681 for (j = 0; j < regions[i]->n_monst; j++)
682 bwrite(fd, (genericptr_t) ®ions[i]->monsters[j],
684 bwrite(fd, (genericptr_t) ®ions[i]->visible, sizeof(boolean));
685 bwrite(fd, (genericptr_t) ®ions[i]->glyph, sizeof(int));
686 bwrite(fd, (genericptr_t) ®ions[i]->arg, sizeof(anything));
690 if (release_data(mode))
695 rest_regions(fd, ghostly)
697 boolean ghostly; /* If a bones file restore */
704 clear_regions(); /* Just for security */
705 mread(fd, (genericptr_t) &tmstamp, sizeof(tmstamp));
709 tmstamp = (moves - tmstamp);
710 mread(fd, (genericptr_t) &n_regions, sizeof(n_regions));
711 max_regions = n_regions;
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) ®ions[i]->bounding_box, sizeof(NhRect));
717 mread(fd, (genericptr_t) ®ions[i]->nrects, sizeof(short));
719 if (regions[i]->nrects > 0)
721 (NhRect *) alloc(sizeof(NhRect) * regions[i]->nrects);
722 for (j = 0; j < regions[i]->nrects; j++)
723 mread(fd, (genericptr_t) ®ions[i]->rects[j], sizeof(NhRect));
724 mread(fd, (genericptr_t) ®ions[i]->attach_2_u, sizeof(boolean));
725 mread(fd, (genericptr_t) ®ions[i]->attach_2_m, sizeof(unsigned));
727 mread(fd, (genericptr_t) &n, sizeof n);
729 msg_buf = (char *) alloc(n + 1);
730 mread(fd, (genericptr_t) msg_buf, n);
732 regions[i]->enter_msg = (const char *) msg_buf;
734 regions[i]->enter_msg = (const char *) 0;
736 mread(fd, (genericptr_t) &n, sizeof n);
738 msg_buf = (char *) alloc(n + 1);
739 mread(fd, (genericptr_t) msg_buf, n);
741 regions[i]->leave_msg = (const char *) msg_buf;
743 regions[i]->leave_msg = (const char *) 0;
745 mread(fd, (genericptr_t) ®ions[i]->ttl, sizeof(long));
746 /* check for expired region */
747 if (regions[i]->ttl >= 0L)
749 (regions[i]->ttl > tmstamp) ? regions[i]->ttl - tmstamp : 0L;
750 mread(fd, (genericptr_t) ®ions[i]->expire_f, sizeof(short));
751 mread(fd, (genericptr_t) ®ions[i]->can_enter_f, sizeof(short));
752 mread(fd, (genericptr_t) ®ions[i]->enter_f, sizeof(short));
753 mread(fd, (genericptr_t) ®ions[i]->can_leave_f, sizeof(short));
754 mread(fd, (genericptr_t) ®ions[i]->leave_f, sizeof(short));
755 mread(fd, (genericptr_t) ®ions[i]->inside_f, sizeof(short));
756 mread(fd, (genericptr_t) ®ions[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]);
762 mread(fd, (genericptr_t) ®ions[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);
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) ®ions[i]->monsters[j],
772 mread(fd, (genericptr_t) ®ions[i]->visible, sizeof(boolean));
773 mread(fd, (genericptr_t) ®ions[i]->glyph, sizeof(int));
774 mread(fd, (genericptr_t) ®ions[i]->arg, sizeof(anything));
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]);
785 /* to support '#stats' wizard-mode command */
787 region_stats(hdrfmt, hdrbuf, count, size)
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) {
801 *size += (long) rg->nrects * (long) sizeof (NhRect);
803 *size += (long) (strlen(rg->enter_msg) + 1);
805 *size += (long) (strlen(rg->leave_msg) + 1);
806 *size += (long) rg->max_monst * (long) sizeof *rg->monsters;
811 /* update monster IDs for region being loaded from bones; `ghostly' implied */
813 reset_region_mids(reg)
816 int i = 0, n = reg->n_monst;
817 unsigned *mid_list = reg->monsters;
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];
824 /* move on to next monster */
834 /*--------------------------------------------------------------*
836 * Create Region with just a message *
838 *--------------------------------------------------------------*/
841 create_msg_region(x, y, w, h, msg_enter, msg_leave)
844 const char *msg_enter;
845 const char *msg_leave;
848 NhRegion *reg = create_region((NhRect *) 0, 0);
851 reg->enter_msg = dupstr(msg_enter);
853 reg->leave_msg = dupstr(msg_leave);
858 add_rect_to_reg(reg, &tmprect);
864 /*--------------------------------------------------------------*
866 * Force Field Related Cod *
868 *--------------------------------------------------------------*/
871 enter_force_field(p1, p2)
877 if (p2 == (genericptr_t) 0) { /* That means the player */
880 You("bump into %s. Ouch!",
881 Hallucination ? "an invisible tree"
882 : "some kind of invisible wall");
884 You("%s
\82É
\82Ô
\82¿
\82 \82½
\82Á
\82½
\81D
\82¢
\82Ä
\82Á
\81I",
885 Hallucination ? "
\96Ú
\82É
\8c©
\82¦
\82È
\82¢
\96Ø"
886 : "
\82È
\82ñ
\82ç
\82©
\82Ì
\96Ú
\82É
\8c©
\82¦
\82È
\82¢
\95Ç");
892 pline("
\82¢
\82Ä
\82Á
\81I");
894 mtmp = (struct monst *) p2;
897 pline("%s bumps into %s!", Monnam(mtmp), something);
899 pline("%s
\82Í%s
\82É
\82Ô
\82¿
\82 \82½
\82Á
\82½
\81I", Monnam(mtmp), something);
905 create_force_field(x, y, radius, ttl)
915 ff = create_region((NhRect *) 0, 0);
919 tmprect.ly = y - (radius - 1);
920 tmprect.hy = y + (radius - 1);
921 for (i = 0; i < nrect; i++) {
922 add_rect_to_reg(ff, &tmprect);
929 if (!in_mklev && !context.mon_moving)
930 set_heros_fault(ff); /* assume player has created it */
931 /* ff->can_enter_f = enter_force_field; */
932 /* ff->can_leave_f = enter_force_field; */
939 /*--------------------------------------------------------------*
941 * Gas cloud related code *
943 *--------------------------------------------------------------*/
946 * Here is an example of an expire function that may prolong
947 * region life after some mods...
951 expire_gas_cloud(p1, p2)
953 genericptr_t p2 UNUSED;
958 reg = (NhRegion *) p1;
959 damage = reg->arg.a_int;
961 /* If it was a thick cloud, it dissipates a little first */
963 damage /= 2; /* It dissipates, let's do less damage */
965 reg->arg.a_int = damage;
966 reg->ttl = 2L; /* Here's the trick : reset ttl */
967 return FALSE; /* THEN return FALSE, means "still there" */
969 return TRUE; /* OK, it's gone, you can free it! */
973 inside_gas_cloud(p1, p2)
982 * Gas clouds can't be targetted at water locations, but they can
983 * start next to water and spread over it.
986 reg = (NhRegion *) p1;
987 dam = reg->arg.a_int;
988 if (p2 == (genericptr_t) 0) { /* This means *YOU* Bozo! */
989 if (u.uinvulnerable || nonliving(youmonst.data) || Breathless
994 Your("%s sting.", makeplural(body_part(EYE)));
996 Your("%s
\82ª
\83`
\83N
\83`
\83N
\82µ
\82½
\81D", body_part(EYE));
997 make_blinded(1L, FALSE);
999 if (!Poison_resistance) {
1001 pline("%s is burning your %s!", Something,
1002 makeplural(body_part(LUNG)));
1004 pline("
\89½
\82©
\96
\82È
\82à
\82Ì
\82ð
\8bz
\82¢
\82±
\82ñ
\82¾
\81I");
1007 You("cough and spit blood!");
1009 You("
\8aP
\82«
\82±
\82Ý
\81C
\8c\8c\82ð
\93f
\82¢
\82½
\81I");
1011 losehp(Maybe_Half_Phys(rnd(dam) + 5), "gas cloud", KILLED_BY_AN);
1013 losehp(Maybe_Half_Phys(rnd(dam) + 5), "
\83K
\83X
\89_
\82Å", KILLED_BY_AN);
1019 You("
\8aP
\82«
\82±
\82ñ
\82¾
\81I");
1022 } else { /* A monster is inside the cloud */
1023 mtmp = (struct monst *) p2;
1025 /* Non living and non breathing monsters are not concerned;
1026 adult green dragon is not affected by gas cloud, baby one is */
1027 if (!(nonliving(mtmp->data) || is_vampshifter(mtmp))
1028 && !breathless(mtmp->data)
1029 /* not is_swimmer(); assume that non-fish are swimming on
1030 the surface and breathing the air above it periodically
1031 unless located at water spot on plane of water */
1032 && !((mtmp->data->mlet == S_EEL || Is_waterlevel(&u.uz))
1033 && is_pool(mtmp->mx, mtmp->my))
1034 /* exclude monsters with poison gas breath attack:
1035 adult green dragon and Chromatic Dragon (and iron golem,
1036 but nonliving() and breathless() tests also catch that) */
1037 && !(attacktype_fordmg(mtmp->data, AT_BREA, AD_DRST)
1038 || attacktype_fordmg(mtmp->data, AT_BREA, AD_RBRE))) {
1039 if (cansee(mtmp->mx, mtmp->my))
1041 pline("%s coughs!", Monnam(mtmp));
1043 pline("%s
\82Í
\8aP
\82«
\82±
\82ñ
\82¾
\81I", Monnam(mtmp));
1044 if (heros_fault(reg))
1045 setmangry(mtmp, TRUE);
1046 if (haseyes(mtmp->data) && mtmp->mcansee) {
1050 if (resists_poison(mtmp))
1052 mtmp->mhp -= rnd(dam) + 5;
1053 if (DEADMONSTER(mtmp)) {
1054 if (heros_fault(reg))
1058 monkilled(mtmp, "gas cloud", AD_DRST);
1060 monkilled(mtmp, "
\83K
\83X
\89_", AD_DRST);
1061 if (DEADMONSTER(mtmp)) { /* not lifesaved */
1067 return FALSE; /* Monster is still alive */
1071 create_gas_cloud(x, y, radius, damage)
1080 cloud = create_region((NhRect *) 0, 0);
1084 tmprect.ly = y - (radius - 1);
1085 tmprect.hy = y + (radius - 1);
1086 for (i = 0; i < nrect; i++) {
1087 add_rect_to_reg(cloud, &tmprect);
1093 cloud->ttl = rn1(3, 4);
1094 if (!in_mklev && !context.mon_moving)
1095 set_heros_fault(cloud); /* assume player has created it */
1096 cloud->inside_f = INSIDE_GAS_CLOUD;
1097 cloud->expire_f = EXPIRE_GAS_CLOUD;
1098 cloud->arg = zeroany;
1099 cloud->arg.a_int = damage;
1100 cloud->visible = TRUE;
1101 cloud->glyph = cmap_to_glyph(damage ? S_poisoncloud : S_cloud);
1106 /* for checking troubles during prayer; is hero at risk? */
1110 int i, f_indx, n = 0;
1112 for (i = 0; i < n_regions; i++) {
1113 /* only care about regions that hero is in */
1114 if (!hero_inside(regions[i]))
1116 f_indx = regions[i]->inside_f;
1117 /* the only type of region we understand is gas_cloud */
1118 if (f_indx == INSIDE_GAS_CLOUD) {
1119 /* completely harmless if you don't need to breathe */
1120 if (nonliving(youmonst.data) || Breathless)
1122 /* minor inconvenience if you're poison resistant;
1123 not harmful enough to be a prayer-level trouble */
1124 if (Poison_resistance)
1129 return n ? TRUE : FALSE;
1132 /* for fixing trouble at end of prayer;
1133 danger detected at start of prayer might have expired by now */
1138 int i, f_indx, n = 0;
1140 for (i = 0; i < n_regions; i++) {
1141 /* only care about regions that hero is in */
1142 if (!hero_inside(regions[i]))
1144 f_indx = regions[i]->inside_f;
1145 /* the only type of region we understand is gas_cloud */
1146 if (f_indx == INSIDE_GAS_CLOUD) {
1147 if (!n++ && regions[i]->ttl >= 0)
1152 if (n > 1 || (n == 1 && !r)) {
1153 /* multiple overlapping cloud regions or non-expiring one */
1158 pline_The("gas cloud enveloping you dissipates.");
1160 pline("
\82 \82È
\82½
\82ð
\95ï
\82ñ
\82Å
\82¢
\82½
\83K
\83X
\89_
\82Í
\8fÁ
\82¦
\82½
\81D");
1162 /* cloud dissipated on its own, so nothing needs to be done */
1164 pline_The("gas cloud has dissipated.");
1166 pline("
\83K
\83X
\89_
\82Í
\8fÁ
\82¦
\82½
\81D");
1168 /* maybe cure blindness too */
1169 if ((Blinded & TIMEOUT) == 1L)
1170 make_blinded(0L, TRUE);