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