Source:SLASH'EM 0.0.7E7F2/region.c

From NetHackWiki
Jump to navigation Jump to search

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.


The NetHack General Public License applies to screenshots, source code and other content from NetHack.

This content was modified from the original NetHack source code distribution (by splitting up NetHack content between wiki pages, and possibly further editing). See the page history for a list of who changed it, and on what dates.

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*/