a75f35be879de8081d5a283da7d8673e12639355
[dragonfly.git] / games / hack / hack.dog.c
1 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
2 /* hack.dog.c - version 1.0.3 */
3 /* $FreeBSD: src/games/hack/hack.dog.c,v 1.3 1999/11/16 02:57:03 billf Exp $ */
4 /* $DragonFly: src/games/hack/hack.dog.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */
5
6 #include "hack.h"
7 #include "hack.mfndpos.h"
8 #include "def.edog.h"
9
10 struct permonst li_dog =
11         { "little dog", 'd', 2, 18, 6, 1, 6, sizeof(struct edog) };
12 struct permonst dog =
13         { "dog", 'd', 4, 16, 5, 1, 6, sizeof(struct edog) };
14 struct permonst la_dog =
15         { "large dog", 'd', 6, 15, 4, 2, 4, sizeof(struct edog) };
16
17 static void initedog(struct monst *);
18 static xchar dogfood(struct obj *);
19
20 void
21 makedog(void)
22 {
23         struct monst *mtmp = makemon(&li_dog, u.ux, u.uy);
24
25         if (!mtmp)
26                 return;         /* dogs were genocided */
27         initedog(mtmp);
28 }
29
30 static void
31 initedog(struct monst *mtmp)
32 {
33         mtmp->mtame = mtmp->mpeaceful = 1;
34         EDOG(mtmp)->hungrytime = 1000 + moves;
35         EDOG(mtmp)->eattime = 0;
36         EDOG(mtmp)->droptime = 0;
37         EDOG(mtmp)->dropdist = 10000;
38         EDOG(mtmp)->apport = 10;
39         EDOG(mtmp)->whistletime = 0;
40 }
41
42 /* attach the monsters that went down (or up) together with @ */
43 struct monst *mydogs = 0;
44 struct monst *fallen_down = 0;  /* monsters that fell through a trapdoor */
45 /* they will appear on the next level @ goes to, even if he goes up! */
46
47 void
48 losedogs(void)
49 {
50         struct monst *mtmp;
51
52         while ((mtmp = mydogs)) {
53                 mydogs = mtmp->nmon;
54                 mtmp->nmon = fmon;
55                 fmon = mtmp;
56                 mnexto(mtmp);
57         }
58         while ((mtmp = fallen_down)) {
59                 fallen_down = mtmp->nmon;
60                 mtmp->nmon = fmon;
61                 fmon = mtmp;
62                 rloc(mtmp);
63         }
64 }
65
66 void
67 keepdogs(void)
68 {
69         struct monst *mtmp;
70
71         for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
72                 if (dist(mtmp->mx, mtmp->my) < 3 && follower(mtmp)
73                     && !mtmp->msleep && !mtmp->mfroz) {
74                         relmon(mtmp);
75                         mtmp->nmon = mydogs;
76                         mydogs = mtmp;
77                         unpmon(mtmp);
78                         keepdogs();     /* we destroyed the link, so use recursion */
79                         return;         /* (admittedly somewhat primitive) */
80                 }
81 }
82
83 void
84 fall_down(struct monst *mtmp)
85 {
86         relmon(mtmp);
87         mtmp->nmon = fallen_down;
88         fallen_down = mtmp;
89         unpmon(mtmp);
90         mtmp->mtame = 0;
91 }
92
93 /* return quality of food; the lower the better */
94 #define DOGFOOD 0
95 #define CADAVER 1
96 #define ACCFOOD 2
97 #define MANFOOD 3
98 #define APPORT  4
99 #define POISON  5
100 #define UNDEF   6
101
102 static xchar
103 dogfood(struct obj *obj)
104 {
105         switch (obj->olet) {
106         case FOOD_SYM:
107                 return (
108                     (obj->otyp == TRIPE_RATION) ? DOGFOOD :
109                     (obj->otyp < CARROT) ? ACCFOOD :
110                     (obj->otyp < CORPSE) ? MANFOOD :
111                     (poisonous(obj) || obj->age + 50 <= moves ||
112                         obj->otyp == DEAD_COCKATRICE)
113                     ? POISON : CADAVER
114                     );
115         default:
116                 if (!obj->cursed)
117                         return (APPORT);
118         /* fall into next case */
119         case BALL_SYM:
120         case CHAIN_SYM:
121         case ROCK_SYM:
122                 return (UNDEF);
123         }
124 }
125
126 /* return 0 (no move), 1 (move) or 2 (dead) */
127 int
128 dog_move(struct monst *mtmp, int after)
129 {
130         int nx, ny, omx, omy, appr, nearer, j;
131         int udist, chi = 0, i, whappr;
132         struct monst *mtmp2;
133         struct permonst *mdat = mtmp->data;
134         struct edog *edog = EDOG(mtmp);
135         struct obj *obj;
136         struct trap *trap;
137         xchar cnt, chcnt, nix, niy;
138         schar dogroom, uroom;
139         xchar gx, gy, gtyp, otyp;       /* current goal */
140         coord poss[9];
141         int info[9];
142 #define GDIST(x, y) ((x - gx) * (x - gx) + (y - gy) * (y - gy))
143 #define DDIST(x, y) ((x - omx) * (x - omx) + (y - omy) * (y - omy))
144
145         if (moves <= edog->eattime)     /* dog is still eating */
146                 return (0);
147         omx = mtmp->mx;
148         omy = mtmp->my;
149         whappr = (moves - EDOG(mtmp)->whistletime < 5);
150         if (moves > edog->hungrytime + 500 && !mtmp->mconf) {
151                 mtmp->mconf = 1;
152                 mtmp->mhpmax /= 3;
153                 if (mtmp->mhp > mtmp->mhpmax)
154                         mtmp->mhp = mtmp->mhpmax;
155                 if (cansee(omx, omy))
156                         pline("%s is confused from hunger.", Monnam(mtmp));
157                 else
158                         pline("You feel worried about %s.", monnam(mtmp));
159         } else if (moves > edog->hungrytime + 750 || mtmp->mhp < 1) {
160                 if (cansee(omx, omy))
161                         pline("%s dies from hunger.", Monnam(mtmp));
162                 else
163                         pline("You have a sad feeling for a moment, then it passes.");
164                 mondied(mtmp);
165                 return (2);
166         }
167         dogroom = inroom(omx, omy);
168         uroom = inroom(u.ux, u.uy);
169         udist = dist(omx, omy);
170
171         /* maybe we tamed him while being swallowed --jgm */
172         if (!udist)
173                 return (0);
174
175         /* if we are carrying sth then we drop it (perhaps near @) */
176         /* Note: if apport == 1 then our behaviour is independent of udist */
177         if (mtmp->minvent) {
178                 if (!rn2(udist) || !rn2((int)edog->apport))
179                         if (rn2(10) < (int)edog->apport) {
180                                 relobj(mtmp, (int)mtmp->minvis);
181                                 if (edog->apport > 1)
182                                         edog->apport--;
183                                 edog->dropdist = udist; /* hpscdi!jon */
184                                 edog->droptime = moves;
185                         }
186         } else if ((obj = o_at(omx, omy))) {
187                 if (!strchr("0_", obj->olet)) {
188                         if ((otyp = dogfood(obj)) <= CADAVER) {
189                                 nix = omx;
190                                 niy = omy;
191                                 goto eatobj;
192                         }
193                         if (obj->owt < 10 * mtmp->data->mlevel) {
194                                 if (rn2(20) < (int)edog->apport + 3) {
195                                         if (rn2(udist) ||
196                                             !rn2((int)edog->apport)) {
197                                                 freeobj(obj);
198                                                 unpobj(obj);
199                                                 /* if (levl[omx][omy].scrsym == obj->olet)
200                                                  *      newsym(omx, omy); */
201                                                 mpickobj(mtmp, obj);
202                                         }
203                                 }
204                         }
205                 }
206         }
207
208         /* first we look for food */
209         gtyp = UNDEF;           /* no goal as yet */
210         gx = gy = 0;            /* suppress 'used before set' message */
211         for (obj = fobj; obj; obj = obj->nobj) {
212                 otyp = dogfood(obj);
213                 if (otyp > gtyp || otyp == UNDEF)
214                         continue;
215                 if (inroom(obj->ox, obj->oy) != dogroom)
216                         continue;
217                 if (otyp < MANFOOD &&
218                     (dogroom >= 0 || DDIST(obj->ox, obj->oy) < 10)) {
219                         if (otyp < gtyp || (otyp == gtyp &&
220                             DDIST(obj->ox, obj->oy) < DDIST(gx, gy))) {
221                                 gx = obj->ox;
222                                 gy = obj->oy;
223                                 gtyp = otyp;
224                         }
225                 } else if (gtyp == UNDEF && dogroom >= 0 &&
226                     uroom == dogroom &&
227                     !mtmp->minvent && (int)edog->apport > rn2(8)) {
228                         gx = obj->ox;
229                         gy = obj->oy;
230                         gtyp = APPORT;
231                 }
232         }
233         if (gtyp == UNDEF ||
234             (gtyp != DOGFOOD && gtyp != APPORT && moves < edog->hungrytime)) {
235                 if (dogroom < 0 || dogroom == uroom) {
236                         gx = u.ux;
237                         gy = u.uy;
238 #ifndef QUEST
239                 } else {
240                         int tmp = rooms[dogroom].fdoor;
241                         cnt = rooms[dogroom].doorct;
242
243                         gx = gy = FAR;  /* random, far away */
244                         while (cnt--) {
245                                 if (dist(gx, gy) >
246                                     dist(doors[tmp].x, doors[tmp].y)) {
247                                         gx = doors[tmp].x;
248                                         gy = doors[tmp].y;
249                                 }
250                                 tmp++;
251                         }
252                         /* here gx == FAR e.g. when dog is in a vault */
253                         if (gx == FAR || (gx == omx && gy == omy)) {
254                                 gx = u.ux;
255                                 gy = u.uy;
256                         }
257 #endif /* QUEST */
258                 }
259                 appr = (udist >= 9) ? 1 : (mtmp->mflee) ? -1 : 0;
260                 if (after && udist <= 4 && gx == u.ux && gy == u.uy)
261                         return (0);
262                 if (udist > 1) {
263                         if (!IS_ROOM(levl[u.ux][u.uy].typ) || !rn2(4) ||
264                             whappr ||
265                             (mtmp->minvent && rn2((int)edog->apport)))
266                                 appr = 1;
267                 }
268                 /* if you have dog food he'll follow you more closely */
269                 if (appr == 0) {
270                         obj = invent;
271                         while (obj) {
272                                 if (obj->otyp == TRIPE_RATION) {
273                                         appr = 1;
274                                         break;
275                                 }
276                                 obj = obj->nobj;
277                         }
278                 }
279         } else          /* gtyp != UNDEF */
280                 appr = 1;
281         if (mtmp->mconf)
282                 appr = 0;
283
284         if (gx == u.ux && gy == u.uy && (dogroom != uroom || dogroom < 0)) {
285                 coord *cp;
286                 cp = gettrack(omx, omy);
287                 if (cp) {
288                         gx = cp->x;
289                         gy = cp->y;
290                 }
291         }
292
293         nix = omx;
294         niy = omy;
295         cnt = mfndpos(mtmp, poss, info, ALLOW_M | ALLOW_TRAPS);
296         chcnt = 0;
297         chi = -1;
298         for (i = 0; i < cnt; i++) {
299                 nx = poss[i].x;
300                 ny = poss[i].y;
301                 if (info[i] & ALLOW_M) {
302                         mtmp2 = m_at(nx, ny);
303                         if (mtmp2->data->mlevel >= mdat->mlevel + 2 ||
304                             mtmp2->data->mlet == 'c')
305                                 continue;
306                         if (after)              /* hit only once each move */
307                                 return (0);
308
309                         if (hitmm(mtmp, mtmp2) == 1 && rn2(4) &&
310                             mtmp2->mlstmv != moves &&
311                             hitmm(mtmp2, mtmp) == 2)
312                                 return (2);
313                         return (0);
314                 }
315
316                 /* dog avoids traps */
317                 /* but perhaps we have to pass a trap in order to follow @ */
318                 if ((info[i] & ALLOW_TRAPS) && (trap = t_at(nx, ny))) {
319                         if (!trap->tseen && rn2(40))
320                                 continue;
321                         if (rn2(10))
322                                 continue;
323                 }
324
325                 /* dog eschewes cursed objects */
326                 /* but likes dog food */
327                 obj = fobj;
328                 while (obj) {
329                         if (obj->ox != nx || obj->oy != ny)
330                                 goto nextobj;
331                         if (obj->cursed)
332                                 goto nxti;
333                         if (obj->olet == FOOD_SYM &&
334                             (otyp = dogfood(obj)) < MANFOOD &&
335                             (otyp < ACCFOOD || edog->hungrytime <= moves)) {
336                                 /*
337                                  * Note: our dog likes the food so much that
338                                  * he might eat it even when it conceals a
339                                  * cursed object
340                                  */
341                                 nix = nx;
342                                 niy = ny;
343                                 chi = i;
344 eatobj:
345                                 edog->eattime =
346                                         moves + obj->quan *
347                                         objects[obj->otyp].oc_delay;
348                                 if (edog->hungrytime < moves)
349                                         edog->hungrytime = moves;
350                                 edog->hungrytime +=
351                                         5 * obj->quan *
352                                         objects[obj->otyp].nutrition;
353                                 mtmp->mconf = 0;
354                                 if (cansee(nix, niy))
355                                         pline("%s ate %s.", Monnam(
356                                                       mtmp), doname(obj));
357                                 /* perhaps this was a reward */
358                                 if (otyp != CADAVER)
359                                         edog->apport += 200 /
360                                                         (edog->dropdist +
361                                                          moves - edog->droptime);
362                                 delobj(obj);
363                                 goto newdogpos;
364                         }
365 nextobj:
366                         obj = obj->nobj;
367                 }
368
369                 for (j = 0; j < MTSZ && j < cnt - 1; j++)
370                         if (nx == mtmp->mtrack[j].x && ny == mtmp->mtrack[j].y)
371                                 if (rn2(4 * (cnt - j)))
372                                         goto nxti;
373
374 /* Some stupid C compilers cannot compute the whole expression at once. */
375                 nearer = GDIST(nx, ny);
376                 nearer -= GDIST(nix, niy);
377                 nearer *= appr;
378                 if ((nearer == 0 && !rn2(++chcnt)) || nearer < 0 ||
379                     (nearer > 0 && !whappr &&
380                      ((omx == nix && omy == niy && !rn2(3))
381                       || !rn2(12))
382                     )) {
383                         nix = nx;
384                         niy = ny;
385                         if (nearer < 0)
386                                 chcnt = 0;
387                         chi = i;
388                 }
389 nxti:;
390         }
391 newdogpos:
392         if (nix != omx || niy != omy) {
393                 if (info[chi] & ALLOW_U) {
394                         hitu(mtmp, d(mdat->damn, mdat->damd) + 1);
395                         return (0);
396                 }
397                 mtmp->mx = nix;
398                 mtmp->my = niy;
399                 for (j = MTSZ - 1; j > 0; j--)
400                         mtmp->mtrack[j] = mtmp->mtrack[j - 1];
401                 mtmp->mtrack[0].x = omx;
402                 mtmp->mtrack[0].y = omy;
403         }
404         if (mintrap(mtmp) == 2) /* he died */
405                 return (2);
406         pmon(mtmp);
407         return (1);
408 }
409
410 /* return roomnumber or -1 */
411 int
412 inroom(xchar x, xchar y)
413 {
414 #ifndef QUEST
415         struct mkroom *croom = &rooms[0];
416
417         while (croom->hx >= 0) {
418                 if (croom->hx >= x - 1 && croom->lx <= x + 1 &&
419                     croom->hy >= y - 1 && croom->ly <= y + 1)
420                         return (croom - rooms);
421                 croom++;
422         }
423 #endif /* QUEST */
424         return (-1);            /* not in room or on door */
425 }
426
427 bool
428 tamedog(struct monst *mtmp, struct obj *obj)
429 {
430         struct monst *mtmp2;
431
432         if (flags.moonphase == FULL_MOON && night() && rn2(6))
433                 return (0);
434
435         /* If we cannot tame him, at least he's no longer afraid. */
436         mtmp->mflee = 0;
437         mtmp->mfleetim = 0;
438         if (mtmp->mtame || mtmp->mfroz ||
439 #ifndef NOWORM
440             mtmp->wormno ||
441 #endif /* NOWORM */
442             mtmp->isshk || mtmp->isgd || strchr(" &@12", mtmp->data->mlet))
443                 return (0);     /* no tame long worms? */
444         if (obj) {
445                 if (dogfood(obj) >= MANFOOD)
446                         return (0);
447                 if (cansee(mtmp->mx, mtmp->my))
448                         pline("%s devours the %s.", Monnam(mtmp),
449                               objects[obj->otyp].oc_name);
450                 obfree(obj, NULL);
451         }
452         mtmp2 = newmonst(sizeof(struct edog) + mtmp->mnamelth);
453         *mtmp2 = *mtmp;
454         mtmp2->mxlth = sizeof(struct edog);
455         if (mtmp->mnamelth)
456                 strcpy(NAME(mtmp2), NAME(mtmp));
457         initedog(mtmp2);
458         replmon(mtmp, mtmp2);
459         return (1);
460 }