Source:Region.c

Below is the full text to src/region.c from NetHack 3.4.3. To link to a particular line, write [[region.c#line123 ]], for example. 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. */ 4.

5.   #include "hack.h"  6.    #include "lev.h"  7. 8.   /*  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. 12.   */  13.    14.   static NhRegion **regions; 15.  static int n_regions = 0; 16.  static int max_regions = 0; 17.   18.   #define NO_CALLBACK (-1) 19.   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 *)); 29.   30.   #if 0 31.  NhRegion *FDECL(clone_region, (NhRegion *)); 32.  #endif 33.  void FDECL(free_region, (NhRegion *)); 34.  void FDECL(add_region, (NhRegion *)); 35.  void FDECL(remove_region, (NhRegion *)); 36.   37.   #if 0 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)); 44.  #endif 45.   46.   static void FDECL(reset_region_mids, (NhRegion *)); 47.   48.   static callback_proc callbacks[] = { 49.  #define INSIDE_GAS_CLOUD 0 50.      inside_gas_cloud, 51.  #define EXPIRE_GAS_CLOUD 1 52.      expire_gas_cloud 53.  };  54.    55.   /* Should be inlined. */ 56.   boolean 57.  inside_rect(r, x, y)  58. NhRect *r; 59.  int x, y;  60. { 61.       return (x >= r->lx && x <= r->hx && y >= r->ly && y <= r->hy); 62.  }  63.    64.   /*  65.    * Check if a point is inside a region. 66.   */  67.   boolean 68.  inside_region(reg, x, y)  69. NhRegion *reg; 70.  int x, y;  71. { 72.       int i;  73. 74.      if (reg == NULL || !inside_rect(&(reg->bounding_box), x, y)) 75.  	return FALSE; 76.      for (i = 0; i < reg->nrects; i++) 77.  	if (inside_rect(&(reg->rects[i]), x, y)) 78.  	    return TRUE; 79.      return FALSE; 80.  }  81.    82.   /*  83.    * Create a region. It does not activate it. 84.   */  85.   NhRegion * 86.  create_region(rects, nrect) 87.  NhRect *rects; 88.  int nrect; 89.  {  90.       int i;  91. NhRegion *reg; 92.   93.       reg = (NhRegion *) alloc(sizeof (NhRegion)); 94.      /* Determines bounding box */ 95.      if (nrect > 0) { 96.  	reg->bounding_box = rects[0]; 97.      } else { 98.  	reg->bounding_box.lx = 99; 99.  	reg->bounding_box.ly = 99; 100. 	reg->bounding_box.hx = 0; 101. 	reg->bounding_box.hy = 0; 102.     }  103.      reg->nrects = nrect; 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]; 115.     }  116.      reg->ttl = -1;		/* Defaults */ 117.     reg->attach_2_u = FALSE; 118.     reg->attach_2_m = 0; 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); 130.     reg->n_monst = 0; 131.     reg->max_monst = 0; 132.     reg->monsters = NULL; 133.     reg->arg = NULL; 134.     return reg; 135. }  136.   137.  /*  138.   * Add rectangle to region. 139.  */  140.  void 141. add_rect_to_reg(reg, rect) 142. NhRegion *reg; 143. NhRect *rect; 144. {  145.      NhRect *tmp_rect; 146.  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); 152.     }  153.      tmp_rect[reg->nrects] = *rect; 154.     reg->nrects++; 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; 165. }  166.   167.  /*  168.   * Add a monster to the region 169.  */  170.  void 171. add_mon_to_reg(reg, mon) 172. NhRegion *reg; 173. struct monst *mon; 174. {  175.      int i;  176. unsigned *tmp_m; 177.  178.      if (reg->max_monst <= reg->n_monst) { 179. 	tmp_m = (unsigned *) 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); 185. 	}  186.  	reg->monsters = tmp_m; 187. 	reg->max_monst += MONST_INC; 188.     }  189.      reg->monsters[reg->n_monst++] = mon->m_id; 190. }  191.   192.  /*  193.   * Remove a monster from the region list (it left or died...) 194.  */  195.  void 196. remove_mon_from_reg(reg, mon) 197. NhRegion *reg; 198. struct monst *mon; 199. {  200.      register int i;  201. 202.     for (i = 0; i < reg->n_monst; i++) 203. 	if (reg->monsters[i] == mon->m_id) { 204. 	    reg->n_monst--; 205. 	    reg->monsters[i] = reg->monsters[reg->n_monst]; 206. 	    return; 207. 	}  208.  }  209.   210.  /*  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. 214.  */  215.  boolean 216. mon_in_region(reg, mon) 217. NhRegion *reg; 218. struct monst *mon; 219. {  220.      int i;  221. 222.     for (i = 0; i < reg->n_monst; i++) 223. 	if (reg->monsters[i] == mon->m_id) 224. 	    return TRUE; 225.     return FALSE; 226. }  227.   228.  #if 0 229. /* not yet used */ 230.  231.  /*  232.   * Clone (make a standalone copy) the region. 233.  */  234.  NhRegion * 235. clone_region(reg) 236. NhRegion *reg; 237. {  238.      NhRegion *ret_reg; 239.  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); 257.     } else 258. 	ret_reg->monsters = NULL; 259.     return ret_reg; 260. }  261.   262.  #endif	/*0*/ 263.  264.  /*  265.   * Free mem from region. 266.  */  267.  void 268. free_region(reg) 269. NhRegion *reg; 270. {  271.      if (reg) { 272. 	if (reg->rects) 273. 	    free((genericptr_t) reg->rects); 274. 	if (reg->monsters) 275. 	    free((genericptr_t) reg->monsters); 276. 	free((genericptr_t) reg); 277.     }  278.  }  279.   280.  /*  281.   * Add a region to the list. 282.  * This actually activates the region. 283.  */  284.  void 285. add_region(reg) 286. NhRegion *reg; 287. {  288.      NhRegion **tmp_reg; 289.     int i, j;  290. 291.     if (max_regions <= n_regions) { 292. 	tmp_reg = 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); 298. 	}  299.  	max_regions += 10; 300.     }  301.      regions[n_regions] = reg; 302.     n_regions++; 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 */ 307. 	    if (!isok(i,j)) 308. 		continue; 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)) 312. 		newsym(i, j); 313. 	}  314.      /* Check for player now... */ 315.      if (inside_region(reg, u.ux, u.uy)) 316. 	set_hero_inside(reg); 317.     else 318. 	clear_hero_inside(reg); 319. }  320.   321.  /*  322.   * Remove a region from the list & free it. 323.  */  324.  void 325. remove_region(reg) 326. NhRegion *reg; 327. {  328.      register int i, x, y;  329. 330.     for (i = 0; i < n_regions; i++) 331. 	if (regions[i] == reg) 332. 	    break; 333.     if (i == n_regions) 334. 	return; 335.  336.      /* Update screen if necessary */ 337.     if (reg->visible) 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)) 341. 		    newsym(x, y); 342.  343.      free_region(reg); 344.     regions[i] = regions[n_regions - 1]; 345.     regions[n_regions - 1] = (NhRegion *) 0; 346.     n_regions--; 347. }  348.   349.  /*  350.   * Remove all regions and clear all related data (This must be down  351.   * when changing level, for instance). 352.  */  353.  void 354. clear_regions 355. {  356.      register int i;  357. 358.     for (i = 0; i < n_regions; i++) 359. 	free_region(regions[i]); 360.     n_regions = 0; 361.     if (max_regions > 0) 362. 	free((genericptr_t) regions); 363.     max_regions = 0; 364.     regions = NULL; 365. }  366.   367.  /*  368.   * This function is called every turn. 369.  * It makes the regions age, if necessary and calls the appropriate 370.  * callbacks when needed. 371.  */  372.  void 373. run_regions 374. {  375.      register int i, j, k;  376. int f_indx; 377.  378.      /* End of life ? */ 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]); 385. 	}  386.      }  387.   388.      /* Process remaining regions */ 389.     for (i = 0; i < n_regions; i++) { 390. 	/* Make the region age */ 391. 	if (regions[i]->ttl > 0) 392. 	    regions[i]->ttl--; 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); 401.  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 */ 409. 		}  410.  	    }  411.  	}  412.      }  413.  }  414.   415.  /*  416.   * check whether player enters/leaves one or more regions. 417.  */  418.  boolean 419. in_out_region(x, y)  420. xchar 421.     x, y;  422. { 423.      int i, f_indx; 424.  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)) 431. 		    return FALSE; 432. 	} else 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)) 438. 		    return FALSE; 439. 	}  440.      }  441.   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); 451. 	}  452.   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); 462. 	}  463.      return TRUE; 464. }  465.   466.  /*  467.   * check wether a monster enters/leaves one or more region. 468. */  469.  boolean 470. m_in_out_region(mon, x, y)  471. struct monst *mon; 472. xchar x, y;  473. { 474.      int i, f_indx; 475.  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)) 483. 		    return FALSE; 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)) 489. 		    return FALSE; 490. 	}  491.      }  492.   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); 501. 	}  502.   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); 510. 	}  511.      return TRUE; 512. }  513.   514.  /*  515.   * Checks player's regions after a teleport for instance. 516.  */  517.  void 518. update_player_regions 519. {  520.      register int i;  521. 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]); 525. 	else 526. 	    clear_hero_inside(regions[i]); 527. }  528.   529.  /*  530.   * Ditto for a specified monster. 531.  */  532.  void 533. update_monster_region(mon) 534. struct monst *mon; 535. {  536.      register int i;  537. 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); 542. 	} else { 543. 	    if (mon_in_region(regions[i], mon)) 544. 		remove_mon_from_reg(regions[i], mon); 545. 	}  546.      }  547.  }  548.   549.  #if 0 550. /* not yet used */ 551.  552.  /*  553.   * Change monster pointer in regions 554.  * This happens, for instance, when a monster grows and 555.  * need a new structure (internally that is). 556.  */  557.  void 558. replace_mon_regions(monold, monnew) 559. struct monst *monold, *monnew; 560. {  561.      register int i;  562. 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); 567. 	}  568.  }  569.   570.  /*  571.   * Remove monster from all regions it was in (ie monster just died) 572.  */  573.  void 574. remove_mon_from_regions(mon) 575. struct monst *mon; 576. {  577.      register int i;  578. 579.     for (i = 0; i < n_regions; i++) 580. 	if (mon_in_region(regions[i], mon)) 581. 	    remove_mon_from_reg(regions[i], mon); 582. }  583.   584.  #endif	/*0*/ 585.  586.  /*  587.   * Check if a spot is under a visible region (eg: gas cloud). 588.  * Returns NULL if not, otherwise returns region. 589.  */  590.  NhRegion * 591. visible_region_at(x, y)  592. xchar x, y; 593. { 594.      register int i;  595. 596.     for (i = 0; i < n_regions; i++) 597. 	if (inside_region(regions[i], x, y) && regions[i]->visible &&  598.  		regions[i]->ttl != 0) 599. 	    return regions[i]; 600.     return (NhRegion *) 0; 601. }  602.   603.  void 604. show_region(reg, x, y)  605. NhRegion *reg; 606. xchar x, y;  607. { 608.      show_glyph(x, y, reg->glyph); 609. }  610.   611.  /**  612.   * save_regions : 613.  */  614.  void 615. save_regions(fd, mode) 616. int fd; 617. int mode; 618. {  619.      int i, j;  620. unsigned n; 621. 622.     if (!perform_bwrite(mode)) goto skip_lots; 623.  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) &regions[i]->bounding_box, sizeof (NhRect)); 628. 	bwrite(fd, (genericptr_t) &regions[i]->nrects, sizeof (short)); 629. 	for (j = 0; j < regions[i]->nrects; j++) 630. 	    bwrite(fd, (genericptr_t) &regions[i]->rects[j], sizeof (NhRect)); 631. 	bwrite(fd, (genericptr_t) &regions[i]->attach_2_u, sizeof (boolean)); 632. 	n = 0; 633. 	bwrite(fd, (genericptr_t) &regions[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); 636. 	if (n > 0) 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); 640. 	if (n > 0) 641. 	    bwrite(fd, (genericptr_t) regions[i]->leave_msg, n); 642. 	bwrite(fd, (genericptr_t) &regions[i]->ttl, sizeof (short)); 643. 	bwrite(fd, (genericptr_t) &regions[i]->expire_f, sizeof (short)); 644. 	bwrite(fd, (genericptr_t) &regions[i]->can_enter_f, sizeof (short)); 645. 	bwrite(fd, (genericptr_t) &regions[i]->enter_f, sizeof (short)); 646. 	bwrite(fd, (genericptr_t) &regions[i]->can_leave_f, sizeof (short)); 647. 	bwrite(fd, (genericptr_t) &regions[i]->leave_f, sizeof (short)); 648. 	bwrite(fd, (genericptr_t) &regions[i]->inside_f, sizeof (short)); 649. 	bwrite(fd, (genericptr_t) &regions[i]->player_flags, sizeof (boolean)); 650. 	bwrite(fd, (genericptr_t) &regions[i]->n_monst, sizeof (short)); 651. 	for (j = 0; j < regions[i]->n_monst; j++) 652. 	    bwrite(fd, (genericptr_t) &regions[i]->monsters[j],  653.  	     sizeof (unsigned)); 654. 	bwrite(fd, (genericptr_t) &regions[i]->visible, sizeof (boolean)); 655. 	bwrite(fd, (genericptr_t) &regions[i]->glyph, sizeof (int)); 656. 	bwrite(fd, (genericptr_t) &regions[i]->arg, sizeof (genericptr_t)); 657.     }  658.   659.  skip_lots: 660.     if (release_data(mode)) 661. 	clear_regions; 662. }  663.   664.  void 665. rest_regions(fd, ghostly) 666. int fd; 667. boolean ghostly; /* If a bones file restore */ 668. {  669.      int i, j;  670. unsigned n; 671. long tmstamp; 672.     char *msg_buf; 673.  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; 680.     if (n_regions > 0) 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) &regions[i]->bounding_box, sizeof (NhRect)); 685. 	mread(fd, (genericptr_t) &regions[i]->nrects, sizeof (short)); 686.  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) &regions[i]->rects[j], sizeof (NhRect)); 692. 	mread(fd, (genericptr_t) &regions[i]->attach_2_u, sizeof (boolean)); 693. 	mread(fd, (genericptr_t) &regions[i]->attach_2_m, sizeof (unsigned)); 694.  695.  	mread(fd, (genericptr_t) &n, sizeof n); 696. 	if (n > 0) { 697. 	    msg_buf = (char *) alloc(n + 1); 698. 	    mread(fd, (genericptr_t) msg_buf, n); 699. 	    msg_buf[n] = '\0'; 700. 	    regions[i]->enter_msg = (const char *) msg_buf; 701. 	} else 702. 	    regions[i]->enter_msg = NULL; 703.  704.  	mread(fd, (genericptr_t) &n, sizeof n); 705. 	if (n > 0) { 706. 	    msg_buf = (char *) alloc(n + 1); 707. 	    mread(fd, (genericptr_t) msg_buf, n); 708. 	    msg_buf[n] = '\0'; 709. 	    regions[i]->leave_msg = (const char *) msg_buf; 710. 	} else 711. 	    regions[i]->leave_msg = NULL; 712.  713.  	mread(fd, (genericptr_t) &regions[i]->ttl, sizeof (short)); 714. 	/* check for expired region */ 715. 	if (regions[i]->ttl >= 0) 716. 	    regions[i]->ttl = 717. 		(regions[i]->ttl > tmstamp) ? regions[i]->ttl - tmstamp : 0; 718. 	mread(fd, (genericptr_t) &regions[i]->expire_f, sizeof (short)); 719. 	mread(fd, (genericptr_t) &regions[i]->can_enter_f, sizeof (short)); 720. 	mread(fd, (genericptr_t) &regions[i]->enter_f, sizeof (short)); 721. 	mread(fd, (genericptr_t) &regions[i]->can_leave_f, sizeof (short)); 722. 	mread(fd, (genericptr_t) &regions[i]->leave_f, sizeof (short)); 723. 	mread(fd, (genericptr_t) &regions[i]->inside_f, sizeof (short)); 724. 	mread(fd, (genericptr_t) &regions[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]); 728. 	}  729.  	mread(fd, (genericptr_t) &regions[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); 733. 	else 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) &regions[i]->monsters[j],  738.  		  sizeof (unsigned)); 739. 	mread(fd, (genericptr_t) &regions[i]->visible, sizeof (boolean)); 740. 	mread(fd, (genericptr_t) &regions[i]->glyph, sizeof (int)); 741. 	mread(fd, (genericptr_t) &regions[i]->arg, sizeof (genericptr_t)); 742.     }  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]); 750. }  751.   752.  /* update monster IDs for region being loaded from bones; `ghostly' implied */ 753. static void 754. reset_region_mids(reg) 755. NhRegion *reg; 756. {  757.      int i = 0, n = reg->n_monst; 758.     unsigned *mid_list = reg->monsters; 759.  760.      while (i < n)  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]; 764. 	} else { 765. 	    /* move on to next monster */ 766. 	    ++i; 767. 	}  768.      reg->n_monst = n;  769. return; 770. }  771.   772.  #if 0 773. /* not yet used */ 774.  775.  /*--*  776.   *								*  777.   *			Create Region with just a message	* 778.  *								*  779.   *--*/  780.   781.  NhRegion * 782. create_msg_region(x, y, w, h, msg_enter, msg_leave) 783. xchar x, y;  784. xchar w, h; 785. const char *msg_enter; 786. const char *msg_leave; 787. {  788.      NhRect tmprect; 789.     NhRegion *reg = create_region((NhRect *) 0, 0); 790.  791.      reg->enter_msg = msg_enter; 792.     reg->leave_msg = msg_leave; 793.     tmprect.lx = x;  794. tmprect.ly = y; 795. tmprect.hx = x + w; 796. tmprect.hy = y + h; 797. add_rect_to_reg(reg, &tmprect); 798.     reg->ttl = -1; 799.     return reg; 800. }  801.   802.   803.  /*--*  804.   *								*  805.   *			Force Field Related Code		* 806.  *			(unused yet)				* 807.  *--*/  808.   809.  boolean 810. enter_force_field(p1, p2) 811. genericptr_t p1; 812. genericptr_t p2; 813. {  814.      struct monst *mtmp; 815.  816.      if (p2 == NULL) {		/* That means the player */ 817. 	if (!Blind) 818. 		You("bump into %s. Ouch!",  819.  		    Hallucination ? "an invisible tree" :  820.  			"some kind of invisible wall"); 821. 	else 822. 	    pline("Ouch!"); 823.     } else { 824. 	mtmp = (struct monst *) p2; 825. 	if (canseemon(mtmp)) 826. 	    pline("%s bumps into %s!", Monnam(mtmp), something); 827.     }  828.      return FALSE; 829. }  830.   831.  NhRegion * 832. create_force_field(x, y, radius, ttl) 833. xchar x, y;  834. int radius, ttl; 835. {  836.      int i;  837. NhRegion *ff; 838.     int nrect; 839.     NhRect tmprect; 840.  841.      ff = create_region((NhRect *) 0, 0); 842.     nrect = radius; 843.     tmprect.lx = x;  844. tmprect.hx = x; 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); 849. 	tmprect.lx--; 850. 	tmprect.hx++; 851. 	tmprect.ly++; 852. 	tmprect.hy--; 853.     }  854.      ff->ttl = ttl; 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; */ 859.     add_region(ff); 860.     return ff; 861. }  862.   863.  #endif	/*0*/ 864.  865.  /*--*  866.   *								*  867.   *			Gas cloud related code			* 868.  *								*  869.   *--*/  870.   871.  /*  872.   * Here is an example of an expire function that may prolong 873.  * region life after some mods...  874. */ 875.  boolean 876. expire_gas_cloud(p1, p2) 877. genericptr_t p1; 878. genericptr_t p2; 879. {  880.      NhRegion *reg; 881.     int damage; 882.  883.      reg = (NhRegion *) p1; 884.     damage = (int) reg->arg; 885.  886.      /* If it was a thick cloud, it dissipates a little first */ 887.     if (damage >= 5) { 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" */ 892.     }  893.      return TRUE;		/* OK, it's gone, you can free it! */ 894.  }  895.   896.  boolean 897. inside_gas_cloud(p1, p2) 898. genericptr_t p1; 899. genericptr_t p2; 900. {  901.      NhRegion *reg; 902.     struct monst *mtmp; 903.     int dam; 904.  905.      reg = (NhRegion *) p1; 906.     dam = (int) reg->arg; 907.     if (p2 == NULL) {		/* This means *YOU* Bozo! */ 908.  	if (nonliving(youmonst.data) || Breathless) 909. 	    return FALSE; 910. 	if (!Blind) 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); 916. 	    return FALSE; 917. 	} else { 918. 	    You("cough!"); 919. 	    return FALSE; 920. 	}  921.      } else {			/* A monster is inside the cloud */ 922. 	mtmp = (struct monst *) p2; 923.  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)); 928. 	    setmangry(mtmp); 929. 	    if (haseyes(mtmp->data) && mtmp->mcansee) { 930. 		mtmp->mblinded = 1; 931. 		mtmp->mcansee = 0; 932. 	    }  933.  	    if (resists_poison(mtmp)) 934. 		return FALSE; 935. 	    mtmp->mhp -= rnd(dam) + 5; 936. 	    if (mtmp->mhp <= 0) { 937. 		if (heros_fault(reg)) 938. 		    killed(mtmp); 939. 		else 940. 		    monkilled(mtmp, "gas cloud", AD_DRST); 941. 		if (mtmp->mhp <= 0) {	/* not lifesaved */ 942. 		    return TRUE; 943. 		}  944.  	    }  945.  	}  946.      }  947.      return FALSE;		/* Monster is still alive */ 948. }  949.   950.  NhRegion * 951. create_gas_cloud(x, y, radius, damage) 952. xchar x, y;  953. int radius; 954. int damage; 955. {  956.      NhRegion *cloud; 957.     int i, nrect; 958.     NhRect tmprect; 959.  960.      cloud = create_region((NhRect *) 0, 0); 961.     nrect = radius; 962.     tmprect.lx = x;  963. tmprect.hx = x; 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); 968. 	tmprect.lx--; 969. 	tmprect.hx++; 970. 	tmprect.ly++; 971. 	tmprect.hy--; 972.     }  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); 981.     add_region(cloud); 982.     return cloud; 983. }  984.   985.  /*region.c*/