Commit | Line | Data |
---|---|---|
7c496796 WJ |
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 | } |