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) ®ions[i]->bounding_box, sizeof (NhRect));
620 | bwrite(fd, (genericptr_t) ®ions[i]->nrects, sizeof (short));
621 | for (j = 0; j < regions[i]->nrects; j++)
622 | bwrite(fd, (genericptr_t) ®ions[i]->rects[j], sizeof (NhRect));
623 | bwrite(fd, (genericptr_t) ®ions[i]->attach_2_u, sizeof (boolean));
624 | n = 0;
625 | bwrite(fd, (genericptr_t) ®ions[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) ®ions[i]->ttl, sizeof (short));
635 | bwrite(fd, (genericptr_t) ®ions[i]->expire_f, sizeof (short));
636 | bwrite(fd, (genericptr_t) ®ions[i]->can_enter_f, sizeof (short));
637 | bwrite(fd, (genericptr_t) ®ions[i]->enter_f, sizeof (short));
638 | bwrite(fd, (genericptr_t) ®ions[i]->can_leave_f, sizeof (short));
639 | bwrite(fd, (genericptr_t) ®ions[i]->leave_f, sizeof (short));
640 | bwrite(fd, (genericptr_t) ®ions[i]->inside_f, sizeof (short));
641 | bwrite(fd, (genericptr_t) ®ions[i]->player_inside, sizeof (boolean));
642 | bwrite(fd, (genericptr_t) ®ions[i]->n_monst, sizeof (short));
643 | for (j = 0; j < regions[i]->n_monst; j++)
644 | bwrite(fd, (genericptr_t) ®ions[i]->monsters[j],
645 | sizeof (unsigned));
646 | bwrite(fd, (genericptr_t) ®ions[i]->visible, sizeof (boolean));
647 | bwrite(fd, (genericptr_t) ®ions[i]->glyph, sizeof (int));
648 | bwrite(fd, (genericptr_t) ®ions[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) ®ions[i]->bounding_box, sizeof (NhRect));
671 | mread(fd, (genericptr_t) ®ions[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) ®ions[i]->rects[j], sizeof (NhRect));
678 | mread(fd, (genericptr_t) ®ions[i]->attach_2_u, sizeof (boolean));
679 | mread(fd, (genericptr_t) ®ions[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) ®ions[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) ®ions[i]->expire_f, sizeof (short));
705 | mread(fd, (genericptr_t) ®ions[i]->can_enter_f, sizeof (short));
706 | mread(fd, (genericptr_t) ®ions[i]->enter_f, sizeof (short));
707 | mread(fd, (genericptr_t) ®ions[i]->can_leave_f, sizeof (short));
708 | mread(fd, (genericptr_t) ®ions[i]->leave_f, sizeof (short));
709 | mread(fd, (genericptr_t) ®ions[i]->inside_f, sizeof (short));
710 | mread(fd, (genericptr_t) ®ions[i]->player_inside, sizeof (boolean));
711 | mread(fd, (genericptr_t) ®ions[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) ®ions[i]->monsters[j],
720 | sizeof (unsigned));
721 | mread(fd, (genericptr_t) ®ions[i]->visible, sizeof (boolean));
722 | mread(fd, (genericptr_t) ®ions[i]->glyph, sizeof (int));
723 | mread(fd, (genericptr_t) ®ions[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*/