'obj' is assumed to be a magic whistle */
| 515 | |
| 516 | /* 'obj' is assumed to be a magic whistle */ |
| 517 | staticfn void |
| 518 | magic_whistled(struct obj *obj) |
| 519 | { |
| 520 | struct monst *mtmp, *nextmon; |
| 521 | char buf[BUFSZ], *mnam = 0, |
| 522 | shiftbuf[BUFSZ + sizeof "shifts location"], |
| 523 | appearbuf[BUFSZ + sizeof "appears"], |
| 524 | disappearbuf[BUFSZ + sizeof "disappears"]; |
| 525 | boolean oseen, nseen, |
| 526 | already_discovered = objects[obj->otyp].oc_name_known != 0; |
| 527 | int omx, omy, shift = 0, appear = 0, disappear = 0, trapped = 0; |
| 528 | |
| 529 | /* stasis prevents magic-whistling */ |
| 530 | if (svl.level.flags.stasis_until >= svm.moves) |
| 531 | return; |
| 532 | |
| 533 | /* need to copy (up to 3) names as they're collected rather than just |
| 534 | save pointers to them, otherwise churning through every mbuf[] might |
| 535 | clobber the ones we care about */ |
| 536 | shiftbuf[0] = appearbuf[0] = disappearbuf[0] = '\0'; |
| 537 | |
| 538 | for (mtmp = fmon; mtmp; mtmp = nextmon) { |
| 539 | nextmon = mtmp->nmon; /* trap might kill mon */ |
| 540 | if (DEADMONSTER(mtmp)) |
| 541 | continue; |
| 542 | /* only tame monsters are affected; |
| 543 | steed is already at your location, so not affected; |
| 544 | this avoids trap issues if you're on a trap location */ |
| 545 | if (!mtmp->mtame || mtmp == u.usteed) |
| 546 | continue; |
| 547 | if (mtmp->mtrapped) { |
| 548 | /* no longer in previous trap (affects mintrap) */ |
| 549 | mtmp->mtrapped = 0; |
| 550 | fill_pit(mtmp->mx, mtmp->my); |
| 551 | } |
| 552 | |
| 553 | oseen = canspotmon(mtmp); /* old 'seen' status */ |
| 554 | if (oseen) /* get name in case it's one we'll remember */ |
| 555 | mnam = y_monnam(mtmp); /* before mnexto(); it might disappear */ |
| 556 | /* mimic must be revealed before we know whether it |
| 557 | actually moves because line-of-sight may change */ |
| 558 | if (M_AP_TYPE(mtmp)) |
| 559 | seemimic(mtmp); |
| 560 | omx = mtmp->mx, omy = mtmp->my; |
| 561 | mnexto(mtmp, !already_discovered ? RLOC_MSG : RLOC_NONE); |
| 562 | |
| 563 | if (mtmp->mx != omx || mtmp->my != omy) { |
| 564 | if (mtmp->mundetected) { /* reveal non-mimic hider that moved */ |
| 565 | mtmp->mundetected = 0; |
| 566 | newsym(mtmp->mx, mtmp->my); |
| 567 | } |
| 568 | /* |
| 569 | * FIXME: |
| 570 | * All relocated monsters should change positions essentially |
| 571 | * simultaneously but we're dealing with them sequentially. |
| 572 | * That could kill some off in the process, each time leaving |
| 573 | * their target position (which should be occupied at least |
| 574 | * momentarily) available as a potential death trap for others. |
no test coverage detected