diff --git a/src/apply.c b/src/apply.c index 63fcdc964..12e8567ca 100644 --- a/src/apply.c +++ b/src/apply.c @@ -702,10 +702,10 @@ o_unleash(struct obj *otmp) for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) if (mtmp->m_id == (unsigned) otmp->leashmon) { mtmp->mleashed = 0; - otmp->leashmon = 0; - update_inventory(); break; } + otmp->leashmon = 0; + update_inventory(); } /* mtmp is about to die, or become untame */ diff --git a/src/mkobj.c b/src/mkobj.c index a78e0f05f..6e2954ade 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -2907,6 +2907,8 @@ objlist_sanity(struct obj *objlist, int wheretype, const char *mesg) insane_object(obj, ofmt0, mesg, (struct monst *) 0); if (obj->where == OBJ_INVENT && obj->how_lost != LOST_NONE) { char lostbuf[40]; + + /* %d: bitfield is unsigned but narrow, so promotes to int */ Sprintf(lostbuf, "how_lost=%d obj in inventory!", obj->how_lost); insane_object(obj, ofmt0, lostbuf, (struct monst *) 0); } @@ -2948,6 +2950,37 @@ objlist_sanity(struct obj *objlist, int wheretype, const char *mesg) break; } } + if (obj->otyp == LEASH && obj->leashmon) { + char buf[BUFSZ]; + struct monst *mtmp = find_mid(obj->leashmon, FM_FMON); + + if (obj->where == OBJ_INVENT) { + if (!mtmp) { /* found leash with phantom mon */ + Sprintf(buf, "leashmon=%u no monst,", + (unsigned) obj->leashmon); + insane_object(obj, ofmt0, buf, (struct monst *) 0); + } else if (!mtmp->mleashed) { /* found leashed mon + * not flagged as leashed */ + Sprintf(buf, "leashmon=%u %s not leashed,", + (unsigned) obj->leashmon, mon_pmname(mtmp)); + insane_object(obj, ofmt0, buf, (struct monst *) 0); + } + } else { + struct monst *mtmp2 = (obj->where == OBJ_MINVENT) + ? obj->ocarry : (struct monst *) 0; + + if (mtmp) { /* found monst leashed by non-invent leash */ + Sprintf(buf, "leashmon:%u %s leashed by %s leash,", + (unsigned) obj->leashmon, + mon_pmname(mtmp), where_name(obj)); + insane_object(obj, ofmt0, buf, mtmp2); + } else { /* found non-invent leash with m_id of phantom mon */ + Sprintf(buf, "leashmon:%u no monst for %s leash,", + (unsigned) obj->leashmon, where_name(obj)); + insane_object(obj, ofmt0, buf, mtmp2); + } + } + } if (obj->globby) check_glob(obj, mesg); /* temporary flags that might have been set but which should @@ -3165,11 +3198,13 @@ init_dummyobj(struct obj *obj, short otyp, long oquan) /* obj->dknown = 0; */ /* suppress known except for amulets (needed for fakes & real AoY) */ obj->known = (obj->oclass == AMULET_CLASS) - ? obj->known + ? obj->known /* default is "on" for types which don't use it */ : !objects[otyp].oc_uses_known; obj->quan = oquan ? oquan : 1L; obj->corpsenm = NON_PM; /* suppress statue and figurine details */ + if (obj->otyp == LEASH) + obj->leashmon = 0; /* overloads corpsenm, avoid NON_PM */ if (obj->otyp == BOULDER) obj->next_boulder = 0; /* overloads corpsenm, avoid NON_PM */ /* but suppressing fruit details leads to "bad fruit #0" */ diff --git a/src/mon.c b/src/mon.c index d1cdfdcd1..993cb32a6 100644 --- a/src/mon.c +++ b/src/mon.c @@ -214,6 +214,24 @@ sanity_check_single_mon( impossible("mimic%s concealed in inaccessible location: %s (%s)", is_mimic ? "" : "ker", typnam, msg); } +#endif + } + if (mtmp->mleashed) { + if (!get_mleash(mtmp)) + impossible("monst %u: leashed but no leash for %s", + mtmp->m_id, mon_pmname(mtmp)); + else if (!mtmp->mtame) + impossible("monst %u: leashed but not tame %s", + mtmp->m_id, mon_pmname(mtmp)); +#if 0 + /* after hero moves, leashed mon won't necessarily pass 'm_next2u()' + test; 90 is farthest observed distance with expert jumping spell + when very slow mon is already several steps away and hero jumps in + opposite direction (if hero teleports, leashed mon moves adjacent + immediately; knockback has shorter range than magical jumping) */ + else if (distu(mtmp->mx, mtmp->my) > 90) /*if (!m_next2u(mtmp))*/ + impossible("monst %u: leashed but not next to you (%d)", + mtmp->m_id, distu(mtmp->mx, mtmp->my)); #endif } } diff --git a/src/objnam.c b/src/objnam.c index eeaefa4ef..7713a5dad 100644 --- a/src/objnam.c +++ b/src/objnam.c @@ -1407,11 +1407,16 @@ doname_base( if (obj->otyp == LEASH && obj->leashmon != 0) { struct monst *mlsh = find_mid(obj->leashmon, FM_FMON); - if (!mlsh) { - impossible("leashed monster not on this level"); - obj->leashmon = 0; - } else { + if (mlsh && !DEADMONSTER(mlsh)) { ConcatF1(bp, 0, " (attached to %s)", noit_mon_nam(mlsh)); + } else { + if (mlsh) /*&& DEADMONSTER(mlsh)*/ + impossible("leashed %s #%u is dead", + mon_pmname(mlsh), (unsigned) obj->leashmon); + else + impossible("leashed monster #%u not found", + (unsigned) obj->leashmon); + obj->leashmon = 0; } break; } diff --git a/src/zap.c b/src/zap.c index 2bec3f75c..127395788 100644 --- a/src/zap.c +++ b/src/zap.c @@ -1769,6 +1769,16 @@ poly_obj(struct obj *obj, int id) otmp->cursed = FALSE; } } + if (obj->otyp == LEASH && obj->leashmon != 0) { + if (otmp->otyp == LEASH) { + otmp->leashmon = obj->leashmon; + /* clear m_id before delobj(), to avoid o_unleash() by obfree() */ + obj->leashmon = 0; + } else { + /* obfree() would do this if we didn't do it here */ + o_unleash(obj); + } + } /* no box contents --KAA */ if (Has_contents(otmp))