Source:SLASH'EM 0.0.7E7F2/region.c

Below is the full text to region.c from the source code of SLASH'EM 0.0.7E7F2. To link to a particular line, write [[SLASH'EM 0.0.7E7F2/region.c#line123 ]], for example.

The latest source code for vanilla NetHack is at Source code.

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(revive_cthulhu, (genericptr, genericptr)); 23.  boolean FDECL(inside_rect, (NhRect *,int,int)); 24.  boolean FDECL(inside_region, (NhRegion *,int,int)); 25.  NhRegion *FDECL(create_region, (NhRect *,int)); 26.  void FDECL(add_rect_to_reg, (NhRegion *,NhRect *)); 27.  void FDECL(add_mon_to_reg, (NhRegion *,struct monst *)); 28.  void FDECL(remove_mon_from_reg, (NhRegion *,struct monst *)); 29.  boolean FDECL(mon_in_region, (NhRegion *,struct monst *)); 30.   31.   #if 0 32.  NhRegion *FDECL(clone_region, (NhRegion *)); 33.  #endif 34.  void FDECL(free_region, (NhRegion *)); 35.  void FDECL(add_region, (NhRegion *)); 36.  void FDECL(remove_region, (NhRegion *)); 37.   38.   #if 0 39.  void FDECL(replace_mon_regions, (struct monst *,struct monst *)); 40.  void FDECL(remove_mon_from_regions, (struct monst *)); 41.  NhRegion *FDECL(create_msg_region, (XCHAR_P,XCHAR_P,XCHAR_P,XCHAR_P, 42.  				    const char *,const char *)); 43.  boolean FDECL(enter_force_field, (genericptr,genericptr)); 44.  NhRegion *FDECL(create_force_field, (XCHAR_P,XCHAR_P,int,int)); 45.  #endif 46.   47.   static void FDECL(reset_region_mids, (NhRegion *)); 48.   49.   static callback_proc callbacks[] = { 50.  #define INSIDE_GAS_CLOUD 0 51.      inside_gas_cloud, 52.  #define EXPIRE_GAS_CLOUD 1 53.      expire_gas_cloud, 54.  #define REVIVE_CTHULHU 2	/* Cthulhu comes back... */ 55.       revive_cthulhu 56.  };  57.    58.   /* Should be inlined. */ 59.   boolean 60.  inside_rect(r, x, y)  61. NhRect *r; 62.  int x, y;  63. { 64.       return (x >= r->lx && x <= r->hx && y >= r->ly && y <= r->hy); 65.  }  66.    67.   /*  68.    * Check if a point is inside a region. 69.   */  70.   boolean 71.  inside_region(reg, x, y)  72. NhRegion *reg; 73.  int x, y;  74. { 75.       int i;  76. 77.      if (reg == NULL || !inside_rect(&(reg->bounding_box), x, y)) 78.  	return FALSE; 79.      for (i = 0; i < reg->nrects; i++) 80.  	if (inside_rect(&(reg->rects[i]), x, y)) 81.  	    return TRUE; 82.      return FALSE; 83.  }  84.    85.   /*  86.    * Create a region. It does not activate it. 87.   */  88.   NhRegion * 89.  create_region(rects, nrect) 90.  NhRect *rects; 91.  int nrect; 92.  {  93.       int i;  94. NhRegion *reg; 95.   96.       reg = (NhRegion *) alloc(sizeof (NhRegion)); 97.      /* Determines bounding box */ 98.      if (nrect > 0) { 99.  	reg->bounding_box = rects[0]; 100.     } else { 101. 	reg->bounding_box.lx = 99; 102. 	reg->bounding_box.ly = 99; 103. 	reg->bounding_box.hx = 0; 104. 	reg->bounding_box.hy = 0; 105.     }  106.      reg->nrects = nrect; 107.     reg->rects = nrect > 0 ? (NhRect *)alloc((sizeof (NhRect)) * nrect) : NULL; 108.     for (i = 0; i < nrect; i++) { 109. 	if (rects[i].lx < reg->bounding_box.lx) 110. 	    reg->bounding_box.lx = rects[i].lx; 111. 	if (rects[i].ly < reg->bounding_box.ly) 112. 	    reg->bounding_box.ly = rects[i].ly; 113. 	if (rects[i].hx > reg->bounding_box.hx) 114. 	    reg->bounding_box.hx = rects[i].hx; 115. 	if (rects[i].hy > reg->bounding_box.hy) 116. 	    reg->bounding_box.hy = rects[i].hy; 117. 	reg->rects[i] = rects[i]; 118.     }  119.      reg->ttl = -1;		/* Defaults */ 120.     reg->attach_2_u = FALSE; 121.     reg->attach_2_m = 0; 122.     /* reg->attach_2_o = NULL; */ 123.     reg->enter_msg = NULL; 124.     reg->leave_msg = NULL; 125.     reg->expire_f = NO_CALLBACK; 126.     reg->enter_f = NO_CALLBACK; 127.     reg->can_enter_f = NO_CALLBACK; 128.     reg->leave_f = NO_CALLBACK; 129.     reg->can_leave_f = NO_CALLBACK; 130.     reg->inside_f = NO_CALLBACK; 131.     clear_hero_inside(reg); 132.     clear_heros_fault(reg); 133.     reg->n_monst = 0; 134.     reg->max_monst = 0; 135.     reg->monsters = NULL; 136.     reg->arg = NULL; 137.     return reg; 138. }  139.   140.  /*  141.   * Add rectangle to region. 142.  */  143.  void 144. add_rect_to_reg(reg, rect) 145. NhRegion *reg; 146. NhRect *rect; 147. {  148.      NhRect *tmp_rect; 149.  150.      tmp_rect = (NhRect *) alloc(sizeof (NhRect) * (reg->nrects + 1)); 151.     if (reg->nrects > 0) { 152. 	(void) memcpy((genericptr_t) tmp_rect, (genericptr_t) reg->rects,  153.  		      (sizeof (NhRect) * reg->nrects)); 154. 	free((genericptr_t) reg->rects); 155.     }  156.      tmp_rect[reg->nrects] = *rect; 157.     reg->nrects++; 158.     reg->rects = tmp_rect; 159.     /* Update bounding box if needed */ 160.     if (reg->bounding_box.lx > rect->lx) 161. 	reg->bounding_box.lx = rect->lx; 162.     if (reg->bounding_box.ly > rect->ly) 163. 	reg->bounding_box.ly = rect->ly; 164.     if (reg->bounding_box.hx < rect->hx) 165. 	reg->bounding_box.hx = rect->hx; 166.     if (reg->bounding_box.hy < rect->hy) 167. 	reg->bounding_box.hy = rect->hy; 168. }  169.   170.  /*  171.   * Add a monster to the region 172.  */  173.  void 174. add_mon_to_reg(reg, mon) 175. NhRegion *reg; 176. struct monst *mon; 177. {  178.      int i;  179. unsigned *tmp_m; 180.  181.      if (reg->max_monst <= reg->n_monst) { 182. 	tmp_m = (unsigned *) 183. 		    alloc(sizeof (unsigned) * (reg->max_monst + MONST_INC)); 184. 	if (reg->max_monst > 0) { 185. 	    for (i = 0; i < reg->max_monst; i++) 186. 		tmp_m[i] = reg->monsters[i]; 187. 	    free((genericptr_t) reg->monsters); 188. 	}  189.  	reg->monsters = tmp_m; 190. 	reg->max_monst += MONST_INC; 191.     }  192.      reg->monsters[reg->n_monst++] = mon->m_id; 193. }  194.   195.  /*  196.   * Remove a monster from the region list (it left or died...) 197.  */  198.  void 199. remove_mon_from_reg(reg, mon) 200. NhRegion *reg; 201. struct monst *mon; 202. {  203.      register int i;  204. 205.     for (i = 0; i < reg->n_monst; i++) 206. 	if (reg->monsters[i] == mon->m_id) { 207. 	    reg->n_monst--; 208. 	    reg->monsters[i] = reg->monsters[reg->n_monst]; 209. 	    return; 210. 	}  211.  }  212.   213.  /*  214.   * Check if a monster is inside the region. 215.  * It's probably quicker to check with the region internal list 216.  * than to check for coordinates. 217.  */  218.  boolean 219. mon_in_region(reg, mon) 220. NhRegion *reg; 221. struct monst *mon; 222. {  223.      int i;  224. 225.     for (i = 0; i < reg->n_monst; i++) 226. 	if (reg->monsters[i] == mon->m_id) 227. 	    return TRUE; 228.     return FALSE; 229. }  230.   231.  #if 0 232. /* not yet used */ 233.  234.  /*  235.   * Clone (make a standalone copy) the region. 236.  */  237.  NhRegion * 238. clone_region(reg) 239. NhRegion *reg; 240. {  241.      NhRegion *ret_reg; 242.  243.      ret_reg = create_region(reg->rects, reg->nrects); 244.     ret_reg->ttl = reg->ttl; 245.     ret_reg->attach_2_u = reg->attach_2_u; 246.     ret_reg->attach_2_m = reg->attach_2_m; 247.  /* ret_reg->attach_2_o = reg->attach_2_o; */ 248.     ret_reg->expire_f = reg->expire_f; 249.     ret_reg->enter_f = reg->enter_f; 250.     ret_reg->can_enter_f = reg->can_enter_f; 251.     ret_reg->leave_f = reg->leave_f; 252.     ret_reg->can_leave_f = reg->can_leave_f; 253.     ret_reg->player_flags = reg->player_flags;	/* set/clear_hero_inside,&c*/ 254.     ret_reg->n_monst = reg->n_monst; 255.     if (reg->n_monst > 0) { 256. 	ret_reg->monsters = (unsigned *) 257. 				alloc((sizeof (unsigned)) * reg->n_monst); 258. 	(void) memcpy((genericptr_t) ret_reg->monsters, (genericptr_t) reg->monsters,  259.  		      sizeof (unsigned) * reg->n_monst); 260.     } else 261. 	ret_reg->monsters = NULL; 262.     return ret_reg; 263. }  264.   265.  #endif	/*0*/ 266.  267.  /*  268.   * Free mem from region. 269.  */  270.  void 271. free_region(reg) 272. NhRegion *reg; 273. {  274.      if (reg) { 275. 	if (reg->rects) 276. 	    free((genericptr_t) reg->rects); 277. 	if (reg->monsters) 278. 	    free((genericptr_t) reg->monsters); 279. 	free((genericptr_t) reg); 280.     }  281.  }  282.   283.  /*  284.   * Add a region to the list. 285.  * This actually activates the region. 286.  */  287.  void 288. add_region(reg) 289. NhRegion *reg; 290. {  291.      NhRegion **tmp_reg; 292.     int i, j;  293. 294.     if (max_regions <= n_regions) { 295. 	tmp_reg = regions; 296. 	regions = (NhRegion **)alloc(sizeof (NhRegion *) * (max_regions + 10)); 297. 	if (max_regions > 0) { 298. 	    (void) memcpy((genericptr_t) regions, (genericptr_t) tmp_reg,  299.  			  max_regions * sizeof (NhRegion *)); 300. 	    free((genericptr_t) tmp_reg); 301. 	}  302.  	max_regions += 10; 303.     }  304.      regions[n_regions] = reg; 305.     n_regions++; 306.     /* Check for monsters inside the region */ 307.     for (i = reg->bounding_box.lx; i <= reg->bounding_box.hx; i++) 308. 	for (j = reg->bounding_box.ly; j <= reg->bounding_box.hy; j++) { 309. 	    /* Some regions can cross the level boundaries */ 310. 	    if (!isok(i,j)) 311. 		continue; 312. 	    if (MON_AT(i, j) && inside_region(reg, i, j)) 313. 		add_mon_to_reg(reg, level.monsters[i][j]); 314. 	    if (reg->visible && cansee(i, j)) 315. 		newsym(i, j); 316. 	}  317.      /* Check for player now... */ 318.      if (inside_region(reg, u.ux, u.uy)) 319. 	set_hero_inside(reg); 320.     else 321. 	clear_hero_inside(reg); 322. }  323.   324.  /*  325.   * Remove a region from the list & free it. 326.  */  327.  void 328. remove_region(reg) 329. NhRegion *reg; 330. {  331.      register int i, x, y;  332. 333.     for (i = 0; i < n_regions; i++) 334. 	if (regions[i] == reg) 335. 	    break; 336.     if (i == n_regions) 337. 	return; 338.  339.      /* Update screen if necessary */ 340.     if (reg->visible) 341. 	for (x = reg->bounding_box.lx; x <= reg->bounding_box.hx; x++) 342. 	    for (y = reg->bounding_box.ly; y <= reg->bounding_box.hy; y++) 343. 		if (isok(x,y) && inside_region(reg, x, y) && cansee(x, y)) 344. 		    newsym(x, y); 345.  346.      free_region(reg); 347.     regions[i] = regions[n_regions - 1]; 348.     regions[n_regions - 1] = (NhRegion *) 0; 349.     n_regions--; 350. }  351.   352.  /*  353.   * Remove all regions and clear all related data (This must be down  354.   * when changing level, for instance). 355.  */  356.  void 357. clear_regions 358. {  359.      register int i;  360. 361.     for (i = 0; i < n_regions; i++) 362. 	free_region(regions[i]); 363.     n_regions = 0; 364.     if (max_regions > 0) 365. 	free((genericptr_t) regions); 366.     max_regions = 0; 367.     regions = NULL; 368. }  369.   370.  /*  371.   * This function is called every turn. 372.  * It makes the regions age, if necessary and calls the appropriate 373.  * callbacks when needed. 374.  */  375.  void 376. run_regions 377. {  378.      register int i, j, k;  379. int f_indx; 380.  381.      /* End of life ? */ 382.      /* Do it backward because the array will be modified */ 383.     for (i = n_regions - 1; i >= 0; i--) { 384. 	if (regions[i]->ttl == 0) { 385. 	    if ((f_indx = regions[i]->expire_f) == NO_CALLBACK ||  386.  		(*callbacks[f_indx])(regions[i], (genericptr_t) 0)) 387. 		remove_region(regions[i]); 388. 	}  389.      }  390.   391.      /* Process remaining regions */ 392.     for (i = 0; i < n_regions; i++) { 393. 	/* Make the region age */ 394. 	if (regions[i]->ttl > 0) 395. 	    regions[i]->ttl--; 396. 	/* Check if player is inside region */ 397. 	f_indx = regions[i]->inside_f; 398. 	if (f_indx != NO_CALLBACK && hero_inside(regions[i])) 399. 	    (void) (*callbacks[f_indx])(regions[i], (genericptr_t) 0); 400. 	/* Check if any monster is inside region */ 401. 	if (f_indx != NO_CALLBACK) { 402. 	    for (j = 0; j < regions[i]->n_monst; j++) { 403. 		struct monst *mtmp = find_mid(regions[i]->monsters[j], FM_FMON); 404.  405.  		if (!mtmp || mtmp->mhp <= 0 ||  406.  				(*callbacks[f_indx])(regions[i], mtmp)) { 407. 		    /* The monster died, remove it from list */ 408. 		    k = (regions[i]->n_monst -= 1); 409. 		    regions[i]->monsters[j] = regions[i]->monsters[k]; 410. 		    regions[i]->monsters[k] = 0; 411. 		    --j;    /* current slot has been reused; recheck it next */ 412. 		}  413.  	    }  414.  	}  415.      }  416.  }  417.   418.  /*  419.   * check whether player enters/leaves one or more regions. 420.  */  421.  boolean 422. in_out_region(x, y)  423. xchar 424.     x, y;  425. { 426.      int i, f_indx; 427.  428.      /* First check if we can do the move */ 429.     for (i = 0; i < n_regions; i++) { 430. 	if (inside_region(regions[i], x, y)  431.  	    && !hero_inside(regions[i]) && !regions[i]->attach_2_u) { 432. 	    if ((f_indx = regions[i]->can_enter_f) != NO_CALLBACK) 433. 		if (!(*callbacks[f_indx])(regions[i], (genericptr_t) 0)) 434. 		    return FALSE; 435. 	} else 436. 	    if (hero_inside(regions[i])  437.  		&& !inside_region(regions[i], x, y)  438.  		&& !regions[i]->attach_2_u) { 439. 	    if ((f_indx = regions[i]->can_leave_f) != NO_CALLBACK) 440. 		if (!(*callbacks[f_indx])(regions[i], (genericptr_t) 0)) 441. 		    return FALSE; 442. 	}  443.      }  444.   445.      /* Callbacks for the regions we do leave */ 446.     for (i = 0; i < n_regions; i++) 447. 	if (hero_inside(regions[i]) &&  448.  		!regions[i]->attach_2_u && !inside_region(regions[i], x, y)) { 449. 	    clear_hero_inside(regions[i]); 450. 	    if (regions[i]->leave_msg != NULL) 451. 		pline(regions[i]->leave_msg); 452. 	    if ((f_indx = regions[i]->leave_f) != NO_CALLBACK) 453. 		(void) (*callbacks[f_indx])(regions[i], (genericptr_t) 0); 454. 	}  455.   456.      /* Callbacks for the regions we do enter */ 457.     for (i = 0; i < n_regions; i++) 458. 	if (!hero_inside(regions[i]) &&  459.  		!regions[i]->attach_2_u && inside_region(regions[i], x, y)) { 460. 	    set_hero_inside(regions[i]); 461. 	    if (regions[i]->enter_msg != NULL) 462. 		pline(regions[i]->enter_msg); 463. 	    if ((f_indx = regions[i]->enter_f) != NO_CALLBACK) 464. 		(void) (*callbacks[f_indx])(regions[i], (genericptr_t) 0); 465. 	}  466.      return TRUE; 467. }  468.   469.  /*  470.   * check wether a monster enters/leaves one or more region. 471. */  472.  boolean 473. m_in_out_region(mon, x, y)  474. struct monst *mon; 475. xchar x, y;  476. { 477.      int i, f_indx; 478.  479.      /* First check if we can do the move */ 480.     for (i = 0; i < n_regions; i++) { 481. 	if (inside_region(regions[i], x, y) &&  482.  		!mon_in_region(regions[i], mon) &&  483.  		regions[i]->attach_2_m != mon->m_id) { 484. 	    if ((f_indx = regions[i]->can_enter_f) != NO_CALLBACK) 485. 		if (!(*callbacks[f_indx])(regions[i], mon)) 486. 		    return FALSE; 487. 	} else if (mon_in_region(regions[i], mon) &&  488.  		!inside_region(regions[i], x, y) &&  489.  		regions[i]->attach_2_m != mon->m_id) { 490. 	    if ((f_indx = regions[i]->can_leave_f) != NO_CALLBACK) 491. 		if (!(*callbacks[f_indx])(regions[i], mon)) 492. 		    return FALSE; 493. 	}  494.      }  495.   496.      /* Callbacks for the regions we do leave */ 497.     for (i = 0; i < n_regions; i++) 498. 	if (mon_in_region(regions[i], mon) &&  499.  		regions[i]->attach_2_m != mon->m_id &&  500.  		!inside_region(regions[i], x, y)) { 501. 	    remove_mon_from_reg(regions[i], mon); 502. 	    if ((f_indx = regions[i]->leave_f) != NO_CALLBACK) 503. 		(void) (*callbacks[f_indx])(regions[i], mon); 504. 	}  505.   506.      /* Callbacks for the regions we do enter */ 507.     for (i = 0; i < n_regions; i++) 508. 	if (!hero_inside(regions[i]) &&  509.  		!regions[i]->attach_2_u && inside_region(regions[i], x, y)) { 510. 	    add_mon_to_reg(regions[i], mon); 511. 	    if ((f_indx = regions[i]->enter_f) != NO_CALLBACK) 512. 		(void) (*callbacks[f_indx])(regions[i], mon); 513. 	}  514.      return TRUE; 515. }  516.   517.  /*  518.   * Checks player's regions after a teleport for instance. 519.  */  520.  void 521. update_player_regions 522. {  523.      register int i;  524. 525.     for (i = 0; i < n_regions; i++) 526. 	if (!regions[i]->attach_2_u && inside_region(regions[i], u.ux, u.uy)) 527. 	    set_hero_inside(regions[i]); 528. 	else 529. 	    clear_hero_inside(regions[i]); 530. }  531.   532.  /*  533.   * Ditto for a specified monster. 534.  */  535.  void 536. update_monster_region(mon) 537. struct monst *mon; 538. {  539.      register int i;  540. 541.     for (i = 0; i < n_regions; i++) { 542. 	if (inside_region(regions[i], mon->mx, mon->my)) { 543. 	    if (!mon_in_region(regions[i], mon)) 544. 		add_mon_to_reg(regions[i], mon); 545. 	} else { 546. 	    if (mon_in_region(regions[i], mon)) 547. 		remove_mon_from_reg(regions[i], mon); 548. 	}  549.      }  550.  }  551.   552.  #if 0 553. /* not yet used */ 554.  555.  /*  556.   * Change monster pointer in regions 557.  * This happens, for instance, when a monster grows and 558.  * need a new structure (internally that is). 559.  */  560.  void 561. replace_mon_regions(monold, monnew) 562. struct monst *monold, *monnew; 563. {  564.      register int i;  565. 566.     for (i = 0; i < n_regions; i++) 567. 	if (mon_in_region(regions[i], monold)) { 568. 	    remove_mon_from_reg(regions[i], monold); 569. 	    add_mon_to_reg(regions[i], monnew); 570. 	}  571.  }  572.   573.  /*  574.   * Remove monster from all regions it was in (ie monster just died) 575.  */  576.  void 577. remove_mon_from_regions(mon) 578. struct monst *mon; 579. {  580.      register int i;  581. 582.     for (i = 0; i < n_regions; i++) 583. 	if (mon_in_region(regions[i], mon)) 584. 	    remove_mon_from_reg(regions[i], mon); 585. }  586.   587.  #endif	/*0*/ 588.  589.  /*  590.   * Check if a spot is under a visible region (eg: gas cloud). 591.  * Returns NULL if not, otherwise returns region. 592.  */  593.  NhRegion * 594. visible_region_at(x, y)  595. xchar x, y; 596. { 597.      register int i;  598. 599.     for (i = 0; i < n_regions; i++) 600. 	if (inside_region(regions[i], x, y) && regions[i]->visible &&  601.  		regions[i]->ttl != 0) 602. 	    return regions[i]; 603.     return (NhRegion *) 0; 604. }  605.   606.  void 607. show_region(reg, x, y)  608. NhRegion *reg; 609. xchar x, y;  610. { 611.      show_glyph(x, y, reg->glyph); 612. }  613.   614.  /**  615.   * save_regions : 616.  */  617.  void 618. save_regions(fd, mode) 619. int fd; 620. int mode; 621. {  622.      int i, j;  623. unsigned n; 624. 625.     if (!perform_bwrite(mode)) goto skip_lots; 626.  627.      bwrite(fd, (genericptr_t) &moves, sizeof (moves));	/* timestamp */ 628.     bwrite(fd, (genericptr_t) &n_regions, sizeof (n_regions)); 629.     for (i = 0; i < n_regions; i++) { 630. 	bwrite(fd, (genericptr_t) &regions[i]->bounding_box, sizeof (NhRect)); 631. 	bwrite(fd, (genericptr_t) &regions[i]->nrects, sizeof (short)); 632. 	for (j = 0; j < regions[i]->nrects; j++) 633. 	    bwrite(fd, (genericptr_t) &regions[i]->rects[j], sizeof (NhRect)); 634. 	bwrite(fd, (genericptr_t) &regions[i]->attach_2_u, sizeof (boolean)); 635. 	n = 0; 636. 	bwrite(fd, (genericptr_t) &regions[i]->attach_2_m, sizeof (unsigned)); 637. 	n = regions[i]->enter_msg != NULL ? strlen(regions[i]->enter_msg) : 0; 638. 	bwrite(fd, (genericptr_t) &n, sizeof n); 639. 	if (n > 0) 640. 	    bwrite(fd, (genericptr_t) regions[i]->enter_msg, n); 641. 	n = regions[i]->leave_msg != NULL ? strlen(regions[i]->leave_msg) : 0; 642. 	bwrite(fd, (genericptr_t) &n, sizeof n); 643. 	if (n > 0) 644. 	    bwrite(fd, (genericptr_t) regions[i]->leave_msg, n); 645. 	bwrite(fd, (genericptr_t) &regions[i]->ttl, sizeof (short)); 646. 	bwrite(fd, (genericptr_t) &regions[i]->expire_f, sizeof (short)); 647. 	bwrite(fd, (genericptr_t) &regions[i]->can_enter_f, sizeof (short)); 648. 	bwrite(fd, (genericptr_t) &regions[i]->enter_f, sizeof (short)); 649. 	bwrite(fd, (genericptr_t) &regions[i]->can_leave_f, sizeof (short)); 650. 	bwrite(fd, (genericptr_t) &regions[i]->leave_f, sizeof (short)); 651. 	bwrite(fd, (genericptr_t) &regions[i]->inside_f, sizeof (short)); 652. 	bwrite(fd, (genericptr_t) &regions[i]->player_flags, sizeof (boolean)); 653. 	bwrite(fd, (genericptr_t) &regions[i]->n_monst, sizeof (short)); 654. 	for (j = 0; j < regions[i]->n_monst; j++) 655. 	    bwrite(fd, (genericptr_t) &regions[i]->monsters[j],  656.  	     sizeof (unsigned)); 657. 	bwrite(fd, (genericptr_t) &regions[i]->visible, sizeof (boolean)); 658. 	bwrite(fd, (genericptr_t) &regions[i]->glyph, sizeof (int)); 659. 	bwrite(fd, (genericptr_t) &regions[i]->arg, sizeof (genericptr_t)); 660.     }  661.   662.  skip_lots: 663.     if (release_data(mode)) 664. 	clear_regions; 665. }  666.   667.  void 668. rest_regions(fd, ghostly) 669. int fd; 670. boolean ghostly; /* If a bones file restore */ 671. {  672.      int i, j;  673. unsigned n; 674. long tmstamp; 675.     char *msg_buf; 676.  677.      clear_regions;		/* Just for security */ 678.     mread(fd, (genericptr_t) &tmstamp, sizeof (tmstamp)); 679.     if (ghostly) tmstamp = 0; 680.     else tmstamp = (moves - tmstamp); 681.     mread(fd, (genericptr_t) &n_regions, sizeof (n_regions)); 682.     max_regions = n_regions; 683.     if (n_regions > 0) 684. 	regions = (NhRegion **) alloc(sizeof (NhRegion *) * n_regions); 685.     for (i = 0; i < n_regions; i++) { 686. 	regions[i] = (NhRegion *) alloc(sizeof (NhRegion)); 687. 	mread(fd, (genericptr_t) &regions[i]->bounding_box, sizeof (NhRect)); 688. 	mread(fd, (genericptr_t) &regions[i]->nrects, sizeof (short)); 689.  690.  	if (regions[i]->nrects > 0) 691. 	    regions[i]->rects = (NhRect *) 692. 				  alloc(sizeof (NhRect) * regions[i]->nrects); 693. 	for (j = 0; j < regions[i]->nrects; j++) 694. 	    mread(fd, (genericptr_t) &regions[i]->rects[j], sizeof (NhRect)); 695. 	mread(fd, (genericptr_t) &regions[i]->attach_2_u, sizeof (boolean)); 696. 	mread(fd, (genericptr_t) &regions[i]->attach_2_m, sizeof (unsigned)); 697.  698.  	mread(fd, (genericptr_t) &n, sizeof n); 699. 	if (n > 0) { 700. 	    msg_buf = (char *) alloc(n + 1); 701. 	    mread(fd, (genericptr_t) msg_buf, n); 702. 	    msg_buf[n] = '\0'; 703. 	    regions[i]->enter_msg = (const char *) msg_buf; 704. 	} else 705. 	    regions[i]->enter_msg = NULL; 706.  707.  	mread(fd, (genericptr_t) &n, sizeof n); 708. 	if (n > 0) { 709. 	    msg_buf = (char *) alloc(n + 1); 710. 	    mread(fd, (genericptr_t) msg_buf, n); 711. 	    msg_buf[n] = '\0'; 712. 	    regions[i]->leave_msg = (const char *) msg_buf; 713. 	} else 714. 	    regions[i]->leave_msg = NULL; 715.  716.  	mread(fd, (genericptr_t) &regions[i]->ttl, sizeof (short)); 717. 	/* check for expired region */ 718. 	if (regions[i]->ttl >= 0) 719. 	    regions[i]->ttl = 720. 		(regions[i]->ttl > tmstamp) ? regions[i]->ttl - tmstamp : 0; 721. 	mread(fd, (genericptr_t) &regions[i]->expire_f, sizeof (short)); 722. 	mread(fd, (genericptr_t) &regions[i]->can_enter_f, sizeof (short)); 723. 	mread(fd, (genericptr_t) &regions[i]->enter_f, sizeof (short)); 724. 	mread(fd, (genericptr_t) &regions[i]->can_leave_f, sizeof (short)); 725. 	mread(fd, (genericptr_t) &regions[i]->leave_f, sizeof (short)); 726. 	mread(fd, (genericptr_t) &regions[i]->inside_f, sizeof (short)); 727. 	mread(fd, (genericptr_t) &regions[i]->player_flags, sizeof (boolean)); 728. 	if (ghostly) {	/* settings pertained to old player */ 729. 	    clear_hero_inside(regions[i]); 730. 	    clear_heros_fault(regions[i]); 731. 	}  732.  	mread(fd, (genericptr_t) &regions[i]->n_monst, sizeof (short)); 733. 	if (regions[i]->n_monst > 0) 734. 	    regions[i]->monsters = 735. 		(unsigned *) alloc(sizeof (unsigned) * regions[i]->n_monst); 736. 	else 737. 	    regions[i]->monsters = NULL; 738. 	regions[i]->max_monst = regions[i]->n_monst; 739. 	for (j = 0; j < regions[i]->n_monst; j++) 740. 	    mread(fd, (genericptr_t) &regions[i]->monsters[j],  741.  		  sizeof (unsigned)); 742. 	mread(fd, (genericptr_t) &regions[i]->visible, sizeof (boolean)); 743. 	mread(fd, (genericptr_t) &regions[i]->glyph, sizeof (int)); 744. 	mread(fd, (genericptr_t) &regions[i]->arg, sizeof (genericptr_t)); 745.     }  746.      /* remove expired regions, do not trigger the expire_f callback (yet!); 747.        also update monster lists if this data is coming from a bones file */ 748.     for (i = n_regions - 1; i >= 0; i--) 749. 	if (regions[i]->ttl == 0) 750. 	    remove_region(regions[i]); 751. 	else if (ghostly && regions[i]->n_monst > 0) 752. 	    reset_region_mids(regions[i]); 753. }  754.   755.  /* update monster IDs for region being loaded from bones; `ghostly' implied */ 756. static void 757. reset_region_mids(reg) 758. NhRegion *reg; 759. {  760.      int i = 0, n = reg->n_monst; 761.     unsigned *mid_list = reg->monsters; 762.  763.      while (i < n)  764. if (!lookup_id_mapping(mid_list[i], &mid_list[i])) { 765. 	    /* shrink list to remove missing monster; order doesn't matter */ 766. 	    mid_list[i] = mid_list[--n]; 767. 	} else { 768. 	    /* move on to next monster */ 769. 	    ++i; 770. 	}  771.      reg->n_monst = n;  772. return; 773. }  774.   775.  #if 0 776. /* not yet used */ 777.  778.  /*--*  779.   *								*  780.   *			Create Region with just a message	* 781.  *								*  782.   *--*/  783.   784.  NhRegion * 785. create_msg_region(x, y, w, h, msg_enter, msg_leave) 786. xchar x, y;  787. xchar w, h; 788. const char *msg_enter; 789. const char *msg_leave; 790. {  791.      NhRect tmprect; 792.     NhRegion *reg = create_region((NhRect *) 0, 0); 793.  794.      reg->enter_msg = msg_enter; 795.     reg->leave_msg = msg_leave; 796.     tmprect.lx = x;  797. tmprect.ly = y; 798. tmprect.hx = x + w; 799. tmprect.hy = y + h; 800. add_rect_to_reg(reg, &tmprect); 801.     reg->ttl = -1; 802.     return reg; 803. }  804.   805.   806.  /*--*  807.   *								*  808.   *			Force Field Related Code		* 809.  *			(unused yet)				* 810.  *--*/  811.   812.  boolean 813. enter_force_field(p1, p2) 814. genericptr_t p1; 815. genericptr_t p2; 816. {  817.      struct monst *mtmp; 818.  819.      if (p2 == NULL) {		/* That means the player */ 820. 	if (!Blind) 821. 		You("bump into %s. Ouch!",  822.  		    Hallucination ? "an invisible tree" :  823.  			"some kind of invisible wall"); 824. 	else 825. 	    pline("Ouch!"); 826.     } else { 827. 	mtmp = (struct monst *) p2; 828. 	if (canseemon(mtmp)) 829. 	    pline("%s bumps into %s!", Monnam(mtmp), something); 830.     }  831.      return FALSE; 832. }  833.   834.  NhRegion * 835. create_force_field(x, y, radius, ttl) 836. xchar x, y;  837. int radius, ttl; 838. {  839.      int i;  840. NhRegion *ff; 841.     int nrect; 842.     NhRect tmprect; 843.  844.      ff = create_region((NhRect *) 0, 0); 845.     nrect = radius; 846.     tmprect.lx = x;  847. tmprect.hx = x; 848. tmprect.ly = y - (radius - 1); 849.     tmprect.hy = y + (radius - 1); 850.     for (i = 0; i < nrect; i++) { 851. 	add_rect_to_reg(ff, &tmprect); 852. 	tmprect.lx--; 853. 	tmprect.hx++; 854. 	tmprect.ly++; 855. 	tmprect.hy--; 856.     }  857.      ff->ttl = ttl; 858.     if (!in_mklev && !flags.mon_moving) 859. 	set_heros_fault(ff);		/* assume player has created it */ 860.  /* ff->can_enter_f = enter_force_field; */ 861.  /* ff->can_leave_f = enter_force_field; */ 862.     add_region(ff); 863.     return ff; 864. }  865.   866.  #endif	/*0*/ 867.  868.  /*--*  869.   *								*  870.   *			Gas cloud related code			* 871.  *								*  872.   *--*/  873.   874.  /*  875.   * Here is an example of an expire function that may prolong 876.  * region life after some mods...  877. */ 878.  boolean 879. expire_gas_cloud(p1, p2) 880. genericptr_t p1; 881. genericptr_t p2; 882. {  883.      NhRegion *reg; 884.     int damage; 885.  886.      reg = (NhRegion *) p1; 887.     damage = (int) reg->arg; 888.  889.      /* If it was a thick cloud, it dissipates a little first */ 890.     if (damage >= 5) { 891. 	damage /= 2;		/* It dissipates, let's do less damage */ 892. 	reg->arg = (genericptr_t) damage; 893. 	reg->ttl = 2;		/* Here's the trick : reset ttl */ 894. 	return FALSE;		/* THEN return FALSE, means "still there" */ 895.     }  896.      return TRUE;		/* OK, it's gone, you can free it! */ 897.  }  898.   899.  boolean 900. revive_cthulhu(p1, p2) 901. genericptr_t p1; 902. genericptr_t p2; 903. {  904.      boolean ret = expire_gas_cloud(p1, p2); 905.     if (ret) { 906. 	/* Bring back Cthulhu! */ 907.  	int cx, cy; 908. 	NhRegion *reg = (NhRegion *) p1; 909. 	struct monst *cthulhu = NULL; 910. 	coord cc; 911. 	  912.  	cx = (reg->bounding_box.lx + reg->bounding_box.hx) / 2; 913. 	cy = (reg->bounding_box.ly + reg->bounding_box.hy) / 2; 914.  915.  	if (enexto(&cc, cx, cy, &mons[PM_CTHULHU])) { 916. 	    cx = cc.x;  917. cy = cc.y; 918. } else { 919. 	    cx = cy = 0;	/* Place Cthulhu randomly */ 920. 	}  921.   922.  	/* Make sure Cthulhu doesn't get the Amulet again! :-) */ 923.  	cthulhu = makemon(&mons[PM_CTHULHU], cx, cy, 924. 				MM_NOCOUNTBIRTH | NO_MINVENT);  925.  	if (cthulhu && canseemon(cthulhu))  926.  	    pline("%s reforms!", Monnam(cthulhu));  927.      }  928.      return ret;  929.  }  930.   931.  boolean  932.  inside_gas_cloud(p1, p2)  933.  genericptr_t p1;  934.  genericptr_t p2;  935.  {  936.      NhRegion *reg;  937.      struct monst *mtmp;  938.      int dam;  939.   940.      reg = (NhRegion *) p1;  941.      dam = (int) reg->arg;  942.      if (p2 == NULL) {		/* This means *YOU* Bozo! */  943.  	if (nonliving(youmonst.data) || Breathless)  944.  	    return FALSE;  945.  	if (!Blind)  946.  	    make_blinded(1L, FALSE);  947.  	if (!Poison_resistance) {  948.  	    pline("%s is burning your %s!", Something, makeplural(body_part(LUNG)));  949.  	    You("cough and spit blood!");  950.  	    losehp(rnd(dam) + 5, "gas cloud", KILLED_BY_AN);  951.  	    return FALSE;  952.  	} else { 953. 	    You("cough!"); 954. 	    return FALSE; 955. 	}  956.      } else {			/* A monster is inside the cloud */ 957. 	mtmp = (struct monst *) p2; 958.  959.  	/* Non living and non breathing monsters are not concerned */ 960. 	if (!nonliving(mtmp->data) && !breathless(mtmp->data)) { 961. 	    if (cansee(mtmp->mx, mtmp->my)) 962. 		pline("%s coughs!", Monnam(mtmp)); 963. 	    if (heros_fault(reg)) 964. 	    	setmangry(mtmp); 965. 	    if (haseyes(mtmp->data) && mtmp->mcansee) { 966. 		mtmp->mblinded = 1; 967. 		mtmp->mcansee = 0; 968. 	    }  969.  	    if (resists_poison(mtmp)) 970. 		return FALSE; 971. 	    mtmp->mhp -= rnd(dam) + 5; 972. 	    if (mtmp->mhp <= 0) { 973. 		if (heros_fault(reg)) 974. 		    killed(mtmp); 975. 		else 976. 		    monkilled(mtmp, "gas cloud", AD_DRST); 977. 		if (mtmp->mhp <= 0) {	/* not lifesaved */ 978. 		    return TRUE; 979. 		}  980.  	    }  981.  	}  982.      }  983.      return FALSE;		/* Monster is still alive */ 984. }  985.   986.  NhRegion * 987. create_cthulhu_death_cloud(x, y, radius, damage) 988. xchar x, y;  989. int radius; 990. int damage; 991. {  992.      NhRegion *cloud; 993.  994.      cloud = create_gas_cloud(x, y, radius, damage); 995.     if (cloud) cloud->expire_f = REVIVE_CTHULHU; 996.  997.      return cloud; 998. }  999.   1000. NhRegion * 1001. create_gas_cloud(x, y, radius, damage) 1002. xchar x, y; 1003. int radius; 1004. int damage; 1005. { 1006.     NhRegion *cloud; 1007.    int i, nrect; 1008.    NhRect tmprect; 1009. 1010.     cloud = create_region((NhRect *) 0, 0); 1011.    nrect = radius; 1012.    tmprect.lx = x;  1013. tmprect.hx = x; 1014. tmprect.ly = y - (radius - 1); 1015.    tmprect.hy = y + (radius - 1); 1016.    for (i = 0; i < nrect; i++) { 1017. 	add_rect_to_reg(cloud, &tmprect); 1018. 	tmprect.lx--; 1019. 	tmprect.hx++; 1020. 	tmprect.ly++; 1021. 	tmprect.hy--; 1022.    }  1023.     cloud->ttl = rn1(3,4); 1024.    if (!in_mklev && !flags.mon_moving) 1025. 	set_heros_fault(cloud);		/* assume player has created it */ 1026.    cloud->inside_f = INSIDE_GAS_CLOUD; 1027.    cloud->expire_f = EXPIRE_GAS_CLOUD; 1028.    cloud->arg = (genericptr_t) damage; 1029.    cloud->visible = TRUE; 1030.    cloud->glyph = cmap_to_glyph(S_cloud); 1031.    add_region(cloud); 1032.    return cloud; 1033. } 1034.  1035. /*region.c*/