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) ®ions[i]->bounding_box, sizeof (NhRect)); 628 bwrite(fd, (genericptr_t) ®ions[i]->nrects, sizeof (short)); 629 for (j = 0; j < regions[i]->nrects; j++) 630 bwrite(fd, (genericptr_t) ®ions[i]->rects[j], sizeof (NhRect)); 631 bwrite(fd, (genericptr_t) ®ions[i]->attach_2_u, sizeof (boolean)); 632 n = 0; 633 bwrite(fd, (genericptr_t) ®ions[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) ®ions[i]->ttl, sizeof (short)); 643 bwrite(fd, (genericptr_t) ®ions[i]->expire_f, sizeof (short)); 644 bwrite(fd, (genericptr_t) ®ions[i]->can_enter_f, sizeof (short)); 645 bwrite(fd, (genericptr_t) ®ions[i]->enter_f, sizeof (short)); 646 bwrite(fd, (genericptr_t) ®ions[i]->can_leave_f, sizeof (short)); 647 bwrite(fd, (genericptr_t) ®ions[i]->leave_f, sizeof (short)); 648 bwrite(fd, (genericptr_t) ®ions[i]->inside_f, sizeof (short)); 649 bwrite(fd, (genericptr_t) ®ions[i]->player_flags, sizeof (boolean)); 650 bwrite(fd, (genericptr_t) ®ions[i]->n_monst, sizeof (short)); 651 for (j = 0; j < regions[i]->n_monst; j++) 652 bwrite(fd, (genericptr_t) ®ions[i]->monsters[j], 653 sizeof (unsigned)); 654 bwrite(fd, (genericptr_t) ®ions[i]->visible, sizeof (boolean)); 655 bwrite(fd, (genericptr_t) ®ions[i]->glyph, sizeof (int)); 656 bwrite(fd, (genericptr_t) ®ions[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) ®ions[i]->bounding_box, sizeof (NhRect)); 685 mread(fd, (genericptr_t) ®ions[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) ®ions[i]->rects[j], sizeof (NhRect)); 692 mread(fd, (genericptr_t) ®ions[i]->attach_2_u, sizeof (boolean)); 693 mread(fd, (genericptr_t) ®ions[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) ®ions[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) ®ions[i]->expire_f, sizeof (short)); 719 mread(fd, (genericptr_t) ®ions[i]->can_enter_f, sizeof (short)); 720 mread(fd, (genericptr_t) ®ions[i]->enter_f, sizeof (short)); 721 mread(fd, (genericptr_t) ®ions[i]->can_leave_f, sizeof (short)); 722 mread(fd, (genericptr_t) ®ions[i]->leave_f, sizeof (short)); 723 mread(fd, (genericptr_t) ®ions[i]->inside_f, sizeof (short)); 724 mread(fd, (genericptr_t) ®ions[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) ®ions[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) ®ions[i]->monsters[j], 738 sizeof (unsigned)); 739 mread(fd, (genericptr_t) ®ions[i]->visible, sizeof (boolean)); 740 mread(fd, (genericptr_t) ®ions[i]->glyph, sizeof (int)); 741 mread(fd, (genericptr_t) ®ions[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