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