/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* hack.dog.c - version 1.0.3 */ /* $FreeBSD: src/games/hack/hack.dog.c,v 1.3 1999/11/16 02:57:03 billf Exp $ */ #include "hack.h" #include "hack.mfndpos.h" #include "def.edog.h" struct permonst li_dog = { "little dog", 'd', 2, 18, 6, 1, 6, sizeof(struct edog) }; struct permonst dog = { "dog", 'd', 4, 16, 5, 1, 6, sizeof(struct edog) }; struct permonst la_dog = { "large dog", 'd', 6, 15, 4, 2, 4, sizeof(struct edog) }; static void initedog(struct monst *); static xchar dogfood(struct obj *); void makedog(void) { struct monst *mtmp = makemon(&li_dog, u.ux, u.uy); if (!mtmp) return; /* dogs were genocided */ initedog(mtmp); } static void initedog(struct monst *mtmp) { mtmp->mtame = mtmp->mpeaceful = 1; EDOG(mtmp)->hungrytime = 1000 + moves; EDOG(mtmp)->eattime = 0; EDOG(mtmp)->droptime = 0; EDOG(mtmp)->dropdist = 10000; EDOG(mtmp)->apport = 10; EDOG(mtmp)->whistletime = 0; } /* attach the monsters that went down (or up) together with @ */ struct monst *mydogs = NULL; struct monst *fallen_down = NULL; /* monsters that fell through a trapdoor */ /* they will appear on the next level @ goes to, even if he goes up! */ void losedogs(void) { struct monst *mtmp; while ((mtmp = mydogs)) { mydogs = mtmp->nmon; mtmp->nmon = fmon; fmon = mtmp; mnexto(mtmp); } while ((mtmp = fallen_down)) { fallen_down = mtmp->nmon; mtmp->nmon = fmon; fmon = mtmp; rloc(mtmp); } } void keepdogs(void) { struct monst *mtmp; for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) if (dist(mtmp->mx, mtmp->my) < 3 && follower(mtmp) && !mtmp->msleep && !mtmp->mfroz) { relmon(mtmp); mtmp->nmon = mydogs; mydogs = mtmp; unpmon(mtmp); keepdogs(); /* we destroyed the link, so use recursion */ return; /* (admittedly somewhat primitive) */ } } void fall_down(struct monst *mtmp) { relmon(mtmp); mtmp->nmon = fallen_down; fallen_down = mtmp; unpmon(mtmp); mtmp->mtame = 0; } /* return quality of food; the lower the better */ #define DOGFOOD 0 #define CADAVER 1 #define ACCFOOD 2 #define MANFOOD 3 #define APPORT 4 #define POISON 5 #define UNDEF 6 static xchar dogfood(struct obj *obj) { switch (obj->olet) { case FOOD_SYM: return ( (obj->otyp == TRIPE_RATION) ? DOGFOOD : (obj->otyp < CARROT) ? ACCFOOD : (obj->otyp < CORPSE) ? MANFOOD : (poisonous(obj) || obj->age + 50 <= moves || obj->otyp == DEAD_COCKATRICE) ? POISON : CADAVER ); default: if (!obj->cursed) return (APPORT); /* fall into next case */ case BALL_SYM: case CHAIN_SYM: case ROCK_SYM: return (UNDEF); } } /* return 0 (no move), 1 (move) or 2 (dead) */ int dog_move(struct monst *mtmp, int after) { int nx, ny, omx, omy, appr, nearer, j; int udist, chi = 0, i, whappr; struct monst *mtmp2; struct permonst *mdat = mtmp->data; struct edog *edog = EDOG(mtmp); struct obj *obj; struct trap *trap; xchar cnt, chcnt, nix, niy; schar dogroom, uroom; xchar gx, gy, gtyp, otyp; /* current goal */ coord poss[9]; int info[9]; #define GDIST(x, y) ((x - gx) * (x - gx) + (y - gy) * (y - gy)) #define DDIST(x, y) ((x - omx) * (x - omx) + (y - omy) * (y - omy)) if (moves <= edog->eattime) /* dog is still eating */ return (0); omx = mtmp->mx; omy = mtmp->my; whappr = (moves - EDOG(mtmp)->whistletime < 5); if (moves > edog->hungrytime + 500 && !mtmp->mconf) { mtmp->mconf = 1; mtmp->mhpmax /= 3; if (mtmp->mhp > mtmp->mhpmax) mtmp->mhp = mtmp->mhpmax; if (cansee(omx, omy)) pline("%s is confused from hunger.", Monnam(mtmp)); else pline("You feel worried about %s.", monnam(mtmp)); } else if (moves > edog->hungrytime + 750 || mtmp->mhp < 1) { if (cansee(omx, omy)) pline("%s dies from hunger.", Monnam(mtmp)); else pline("You have a sad feeling for a moment, then it passes."); mondied(mtmp); return (2); } dogroom = inroom(omx, omy); uroom = inroom(u.ux, u.uy); udist = dist(omx, omy); /* maybe we tamed him while being swallowed --jgm */ if (!udist) return (0); /* if we are carrying sth then we drop it (perhaps near @) */ /* Note: if apport == 1 then our behaviour is independent of udist */ if (mtmp->minvent) { if (!rn2(udist) || !rn2((int)edog->apport)) if (rn2(10) < (int)edog->apport) { relobj(mtmp, (int)mtmp->minvis); if (edog->apport > 1) edog->apport--; edog->dropdist = udist; /* hpscdi!jon */ edog->droptime = moves; } } else if ((obj = o_at(omx, omy))) { if (!strchr("0_", obj->olet)) { if ((otyp = dogfood(obj)) <= CADAVER) { nix = omx; niy = omy; goto eatobj; } if (obj->owt < 10 * mtmp->data->mlevel) { if (rn2(20) < (int)edog->apport + 3) { if (rn2(udist) || !rn2((int)edog->apport)) { freeobj(obj); unpobj(obj); /* if (levl[omx][omy].scrsym == obj->olet) * newsym(omx, omy); */ mpickobj(mtmp, obj); } } } } } /* first we look for food */ gtyp = UNDEF; /* no goal as yet */ gx = gy = 0; /* suppress 'used before set' message */ for (obj = fobj; obj; obj = obj->nobj) { otyp = dogfood(obj); if (otyp > gtyp || otyp == UNDEF) continue; if (inroom(obj->ox, obj->oy) != dogroom) continue; if (otyp < MANFOOD && (dogroom >= 0 || DDIST(obj->ox, obj->oy) < 10)) { if (otyp < gtyp || (otyp == gtyp && DDIST(obj->ox, obj->oy) < DDIST(gx, gy))) { gx = obj->ox; gy = obj->oy; gtyp = otyp; } } else if (gtyp == UNDEF && dogroom >= 0 && uroom == dogroom && !mtmp->minvent && (int)edog->apport > rn2(8)) { gx = obj->ox; gy = obj->oy; gtyp = APPORT; } } if (gtyp == UNDEF || (gtyp != DOGFOOD && gtyp != APPORT && moves < edog->hungrytime)) { if (dogroom < 0 || dogroom == uroom) { gx = u.ux; gy = u.uy; #ifndef QUEST } else { int tmp = rooms[dogroom].fdoor; cnt = rooms[dogroom].doorct; gx = gy = FAR; /* random, far away */ while (cnt--) { if (dist(gx, gy) > dist(doors[tmp].x, doors[tmp].y)) { gx = doors[tmp].x; gy = doors[tmp].y; } tmp++; } /* here gx == FAR e.g. when dog is in a vault */ if (gx == FAR || (gx == omx && gy == omy)) { gx = u.ux; gy = u.uy; } #endif /* QUEST */ } appr = (udist >= 9) ? 1 : (mtmp->mflee) ? -1 : 0; if (after && udist <= 4 && gx == u.ux && gy == u.uy) return (0); if (udist > 1) { if (!IS_ROOM(levl[u.ux][u.uy].typ) || !rn2(4) || whappr || (mtmp->minvent && rn2((int)edog->apport))) appr = 1; } /* if you have dog food he'll follow you more closely */ if (appr == 0) { obj = invent; while (obj) { if (obj->otyp == TRIPE_RATION) { appr = 1; break; } obj = obj->nobj; } } } else /* gtyp != UNDEF */ appr = 1; if (mtmp->mconf) appr = 0; if (gx == u.ux && gy == u.uy && (dogroom != uroom || dogroom < 0)) { coord *cp; cp = gettrack(omx, omy); if (cp) { gx = cp->x; gy = cp->y; } } nix = omx; niy = omy; cnt = mfndpos(mtmp, poss, info, ALLOW_M | ALLOW_TRAPS); chcnt = 0; chi = -1; for (i = 0; i < cnt; i++) { nx = poss[i].x; ny = poss[i].y; if (info[i] & ALLOW_M) { mtmp2 = m_at(nx, ny); if (mtmp2->data->mlevel >= mdat->mlevel + 2 || mtmp2->data->mlet == 'c') continue; if (after) /* hit only once each move */ return (0); if (hitmm(mtmp, mtmp2) == 1 && rn2(4) && mtmp2->mlstmv != moves && hitmm(mtmp2, mtmp) == 2) return (2); return (0); } /* dog avoids traps */ /* but perhaps we have to pass a trap in order to follow @ */ if ((info[i] & ALLOW_TRAPS) && (trap = t_at(nx, ny))) { if (!trap->tseen && rn2(40)) continue; if (rn2(10)) continue; } /* dog eschewes cursed objects */ /* but likes dog food */ obj = fobj; while (obj) { if (obj->ox != nx || obj->oy != ny) goto nextobj; if (obj->cursed) goto nxti; if (obj->olet == FOOD_SYM && (otyp = dogfood(obj)) < MANFOOD && (otyp < ACCFOOD || edog->hungrytime <= moves)) { /* * 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; eatobj: edog->eattime = moves + obj->quan * objects[obj->otyp].oc_delay; if (edog->hungrytime < moves) edog->hungrytime = moves; edog->hungrytime += 5 * obj->quan * objects[obj->otyp].nutrition; mtmp->mconf = 0; if (cansee(nix, niy)) pline("%s ate %s.", Monnam( mtmp), doname(obj)); /* perhaps this was a reward */ if (otyp != CADAVER) edog->apport += 200 / (edog->dropdist + moves - edog->droptime); delobj(obj); goto newdogpos; } nextobj: obj = obj->nobj; } for (j = 0; j < MTSZ && j < cnt - 1; j++) if (nx == mtmp->mtrack[j].x && ny == mtmp->mtrack[j].y) if (rn2(4 * (cnt - j))) goto nxti; /* Some stupid C compilers cannot compute the whole expression at once. */ nearer = GDIST(nx, ny); nearer -= GDIST(nix, niy); nearer *= appr; if ((nearer == 0 && !rn2(++chcnt)) || nearer < 0 || (nearer > 0 && !whappr && ((omx == nix && omy == niy && !rn2(3)) || !rn2(12)) )) { nix = nx; niy = ny; if (nearer < 0) chcnt = 0; chi = i; } nxti:; } newdogpos: if (nix != omx || niy != omy) { if (info[chi] & ALLOW_U) { hitu(mtmp, d(mdat->damn, mdat->damd) + 1); return (0); } mtmp->mx = nix; mtmp->my = niy; for (j = MTSZ - 1; j > 0; j--) mtmp->mtrack[j] = mtmp->mtrack[j - 1]; mtmp->mtrack[0].x = omx; mtmp->mtrack[0].y = omy; } if (mintrap(mtmp) == 2) /* he died */ return (2); pmon(mtmp); return (1); } /* return roomnumber or -1 */ int inroom(xchar x, xchar y) { #ifndef QUEST struct mkroom *croom = &rooms[0]; while (croom->hx >= 0) { if (croom->hx >= x - 1 && croom->lx <= x + 1 && croom->hy >= y - 1 && croom->ly <= y + 1) return (croom - rooms); croom++; } #endif /* QUEST */ return (-1); /* not in room or on door */ } bool tamedog(struct monst *mtmp, struct obj *obj) { struct monst *mtmp2; if (flags.moonphase == FULL_MOON && night() && rn2(6)) return (0); /* If we cannot tame him, at least he's no longer afraid. */ mtmp->mflee = 0; mtmp->mfleetim = 0; if (mtmp->mtame || mtmp->mfroz || #ifndef NOWORM mtmp->wormno || #endif /* NOWORM */ mtmp->isshk || mtmp->isgd || strchr(" &@12", mtmp->data->mlet)) return (0); /* no tame long worms? */ if (obj) { if (dogfood(obj) >= MANFOOD) return (0); if (cansee(mtmp->mx, mtmp->my)) pline("%s devours the %s.", Monnam(mtmp), objects[obj->otyp].oc_name); obfree(obj, NULL); } mtmp2 = newmonst(sizeof(struct edog) + mtmp->mnamelth); *mtmp2 = *mtmp; mtmp2->mxlth = sizeof(struct edog); if (mtmp->mnamelth) strcpy(NAME(mtmp2), NAME(mtmp)); initedog(mtmp2); replmon(mtmp, mtmp2); return (1); }