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