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