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