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 = 99;
99 reg->bounding_box.ly = 99;
100 reg->bounding_box.hx = 0;
101 reg->bounding_box.hy = 0;
105 nrect > 0 ? (NhRect *) alloc((sizeof(NhRect)) * nrect) : (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(sizeof(NhRect) * (reg->nrects + 1));
149 if (reg->nrects > 0) {
150 (void) memcpy((genericptr_t) tmp_rect, (genericptr_t) reg->rects,
151 (sizeof(NhRect) * reg->nrects));
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(sizeof(NhRegion *) * (max_regions + 10));
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 /* update monster IDs for region being loaded from bones; `ghostly' implied */
768 reset_region_mids(reg)
771 int i = 0, n = reg->n_monst;
772 unsigned *mid_list = reg->monsters;
775 if (!lookup_id_mapping(mid_list[i], &mid_list[i])) {
776 /* shrink list to remove missing monster; order doesn't matter */
777 mid_list[i] = mid_list[--n];
779 /* move on to next monster */
789 /*--------------------------------------------------------------*
791 * Create Region with just a message *
793 *--------------------------------------------------------------*/
796 create_msg_region(x, y, w, h, msg_enter, msg_leave)
799 const char *msg_enter;
800 const char *msg_leave;
803 NhRegion *reg = create_region((NhRect *) 0, 0);
806 reg->enter_msg = dupstr(msg_enter);
808 reg->leave_msg = dupstr(msg_leave);
813 add_rect_to_reg(reg, &tmprect);
819 /*--------------------------------------------------------------*
821 * Force Field Related Cod *
823 *--------------------------------------------------------------*/
826 enter_force_field(p1, p2)
832 if (p2 == (genericptr_t) 0) { /* That means the player */
834 You("bump into %s. Ouch!",
835 Hallucination ? "an invisible tree"
836 : "some kind of invisible wall");
840 mtmp = (struct monst *) p2;
842 pline("%s bumps into %s!", Monnam(mtmp), something);
848 create_force_field(x, y, radius, ttl)
858 ff = create_region((NhRect *) 0, 0);
862 tmprect.ly = y - (radius - 1);
863 tmprect.hy = y + (radius - 1);
864 for (i = 0; i < nrect; i++) {
865 add_rect_to_reg(ff, &tmprect);
872 if (!in_mklev && !context.mon_moving)
873 set_heros_fault(ff); /* assume player has created it */
874 /* ff->can_enter_f = enter_force_field; */
875 /* ff->can_leave_f = enter_force_field; */
882 /*--------------------------------------------------------------*
884 * Gas cloud related code *
886 *--------------------------------------------------------------*/
889 * Here is an example of an expire function that may prolong
890 * region life after some mods...
894 expire_gas_cloud(p1, p2)
896 genericptr_t p2 UNUSED;
901 reg = (NhRegion *) p1;
902 damage = reg->arg.a_int;
904 /* If it was a thick cloud, it dissipates a little first */
906 damage /= 2; /* It dissipates, let's do less damage */
908 reg->arg.a_int = damage;
909 reg->ttl = 2L; /* Here's the trick : reset ttl */
910 return FALSE; /* THEN return FALSE, means "still there" */
912 return TRUE; /* OK, it's gone, you can free it! */
916 inside_gas_cloud(p1, p2)
924 reg = (NhRegion *) p1;
925 dam = reg->arg.a_int;
926 if (p2 == (genericptr_t) 0) { /* This means *YOU* Bozo! */
927 if (u.uinvulnerable || nonliving(youmonst.data) || Breathless)
930 Your("%s sting.", makeplural(body_part(EYE)));
931 make_blinded(1L, FALSE);
933 if (!Poison_resistance) {
935 pline("%s is burning your %s!", Something,
936 makeplural(body_part(LUNG)));
938 pline("
\89½
\82©
\96
\82È
\82à
\82Ì
\82ð
\8bz
\82¢
\82±
\82ñ
\82¾
\81I");
941 You("cough and spit blood!");
943 You("
\8aP
\82«
\82±
\82Ý
\81C
\8c\8c\82ð
\93f
\82¢
\82½
\81I");
945 losehp(Maybe_Half_Phys(rnd(dam) + 5), "gas cloud", KILLED_BY_AN);
947 losehp(Maybe_Half_Phys(rnd(dam) + 5), "
\83K
\83X
\89_
\82Å", KILLED_BY_AN);
953 You("
\8aP
\82«
\82±
\82ñ
\82¾
\81I");
956 } else { /* A monster is inside the cloud */
957 mtmp = (struct monst *) p2;
959 /* Non living and non breathing monsters are not concerned */
960 if (!(nonliving(mtmp->data) || is_vampshifter(mtmp))
961 && !breathless(mtmp->data)) {
962 if (cansee(mtmp->mx, mtmp->my))
964 pline("%s coughs!", Monnam(mtmp));
966 pline("%s
\82Í
\8aP
\82«
\82±
\82ñ
\82¾
\81I", Monnam(mtmp));
967 if (heros_fault(reg))
969 if (haseyes(mtmp->data) && mtmp->mcansee) {
973 if (resists_poison(mtmp))
975 mtmp->mhp -= rnd(dam) + 5;
976 if (mtmp->mhp <= 0) {
977 if (heros_fault(reg))
981 monkilled(mtmp, "gas cloud", AD_DRST);
983 monkilled(mtmp, "
\83K
\83X
\89_", AD_DRST);
984 if (mtmp->mhp <= 0) { /* not lifesaved */
990 return FALSE; /* Monster is still alive */
994 create_gas_cloud(x, y, radius, damage)
1003 cloud = create_region((NhRect *) 0, 0);
1007 tmprect.ly = y - (radius - 1);
1008 tmprect.hy = y + (radius - 1);
1009 for (i = 0; i < nrect; i++) {
1010 add_rect_to_reg(cloud, &tmprect);
1016 cloud->ttl = rn1(3, 4);
1017 if (!in_mklev && !context.mon_moving)
1018 set_heros_fault(cloud); /* assume player has created it */
1019 cloud->inside_f = INSIDE_GAS_CLOUD;
1020 cloud->expire_f = EXPIRE_GAS_CLOUD;
1021 cloud->arg = zeroany;
1022 cloud->arg.a_int = damage;
1023 cloud->visible = TRUE;
1024 cloud->glyph = cmap_to_glyph(damage ? S_poisoncloud : S_cloud);
1029 /* for checking troubles during prayer; is hero at risk? */
1033 int i, f_indx, n = 0;
1035 for (i = 0; i < n_regions; i++) {
1036 /* only care about regions that hero is in */
1037 if (!hero_inside(regions[i]))
1039 f_indx = regions[i]->inside_f;
1040 /* the only type of region we understand is gas_cloud */
1041 if (f_indx == INSIDE_GAS_CLOUD) {
1042 /* completely harmless if you don't need to breathe */
1043 if (nonliving(youmonst.data) || Breathless)
1045 /* minor inconvenience if you're poison resistant;
1046 not harmful enough to be a prayer-level trouble */
1047 if (Poison_resistance)
1052 return n ? TRUE : FALSE;
1055 /* for fixing trouble at end of prayer;
1056 danger detected at start of prayer might have expired by now */
1061 int i, f_indx, n = 0;
1063 for (i = 0; i < n_regions; i++) {
1064 /* only care about regions that hero is in */
1065 if (!hero_inside(regions[i]))
1067 f_indx = regions[i]->inside_f;
1068 /* the only type of region we understand is gas_cloud */
1069 if (f_indx == INSIDE_GAS_CLOUD) {
1070 if (!n++ && regions[i]->ttl >= 0)
1075 if (n > 1 || (n == 1 && !r)) {
1076 /* multiple overlapping cloud regions or non-expiring one */
1080 pline_The("gas cloud enveloping you dissipates.");
1082 /* cloud dissipated on its own, so nothing needs to be done */
1083 pline_The("gas cloud has dissipated.");
1085 /* maybe cure blindness too */
1086 if ((Blinded & TIMEOUT) == 1L)
1087 make_blinded(0L, TRUE);