1 /*        $NetBSD: hack.dog.c,v 1.12 2011/08/07 06:03:45 dholland Exp $         */
2 
3 /*
4  * Copyright (c) 1985, Stichting Centrum voor Wiskunde en Informatica,
5  * Amsterdam
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are
10  * met:
11  *
12  * - Redistributions of source code must retain the above copyright notice,
13  * this list of conditions and the following disclaimer.
14  *
15  * - Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in the
17  * documentation and/or other materials provided with the distribution.
18  *
19  * - Neither the name of the Stichting Centrum voor Wiskunde en
20  * Informatica, nor the names of its contributors may be used to endorse or
21  * promote products derived from this software without specific prior
22  * written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
25  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
27  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
28  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
30  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
31  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35  */
36 
37 /*
38  * Copyright (c) 1982 Jay Fenlason <hack@gnu.org>
39  * All rights reserved.
40  *
41  * Redistribution and use in source and binary forms, with or without
42  * modification, are permitted provided that the following conditions
43  * are met:
44  * 1. Redistributions of source code must retain the above copyright
45  *    notice, this list of conditions and the following disclaimer.
46  * 2. Redistributions in binary form must reproduce the above copyright
47  *    notice, this list of conditions and the following disclaimer in the
48  *    documentation and/or other materials provided with the distribution.
49  * 3. The name of the author may not be used to endorse or promote products
50  *    derived from this software without specific prior written permission.
51  *
52  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
53  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
54  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
55  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
56  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
57  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
58  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
59  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
60  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
61  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
62  */
63 
64 #include <sys/cdefs.h>
65 #ifndef lint
66 __RCSID("$NetBSD: hack.dog.c,v 1.12 2011/08/07 06:03:45 dholland Exp $");
67 #endif                                  /* not lint */
68 
69 #include "hack.h"
70 #include "extern.h"
71 #include "hack.mfndpos.h"
72 #include "def.edog.h"
73 #include "def.mkroom.h"
74 
75 const struct permonst li_dog =
76 {"little dog", 'd', 2, 18, 6, 1, 6, sizeof(struct edog)};
77 const struct permonst dog =
78 {"dog", 'd', 4, 16, 5, 1, 6, sizeof(struct edog)};
79 const struct permonst la_dog =
80 {"large dog", 'd', 6, 15, 4, 2, 4, sizeof(struct edog)};
81 
82 static void initedog(struct monst *);
83 static int dogfood(struct obj *);
84 
85 void
makedog(void)86 makedog(void)
87 {
88           struct monst   *mtmp = makemon(&li_dog, u.ux, u.uy);
89           if (!mtmp)
90                     return;             /* dogs were genocided */
91           initedog(mtmp);
92 }
93 
94 static void
initedog(struct monst * mtmp)95 initedog(struct monst *mtmp)
96 {
97           mtmp->mtame = mtmp->mpeaceful = 1;
98           EDOG(mtmp)->hungrytime = 1000 + moves;
99           EDOG(mtmp)->eattime = 0;
100           EDOG(mtmp)->droptime = 0;
101           EDOG(mtmp)->dropdist = 10000;
102           EDOG(mtmp)->apport = 10;
103           EDOG(mtmp)->whistletime = 0;
104 }
105 
106 /* attach the monsters that went down (or up) together with @ */
107 struct monst   *mydogs = 0;
108 struct monst   *fallen_down = 0;/* monsters that fell through a trapdoor */
109 /* they will appear on the next level @ goes to, even if he goes up! */
110 
111 void
losedogs(void)112 losedogs(void)
113 {
114           struct monst   *mtmp;
115           while ((mtmp = mydogs) != NULL) {
116                     mydogs = mtmp->nmon;
117                     mtmp->nmon = fmon;
118                     fmon = mtmp;
119                     mnexto(mtmp);
120           }
121           while ((mtmp = fallen_down) != NULL) {
122                     fallen_down = mtmp->nmon;
123                     mtmp->nmon = fmon;
124                     fmon = mtmp;
125                     rloc(mtmp);
126           }
127 }
128 
129 void
keepdogs(void)130 keepdogs(void)
131 {
132           struct monst   *mtmp;
133           for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
134                     if (dist(mtmp->mx, mtmp->my) < 3 && follower(mtmp)
135                         && !mtmp->msleep && !mtmp->mfroz) {
136                               relmon(mtmp);
137                               mtmp->nmon = mydogs;
138                               mydogs = mtmp;
139                               unpmon(mtmp);
140                               keepdogs();         /* we destroyed the link, so use
141                                                    * recursion */
142                               return;   /* (admittedly somewhat primitive) */
143                     }
144 }
145 
146 void
fall_down(struct monst * mtmp)147 fall_down(struct monst *mtmp)
148 {
149           relmon(mtmp);
150           mtmp->nmon = fallen_down;
151           fallen_down = mtmp;
152           unpmon(mtmp);
153           mtmp->mtame = 0;
154 }
155 
156 /* return quality of food; the lower the better */
157 #define   DOGFOOD   0
158 #define   CADAVER   1
159 #define   ACCFOOD   2
160 #define   MANFOOD   3
161 #define   APPORT    4
162 #define   POISON    5
163 #define   UNDEF     6
164 static int
dogfood(struct obj * obj)165 dogfood(struct obj *obj)
166 {
167           switch (obj->olet) {
168           case FOOD_SYM:
169                     return (
170                               (obj->otyp == TRIPE_RATION) ? DOGFOOD :
171                               (obj->otyp < CARROT) ? ACCFOOD :
172                               (obj->otyp < CORPSE) ? MANFOOD :
173                               (poisonous(obj) || obj->age + 50 <= moves ||
174                                obj->otyp == DEAD_COCKATRICE)
175                               ? POISON : CADAVER
176                               );
177           default:
178                     if (!obj->cursed)
179                               return (APPORT);
180                     /* FALLTHROUGH */
181           case BALL_SYM:
182           case CHAIN_SYM:
183           case ROCK_SYM:
184                     return (UNDEF);
185           }
186 }
187 
188 /* return 0 (no move), 1 (move) or 2 (dead) */
189 int
dog_move(struct monst * mtmp,int after)190 dog_move(struct monst *mtmp, int after)
191 {
192           int             nx, ny, omx, omy, appr, nearer, j;
193           int             udist, chi = 0, i, whappr;
194           struct monst   *mtmp2;
195           const struct permonst *mdat = mtmp->data;
196           struct edog    *edog = EDOG(mtmp);
197           struct obj     *obj;
198           struct trap    *trap;
199           xchar           cnt, chcnt, nix, niy;
200           schar           dogroom, uroom;
201           xchar           gx = 0, gy = 0, gtyp, otyp;       /* current goal */
202           coord           poss[9];
203           int             info[9];
204 #define GDIST(x,y) ((x-gx)*(x-gx) + (y-gy)*(y-gy))
205 #define DDIST(x,y) ((x-omx)*(x-omx) + (y-omy)*(y-omy))
206 
207           if (moves <= edog->eattime)
208                     return (0);         /* dog is still eating */
209           omx = mtmp->mx;
210           omy = mtmp->my;
211           whappr = (moves - EDOG(mtmp)->whistletime < 5);
212           if (moves > edog->hungrytime + 500 && !mtmp->mconf) {
213                     mtmp->mconf = 1;
214                     mtmp->mhpmax /= 3;
215                     if (mtmp->mhp > mtmp->mhpmax)
216                               mtmp->mhp = mtmp->mhpmax;
217                     if (cansee(omx, omy))
218                               pline("%s is confused from hunger.", Monnam(mtmp));
219                     else
220                               pline("You feel worried about %s.", monnam(mtmp));
221           } else if (moves > edog->hungrytime + 750 || mtmp->mhp < 1) {
222                     if (cansee(omx, omy))
223                               pline("%s dies from hunger.", Monnam(mtmp));
224                     else
225                               pline("You have a sad feeling for a moment, then it passes.");
226                     mondied(mtmp);
227                     return (2);
228           }
229           dogroom = inroom(omx, omy);
230           uroom = inroom(u.ux, u.uy);
231           udist = dist(omx, omy);
232 
233           /* maybe we tamed him while being swallowed --jgm */
234           if (!udist)
235                     return (0);
236 
237           /* if we are carrying sth then we drop it (perhaps near @) */
238           /* Note: if apport == 1 then our behaviour is independent of udist */
239           if (mtmp->minvent) {
240                     if (!rn2(udist) || !rn2((int) edog->apport))
241                               if ((unsigned) rn2(10) < edog->apport) {
242                                         relobj(mtmp, (int) mtmp->minvis);
243                                         if (edog->apport > 1)
244                                                   edog->apport--;
245                                         edog->dropdist = udist;       /* hpscdi!jon */
246                                         edog->droptime = moves;
247                               }
248           } else {
249                     if ((obj = o_at(omx, omy)) != NULL)
250                               if (!strchr("0_", obj->olet)) {
251                                         if ((otyp = dogfood(obj)) <= CADAVER) {
252                                                   nix = omx;
253                                                   niy = omy;
254                                                   goto eatobj;
255                                         }
256                                         if (obj->owt < 10 * mtmp->data->mlevel)
257                                                   if ((unsigned) rn2(20) < edog->apport + 3)
258                                                             if (rn2(udist) || !rn2((int) edog->apport)) {
259                                                                       freeobj(obj);
260                                                                       unpobj(obj);
261                                                                       /*
262                                                                        * if(levl[omx][omy].s
263                                                                        * crsym ==
264                                                                        * obj->olet)
265                                                                        * newsym(omx,omy);
266                                                                        */
267                                                                       mpickobj(mtmp, obj);
268                                                             }
269                               }
270           }
271 
272           /* first we look for food */
273           gtyp = UNDEF;                 /* no goal as yet */
274 #ifdef LINT
275           gx = gy = 0;                  /* suppress 'used before set' message */
276 #endif    /* LINT */
277           for (obj = fobj; obj; obj = obj->nobj) {
278                     otyp = dogfood(obj);
279                     if (otyp > gtyp || otyp == UNDEF)
280                               continue;
281                     if (inroom(obj->ox, obj->oy) != dogroom)
282                               continue;
283                     if (otyp < MANFOOD &&
284                         (dogroom >= 0 || DDIST(obj->ox, obj->oy) < 10)) {
285                               if (otyp < gtyp || (otyp == gtyp &&
286                                          DDIST(obj->ox, obj->oy) < DDIST(gx, gy))) {
287                                         gx = obj->ox;
288                                         gy = obj->oy;
289                                         gtyp = otyp;
290                               }
291                     } else if (gtyp == UNDEF && dogroom >= 0 &&
292                                  uroom == dogroom &&
293                                  !mtmp->minvent && edog->apport > (unsigned)rn2(8)) {
294                               gx = obj->ox;
295                               gy = obj->oy;
296                               gtyp = APPORT;
297                     }
298           }
299           if (gtyp == UNDEF ||
300             (gtyp != DOGFOOD && gtyp != APPORT && moves < edog->hungrytime)) {
301                     if (dogroom < 0 || dogroom == uroom) {
302                               gx = u.ux;
303                               gy = u.uy;
304 #ifndef QUEST
305                     } else {
306                               int             tmp = rooms[dogroom].fdoor;
307                               cnt = rooms[dogroom].doorct;
308 
309                               gx = gy = FAR;      /* random, far away */
310                               while (cnt--) {
311                                         if (dist(gx, gy) >
312                                             dist(doors[tmp].x, doors[tmp].y)) {
313                                                   gx = doors[tmp].x;
314                                                   gy = doors[tmp].y;
315                                         }
316                                         tmp++;
317                               }
318                               /* here gx == FAR e.g. when dog is in a vault */
319                               if (gx == FAR || (gx == omx && gy == omy)) {
320                                         gx = u.ux;
321                                         gy = u.uy;
322                               }
323 #endif    /* QUEST */
324                     }
325                     appr = (udist >= 9) ? 1 : (mtmp->mflee) ? -1 : 0;
326                     if (after && udist <= 4 && gx == u.ux && gy == u.uy)
327                               return (0);
328                     if (udist > 1) {
329                               if (!IS_ROOM(levl[u.ux][u.uy].typ) || !rn2(4) ||
330                                   whappr ||
331                                   (mtmp->minvent && rn2((int) edog->apport)))
332                                         appr = 1;
333                     }
334                     /* if you have dog food he'll follow you more closely */
335                     if (appr == 0) {
336                               obj = invent;
337                               while (obj) {
338                                         if (obj->otyp == TRIPE_RATION) {
339                                                   appr = 1;
340                                                   break;
341                                         }
342                                         obj = obj->nobj;
343                               }
344                     }
345           } else
346                     appr = 1; /* gtyp != UNDEF */
347           if (mtmp->mconf)
348                     appr = 0;
349 
350           if (gx == u.ux && gy == u.uy && (dogroom != uroom || dogroom < 0)) {
351                     coord          *cp;
352                     cp = gettrack(omx, omy);
353                     if (cp) {
354                               gx = cp->x;
355                               gy = cp->y;
356                     }
357           }
358           nix = omx;
359           niy = omy;
360           cnt = mfndpos(mtmp, poss, info, ALLOW_M | ALLOW_TRAPS);
361           chcnt = 0;
362           chi = -1;
363           for (i = 0; i < cnt; i++) {
364                     nx = poss[i].x;
365                     ny = poss[i].y;
366                     if (info[i] & ALLOW_M) {
367                               mtmp2 = m_at(nx, ny);
368                               if (mtmp2 == NULL)
369                                         panic("error in dog_move");
370                               if (mtmp2->data->mlevel >= mdat->mlevel + 2 ||
371                                   mtmp2->data->mlet == 'c')
372                                         continue;
373                               if (after)
374                                         return (0);         /* hit only once each move */
375 
376                               if (hitmm(mtmp, mtmp2) == 1 && rn2(4) &&
377                                   mtmp2->mlstmv != moves &&
378                                   hitmm(mtmp2, mtmp) == 2)
379                                         return (2);
380                               return (0);
381                     }
382                     /* dog avoids traps */
383                     /* but perhaps we have to pass a trap in order to follow @ */
384                     if ((info[i] & ALLOW_TRAPS) && (trap = t_at(nx, ny))) {
385                               if (!trap->tseen && rn2(40))
386                                         continue;
387                               if (rn2(10))
388                                         continue;
389                     }
390                     /* dog eschewes cursed objects */
391                     /* but likes dog food */
392                     obj = fobj;
393                     while (obj) {
394                               if (obj->ox != nx || obj->oy != ny)
395                                         goto nextobj;
396                               if (obj->cursed)
397                                         goto nxti;
398                               if (obj->olet == FOOD_SYM &&
399                                   (otyp = dogfood(obj)) < MANFOOD &&
400                                   (otyp < ACCFOOD || edog->hungrytime <= moves)) {
401                                         /*
402                                          * Note: our dog likes the food so much that
403                                          * he might eat it even when it conceals a
404                                          * cursed object
405                                          */
406                                         nix = nx;
407                                         niy = ny;
408                                         chi = i;
409                     eatobj:
410                                         edog->eattime =
411                                                   moves + obj->quan * objects[obj->otyp].oc_delay;
412                                         if (edog->hungrytime < moves)
413                                                   edog->hungrytime = moves;
414                                         edog->hungrytime +=
415                                                   5 * obj->quan * objects[obj->otyp].nutrition;
416                                         mtmp->mconf = 0;
417                                         if (cansee(nix, niy))
418                                                   pline("%s ate %s.", Monnam(mtmp), doname(obj));
419                                         /* perhaps this was a reward */
420                                         if (otyp != CADAVER)
421                                                   edog->apport += 200 / (edog->dropdist + moves - edog->droptime);
422                                         delobj(obj);
423                                         goto newdogpos;
424                               }
425           nextobj:
426                               obj = obj->nobj;
427                     }
428 
429                     for (j = 0; j < MTSZ && j < cnt - 1; j++)
430                               if (nx == mtmp->mtrack[j].x && ny == mtmp->mtrack[j].y)
431                                         if (rn2(4 * (cnt - j)))
432                                                   goto nxti;
433 
434                     /*
435                      * Some stupid C compilers cannot compute the whole
436                      * expression at once.
437                      */
438                     nearer = GDIST(nx, ny);
439                     nearer -= GDIST(nix, niy);
440                     nearer *= appr;
441                     if ((nearer == 0 && !rn2(++chcnt)) || nearer < 0 ||
442                         (nearer > 0 && !whappr &&
443                          ((omx == nix && omy == niy && !rn2(3))
444                           || !rn2(12))
445                          )) {
446                               nix = nx;
447                               niy = ny;
448                               if (nearer < 0)
449                                         chcnt = 0;
450                               chi = i;
451                     }
452 nxti:               ;
453           }
454 newdogpos:
455           if (nix != omx || niy != omy) {
456                     if (info[chi] & ALLOW_U) {
457                               (void) hitu(mtmp, d(mdat->damn, mdat->damd) + 1);
458                               return (0);
459                     }
460                     mtmp->mx = nix;
461                     mtmp->my = niy;
462                     for (j = MTSZ - 1; j > 0; j--)
463                               mtmp->mtrack[j] = mtmp->mtrack[j - 1];
464                     mtmp->mtrack[0].x = omx;
465                     mtmp->mtrack[0].y = omy;
466           }
467           if (mintrap(mtmp) == 2)       /* he died */
468                     return (2);
469           pmon(mtmp);
470           return (1);
471 }
472 
473 /* return roomnumber or -1 */
474 int
inroom(xchar x,xchar y)475 inroom(xchar x, xchar y)
476 {
477 #ifndef QUEST
478           int pos = 0;
479 
480           while (rooms[pos].hx >= 0) {
481                     if (rooms[pos].hx >= x - 1 && rooms[pos].lx <= x + 1 &&
482                         rooms[pos].hy >= y - 1 && rooms[pos].ly <= y + 1)
483                               return pos;
484                     pos++;
485           }
486 #endif    /* QUEST */
487           return (-1);                  /* not in room or on door */
488 }
489 
490 int
tamedog(struct monst * mtmp,struct obj * obj)491 tamedog(struct monst *mtmp, struct obj *obj)
492 {
493           struct monst   *mtmp2;
494 
495           if (flags.moonphase == FULL_MOON && night() && rn2(6))
496                     return (0);
497 
498           /* If we cannot tame him, at least he's no longer afraid. */
499           mtmp->mflee = 0;
500           mtmp->mfleetim = 0;
501           if (mtmp->mtame || mtmp->mfroz ||
502 #ifndef NOWORM
503               mtmp->wormno ||
504 #endif    /* NOWORM */
505               mtmp->isshk || mtmp->isgd || strchr(" &@12", mtmp->data->mlet))
506                     return (0);         /* no tame long worms? */
507           if (obj) {
508                     if (dogfood(obj) >= MANFOOD)
509                               return (0);
510                     if (cansee(mtmp->mx, mtmp->my)) {
511                               pline("%s devours the %s.", Monnam(mtmp),
512                                     objects[obj->otyp].oc_name);
513                     }
514                     obfree(obj, (struct obj *) 0);
515           }
516           mtmp2 = newmonst(sizeof(struct edog) + mtmp->mnamelth);
517           *mtmp2 = *mtmp;
518           mtmp2->mxlth = sizeof(struct edog);
519           if (mtmp->mnamelth)
520                     (void) strcpy(NAME(mtmp2), NAME(mtmp));
521           initedog(mtmp2);
522           replmon(mtmp, mtmp2);
523           return (1);
524 }
525