From 7ccaacd7a1d76b21f6796dc268f602b52f5dbc93 Mon Sep 17 00:00:00 2001 From: jwalz Date: Sat, 5 Jan 2002 21:05:48 +0000 Subject: [PATCH] *** empty log message *** --- src/dogmove.c | 801 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 801 insertions(+) create mode 100644 src/dogmove.c diff --git a/src/dogmove.c b/src/dogmove.c new file mode 100644 index 000000000..5ace67462 --- /dev/null +++ b/src/dogmove.c @@ -0,0 +1,801 @@ +/* SCCS Id: @(#)dogmove.c 3.3 97/05/25 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +#include "mfndpos.h" +#include "edog.h" + +extern boolean notonhead; + +#ifdef OVL0 + +STATIC_DCL boolean FDECL(dog_hunger,(struct monst *,struct edog *)); +STATIC_DCL int FDECL(dog_invent,(struct monst *,struct edog *,int)); +STATIC_DCL int FDECL(dog_goal,(struct monst *,struct edog *,int,int,int)); + +STATIC_DCL struct obj *FDECL(DROPPABLES, (struct monst *)); + +STATIC_OVL struct obj * +DROPPABLES(mon) +register struct monst *mon; +{ + register struct obj *obj; + struct obj *wep = MON_WEP(mon); + boolean item1 = FALSE, item2 = FALSE; + + if (is_animal(mon->data) || mindless(mon->data)) + item1 = item2 = TRUE; + if (!tunnels(mon->data) || !needspick(mon->data)) + item1 = TRUE; + for(obj = mon->minvent; obj; obj = obj->nobj) { + if (!item1 && is_pick(obj) && (obj->otyp != DWARVISH_MATTOCK + || !which_armor(mon, W_ARMS))) { + item1 = TRUE; + continue; + } + if (!item2 && obj->otyp == UNICORN_HORN && !obj->cursed) { + item2 = TRUE; + continue; + } + if (!obj->owornmask && obj != wep) return obj; + } + return (struct obj *)0; +} + +static NEARDATA const char nofetch[] = { BALL_CLASS, CHAIN_CLASS, ROCK_CLASS, 0 }; + +#endif /* OVL0 */ + +STATIC_OVL boolean FDECL(cursed_object_at, (int, int)); + +STATIC_VAR xchar gtyp, gx, gy; /* type and position of dog's current goal */ + +STATIC_PTR void FDECL(wantdoor, (int, int, genericptr_t)); + +#ifdef OVLB +STATIC_OVL boolean +cursed_object_at(x, y) +int x, y; +{ + struct obj *otmp; + + for(otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere) + if (otmp->cursed) return TRUE; + return FALSE; +} + +int +dog_nutrition(mtmp, obj) +struct monst *mtmp; +struct obj *obj; +{ + int nutrit; + + /* + * It is arbitrary that the pet takes the same length of time to eat + * as a human, but gets more nutritional value. + */ + if (obj->oclass == FOOD_CLASS) { + if(obj->otyp == CORPSE) { + mtmp->meating = 3 + (mons[obj->corpsenm].cwt >> 6); + nutrit = mons[obj->corpsenm].cnutrit; + } else { + mtmp->meating = objects[obj->otyp].oc_delay; + nutrit = objects[obj->otyp].oc_nutrition; + } + switch(mtmp->data->msize) { + case MZ_TINY: nutrit *= 8; break; + case MZ_SMALL: nutrit *= 6; break; + default: + case MZ_MEDIUM: nutrit *= 5; break; + case MZ_LARGE: nutrit *= 4; break; + case MZ_HUGE: nutrit *= 3; break; + case MZ_GIGANTIC: nutrit *= 2; break; + } + if(obj->oeaten) { + mtmp->meating = eaten_stat(mtmp->meating, obj); + nutrit = eaten_stat(nutrit, obj); + } + } else if (obj->oclass == GOLD_CLASS) { + mtmp->meating = (int)(obj->quan/2000) + 1; + if (mtmp->meating < 0) mtmp->meating = 1; + nutrit = (int)(obj->quan/20); + if (nutrit < 0) nutrit = 0; + } else { + /* Unusual pet such as gelatinous cube eating odd stuff. + * meating made consistent with wild monsters in mon.c. + * nutrit made consistent with polymorphed player nutrit in + * eat.c. (This also applies to pets eating gold.) + */ + mtmp->meating = obj->owt/20 + 1; + nutrit = 5*objects[obj->otyp].oc_nutrition; + } + return nutrit; +} + +/* returns 2 if pet dies, otherwise 1 */ +int +dog_eat(mtmp, obj, x, y, devour) +register struct monst *mtmp; +register struct obj * obj; +int x, y; +boolean devour; +{ + register struct edog *edog = EDOG(mtmp); + boolean poly = FALSE, grow = FALSE, heal = FALSE; + int nutrit; + + if(edog->hungrytime < monstermoves) + edog->hungrytime = monstermoves; + nutrit = dog_nutrition(mtmp, obj); + poly = polyfodder(obj); + grow = mlevelgain(obj); + heal = mhealup(obj); + if (devour) { + if (mtmp->meating > 1) mtmp->meating /= 2; + if (nutrit > 1) nutrit = (nutrit * 3) / 4; + } + edog->hungrytime += nutrit; + mtmp->mconf = 0; + if (edog->mhpmax_penalty) { + /* no longer starving */ + mtmp->mhpmax += edog->mhpmax_penalty; + edog->mhpmax_penalty = 0; + } + if (mtmp->mflee && mtmp->mfleetim > 1) mtmp->mfleetim /= 2; + if (mtmp->mtame < 20) mtmp->mtame++; + if (x != mtmp->mx || y != mtmp->my) { /* moved & ate on same turn */ + newsym(x, y); + newsym(mtmp->mx, mtmp->my); + } + if (is_pool(x, y) && !Underwater) { + /* Don't print obj */ + /* TODO: Reveal presence of sea monster (especially sharks) */ + } else + /* hack: observe the action if either new or old location is in view */ + if (cansee(x, y) || cansee(mtmp->mx, mtmp->my)) + pline("%s %s %s.", noit_Monnam(mtmp), + devour ? "devours" : "eats", + (obj->oclass == FOOD_CLASS) ? + singular(obj, doname) : doname(obj)); + /* It's a reward if it's DOGFOOD and the player dropped/threw it. */ + /* We know the player had it if invlet is set -dlc */ + if(dogfood(mtmp,obj) == DOGFOOD && obj->invlet) +#ifdef LINT + edog->apport = 0; +#else + edog->apport += (int)(200L/ + ((long)edog->dropdist + monstermoves - edog->droptime)); +#endif + if (mtmp->data == &mons[PM_RUST_MONSTER] && obj->oerodeproof) { + /* The object's rustproofing is gone now */ + obj->oerodeproof = 0; + mtmp->mstun = 1; + if (canseemon(mtmp) && flags.verbose) { + pline("%s spits %s out in disgust!", + Monnam(mtmp), distant_name(obj,doname)); + } + } else if (obj == uball) { + unpunish(); + delobj(obj); + } else if (obj == uchain) + unpunish(); + else if (obj->quan > 1L && obj->oclass == FOOD_CLASS) { + obj->quan--; + obj->owt = weight(obj); + } else + delobj(obj); + + if (poly) { + char oldpet[BUFSZ]; +#ifdef STEED + long mw = mtmp->misc_worn_check; + + mtmp->misc_worn_check &= ~W_SADDLE; +#endif + Strcpy(oldpet, Monnam(mtmp)); +#ifdef STEED + mtmp->misc_worn_check = mw; +#endif + if (newcham(mtmp, (struct permonst *)0, FALSE) && + cansee(mtmp->mx, mtmp->my)) { + uchar save_mnamelth = mtmp->mnamelth; + mtmp->mnamelth = 0; + pline("%s turns into %s!", oldpet, a_monnam(mtmp)); + mtmp->mnamelth = save_mnamelth; + } + } + /* limit "instant" growth to prevent potential abuse */ + if (grow && (int) mtmp->m_lev < (int)mtmp->data->mlevel + 15) { + if (!grow_up(mtmp, (struct monst *)0)) return 2; + } + if (heal) mtmp->mhp = mtmp->mhpmax; + return 1; +} + +#endif /* OVLB */ +#ifdef OVL0 + +/* hunger effects -- returns TRUE on starvation */ +STATIC_OVL boolean +dog_hunger(mtmp, edog) +register struct monst *mtmp; +register struct edog *edog; +{ + if (monstermoves > edog->hungrytime + 500) { + if (!carnivorous(mtmp->data) && !herbivorous(mtmp->data)) { + edog->hungrytime = monstermoves + 500; + /* but not too high; it might polymorph */ + } else if (!edog->mhpmax_penalty) { + /* starving pets are limited in healing */ + int newmhpmax = mtmp->mhpmax / 3; + mtmp->mconf = 1; + edog->mhpmax_penalty = mtmp->mhpmax - newmhpmax; + mtmp->mhpmax = newmhpmax; + if (mtmp->mhp > mtmp->mhpmax) + mtmp->mhp = mtmp->mhpmax; + if (mtmp->mhp < 1) goto dog_died; + if (cansee(mtmp->mx, mtmp->my)) + pline("%s is confused from hunger.", Monnam(mtmp)); + else if (couldsee(mtmp->mx, mtmp->my)) + beg(mtmp); + else + You_feel("worried about %s.", y_monnam(mtmp)); + stop_occupation(); + } else if (monstermoves > edog->hungrytime + 750 || mtmp->mhp < 1) { + dog_died: + if (mtmp->mleashed) + Your("leash goes slack."); + else if (cansee(mtmp->mx, mtmp->my)) + pline("%s dies%s.", Monnam(mtmp), + (mtmp->mhp >= 1) ? "" : " from hunger"); + else + You_feel("%s for a moment.", + Hallucination ? "bummed" : "sad"); + mondied(mtmp); + return(TRUE); + } + } + return(FALSE); +} + +/* do something with object (drop, pick up, eat) at current position + * returns 1 if object eaten (since that counts as dog's move), 2 if died + */ +STATIC_OVL int +dog_invent(mtmp, edog, udist) +register struct monst *mtmp; +register struct edog *edog; +int udist; +{ + register int omx, omy; + struct obj *obj; + + if (mtmp->msleeping || !mtmp->mcanmove) return(0); + + omx = mtmp->mx; + omy = mtmp->my; + + /* if we are carrying sth then we drop it (perhaps near @) */ + /* Note: if apport == 1 then our behaviour is independent of udist */ + /* Use udist+1 so steed won't cause divide by zero */ +#ifndef GOLDOBJ + if(DROPPABLES(mtmp) || mtmp->mgold) { +#else + if(DROPPABLES(mtmp)) { +#endif + if (!rn2(udist+1) || !rn2(edog->apport)) + if(rn2(10) < edog->apport){ + relobj(mtmp, (int)mtmp->minvis, TRUE); + if(edog->apport > 1) edog->apport--; + edog->dropdist = udist; /* hpscdi!jon */ + edog->droptime = monstermoves; + } + } else { + if((obj=level.objects[omx][omy]) && !index(nofetch,obj->oclass) +#ifdef MAIL + && obj->otyp != SCR_MAIL +#endif + ){ + if (dogfood(mtmp, obj) <= CADAVER) + return dog_eat(mtmp, obj, omx, omy, FALSE); + + if(can_carry(mtmp, obj) && !obj->cursed && + !is_pool(mtmp->mx,mtmp->my)) { + if(rn2(20) < edog->apport+3) { + if (rn2(udist) || !rn2(edog->apport)) { + if (cansee(omx, omy) && flags.verbose) + pline("%s picks up %s.", Monnam(mtmp), + distant_name(obj, doname)); + obj_extract_self(obj); + newsym(omx,omy); + (void) mpickobj(mtmp,obj); + if (attacktype(mtmp->data, AT_WEAP) && + mtmp->weapon_check == NEED_WEAPON) { + mtmp->weapon_check = NEED_HTH_WEAPON; + (void) mon_wield_item(mtmp); + } + m_dowear(mtmp, FALSE); + } + } + } + } + } + return 0; +} + +/* set dog's goal -- gtyp, gx, gy + * returns -1/0/1 (dog's desire to approach player) or -2 (abort move) + */ +STATIC_OVL int +dog_goal(mtmp, edog, after, udist, whappr) +register struct monst *mtmp; +struct edog *edog; +int after, udist, whappr; +{ + register int omx, omy; + boolean in_masters_sight; + register struct obj *obj; + xchar otyp; + int appr; + + +#ifdef STEED + /* Steeds don't move on their own will */ + if (mtmp == u.usteed) + return (-2); +#endif + + omx = mtmp->mx; + omy = mtmp->my; + + in_masters_sight = couldsee(omx, omy); + + if (!edog || mtmp->mleashed) { /* he's not going anywhere... */ + gtyp = APPORT; + gx = u.ux; + gy = u.uy; + } else { +#define DDIST(x,y) (dist2(x,y,omx,omy)) +#define SQSRCHRADIUS 5 + int min_x, max_x, min_y, max_y; + register int nx, ny; + + gtyp = UNDEF; /* no goal as yet */ + gx = gy = 0; /* suppress 'used before set' message */ + + if ((min_x = omx - SQSRCHRADIUS) < 0) min_x = 0; + if ((max_x = omx + SQSRCHRADIUS) >= COLNO) max_x = COLNO - 1; + if ((min_y = omy - SQSRCHRADIUS) < 0) min_y = 0; + if ((max_y = omy + SQSRCHRADIUS) >= ROWNO) max_y = ROWNO - 1; + + /* nearby food is the first choice, then other objects */ + for (obj = fobj; obj; obj = obj->nobj) { + nx = obj->ox; + ny = obj->oy; + if (nx >= min_x && nx <= max_x && ny >= min_y && ny <= max_y) { + otyp = dogfood(mtmp, obj); + if (otyp > gtyp || otyp == UNDEF) + continue; + if (cursed_object_at(nx, ny)) + continue; + if (otyp < MANFOOD) { + if (otyp < gtyp || DDIST(nx,ny) < DDIST(gx,gy)) { + gx = nx; + gy = ny; + gtyp = otyp; + } + } else if(gtyp == UNDEF && in_masters_sight && + !mtmp->minvent && + (!levl[omx][omy].lit || levl[u.ux][u.uy].lit) && + (otyp == MANFOOD || m_cansee(mtmp, nx, ny)) && + edog->apport > rn2(8) && + can_carry(mtmp,obj)) { + gx = nx; + gy = ny; + gtyp = APPORT; + } + } + } + } + + /* follow player if appropriate */ + if (gtyp == UNDEF || + (gtyp != DOGFOOD && gtyp != APPORT && monstermoves < edog->hungrytime)) { + gx = u.ux; + gy = u.uy; + if (after && udist <= 4 && gx == u.ux && gy == u.uy) + return(-2); + appr = (udist >= 9) ? 1 : (mtmp->mflee) ? -1 : 0; + if (udist > 1) { + if (!IS_ROOM(levl[u.ux][u.uy].typ) || !rn2(4) || + whappr || + (mtmp->minvent && rn2(edog->apport))) + appr = 1; + } + /* if you have dog food it'll follow you more closely */ + if (appr == 0) { + obj = invent; + while (obj) { + if(dogfood(mtmp, obj) == DOGFOOD) { + appr = 1; + break; + } + obj = obj->nobj; + } + } + } else + appr = 1; /* gtyp != UNDEF */ + if(mtmp->mconf) + appr = 0; + +#define FARAWAY (COLNO + 2) /* position outside screen */ + if (gx == u.ux && gy == u.uy && !in_masters_sight) { + register coord *cp; + + cp = gettrack(omx,omy); + if (cp) { + gx = cp->x; + gy = cp->y; + if(edog) edog->ogoal.x = 0; + } else { + /* assume master hasn't moved far, and reuse previous goal */ + if(edog && edog->ogoal.x && + ((edog->ogoal.x != omx) || (edog->ogoal.y != omy))) { + gx = edog->ogoal.x; + gy = edog->ogoal.y; + edog->ogoal.x = 0; + } else { + int fardist = FARAWAY * FARAWAY; + gx = gy = FARAWAY; /* random */ + do_clear_area(omx, omy, 9, wantdoor, + (genericptr_t)&fardist); + + /* here gx == FARAWAY e.g. when dog is in a vault */ + if (gx == FARAWAY || (gx == omx && gy == omy)) { + gx = u.ux; + gy = u.uy; + } else if(edog) { + edog->ogoal.x = gx; + edog->ogoal.y = gy; + } + } + } + } else if(edog) { + edog->ogoal.x = 0; + } + return appr; +} + +/* return 0 (no move), 1 (move) or 2 (dead) */ +int +dog_move(mtmp, after) +register struct monst *mtmp; +register int after; /* this is extra fast monster movement */ +{ + int omx, omy; /* original mtmp position */ + int appr, whappr, udist; + int i, j, k; + register struct edog *edog = EDOG(mtmp); + struct obj *obj = (struct obj *) 0; + xchar otyp; + boolean has_edog, cursemsg[9], do_eat = FALSE; + xchar nix, niy; /* position mtmp is (considering) moving to */ + register int nx, ny; /* temporary coordinates */ + xchar cnt, uncursedcnt, chcnt; + int chi = -1, nidist, ndist; + coord poss[9]; + long info[9], allowflags; +#define GDIST(x,y) (dist2(x,y,gx,gy)) + + /* + * Tame Angels have isminion set and an ispriest structure instead of + * an edog structure. Fortunately, guardian Angels need not worry + * about mundane things like eating and fetching objects, and can + * spend all their energy defending the player. (They are the only + * monsters with other structures that can be tame.) + */ + has_edog = !mtmp->isminion; + + omx = mtmp->mx; + omy = mtmp->my; + if (has_edog && dog_hunger(mtmp, edog)) return(2); /* starved */ + + udist = distu(omx,omy); +#ifdef STEED + /* Let steeds eat and maybe throw rider during Conflict */ + if (mtmp == u.usteed) { + if (Conflict && !resist(mtmp, RING_CLASS, 0, 0)) { + dismount_steed(DISMOUNT_THROWN); + return (1); + } + udist = 1; + } else +#endif + /* maybe we tamed him while being swallowed --jgm */ + if (!udist) return(0); + + nix = omx; /* set before newdogpos */ + niy = omy; + cursemsg[0] = FALSE; /* lint suppression */ + info[0] = 0; /* ditto */ + + if (has_edog) { + j = dog_invent(mtmp, edog, udist); + if (j == 2) return 2; /* died */ + else if (j == 1) goto newdogpos; /* eating something */ + + whappr = (monstermoves - edog->whistletime < 5); + } else + whappr = 0; + + appr = dog_goal(mtmp, has_edog ? edog : (struct edog *)0, + after, udist, whappr); + if (appr == -2) return(0); + + allowflags = ALLOW_M | ALLOW_TRAPS | ALLOW_SSM | ALLOW_SANCT; + if (passes_walls(mtmp->data)) allowflags |= (ALLOW_ROCK|ALLOW_WALL); + if (throws_rocks(mtmp->data)) allowflags |= ALLOW_ROCK; + if (Conflict && !resist(mtmp, RING_CLASS, 0, 0)) { + allowflags |= ALLOW_U; + if (!has_edog) { + coord mm; + /* Guardian angel refuses to be conflicted; rather, + * it disappears, angrily, and sends in some nasties + */ + if (canspotmon(mtmp)) { + pline("%s rebukes you, saying:", Monnam(mtmp)); + verbalize("Since you desire conflict, have some more!"); + } + mongone(mtmp); + i = rnd(4); + while(i--) { + mm.x = u.ux; + mm.y = u.uy; + if(enexto(&mm, mm.x, mm.y, &mons[PM_ANGEL])) + (void) mk_roamer(&mons[PM_ANGEL], u.ualign.type, + mm.x, mm.y, FALSE); + } + return(2); + + } + } + if (!Conflict && !mtmp->mconf && + mtmp == u.ustuck && !sticks(youmonst.data)) { + unstuck(mtmp); /* swallowed case handled above */ + You("get released!"); + } + if (!nohands(mtmp->data) && !verysmall(mtmp->data)) { + allowflags |= OPENDOOR; + if (m_carrying(mtmp, SKELETON_KEY)) allowflags |= BUSTDOOR; + } + if (is_giant(mtmp->data)) allowflags |= BUSTDOOR; + if (tunnels(mtmp->data) && (!needspick(mtmp->data) || + m_carrying(mtmp, PICK_AXE) || + m_carrying(mtmp, DWARVISH_MATTOCK))) + allowflags |= ALLOW_DIG; + cnt = mfndpos(mtmp, poss, info, allowflags); + + /* Normally dogs don't step on cursed items, but if they have no + * other choice they will. This requires checking ahead of time + * to see how many uncursed item squares are around. + */ + uncursedcnt = 0; + for (i = 0; i < cnt; i++) { + nx = poss[i].x; ny = poss[i].y; + if (MON_AT(nx,ny) && !(info[i] & ALLOW_M)) continue; + if (cursed_object_at(nx, ny)) continue; + uncursedcnt++; + } + + chcnt = 0; + chi = -1; + nidist = GDIST(nix,niy); + + for (i = 0; i < cnt; i++) { + nx = poss[i].x; + ny = poss[i].y; + cursemsg[i] = FALSE; + + /* if leashed, we drag him along. */ + if (mtmp->mleashed && distu(nx, ny) > 4) continue; + + /* if a guardian, try to stay close by choice */ + if (!has_edog && + (j = distu(nx, ny)) > 16 && j >= udist) continue; + + if ((info[i] & ALLOW_M) && MON_AT(nx, ny)) { + int mstatus; + register struct monst *mtmp2 = m_at(nx,ny); + + if ((int)mtmp2->m_lev >= (int)mtmp->m_lev+2 || + (mtmp2->data == &mons[PM_FLOATING_EYE] && rn2(10) && + mtmp->mcansee && haseyes(mtmp->data) && mtmp2->mcansee + && (perceives(mtmp->data) || !mtmp2->minvis)) || + (mtmp2->data==&mons[PM_GELATINOUS_CUBE] && rn2(10)) || + (max_passive_dmg(mtmp2, mtmp) >= mtmp->mhp) || + ((mtmp->mhp*4 < mtmp->mhpmax + || mtmp2->data->msound == MS_GUARDIAN + || mtmp2->data->msound == MS_LEADER) && + mtmp2->mpeaceful && !Conflict) || + (touch_petrifies(mtmp2->data) && + !resists_ston(mtmp))) + continue; + + if (after) return(0); /* hit only once each move */ + + notonhead = 0; + mstatus = mattackm(mtmp, mtmp2); + + /* aggressor (pet) died */ + if (mstatus & MM_AGR_DIED) return 2; + + if ((mstatus & MM_HIT) && !(mstatus & MM_DEF_DIED) && + rn2(4) && mtmp2->mlstmv != monstermoves && + !onscary(mtmp->mx, mtmp->my, mtmp2) && + /* monnear check needed: long worms hit on tail */ + monnear(mtmp2, mtmp->mx, mtmp->my)) { + mstatus = mattackm(mtmp2, mtmp); /* return attack */ + if (mstatus & MM_DEF_DIED) return 2; + } + + return 0; + } + + { /* Dog avoids harmful traps, but perhaps it has to pass one + * in order to follow player. (Non-harmful traps do not + * have ALLOW_TRAPS in info[].) The dog only avoids the + * trap if you've seen it, unlike enemies who avoid traps + * if they've seen some trap of that type sometime in the + * past. (Neither behavior is really realistic.) + */ + struct trap *trap; + + if ((info[i] & ALLOW_TRAPS) && (trap = t_at(nx,ny))) { + if (mtmp->mleashed) { + if (flags.soundok) whimper(mtmp); + } else + /* 1/40 chance of stepping on it anyway, in case + * it has to pass one to follow the player... + */ + if (trap->tseen && rn2(40)) continue; + } + } + + /* dog eschews cursed objects, but likes dog food */ + /* (minion isn't interested; `cursemsg' stays FALSE) */ + if (has_edog) + for (obj = level.objects[nx][ny]; obj; obj = obj->nexthere) { + if (obj->cursed) cursemsg[i] = TRUE; + else if ((otyp = dogfood(mtmp, obj)) < MANFOOD && + (otyp < ACCFOOD || edog->hungrytime <= monstermoves)) { + /* Note: our dog likes the food so much that he + * might eat it even when it conceals a cursed object */ + nix = nx; + niy = ny; + chi = i; + do_eat = TRUE; + cursemsg[i] = FALSE; /* not reluctant */ + goto newdogpos; + } + } + /* didn't find something to eat; if we saw a cursed item and + aren't being forced to walk on it, usually keep looking */ + if (cursemsg[i] && !mtmp->mleashed && uncursedcnt > 0 && + rn2(13 * uncursedcnt)) continue; + + /* lessen the chance of backtracking to previous position(s) */ + k = has_edog ? uncursedcnt : cnt; + for (j = 0; j < MTSZ && j < k - 1; j++) + if (nx == mtmp->mtrack[j].x && ny == mtmp->mtrack[j].y) + if (rn2(MTSZ * (k - j))) goto nxti; + + j = ((ndist = GDIST(nx,ny)) - nidist) * appr; + if ((j == 0 && !rn2(++chcnt)) || j < 0 || + (j > 0 && !whappr && + ((omx == nix && omy == niy && !rn2(3)) + || !rn2(12)) + )) { + nix = nx; + niy = ny; + nidist = ndist; + if(j < 0) chcnt = 0; + chi = i; + } + nxti: ; + } +newdogpos: + if (nix != omx || niy != omy) { + struct obj *mw_tmp; + + if (info[chi] & ALLOW_U) { + if (mtmp->mleashed) { /* play it safe */ + pline("%s breaks loose of %s leash!", + Monnam(mtmp), mhis(mtmp)); + m_unleash(mtmp); + } + (void) mattacku(mtmp); + return(0); + } + if (!m_in_out_region(mtmp, nix, niy)) + return 1; + if(IS_ROCK(levl[nix][niy].typ) && may_dig(nix,niy) && + mtmp->weapon_check != NO_WEAPON_WANTED && + tunnels(mtmp->data) && needspick(mtmp->data) && + (!(mw_tmp = MON_WEP(mtmp)) || !is_pick(mw_tmp))) { + mtmp->weapon_check = NEED_PICK_AXE; + if (mon_wield_item(mtmp)) + return 0; + } + /* insert a worm_move() if worms ever begin to eat things */ + remove_monster(omx, omy); + place_monster(mtmp, nix, niy); + if (cursemsg[chi] && (cansee(omx,omy) || cansee(nix,niy))) + pline("%s moves only reluctantly.", Monnam(mtmp)); + for (j=MTSZ-1; j>0; j--) mtmp->mtrack[j] = mtmp->mtrack[j-1]; + mtmp->mtrack[0].x = omx; + mtmp->mtrack[0].y = omy; + /* We have to know if the pet's gonna do a combined eat and + * move before moving it, but it can't eat until after being + * moved. Thus the do_eat flag. + */ + if (do_eat) { + if (dog_eat(mtmp, obj, omx, omy, FALSE) == 2) return 2; + } + } else if (mtmp->mleashed && distu(omx, omy) > 4) { + /* an incredible kludge, but the only way to keep pooch near + * after it spends time eating or in a trap, etc. + */ + coord cc; + + nx = sgn(omx - u.ux); + ny = sgn(omy - u.uy); + cc.x = u.ux + nx; + cc.y = u.uy + ny; + if (goodpos(cc.x, cc.y, mtmp)) goto dognext; + + i = xytod(nx, ny); + for (j = (i + 7)%8; j < (i + 1)%8; j++) { + dtoxy(&cc, j); + if (goodpos(cc.x, cc.y, mtmp)) goto dognext; + } + for (j = (i + 6)%8; j < (i + 2)%8; j++) { + dtoxy(&cc, j); + if (goodpos(cc.x, cc.y, mtmp)) goto dognext; + } + cc.x = mtmp->mx; + cc.y = mtmp->my; +dognext: + if (!m_in_out_region(mtmp, nix, niy)) + return 1; + remove_monster(mtmp->mx, mtmp->my); + place_monster(mtmp, cc.x, cc.y); + newsym(cc.x,cc.y); + set_apparxy(mtmp); + } + return(1); +} + +#endif /* OVL0 */ +#ifdef OVLB + +/*ARGSUSED*/ /* do_clear_area client */ +STATIC_PTR void +wantdoor(x, y, distance) +int x, y; +genericptr_t distance; +{ + int ndist; + + if (*(int*)distance > (ndist = distu(x, y))) { + gx = x; + gy = y; + *(int*)distance = ndist; + } +} + +#endif /* OVLB */ + +/*dogmove.c*/