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 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,int));
46 static 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 (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 == NULL || !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;
104 reg->rects = nrect > 0 ? (NhRect *)alloc((sizeof (NhRect)) * nrect) : NULL;
105 for (i = 0; i < nrect; i++) {
106 if (rects[i].lx < reg->bounding_box.lx)
107 reg->bounding_box.lx = rects[i].lx;
108 if (rects[i].ly < reg->bounding_box.ly)
109 reg->bounding_box.ly = rects[i].ly;
110 if (rects[i].hx > reg->bounding_box.hx)
111 reg->bounding_box.hx = rects[i].hx;
112 if (rects[i].hy > reg->bounding_box.hy)
113 reg->bounding_box.hy = rects[i].hy;
114 reg->rects[i] = rects[i];
116 reg->ttl = -1; /* Defaults */
117 reg->attach_2_u = FALSE;
119 /* reg->attach_2_o = NULL; */
120 reg->enter_msg = NULL;
121 reg->leave_msg = NULL;
122 reg->expire_f = NO_CALLBACK;
123 reg->enter_f = NO_CALLBACK;
124 reg->can_enter_f = NO_CALLBACK;
125 reg->leave_f = NO_CALLBACK;
126 reg->can_leave_f = NO_CALLBACK;
127 reg->inside_f = NO_CALLBACK;
128 clear_hero_inside(reg);
129 clear_heros_fault(reg);
132 reg->monsters = NULL;
138 * Add rectangle to region.
141 add_rect_to_reg(reg, rect)
147 tmp_rect = (NhRect *) alloc(sizeof (NhRect) * (reg->nrects + 1));
148 if (reg->nrects > 0) {
149 (void) memcpy((genericptr_t) tmp_rect, (genericptr_t) reg->rects,
150 (sizeof (NhRect) * reg->nrects));
151 free((genericptr_t) reg->rects);
153 tmp_rect[reg->nrects] = *rect;
155 reg->rects = tmp_rect;
156 /* Update bounding box if needed */
157 if (reg->bounding_box.lx > rect->lx)
158 reg->bounding_box.lx = rect->lx;
159 if (reg->bounding_box.ly > rect->ly)
160 reg->bounding_box.ly = rect->ly;
161 if (reg->bounding_box.hx < rect->hx)
162 reg->bounding_box.hx = rect->hx;
163 if (reg->bounding_box.hy < rect->hy)
164 reg->bounding_box.hy = rect->hy;
168 * Add a monster to the region
171 add_mon_to_reg(reg, mon)
178 if (reg->max_monst <= reg->n_monst) {
180 alloc(sizeof (unsigned) * (reg->max_monst + MONST_INC));
181 if (reg->max_monst > 0) {
182 for (i = 0; i < reg->max_monst; i++)
183 tmp_m[i] = reg->monsters[i];
184 free((genericptr_t) reg->monsters);
186 reg->monsters = tmp_m;
187 reg->max_monst += MONST_INC;
189 reg->monsters[reg->n_monst++] = mon->m_id;
193 * Remove a monster from the region list (it left or died...)
196 remove_mon_from_reg(reg, mon)
202 for (i = 0; i < reg->n_monst; i++)
203 if (reg->monsters[i] == mon->m_id) {
205 reg->monsters[i] = reg->monsters[reg->n_monst];
211 * Check if a monster is inside the region.
212 * It's probably quicker to check with the region internal list
213 * than to check for coordinates.
216 mon_in_region(reg, mon)
222 for (i = 0; i < reg->n_monst; i++)
223 if (reg->monsters[i] == mon->m_id)
232 * Clone (make a standalone copy) the region.
240 ret_reg = create_region(reg->rects, reg->nrects);
241 ret_reg->ttl = reg->ttl;
242 ret_reg->attach_2_u = reg->attach_2_u;
243 ret_reg->attach_2_m = reg->attach_2_m;
244 /* ret_reg->attach_2_o = reg->attach_2_o; */
245 ret_reg->expire_f = reg->expire_f;
246 ret_reg->enter_f = reg->enter_f;
247 ret_reg->can_enter_f = reg->can_enter_f;
248 ret_reg->leave_f = reg->leave_f;
249 ret_reg->can_leave_f = reg->can_leave_f;
250 ret_reg->player_flags = reg->player_flags; /* set/clear_hero_inside,&c*/
251 ret_reg->n_monst = reg->n_monst;
252 if (reg->n_monst > 0) {
253 ret_reg->monsters = (unsigned *)
254 alloc((sizeof (unsigned)) * reg->n_monst);
255 (void) memcpy((genericptr_t) ret_reg->monsters, (genericptr_t) reg->monsters,
256 sizeof (unsigned) * reg->n_monst);
258 ret_reg->monsters = NULL;
265 * Free mem from region.
273 free((genericptr_t) reg->rects);
275 free((genericptr_t) reg->monsters);
276 free((genericptr_t) reg);
281 * Add a region to the list.
282 * This actually activates the region.
291 if (max_regions <= n_regions) {
293 regions = (NhRegion **)alloc(sizeof (NhRegion *) * (max_regions + 10));
294 if (max_regions > 0) {
295 (void) memcpy((genericptr_t) regions, (genericptr_t) tmp_reg,
296 max_regions * sizeof (NhRegion *));
297 free((genericptr_t) tmp_reg);
301 regions[n_regions] = reg;
303 /* Check for monsters inside the region */
304 for (i = reg->bounding_box.lx; i <= reg->bounding_box.hx; i++)
305 for (j = reg->bounding_box.ly; j <= reg->bounding_box.hy; j++) {
306 /* Some regions can cross the level boundaries */
309 if (MON_AT(i, j) && inside_region(reg, i, j))
310 add_mon_to_reg(reg, level.monsters[i][j]);
311 if (reg->visible && cansee(i, j))
314 /* Check for player now... */
315 if (inside_region(reg, u.ux, u.uy))
316 set_hero_inside(reg);
318 clear_hero_inside(reg);
322 * Remove a region from the list & free it.
328 register int i, x, y;
330 for (i = 0; i < n_regions; i++)
331 if (regions[i] == reg)
336 /* Update screen if necessary */
338 for (x = reg->bounding_box.lx; x <= reg->bounding_box.hx; x++)
339 for (y = reg->bounding_box.ly; y <= reg->bounding_box.hy; y++)
340 if (isok(x,y) && inside_region(reg, x, y) && cansee(x, y))
344 regions[i] = regions[n_regions - 1];
345 regions[n_regions - 1] = (NhRegion *) 0;
350 * Remove all regions and clear all related data (This must be down
351 * when changing level, for instance).
358 for (i = 0; i < n_regions; i++)
359 free_region(regions[i]);
362 free((genericptr_t) regions);
368 * This function is called every turn.
369 * It makes the regions age, if necessary and calls the appropriate
370 * callbacks when needed.
375 register int i, j, k;
379 /* Do it backward because the array will be modified */
380 for (i = n_regions - 1; i >= 0; i--) {
381 if (regions[i]->ttl == 0) {
382 if ((f_indx = regions[i]->expire_f) == NO_CALLBACK ||
383 (*callbacks[f_indx])(regions[i], (genericptr_t) 0))
384 remove_region(regions[i]);
388 /* Process remaining regions */
389 for (i = 0; i < n_regions; i++) {
390 /* Make the region age */
391 if (regions[i]->ttl > 0)
393 /* Check if player is inside region */
394 f_indx = regions[i]->inside_f;
395 if (f_indx != NO_CALLBACK && hero_inside(regions[i]))
396 (void) (*callbacks[f_indx])(regions[i], (genericptr_t) 0);
397 /* Check if any monster is inside region */
398 if (f_indx != NO_CALLBACK) {
399 for (j = 0; j < regions[i]->n_monst; j++) {
400 struct monst *mtmp = find_mid(regions[i]->monsters[j], FM_FMON);
402 if (!mtmp || mtmp->mhp <= 0 ||
403 (*callbacks[f_indx])(regions[i], mtmp)) {
404 /* The monster died, remove it from list */
405 k = (regions[i]->n_monst -= 1);
406 regions[i]->monsters[j] = regions[i]->monsters[k];
407 regions[i]->monsters[k] = 0;
408 --j; /* current slot has been reused; recheck it next */
416 * check whether player enters/leaves one or more regions.
425 /* First check if we can do the move */
426 for (i = 0; i < n_regions; i++) {
427 if (inside_region(regions[i], x, y)
428 && !hero_inside(regions[i]) && !regions[i]->attach_2_u) {
429 if ((f_indx = regions[i]->can_enter_f) != NO_CALLBACK)
430 if (!(*callbacks[f_indx])(regions[i], (genericptr_t) 0))
433 if (hero_inside(regions[i])
434 && !inside_region(regions[i], x, y)
435 && !regions[i]->attach_2_u) {
436 if ((f_indx = regions[i]->can_leave_f) != NO_CALLBACK)
437 if (!(*callbacks[f_indx])(regions[i], (genericptr_t) 0))
442 /* Callbacks for the regions we do leave */
443 for (i = 0; i < n_regions; i++)
444 if (hero_inside(regions[i]) &&
445 !regions[i]->attach_2_u && !inside_region(regions[i], x, y)) {
446 clear_hero_inside(regions[i]);
447 if (regions[i]->leave_msg != NULL)
448 pline(regions[i]->leave_msg);
449 if ((f_indx = regions[i]->leave_f) != NO_CALLBACK)
450 (void) (*callbacks[f_indx])(regions[i], (genericptr_t) 0);
453 /* Callbacks for the regions we do enter */
454 for (i = 0; i < n_regions; i++)
455 if (!hero_inside(regions[i]) &&
456 !regions[i]->attach_2_u && inside_region(regions[i], x, y)) {
457 set_hero_inside(regions[i]);
458 if (regions[i]->enter_msg != NULL)
459 pline(regions[i]->enter_msg);
460 if ((f_indx = regions[i]->enter_f) != NO_CALLBACK)
461 (void) (*callbacks[f_indx])(regions[i], (genericptr_t) 0);
467 * check wether a monster enters/leaves one or more region.
470 m_in_out_region(mon, x, y)
476 /* First check if we can do the move */
477 for (i = 0; i < n_regions; i++) {
478 if (inside_region(regions[i], x, y) &&
479 !mon_in_region(regions[i], mon) &&
480 regions[i]->attach_2_m != mon->m_id) {
481 if ((f_indx = regions[i]->can_enter_f) != NO_CALLBACK)
482 if (!(*callbacks[f_indx])(regions[i], mon))
484 } else if (mon_in_region(regions[i], mon) &&
485 !inside_region(regions[i], x, y) &&
486 regions[i]->attach_2_m != mon->m_id) {
487 if ((f_indx = regions[i]->can_leave_f) != NO_CALLBACK)
488 if (!(*callbacks[f_indx])(regions[i], mon))
493 /* Callbacks for the regions we do leave */
494 for (i = 0; i < n_regions; i++)
495 if (mon_in_region(regions[i], mon) &&
496 regions[i]->attach_2_m != mon->m_id &&
497 !inside_region(regions[i], x, y)) {
498 remove_mon_from_reg(regions[i], mon);
499 if ((f_indx = regions[i]->leave_f) != NO_CALLBACK)
500 (void) (*callbacks[f_indx])(regions[i], mon);
503 /* Callbacks for the regions we do enter */
504 for (i = 0; i < n_regions; i++)
505 if (!hero_inside(regions[i]) &&
506 !regions[i]->attach_2_u && inside_region(regions[i], x, y)) {
507 add_mon_to_reg(regions[i], mon);
508 if ((f_indx = regions[i]->enter_f) != NO_CALLBACK)
509 (void) (*callbacks[f_indx])(regions[i], mon);
515 * Checks player's regions after a teleport for instance.
518 update_player_regions()
522 for (i = 0; i < n_regions; i++)
523 if (!regions[i]->attach_2_u && inside_region(regions[i], u.ux, u.uy))
524 set_hero_inside(regions[i]);
526 clear_hero_inside(regions[i]);
530 * Ditto for a specified monster.
533 update_monster_region(mon)
538 for (i = 0; i < n_regions; i++) {
539 if (inside_region(regions[i], mon->mx, mon->my)) {
540 if (!mon_in_region(regions[i], mon))
541 add_mon_to_reg(regions[i], mon);
543 if (mon_in_region(regions[i], mon))
544 remove_mon_from_reg(regions[i], mon);
553 * Change monster pointer in regions
554 * This happens, for instance, when a monster grows and
555 * need a new structure (internally that is).
558 replace_mon_regions(monold, monnew)
559 struct monst *monold, *monnew;
563 for (i = 0; i < n_regions; i++)
564 if (mon_in_region(regions[i], monold)) {
565 remove_mon_from_reg(regions[i], monold);
566 add_mon_to_reg(regions[i], monnew);
571 * Remove monster from all regions it was in (ie monster just died)
574 remove_mon_from_regions(mon)
579 for (i = 0; i < n_regions; i++)
580 if (mon_in_region(regions[i], mon))
581 remove_mon_from_reg(regions[i], mon);
587 * Check if a spot is under a visible region (eg: gas cloud).
588 * Returns NULL if not, otherwise returns region.
591 visible_region_at(x, y)
596 for (i = 0; i < n_regions; i++)
597 if (inside_region(regions[i], x, y) && regions[i]->visible &&
598 regions[i]->ttl != 0)
600 return (NhRegion *) 0;
604 show_region(reg, x, y)
608 show_glyph(x, y, reg->glyph);
615 save_regions(fd, mode)
622 if (!perform_bwrite(mode)) goto skip_lots;
624 bwrite(fd, (genericptr_t) &moves, sizeof (moves)); /* timestamp */
625 bwrite(fd, (genericptr_t) &n_regions, sizeof (n_regions));
626 for (i = 0; i < n_regions; i++) {
627 bwrite(fd, (genericptr_t) ®ions[i]->bounding_box, sizeof (NhRect));
628 bwrite(fd, (genericptr_t) ®ions[i]->nrects, sizeof (short));
629 for (j = 0; j < regions[i]->nrects; j++)
630 bwrite(fd, (genericptr_t) ®ions[i]->rects[j], sizeof (NhRect));
631 bwrite(fd, (genericptr_t) ®ions[i]->attach_2_u, sizeof (boolean));
633 bwrite(fd, (genericptr_t) ®ions[i]->attach_2_m, sizeof (unsigned));
634 n = regions[i]->enter_msg != NULL ? strlen(regions[i]->enter_msg) : 0;
635 bwrite(fd, (genericptr_t) &n, sizeof n);
637 bwrite(fd, (genericptr_t) regions[i]->enter_msg, n);
638 n = regions[i]->leave_msg != NULL ? strlen(regions[i]->leave_msg) : 0;
639 bwrite(fd, (genericptr_t) &n, sizeof n);
641 bwrite(fd, (genericptr_t) regions[i]->leave_msg, n);
642 bwrite(fd, (genericptr_t) ®ions[i]->ttl, sizeof (short));
643 bwrite(fd, (genericptr_t) ®ions[i]->expire_f, sizeof (short));
644 bwrite(fd, (genericptr_t) ®ions[i]->can_enter_f, sizeof (short));
645 bwrite(fd, (genericptr_t) ®ions[i]->enter_f, sizeof (short));
646 bwrite(fd, (genericptr_t) ®ions[i]->can_leave_f, sizeof (short));
647 bwrite(fd, (genericptr_t) ®ions[i]->leave_f, sizeof (short));
648 bwrite(fd, (genericptr_t) ®ions[i]->inside_f, sizeof (short));
649 bwrite(fd, (genericptr_t) ®ions[i]->player_flags, sizeof (boolean));
650 bwrite(fd, (genericptr_t) ®ions[i]->n_monst, sizeof (short));
651 for (j = 0; j < regions[i]->n_monst; j++)
652 bwrite(fd, (genericptr_t) ®ions[i]->monsters[j],
654 bwrite(fd, (genericptr_t) ®ions[i]->visible, sizeof (boolean));
655 bwrite(fd, (genericptr_t) ®ions[i]->glyph, sizeof (int));
656 bwrite(fd, (genericptr_t) ®ions[i]->arg, sizeof (genericptr_t));
660 if (release_data(mode))
665 rest_regions(fd, ghostly)
667 boolean ghostly; /* If a bones file restore */
674 clear_regions(); /* Just for security */
675 mread(fd, (genericptr_t) &tmstamp, sizeof (tmstamp));
676 if (ghostly) tmstamp = 0;
677 else tmstamp = (moves - tmstamp);
678 mread(fd, (genericptr_t) &n_regions, sizeof (n_regions));
679 max_regions = n_regions;
681 regions = (NhRegion **) alloc(sizeof (NhRegion *) * n_regions);
682 for (i = 0; i < n_regions; i++) {
683 regions[i] = (NhRegion *) alloc(sizeof (NhRegion));
684 mread(fd, (genericptr_t) ®ions[i]->bounding_box, sizeof (NhRect));
685 mread(fd, (genericptr_t) ®ions[i]->nrects, sizeof (short));
687 if (regions[i]->nrects > 0)
688 regions[i]->rects = (NhRect *)
689 alloc(sizeof (NhRect) * regions[i]->nrects);
690 for (j = 0; j < regions[i]->nrects; j++)
691 mread(fd, (genericptr_t) ®ions[i]->rects[j], sizeof (NhRect));
692 mread(fd, (genericptr_t) ®ions[i]->attach_2_u, sizeof (boolean));
693 mread(fd, (genericptr_t) ®ions[i]->attach_2_m, sizeof (unsigned));
695 mread(fd, (genericptr_t) &n, sizeof n);
697 msg_buf = (char *) alloc(n + 1);
698 mread(fd, (genericptr_t) msg_buf, n);
700 regions[i]->enter_msg = (const char *) msg_buf;
702 regions[i]->enter_msg = NULL;
704 mread(fd, (genericptr_t) &n, sizeof n);
706 msg_buf = (char *) alloc(n + 1);
707 mread(fd, (genericptr_t) msg_buf, n);
709 regions[i]->leave_msg = (const char *) msg_buf;
711 regions[i]->leave_msg = NULL;
713 mread(fd, (genericptr_t) ®ions[i]->ttl, sizeof (short));
714 /* check for expired region */
715 if (regions[i]->ttl >= 0)
717 (regions[i]->ttl > tmstamp) ? regions[i]->ttl - tmstamp : 0;
718 mread(fd, (genericptr_t) ®ions[i]->expire_f, sizeof (short));
719 mread(fd, (genericptr_t) ®ions[i]->can_enter_f, sizeof (short));
720 mread(fd, (genericptr_t) ®ions[i]->enter_f, sizeof (short));
721 mread(fd, (genericptr_t) ®ions[i]->can_leave_f, sizeof (short));
722 mread(fd, (genericptr_t) ®ions[i]->leave_f, sizeof (short));
723 mread(fd, (genericptr_t) ®ions[i]->inside_f, sizeof (short));
724 mread(fd, (genericptr_t) ®ions[i]->player_flags, sizeof (boolean));
725 if (ghostly) { /* settings pertained to old player */
726 clear_hero_inside(regions[i]);
727 clear_heros_fault(regions[i]);
729 mread(fd, (genericptr_t) ®ions[i]->n_monst, sizeof (short));
730 if (regions[i]->n_monst > 0)
731 regions[i]->monsters =
732 (unsigned *) alloc(sizeof (unsigned) * regions[i]->n_monst);
734 regions[i]->monsters = NULL;
735 regions[i]->max_monst = regions[i]->n_monst;
736 for (j = 0; j < regions[i]->n_monst; j++)
737 mread(fd, (genericptr_t) ®ions[i]->monsters[j],
739 mread(fd, (genericptr_t) ®ions[i]->visible, sizeof (boolean));
740 mread(fd, (genericptr_t) ®ions[i]->glyph, sizeof (int));
741 mread(fd, (genericptr_t) ®ions[i]->arg, sizeof (genericptr_t));
743 /* remove expired regions, do not trigger the expire_f callback (yet!);
744 also update monster lists if this data is coming from a bones file */
745 for (i = n_regions - 1; i >= 0; i--)
746 if (regions[i]->ttl == 0)
747 remove_region(regions[i]);
748 else if (ghostly && regions[i]->n_monst > 0)
749 reset_region_mids(regions[i]);
752 /* update monster IDs for region being loaded from bones; `ghostly' implied */
754 reset_region_mids(reg)
757 int i = 0, n = reg->n_monst;
758 unsigned *mid_list = reg->monsters;
761 if (!lookup_id_mapping(mid_list[i], &mid_list[i])) {
762 /* shrink list to remove missing monster; order doesn't matter */
763 mid_list[i] = mid_list[--n];
765 /* move on to next monster */
775 /*--------------------------------------------------------------*
777 * Create Region with just a message *
779 *--------------------------------------------------------------*/
782 create_msg_region(x, y, w, h, msg_enter, msg_leave)
785 const char *msg_enter;
786 const char *msg_leave;
789 NhRegion *reg = create_region((NhRect *) 0, 0);
791 reg->enter_msg = msg_enter;
792 reg->leave_msg = msg_leave;
797 add_rect_to_reg(reg, &tmprect);
803 /*--------------------------------------------------------------*
805 * Force Field Related Code *
807 *--------------------------------------------------------------*/
810 enter_force_field(p1, p2)
816 if (p2 == NULL) { /* That means the player */
818 You("bump into %s. Ouch!",
819 Hallucination ? "an invisible tree" :
820 "some kind of invisible wall");
824 mtmp = (struct monst *) p2;
826 pline("%s bumps into %s!", Monnam(mtmp), something);
832 create_force_field(x, y, radius, ttl)
841 ff = create_region((NhRect *) 0, 0);
845 tmprect.ly = y - (radius - 1);
846 tmprect.hy = y + (radius - 1);
847 for (i = 0; i < nrect; i++) {
848 add_rect_to_reg(ff, &tmprect);
855 if (!in_mklev && !flags.mon_moving)
856 set_heros_fault(ff); /* assume player has created it */
857 /* ff->can_enter_f = enter_force_field; */
858 /* ff->can_leave_f = enter_force_field; */
865 /*--------------------------------------------------------------*
867 * Gas cloud related code *
869 *--------------------------------------------------------------*/
872 * Here is an example of an expire function that may prolong
873 * region life after some mods...
876 expire_gas_cloud(p1, p2)
883 reg = (NhRegion *) p1;
884 damage = (int) reg->arg;
886 /* If it was a thick cloud, it dissipates a little first */
888 damage /= 2; /* It dissipates, let's do less damage */
889 reg->arg = (genericptr_t) damage;
890 reg->ttl = 2; /* Here's the trick : reset ttl */
891 return FALSE; /* THEN return FALSE, means "still there" */
893 return TRUE; /* OK, it's gone, you can free it! */
897 inside_gas_cloud(p1, p2)
905 reg = (NhRegion *) p1;
906 dam = (int) reg->arg;
907 if (p2 == NULL) { /* This means *YOU* Bozo! */
908 if (nonliving(youmonst.data) || Breathless)
911 make_blinded(1L, FALSE);
912 if (!Poison_resistance) {
913 pline("%s is burning your %s!", Something, makeplural(body_part(LUNG)));
914 You("cough and spit blood!");
915 losehp(rnd(dam) + 5, "gas cloud", KILLED_BY_AN);
921 } else { /* A monster is inside the cloud */
922 mtmp = (struct monst *) p2;
924 /* Non living and non breathing monsters are not concerned */
925 if (!nonliving(mtmp->data) && !breathless(mtmp->data)) {
926 if (cansee(mtmp->mx, mtmp->my))
927 pline("%s coughs!", Monnam(mtmp));
929 if (haseyes(mtmp->data) && mtmp->mcansee) {
933 if (resists_poison(mtmp))
935 mtmp->mhp -= rnd(dam) + 5;
936 if (mtmp->mhp <= 0) {
937 if (heros_fault(reg))
940 monkilled(mtmp, "gas cloud", AD_DRST);
941 if (mtmp->mhp <= 0) { /* not lifesaved */
947 return FALSE; /* Monster is still alive */
951 create_gas_cloud(x, y, radius, damage)
960 cloud = create_region((NhRect *) 0, 0);
964 tmprect.ly = y - (radius - 1);
965 tmprect.hy = y + (radius - 1);
966 for (i = 0; i < nrect; i++) {
967 add_rect_to_reg(cloud, &tmprect);
973 cloud->ttl = rn1(3,4);
974 if (!in_mklev && !flags.mon_moving)
975 set_heros_fault(cloud); /* assume player has created it */
976 cloud->inside_f = INSIDE_GAS_CLOUD;
977 cloud->expire_f = EXPIRE_GAS_CLOUD;
978 cloud->arg = (genericptr_t) damage;
979 cloud->visible = TRUE;
980 cloud->glyph = cmap_to_glyph(S_cloud);