From 2c9ab0da52cb2d96d55b6605a47ddee043b5b4ee Mon Sep 17 00:00:00 2001 From: jwalz Date: Sat, 5 Jan 2002 21:05:52 +0000 Subject: [PATCH] *** empty log message *** --- src/region.c | 944 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 944 insertions(+) create mode 100644 src/region.c diff --git a/src/region.c b/src/region.c new file mode 100644 index 000000000..9a215a850 --- /dev/null +++ b/src/region.c @@ -0,0 +1,944 @@ +/* SCCS Id: @(#)region.c 3.3 1999/12/29 */ +/* Copyright (c) 1996 by Jean-Christophe Collet */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "lev.h" + +/* + * This should really go into the level structure, but + * I'll start here for ease. It *WILL* move into the level + * structure eventually. + */ + +static NhRegion **regions; +static int n_regions = 0; +static int max_regions = 0; + +#define NO_CALLBACK (-1) + +boolean FDECL(inside_gas_cloud, (genericptr,genericptr)); +boolean FDECL(expire_gas_cloud, (genericptr,genericptr)); +boolean FDECL(inside_rect, (NhRect *,int,int)); +boolean FDECL(inside_region, (NhRegion *,int,int)); +NhRegion *FDECL(create_region, (NhRect *,int)); +void FDECL(add_rect_to_reg, (NhRegion *,NhRect *)); +void FDECL(add_mon_to_reg, (NhRegion *,struct monst *)); +void FDECL(remove_mon_from_reg, (NhRegion *,struct monst *)); +boolean FDECL(mon_in_region, (NhRegion *,struct monst *)); + +#if 0 +NhRegion *FDECL(clone_region, (NhRegion *)); +#endif +void FDECL(free_region, (NhRegion *)); +void FDECL(add_region, (NhRegion *)); +void FDECL(remove_region, (NhRegion *)); + +#if 0 +void FDECL(replace_mon_regions, (struct monst *,struct monst *)); +void FDECL(remove_mon_from_regions, (struct monst *)); +NhRegion *FDECL(create_msg_region, (XCHAR_P,XCHAR_P,XCHAR_P,XCHAR_P, + const char *,const char *)); +boolean FDECL(enter_force_field, (genericptr,genericptr)); +NhRegion *FDECL(create_force_field, (XCHAR_P,XCHAR_P,int,int)); +#endif + + +static callback_proc callbacks[] = { +#define INSIDE_GAS_CLOUD 0 + inside_gas_cloud, +#define EXPIRE_GAS_CLOUD 1 + expire_gas_cloud +}; + +/* Should be inlined. */ +boolean +inside_rect(r, x, y) +NhRect *r; +int x, y; +{ + return (x >= r->lx && x <= r->hx && y >= r->ly && y <= r->hy); +} + +/* + * Check if a point is inside a region. + */ +boolean +inside_region(reg, x, y) +NhRegion *reg; +int x, y; +{ + int i; + + if (reg == NULL || !inside_rect(&(reg->bounding_box), x, y)) + return FALSE; + for (i = 0; i < reg->nrects; i++) + if (inside_rect(&(reg->rects[i]), x, y)) + return TRUE; + return FALSE; +} + +/* + * Create a region. It does not activate it. + */ +NhRegion * +create_region(rects, nrect) +NhRect *rects; +int nrect; +{ + int i; + NhRegion *reg; + + reg = (NhRegion *) alloc(sizeof (NhRegion)); + /* Determines bounding box */ + if (nrect > 0) { + reg->bounding_box = rects[0]; + } else { + reg->bounding_box.lx = 99; + reg->bounding_box.ly = 99; + reg->bounding_box.hx = 0; + reg->bounding_box.hy = 0; + } + reg->nrects = nrect; + reg->rects = nrect > 0 ? (NhRect *)alloc((sizeof (NhRect)) * nrect) : NULL; + for (i = 0; i < nrect; i++) { + if (rects[i].lx < reg->bounding_box.lx) + reg->bounding_box.lx = rects[i].lx; + if (rects[i].ly < reg->bounding_box.ly) + reg->bounding_box.ly = rects[i].ly; + if (rects[i].hx > reg->bounding_box.hx) + reg->bounding_box.hx = rects[i].hx; + if (rects[i].hy > reg->bounding_box.hy) + reg->bounding_box.hy = rects[i].hy; + reg->rects[i] = rects[i]; + } + reg->ttl = -1; /* Defaults */ + reg->attach_2_u = FALSE; + reg->attach_2_m = 0; + /* reg->attach_2_o = NULL; */ + reg->enter_msg = NULL; + reg->leave_msg = NULL; + reg->expire_f = NO_CALLBACK; + reg->enter_f = NO_CALLBACK; + reg->can_enter_f = NO_CALLBACK; + reg->leave_f = NO_CALLBACK; + reg->can_leave_f = NO_CALLBACK; + reg->inside_f = NO_CALLBACK; + reg->player_inside = FALSE; + reg->n_monst = 0; + reg->max_monst = 0; + reg->monsters = NULL; + reg->arg = NULL; + return reg; +} + +/* + * Add rectangle to region. + */ +void +add_rect_to_reg(reg, rect) +NhRegion *reg; +NhRect *rect; +{ + NhRect *tmp_rect; + + tmp_rect = (NhRect *) alloc(sizeof (NhRect) * (reg->nrects + 1)); + if (reg->nrects > 0) { + (void) memcpy((genericptr_t) tmp_rect, (genericptr_t) reg->rects, + (sizeof (NhRect) * reg->nrects)); + free((genericptr_t) reg->rects); + } + tmp_rect[reg->nrects] = *rect; + reg->nrects++; + reg->rects = tmp_rect; + /* Update bounding box if needed */ + if (reg->bounding_box.lx > rect->lx) + reg->bounding_box.lx = rect->lx; + if (reg->bounding_box.ly > rect->ly) + reg->bounding_box.ly = rect->ly; + if (reg->bounding_box.hx < rect->hx) + reg->bounding_box.hx = rect->hx; + if (reg->bounding_box.hy < rect->hy) + reg->bounding_box.hy = rect->hy; +} + +/* + * Add a monster to the region + */ +void +add_mon_to_reg(reg, mon) +NhRegion *reg; +struct monst *mon; +{ + int i; + unsigned *tmp_m; + + if (reg->max_monst <= reg->n_monst) { + tmp_m = (unsigned *) + alloc(sizeof (unsigned) * (reg->max_monst + MONST_INC)); + if (reg->max_monst > 0) { + for (i = 0; i < reg->max_monst; i++) + tmp_m[i] = reg->monsters[i]; + free((genericptr_t) reg->monsters); + } + reg->monsters = tmp_m; + reg->max_monst += MONST_INC; + } + reg->monsters[reg->n_monst++] = mon->m_id; +} + +/* + * Remove a monster from the region list (it left or died...) + */ +void +remove_mon_from_reg(reg, mon) +NhRegion *reg; +struct monst *mon; +{ + register int i; + + for (i = 0; i < reg->n_monst; i++) + if (reg->monsters[i] == mon->m_id) { + reg->n_monst--; + reg->monsters[i] = reg->monsters[reg->n_monst]; + return; + } +} + +/* + * Check if a monster is inside the region. + * It's probably quicker to check with the region internal list + * than to check for coordinates. + */ +boolean +mon_in_region(reg, mon) +NhRegion *reg; +struct monst *mon; +{ + int i; + + for (i = 0; i < reg->n_monst; i++) + if (reg->monsters[i] == mon->m_id) + return TRUE; + return FALSE; +} + +#if 0 +/* not yet used */ + +/* + * Clone (make a standalone copy) the region. + */ +NhRegion * +clone_region(reg) +NhRegion *reg; +{ + NhRegion *ret_reg; + + ret_reg = create_region(reg->rects, reg->nrects); + ret_reg->ttl = reg->ttl; + ret_reg->attach_2_u = reg->attach_2_u; + ret_reg->attach_2_m = reg->attach_2_m; + /* ret_reg->attach_2_o = reg->attach_2_o; */ + ret_reg->expire_f = reg->expire_f; + ret_reg->enter_f = reg->enter_f; + ret_reg->can_enter_f = reg->can_enter_f; + ret_reg->leave_f = reg->leave_f; + ret_reg->can_leave_f = reg->can_leave_f; + ret_reg->player_inside = reg->player_inside; + ret_reg->n_monst = reg->n_monst; + if (reg->n_monst > 0) { + ret_reg->monsters = (unsigned *) + alloc((sizeof (unsigned)) * reg->n_monst); + (void) memcpy((genericptr_t) ret_reg->monsters, (genericptr_t) reg->monsters, + sizeof (unsigned) * reg->n_monst); + } else + ret_reg->monsters = NULL; + return ret_reg; +} + +#endif /*0*/ + +/* + * Free mem from region. + */ +void +free_region(reg) +NhRegion *reg; +{ + if (reg) { + if (reg->rects) + free((genericptr_t) reg->rects); + if (reg->monsters) + free((genericptr_t) reg->monsters); + free((genericptr_t) reg); + } +} + +/* + * Add a region to the list. + * This actually activates the region. + */ +void +add_region(reg) +NhRegion *reg; +{ + NhRegion **tmp_reg; + int i, j; + + if (max_regions <= n_regions) { + tmp_reg = regions; + regions = (NhRegion **)alloc(sizeof (NhRegion *) * (max_regions + 10)); + if (max_regions > 0) { + (void) memcpy((genericptr_t) regions, (genericptr_t) tmp_reg, + max_regions * sizeof (NhRegion *)); + free((genericptr_t) tmp_reg); + } + max_regions += 10; + } + regions[n_regions] = reg; + n_regions++; + /* Check for monsters inside the region */ + for (i = reg->bounding_box.lx; i <= reg->bounding_box.hx; i++) + for (j = reg->bounding_box.ly; j <= reg->bounding_box.hy; j++) { + /* Some regions can cross the level boundaries */ + if (!isok(i,j)) + continue; + if (MON_AT(i, j) && inside_region(reg, i, j)) + add_mon_to_reg(reg, level.monsters[i][j]); + if (reg->visible && cansee(i, j)) + newsym(i, j); + } + /* Check for player now... */ + reg->player_inside = inside_region(reg, u.ux, u.uy); +} + +/* + * Remove a region from the list & free it. + */ +void +remove_region(reg) +NhRegion *reg; +{ + register int i, x, y; + + for (i = 0; i < n_regions; i++) + if (regions[i] == reg) + break; + if (i == n_regions) + return; + + /* Update screen if necessary */ + if (reg->visible) + for (x = reg->bounding_box.lx; x <= reg->bounding_box.hx; x++) + for (y = reg->bounding_box.ly; y <= reg->bounding_box.hy; y++) + if (isok(x,y) && inside_region(reg, x, y) && cansee(x, y)) + newsym(x, y); + + free_region(reg); + regions[i] = regions[n_regions - 1]; + regions[n_regions - 1] = (NhRegion *) 0; + n_regions--; +} + +/* + * Remove all regions and clear all related data (This must be down + * when changing level, for instance). + */ +void +clear_regions() +{ + register int i; + + for (i = 0; i < n_regions; i++) + free_region(regions[i]); + n_regions = 0; + if (max_regions > 0) + free((genericptr_t) regions); + max_regions = 0; + regions = NULL; +} + +/* + * This function is called every turn. + * It makes the regions age, if necessary and calls the appropriate + * callbacks when needed. + */ +void +run_regions() +{ + register int i, j, k; + int f_indx; + + /* End of life ? */ + /* Do it backward because the array will be modified */ + for (i = n_regions - 1; i >= 0; i--) { + if (regions[i]->ttl == 0) { + if ((f_indx = regions[i]->expire_f) == NO_CALLBACK || + (*callbacks[f_indx])(regions[i], (genericptr_t) 0)) + remove_region(regions[i]); + } + } + + /* Process remaining regions */ + for (i = 0; i < n_regions; i++) { + /* Make the region age */ + if (regions[i]->ttl > 0) + regions[i]->ttl--; + /* Check if player is inside region */ + f_indx = regions[i]->inside_f; + if (f_indx != NO_CALLBACK && regions[i]->player_inside) + (void) (*callbacks[f_indx])(regions[i], (genericptr_t) 0); + /* Check if any monster is inside region */ + if (f_indx != NO_CALLBACK) { + for (j = 0; j < regions[i]->n_monst; j++) { + struct monst *mtmp = find_mid(regions[i]->monsters[j], FM_FMON); + + if (!mtmp || mtmp->mhp <= 0 || + (*callbacks[f_indx])(regions[i], mtmp)) { + /* The monster died, remove it from list */ + k = (regions[i]->n_monst -= 1); + regions[i]->monsters[j] = regions[i]->monsters[k]; + regions[i]->monsters[k] = 0; + --j; /* current slot has been reused; recheck it next */ + } + } + } + } +} + +/* + * check whether player enters/leaves one or more regions. + */ +boolean +in_out_region(x, y) +xchar + x, y; +{ + int i, f_indx; + + /* First check if we can do the move */ + for (i = 0; i < n_regions; i++) { + if (inside_region(regions[i], x, y) + && !regions[i]->player_inside && !regions[i]->attach_2_u) { + if ((f_indx = regions[i]->can_enter_f) != NO_CALLBACK) + if (!(*callbacks[f_indx])(regions[i], (genericptr_t) 0)) + return FALSE; + } else + if (regions[i]->player_inside + && !inside_region(regions[i], x, y) + && !regions[i]->attach_2_u) { + if ((f_indx = regions[i]->can_leave_f) != NO_CALLBACK) + if (!(*callbacks[f_indx])(regions[i], (genericptr_t) 0)) + return FALSE; + } + } + + /* Callbacks for the regions we do leave */ + for (i = 0; i < n_regions; i++) + if (regions[i]->player_inside && + !regions[i]->attach_2_u && !inside_region(regions[i], x, y)) { + regions[i]->player_inside = FALSE; + if (regions[i]->leave_msg != NULL) + pline(regions[i]->leave_msg); + if ((f_indx = regions[i]->leave_f) != NO_CALLBACK) + (void) (*callbacks[f_indx])(regions[i], (genericptr_t) 0); + } + + /* Callbacks for the regions we do enter */ + for (i = 0; i < n_regions; i++) + if (!regions[i]->player_inside && + !regions[i]->attach_2_u && inside_region(regions[i], x, y)) { + regions[i]->player_inside = TRUE; + if (regions[i]->enter_msg != NULL) + pline(regions[i]->enter_msg); + if ((f_indx = regions[i]->enter_f) != NO_CALLBACK) + (void) (*callbacks[f_indx])(regions[i], (genericptr_t) 0); + } + return TRUE; +} + +/* + * check wether a monster enters/leaves one or more region. +*/ +boolean +m_in_out_region(mon, x, y) +struct monst *mon; +xchar x, y; +{ + int i, f_indx; + + /* First check if we can do the move */ + for (i = 0; i < n_regions; i++) { + if (inside_region(regions[i], x, y) && + !mon_in_region(regions[i], mon) && + regions[i]->attach_2_m != mon->m_id) { + if ((f_indx = regions[i]->can_enter_f) != NO_CALLBACK) + if (!(*callbacks[f_indx])(regions[i], mon)) + return FALSE; + } else if (mon_in_region(regions[i], mon) && + !inside_region(regions[i], x, y) && + regions[i]->attach_2_m != mon->m_id) { + if ((f_indx = regions[i]->can_leave_f) != NO_CALLBACK) + if (!(*callbacks[f_indx])(regions[i], mon)) + return FALSE; + } + } + + /* Callbacks for the regions we do leave */ + for (i = 0; i < n_regions; i++) + if (mon_in_region(regions[i], mon) && + regions[i]->attach_2_m != mon->m_id && + !inside_region(regions[i], x, y)) { + remove_mon_from_reg(regions[i], mon); + if ((f_indx = regions[i]->leave_f) != NO_CALLBACK) + (void) (*callbacks[f_indx])(regions[i], mon); + } + + /* Callbacks for the regions we do enter */ + for (i = 0; i < n_regions; i++) + if (!regions[i]->player_inside && + !regions[i]->attach_2_u && inside_region(regions[i], x, y)) { + add_mon_to_reg(regions[i], mon); + if ((f_indx = regions[i]->enter_f) != NO_CALLBACK) + (void) (*callbacks[f_indx])(regions[i], mon); + } + return TRUE; +} + +/* + * Checks player's regions after a teleport for instance. + */ +void +update_player_regions() +{ + register int i; + + for (i = 0; i < n_regions; i++) + if (!regions[i]->attach_2_u) + regions[i]->player_inside = inside_region(regions[i], u.ux, u.uy); + else + regions[i]->player_inside = FALSE; +} + +/* + * Ditto for a specified monster. + */ +void +update_monster_region(mon) +struct monst *mon; +{ + register int i; + + for (i = 0; i < n_regions; i++) { + if (inside_region(regions[i], mon->mx, mon->my)) { + if (!mon_in_region(regions[i], mon)) + add_mon_to_reg(regions[i], mon); + } else { + if (mon_in_region(regions[i], mon)) + remove_mon_from_reg(regions[i], mon); + } + } +} + +#if 0 +/* not yet used */ + +/* + * Change monster pointer in regions + * This happens, for instance, when a monster grows and + * need a new structure (internally that is). + */ +void +replace_mon_regions(monold, monnew) +struct monst *monold, *monnew; +{ + register int i; + + for (i = 0; i < n_regions; i++) + if (mon_in_region(regions[i], monold)) { + remove_mon_from_reg(regions[i], monold); + add_mon_to_reg(regions[i], monnew); + } +} + +/* + * Remove monster from all regions it was in (ie monster just died) + */ +void +remove_mon_from_regions(mon) +struct monst *mon; +{ + register int i; + + for (i = 0; i < n_regions; i++) + if (mon_in_region(regions[i], mon)) + remove_mon_from_reg(regions[i], mon); +} + +#endif /*0*/ + +/* + * Check if a spot is under a visible region (eg: gas cloud). + * Returns NULL if not, otherwise returns region. + */ +NhRegion * +visible_region_at(x, y) +xchar x, y; +{ + register int i; + + for (i = 0; i < n_regions; i++) + if (inside_region(regions[i], x, y) && regions[i]->visible && + regions[i]->ttl != 0) + return regions[i]; + return (NhRegion *) 0; +} + +void +show_region(reg, x, y) +NhRegion *reg; +xchar x, y; +{ + show_glyph(x, y, reg->glyph); +} + +/** + * save_regions : + */ +void +save_regions(fd, mode) +int fd; +int mode; +{ + int i, j; + unsigned n; + + if (!perform_bwrite(mode)) goto skip_lots; + + bwrite(fd, (genericptr_t) &moves, sizeof (moves)); /* timestamp */ + bwrite(fd, (genericptr_t) &n_regions, sizeof (n_regions)); + for (i = 0; i < n_regions; i++) { + bwrite(fd, (genericptr_t) ®ions[i]->bounding_box, sizeof (NhRect)); + bwrite(fd, (genericptr_t) ®ions[i]->nrects, sizeof (short)); + for (j = 0; j < regions[i]->nrects; j++) + bwrite(fd, (genericptr_t) ®ions[i]->rects[j], sizeof (NhRect)); + bwrite(fd, (genericptr_t) ®ions[i]->attach_2_u, sizeof (boolean)); + n = 0; + bwrite(fd, (genericptr_t) ®ions[i]->attach_2_m, sizeof (unsigned)); + n = regions[i]->enter_msg != NULL ? strlen(regions[i]->enter_msg) : 0; + bwrite(fd, (genericptr_t) &n, sizeof n); + if (n > 0) + bwrite(fd, (genericptr_t) regions[i]->enter_msg, n); + n = regions[i]->leave_msg != NULL ? strlen(regions[i]->leave_msg) : 0; + bwrite(fd, (genericptr_t) &n, sizeof n); + if (n > 0) + bwrite(fd, (genericptr_t) regions[i]->leave_msg, n); + bwrite(fd, (genericptr_t) ®ions[i]->ttl, sizeof (short)); + bwrite(fd, (genericptr_t) ®ions[i]->expire_f, sizeof (short)); + bwrite(fd, (genericptr_t) ®ions[i]->can_enter_f, sizeof (short)); + bwrite(fd, (genericptr_t) ®ions[i]->enter_f, sizeof (short)); + bwrite(fd, (genericptr_t) ®ions[i]->can_leave_f, sizeof (short)); + bwrite(fd, (genericptr_t) ®ions[i]->leave_f, sizeof (short)); + bwrite(fd, (genericptr_t) ®ions[i]->inside_f, sizeof (short)); + bwrite(fd, (genericptr_t) ®ions[i]->player_inside, sizeof (boolean)); + bwrite(fd, (genericptr_t) ®ions[i]->n_monst, sizeof (short)); + for (j = 0; j < regions[i]->n_monst; j++) + bwrite(fd, (genericptr_t) ®ions[i]->monsters[j], + sizeof (unsigned)); + bwrite(fd, (genericptr_t) ®ions[i]->visible, sizeof (boolean)); + bwrite(fd, (genericptr_t) ®ions[i]->glyph, sizeof (int)); + bwrite(fd, (genericptr_t) ®ions[i]->arg, sizeof (genericptr_t)); + } + +skip_lots: + if (release_data(mode)) + clear_regions(); +} + +void +rest_regions(fd) +int fd; +{ + int i, j; + unsigned n; + long tmstamp; + char *msg_buf; + + clear_regions(); /* Just for security */ + mread(fd, (genericptr_t) &tmstamp, sizeof (tmstamp)); + tmstamp = (moves - tmstamp); + mread(fd, (genericptr_t) &n_regions, sizeof (n_regions)); + max_regions = n_regions; + if (n_regions > 0) + regions = (NhRegion **) alloc(sizeof (NhRegion *) * n_regions); + for (i = 0; i < n_regions; i++) { + regions[i] = (NhRegion *) alloc(sizeof (NhRegion)); + mread(fd, (genericptr_t) ®ions[i]->bounding_box, sizeof (NhRect)); + mread(fd, (genericptr_t) ®ions[i]->nrects, sizeof (short)); + + if (regions[i]->nrects > 0) + regions[i]->rects = (NhRect *) + alloc(sizeof (NhRect) * regions[i]->nrects); + for (j = 0; j < regions[i]->nrects; j++) + mread(fd, (genericptr_t) ®ions[i]->rects[j], sizeof (NhRect)); + mread(fd, (genericptr_t) ®ions[i]->attach_2_u, sizeof (boolean)); + mread(fd, (genericptr_t) ®ions[i]->attach_2_m, sizeof (unsigned)); + + mread(fd, (genericptr_t) &n, sizeof n); + if (n > 0) { + msg_buf = (char *) alloc(n + 1); + mread(fd, (genericptr_t) msg_buf, n); + msg_buf[n] = '\0'; + regions[i]->enter_msg = (const char *) msg_buf; + } else + regions[i]->enter_msg = NULL; + + mread(fd, (genericptr_t) &n, sizeof n); + if (n > 0) { + msg_buf = (char *) alloc(n + 1); + mread(fd, (genericptr_t) msg_buf, n); + msg_buf[n] = '\0'; + regions[i]->leave_msg = (const char *) msg_buf; + } else + regions[i]->leave_msg = NULL; + + mread(fd, (genericptr_t) ®ions[i]->ttl, sizeof (short)); + /* check for expired region */ + if (regions[i]->ttl >= 0) + regions[i]->ttl = + (regions[i]->ttl > tmstamp) ? regions[i]->ttl - tmstamp : 0; + mread(fd, (genericptr_t) ®ions[i]->expire_f, sizeof (short)); + mread(fd, (genericptr_t) ®ions[i]->can_enter_f, sizeof (short)); + mread(fd, (genericptr_t) ®ions[i]->enter_f, sizeof (short)); + mread(fd, (genericptr_t) ®ions[i]->can_leave_f, sizeof (short)); + mread(fd, (genericptr_t) ®ions[i]->leave_f, sizeof (short)); + mread(fd, (genericptr_t) ®ions[i]->inside_f, sizeof (short)); + mread(fd, (genericptr_t) ®ions[i]->player_inside, sizeof (boolean)); + mread(fd, (genericptr_t) ®ions[i]->n_monst, sizeof (short)); + if (regions[i]->n_monst > 0) + regions[i]->monsters = + (unsigned *) alloc(sizeof (unsigned) * regions[i]->n_monst); + else + regions[i]->monsters = NULL; + regions[i]->max_monst = regions[i]->n_monst; + for (j = 0; j < regions[i]->n_monst; j++) + mread(fd, (genericptr_t) ®ions[i]->monsters[j], + sizeof (unsigned)); + mread(fd, (genericptr_t) ®ions[i]->visible, sizeof (boolean)); + mread(fd, (genericptr_t) ®ions[i]->glyph, sizeof (int)); + mread(fd, (genericptr_t) ®ions[i]->arg, sizeof (genericptr_t)); + } + /* remove expired regions, do not trigger the expire_f callback (yet!) */ + for (i = n_regions - 1; i >= 0; i--) + if (regions[i]->ttl == 0) + remove_region(regions[i]); +} + +#if 0 +/* not yet used */ + +/*--------------------------------------------------------------* + * * + * Create Region with just a message * + * * + *--------------------------------------------------------------*/ + +NhRegion * +create_msg_region(x, y, w, h, msg_enter, msg_leave) +xchar x, y; +xchar w, h; +const char *msg_enter; +const char *msg_leave; +{ + NhRect tmprect; + NhRegion *reg = create_region((NhRect *) 0, 0); + + reg->enter_msg = msg_enter; + reg->leave_msg = msg_leave; + tmprect.lx = x; + tmprect.ly = y; + tmprect.hx = x + w; + tmprect.hy = y + h; + add_rect_to_reg(reg, &tmprect); + reg->ttl = -1; + return reg; +} + + +/*--------------------------------------------------------------* + * * + * Force Field Related Code * + * (unused yet) * + *--------------------------------------------------------------*/ + +boolean +enter_force_field(p1, p2) +genericptr_t p1; +genericptr_t p2; +{ + struct monst *mtmp; + + if (p2 == NULL) { /* That means the player */ + if (!Blind) + You("bump into %s. Ouch!", + Hallucination ? "an invisible tree" : + "some kind of invisible wall"); + else + pline("Ouch!"); + } else { + mtmp = (struct monst *) p2; + if (canseemon(mtmp)) + pline("%s bumps into %s!", Monnam(mtmp), something); + } + return FALSE; +} + +NhRegion * +create_force_field(x, y, radius, ttl) +xchar x, y; +int radius, ttl; +{ + int i; + NhRegion *ff; + int nrect; + NhRect tmprect; + + ff = create_region((NhRect *) 0, 0); + nrect = radius; + tmprect.lx = x; + tmprect.hx = x; + tmprect.ly = y - (radius - 1); + tmprect.hy = y + (radius - 1); + for (i = 0; i < nrect; i++) { + add_rect_to_reg(ff, &tmprect); + tmprect.lx--; + tmprect.hx++; + tmprect.ly++; + tmprect.hy--; + } + ff->ttl = ttl; + /* ff->can_enter_f = enter_force_field; */ + /* ff->can_leave_f = enter_force_field; */ + add_region(ff); + return ff; +} + +#endif /*0*/ + +/*--------------------------------------------------------------* + * * + * Gas cloud related code * + * * + *--------------------------------------------------------------*/ + +/* + * Here is an example of an expire function that may prolong + * region life after some mods... + */ +boolean +expire_gas_cloud(p1, p2) +genericptr_t p1; +genericptr_t p2; +{ + NhRegion *reg; + int damage; + + reg = (NhRegion *) p1; + damage = (int) reg->arg; + + /* If it was a thick cloud, it dissipates a little first */ + if (damage >= 5) { + damage /= 2; /* It dissipates, let's do less damage */ + reg->arg = (genericptr_t) damage; + reg->ttl = 2; /* Here's the trick : reset ttl */ + return FALSE; /* THEN return FALSE, means "still there" */ + } + return TRUE; /* OK, it's gone, you can free it! */ +} + +boolean +inside_gas_cloud(p1, p2) +genericptr_t p1; +genericptr_t p2; +{ + NhRegion *reg; + struct monst *mtmp; + int dam; + + reg = (NhRegion *) p1; + dam = (int) reg->arg; + if (p2 == NULL) { /* This means *YOU* Bozo! */ + if (nonliving(youmonst.data) || Breathless) + return FALSE; + if (!Blind) + make_blinded(1L, FALSE); + if (!Poison_resistance) { + pline("%s is burning your %s!", Something, makeplural(body_part(LUNG))); + You("cough and spit blood!"); + losehp(rnd(dam) + 5, "gas cloud", KILLED_BY_AN); + return FALSE; + } else { + You("cough!"); + return FALSE; + } + } else { /* A monster is inside the cloud */ + mtmp = (struct monst *) p2; + + /* Non living and non breathing monsters are not concerned */ + if (!nonliving(mtmp->data) && !breathless(mtmp->data)) { + if (cansee(mtmp->mx, mtmp->my)) + pline("%s coughs!", Monnam(mtmp)); + setmangry(mtmp); + if (haseyes(mtmp->data) && mtmp->mcansee) { + mtmp->mblinded = 1; + mtmp->mcansee = 0; + } + if (resists_poison(mtmp)) + return FALSE; + mtmp->mhp -= rnd(dam) + 5; + if (mtmp->mhp <= 0) { + killed(mtmp); + if (mtmp->mhp <= 0) { /* not lifesaved */ + return TRUE; + } + } + } + } + return FALSE; /* Monster is still alive */ +} + +NhRegion * +create_gas_cloud(x, y, radius, damage) +xchar x, y; +int radius; +int damage; +{ + NhRegion *cloud; + int i, nrect; + NhRect tmprect; + + cloud = create_region((NhRect *) 0, 0); + nrect = radius; + tmprect.lx = x; + tmprect.hx = x; + tmprect.ly = y - (radius - 1); + tmprect.hy = y + (radius - 1); + for (i = 0; i < nrect; i++) { + add_rect_to_reg(cloud, &tmprect); + tmprect.lx--; + tmprect.hx++; + tmprect.ly++; + tmprect.hy--; + } + cloud->ttl = rn1(3,4); + cloud->inside_f = INSIDE_GAS_CLOUD; + cloud->expire_f = EXPIRE_GAS_CLOUD; + cloud->arg = (genericptr_t) damage; + cloud->visible = TRUE; + cloud->glyph = cmap_to_glyph(S_cloud); + add_region(cloud); + return cloud; +} + +/*region.c*/