aboutsummaryrefslogtreecommitdiff
path: root/hack/hack.dog.c
diff options
context:
space:
mode:
Diffstat (limited to 'hack/hack.dog.c')
-rw-r--r--hack/hack.dog.c459
1 files changed, 459 insertions, 0 deletions
diff --git a/hack/hack.dog.c b/hack/hack.dog.c
new file mode 100644
index 0000000..531a3a4
--- /dev/null
+++ b/hack/hack.dog.c
@@ -0,0 +1,459 @@
+/* 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);
+}