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
14static NhRegion **regions;
15static int n_regions = 0;
16static int max_regions = 0;
17
18#define NO_CALLBACK (-1)
19
20boolean FDECL(inside_gas_cloud, (genericptr,genericptr));
21boolean FDECL(expire_gas_cloud, (genericptr,genericptr));
22boolean FDECL(inside_rect, (NhRect *,int,int));
23boolean FDECL(inside_region, (NhRegion *,int,int));
24NhRegion *FDECL(create_region, (NhRect *,int));
25void FDECL(add_rect_to_reg, (NhRegion *,NhRect *));
26void FDECL(add_mon_to_reg, (NhRegion *,struct monst *));
27void FDECL(remove_mon_from_reg, (NhRegion *,struct monst *));
28boolean FDECL(mon_in_region, (NhRegion *,struct monst *));
29
30#if 0
31NhRegion *FDECL(clone_region, (NhRegion *));
32#endif
33void FDECL(free_region, (NhRegion *));
34void FDECL(add_region, (NhRegion *));
35void FDECL(remove_region, (NhRegion *));
36
37#if 0
38void FDECL(replace_mon_regions, (struct monst *,struct monst *));
39void FDECL(remove_mon_from_regions, (struct monst *));
40NhRegion *FDECL(create_msg_region, (XCHAR_P,XCHAR_P,XCHAR_P,XCHAR_P,
41				    const char *,const char *));
42boolean FDECL(enter_force_field, (genericptr,genericptr));
43NhRegion *FDECL(create_force_field, (XCHAR_P,XCHAR_P,int,int));
44#endif
45
46static void FDECL(reset_region_mids, (NhRegion *));
47
48static 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. */
56boolean
57inside_rect(r, x, y)
58NhRect *r;
59int 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 */
67boolean
68inside_region(reg, x, y)
69NhRegion *reg;
70int 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 */
85NhRegion *
86create_region(rects, nrect)
87NhRect *rects;
88int 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 */
140void
141add_rect_to_reg(reg, rect)
142NhRegion *reg;
143NhRect *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 */
170void
171add_mon_to_reg(reg, mon)
172NhRegion *reg;
173struct 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 */
195void
196remove_mon_from_reg(reg, mon)
197NhRegion *reg;
198struct 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 */
215boolean
216mon_in_region(reg, mon)
217NhRegion *reg;
218struct 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 */
234NhRegion *
235clone_region(reg)
236NhRegion *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 */
267void
268free_region(reg)
269NhRegion *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 */
284void
285add_region(reg)
286NhRegion *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 */
324void
325remove_region(reg)
326NhRegion *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 */
353void
354clear_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 */
372void
373run_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 */
418boolean
419in_out_region(x, y)
420xchar
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*/
469boolean
470m_in_out_region(mon, x, y)
471struct monst *mon;
472xchar 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 */
517void
518update_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 */
532void
533update_monster_region(mon)
534struct 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 */
557void
558replace_mon_regions(monold, monnew)
559struct 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 */
573void
574remove_mon_from_regions(mon)
575struct 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 */
590NhRegion *
591visible_region_at(x, y)
592xchar 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
603void
604show_region(reg, x, y)
605NhRegion *reg;
606xchar x, y;
607{
608    show_glyph(x, y, reg->glyph);
609}
610
611/**
612 * save_regions :
613 */
614void
615save_regions(fd, mode)
616int fd;
617int 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
659skip_lots:
660    if (release_data(mode))
661	clear_regions();
662}
663
664void
665rest_regions(fd, ghostly)
666int fd;
667boolean 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 */
753static void
754reset_region_mids(reg)
755NhRegion *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
781NhRegion *
782create_msg_region(x, y, w, h, msg_enter, msg_leave)
783xchar x, y;
784xchar w, h;
785const char *msg_enter;
786const 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
809boolean
810enter_force_field(p1, p2)
811genericptr_t p1;
812genericptr_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
831NhRegion *
832create_force_field(x, y, radius, ttl)
833xchar x, y;
834int 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 */
875boolean
876expire_gas_cloud(p1, p2)
877genericptr_t p1;
878genericptr_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
896boolean
897inside_gas_cloud(p1, p2)
898genericptr_t p1;
899genericptr_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
950NhRegion *
951create_gas_cloud(x, y, radius, damage)
952xchar x, y;
953int radius;
954int 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