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