xref: /openbsd/games/hack/hack.do.c (revision df69c215)
1 /*	$OpenBSD: hack.do.c,v 1.11 2019/06/28 13:32:52 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 /* Contains code for 'd', 'D' (drop), '>', '<' (up, down) and 't' (throw) */
65 
66 #include <stdlib.h>
67 #include <unistd.h>
68 
69 #include "hack.h"
70 
71 extern boolean level_exists[];
72 extern struct monst youmonst;
73 extern char *nomovemsg;
74 
75 static int drop(struct obj *);
76 
77 int
dodrop(void)78 dodrop(void)
79 {
80 	return(drop(getobj("0$#", "drop")));
81 }
82 
83 static int
drop(struct obj * obj)84 drop(struct obj *obj)
85 {
86 	if(!obj) return(0);
87 	if(obj->olet == '$') {		/* pseudo object */
88 		long amount = OGOLD(obj);
89 
90 		if(amount == 0)
91 			pline("You didn't drop any gold pieces.");
92 		else {
93 			mkgold(amount, u.ux, u.uy);
94 			pline("You dropped %ld gold piece%s.",
95 				amount, plur(amount));
96 			if(Invisible) newsym(u.ux, u.uy);
97 		}
98 		free(obj);
99 		return(1);
100 	}
101 	if(obj->owornmask & (W_ARMOR | W_RING)){
102 		pline("You cannot drop something you are wearing.");
103 		return(0);
104 	}
105 	if(obj == uwep) {
106 		if(uwep->cursed) {
107 			pline("Your weapon is welded to your hand!");
108 			return(0);
109 		}
110 		setuwep((struct obj *) 0);
111 	}
112 	pline("You dropped %s.", doname(obj));
113 	dropx(obj);
114 	return(1);
115 }
116 
117 /* Called in several places - should not produce texts */
118 void
dropx(struct obj * obj)119 dropx(struct obj *obj)
120 {
121 	freeinv(obj);
122 	dropy(obj);
123 }
124 
125 void
dropy(struct obj * obj)126 dropy(struct obj *obj)
127 {
128 	if(obj->otyp == CRYSKNIFE)
129 		obj->otyp = WORM_TOOTH;
130 	obj->ox = u.ux;
131 	obj->oy = u.uy;
132 	obj->nobj = fobj;
133 	fobj = obj;
134 	if(Invisible) newsym(u.ux,u.uy);
135 	subfrombill(obj);
136 	stackobj(obj);
137 }
138 
139 /* drop several things */
140 int
doddrop(void)141 doddrop(void)
142 {
143 	return(ggetobj("drop", drop, 0));
144 }
145 
146 int
dodown(void)147 dodown(void)
148 {
149 	if(u.ux != xdnstair || u.uy != ydnstair) {
150 		pline("You can't go down here.");
151 		return(0);
152 	}
153 	if(u.ustuck) {
154 		pline("You are being held, and cannot go down.");
155 		return(1);
156 	}
157 	if(Levitation) {
158 		pline("You're floating high above the stairs.");
159 		return(0);
160 	}
161 
162 	goto_level(dlevel+1, TRUE);
163 	return(1);
164 }
165 
166 int
doup(void)167 doup(void)
168 {
169 	if(u.ux != xupstair || u.uy != yupstair) {
170 		pline("You can't go up here.");
171 		return(0);
172 	}
173 	if(u.ustuck) {
174 		pline("You are being held, and cannot go up.");
175 		return(1);
176 	}
177 	if(!Levitation && inv_weight() + 5 > 0) {
178 		pline("Your load is too heavy to climb the stairs.");
179 		return(1);
180 	}
181 
182 	goto_level(dlevel-1, TRUE);
183 	return(1);
184 }
185 
186 void
goto_level(int newlevel,boolean at_stairs)187 goto_level(int newlevel, boolean at_stairs)
188 {
189 	int fd;
190 	boolean up = (newlevel < dlevel);
191 
192 	if(newlevel <= 0) done("escaped");    /* in fact < 0 is impossible */
193 	if(newlevel > MAXLEVEL) newlevel = MAXLEVEL;	/* strange ... */
194 	if(newlevel == dlevel) return;	      /* this can happen */
195 
196 	glo(dlevel);
197 	fd = open(lock, O_CREAT | O_TRUNC | O_WRONLY, FMASK);
198 	if(fd == -1) {
199 		/*
200 		 * This is not quite impossible: e.g., we may have
201 		 * exceeded our quota. If that is the case then we
202 		 * cannot leave this level, and cannot save either.
203 		 * Another possibility is that the directory was not
204 		 * writable.
205 		 */
206 		pline("A mysterious force prevents you from going %s.",
207 			up ? "up" : "down");
208 		return;
209 	}
210 
211 	if(Punished) unplacebc();
212 	u.utrap = 0;				/* needed in level_tele */
213 	u.ustuck = 0;				/* idem */
214 	keepdogs();
215 	seeoff(1);
216 	if(u.uswallow)				/* idem */
217 		u.uswldtim = u.uswallow = 0;
218 	flags.nscrinh = 1;
219 	u.ux = FAR;				/* hack */
220 	(void) inshop();			/* probably was a trapdoor */
221 
222 	savelev(fd,dlevel);
223 	(void) close(fd);
224 
225 	dlevel = newlevel;
226 	if(maxdlevel < dlevel)
227 		maxdlevel = dlevel;
228 	glo(dlevel);
229 
230 	if(!level_exists[(int)dlevel])
231 		mklev();
232 	else {
233 		extern int hackpid;
234 
235 		if((fd = open(lock, O_RDONLY)) == -1) {
236 			pline("Cannot open %s .", lock);
237 			pline("Probably someone removed it.");
238 			done("tricked");
239 		}
240 		getlev(fd, hackpid, dlevel);
241 		(void) close(fd);
242 	}
243 
244 	if(at_stairs) {
245 	    if(up) {
246 		u.ux = xdnstair;
247 		u.uy = ydnstair;
248 		if(!u.ux) {		/* entering a maze from below? */
249 		    u.ux = xupstair;	/* this will confuse the player! */
250 		    u.uy = yupstair;
251 		}
252 		if(Punished && !Levitation){
253 			pline("With great effort you climb the stairs.");
254 			placebc(1);
255 		}
256 	    } else {
257 		u.ux = xupstair;
258 		u.uy = yupstair;
259 		if(inv_weight() + 5 > 0 || Punished){
260 			pline("You fall down the stairs.");	/* %% */
261 			losehp(rnd(3), "fall");
262 			if(Punished) {
263 			    if(uwep != uball && rn2(3)){
264 				pline("... and are hit by the iron ball.");
265 				losehp(rnd(20), "iron ball");
266 			    }
267 			    placebc(1);
268 			}
269 			selftouch("Falling, you");
270 		}
271 	    }
272 	    { struct monst *mtmp = m_at(u.ux, u.uy);
273 	      if(mtmp)
274 		mnexto(mtmp);
275 	    }
276 	} else {	/* trapdoor or level_tele */
277 	    do {
278 		u.ux = rnd(COLNO-1);
279 		u.uy = rn2(ROWNO);
280 	    } while(levl[(int)u.ux][(int)u.uy].typ != ROOM ||
281 			m_at(u.ux,u.uy));
282 	    if(Punished){
283 		if(uwep != uball && !up /* %% */ && rn2(5)){
284 			pline("The iron ball falls on your head.");
285 			losehp(rnd(25), "iron ball");
286 		}
287 		placebc(1);
288 	    }
289 	    selftouch("Falling, you");
290 	}
291 	(void) inshop();
292 	initrack();
293 
294 	losedogs();
295 	{ struct monst *mtmp;
296 	  if ((mtmp = m_at(u.ux, u.uy)))
297 		  mnexto(mtmp);	/* riv05!a3 */
298 	}
299 	flags.nscrinh = 0;
300 	setsee();
301 	seeobjs();	/* make old cadavers disappear - riv05!a3 */
302 	docrt();
303 	pickup(1);
304 	read_engr_at(u.ux,u.uy);
305 }
306 
307 int
donull(void)308 donull(void)
309 {
310 	return(1);	/* Do nothing, but let other things happen */
311 }
312 
313 int
dopray(void)314 dopray(void)
315 {
316 	nomovemsg = "You finished your prayer.";
317 	nomul(-3);
318 	return(1);
319 }
320 
321 int
dothrow(void)322 dothrow(void)
323 {
324 	struct obj *obj;
325 	struct monst *mon;
326 	int tmp;
327 
328 	obj = getobj("#)", "throw");   /* it is also possible to throw food */
329 				       /* (or jewels, or iron balls ... ) */
330 	if(!obj || !getdir(1))	       /* ask "in what direction?" */
331 		return(0);
332 	if(obj->owornmask & (W_ARMOR | W_RING)){
333 		pline("You can't throw something you are wearing.");
334 		return(0);
335 	}
336 
337 	u_wipe_engr(2);
338 
339 	if(obj == uwep){
340 		if(obj->cursed){
341 			pline("Your weapon is welded to your hand.");
342 			return(1);
343 		}
344 		if(obj->quan > 1)
345 			setuwep(splitobj(obj, 1));
346 		else
347 			setuwep((struct obj *) 0);
348 	}
349 	else if(obj->quan > 1)
350 		(void) splitobj(obj, 1);
351 	freeinv(obj);
352 	if(u.uswallow) {
353 		mon = u.ustuck;
354 		bhitpos.x = mon->mx;
355 		bhitpos.y = mon->my;
356 	} else if(u.dz) {
357 	  if(u.dz < 0) {
358 	    pline("%s hits the ceiling, then falls back on top of your head.",
359 		Doname(obj));		/* note: obj->quan == 1 */
360 	    if(obj->olet == POTION_SYM)
361 		potionhit(&youmonst, obj);
362 	    else {
363 		if(uarmh) pline("Fortunately, you are wearing a helmet!");
364 		losehp(uarmh ? 1 : rnd((int)(obj->owt)), "falling object");
365 		dropy(obj);
366 	    }
367 	  } else {
368 	    pline("%s hits the floor.", Doname(obj));
369 	    if(obj->otyp == EXPENSIVE_CAMERA) {
370 		pline("It is shattered in a thousand pieces!");
371 		obfree(obj, Null(obj));
372 	    } else if(obj->otyp == EGG) {
373 		pline("\"Splash!\"");
374 		obfree(obj, Null(obj));
375 	    } else if(obj->olet == POTION_SYM) {
376 		pline("The flask breaks, and you smell a peculiar odor ...");
377 		potionbreathe(obj);
378 		obfree(obj, Null(obj));
379 	    } else {
380 		dropy(obj);
381 	    }
382 	  }
383 	  return(1);
384 	} else if(obj->otyp == BOOMERANG) {
385 		mon = boomhit(u.dx, u.dy);
386 		if(mon == &youmonst) {		/* the thing was caught */
387 			(void) addinv(obj);
388 			return(1);
389 		}
390 	} else {
391 		if(obj->otyp == PICK_AXE && shkcatch(obj))
392 		    return(1);
393 
394 		mon = bhit(u.dx, u.dy, (obj->otyp == ICE_BOX) ? 1 :
395 			(!Punished || obj != uball) ? 8 : !u.ustuck ? 5 : 1,
396 			obj->olet, NULL, NULL, obj);
397 	}
398 	if(mon) {
399 		/* awake monster if sleeping */
400 		wakeup(mon);
401 
402 		if(obj->olet == WEAPON_SYM) {
403 			tmp = -1+u.ulevel+mon->data->ac+abon();
404 			if(obj->otyp < ROCK) {
405 				if(!uwep ||
406 				    uwep->otyp != obj->otyp+(BOW-ARROW))
407 					tmp -= 4;
408 				else {
409 					tmp += uwep->spe;
410 				}
411 			} else
412 			if(obj->otyp == BOOMERANG) tmp += 4;
413 			tmp += obj->spe;
414 			if(u.uswallow || tmp >= rnd(20)) {
415 				if(hmon(mon,obj,1) == TRUE){
416 				  /* mon still alive */
417 #ifndef NOWORM
418 				  cutworm(mon,bhitpos.x,bhitpos.y,obj->otyp);
419 #endif /* NOWORM */
420 				} else mon = 0;
421 				/* weapons thrown disappear sometimes */
422 				if(obj->otyp < BOOMERANG && rn2(3)) {
423 					/* check bill; free */
424 					obfree(obj, (struct obj *) 0);
425 					return(1);
426 				}
427 			} else miss(objects[obj->otyp].oc_name, mon);
428 		} else if(obj->otyp == HEAVY_IRON_BALL) {
429 			tmp = -1+u.ulevel+mon->data->ac+abon();
430 			if(!Punished || obj != uball) tmp += 2;
431 			if(u.utrap) tmp -= 2;
432 			if(u.uswallow || tmp >= rnd(20)) {
433 				if(hmon(mon,obj,1) == FALSE)
434 					mon = 0;	/* he died */
435 			} else miss("iron ball", mon);
436 		} else if(obj->olet == POTION_SYM && u.ulevel > rn2(15)) {
437 			potionhit(mon, obj);
438 			return(1);
439 		} else {
440 			if(cansee(bhitpos.x,bhitpos.y))
441 				pline("You miss %s.",monnam(mon));
442 			else pline("You miss it.");
443 			if(obj->olet == FOOD_SYM && mon->data->mlet == 'd')
444 				if(tamedog(mon,obj)) return(1);
445 			if(obj->olet == GEM_SYM && mon->data->mlet == 'u' &&
446 				!mon->mtame){
447 			 if(obj->dknown && objects[obj->otyp].oc_name_known){
448 			  if(objects[obj->otyp].g_val > 0){
449 			    u.uluck += 5;
450 			    goto valuable;
451 			  } else {
452 			    pline("%s is not interested in your junk.",
453 				Monnam(mon));
454 			  }
455 			 } else { /* value unknown to @ */
456 			    u.uluck++;
457 			valuable:
458 			    if(u.uluck > LUCKMAX)	/* dan@ut-ngp */
459 				u.uluck = LUCKMAX;
460 			    pline("%s graciously accepts your gift.",
461 				Monnam(mon));
462 			    mpickobj(mon, obj);
463 			    rloc(mon);
464 			    return(1);
465 			 }
466 			}
467 		}
468 	}
469 		/* the code following might become part of dropy() */
470 	if(obj->otyp == CRYSKNIFE)
471 		obj->otyp = WORM_TOOTH;
472 	obj->ox = bhitpos.x;
473 	obj->oy = bhitpos.y;
474 	obj->nobj = fobj;
475 	fobj = obj;
476 	/* prevent him from throwing articles to the exit and escaping */
477 	/* subfrombill(obj); */
478 	stackobj(obj);
479 	if(Punished && obj == uball &&
480 		(bhitpos.x != u.ux || bhitpos.y != u.uy)){
481 		freeobj(uchain);
482 		unpobj(uchain);
483 		if(u.utrap){
484 			if(u.utraptype == TT_PIT)
485 				pline("The ball pulls you out of the pit!");
486 			else {
487 			    long side =
488 				rn2(3) ? LEFT_SIDE : RIGHT_SIDE;
489 			    pline("The ball pulls you out of the bear trap.");
490 			    pline("Your %s leg is severely damaged.",
491 				(side == LEFT_SIDE) ? "left" : "right");
492 			    set_wounded_legs(side, 500+rn2(1000));
493 			    losehp(2, "thrown ball");
494 			}
495 			u.utrap = 0;
496 		}
497 		unsee();
498 		uchain->nobj = fobj;
499 		fobj = uchain;
500 		u.ux = uchain->ox = bhitpos.x - u.dx;
501 		u.uy = uchain->oy = bhitpos.y - u.dy;
502 		setsee();
503 		(void) inshop();
504 	}
505 	if(cansee(bhitpos.x, bhitpos.y)) prl(bhitpos.x,bhitpos.y);
506 	return(1);
507 }
508 
509 /* split obj so that it gets size num */
510 /* remainder is put in the object structure delivered by this call */
511 struct obj *
splitobj(struct obj * obj,int num)512 splitobj(struct obj *obj, int num)
513 {
514 	struct obj *otmp;
515 
516 	otmp = newobj(0);
517 	*otmp = *obj;		/* copies whole structure */
518 	otmp->o_id = flags.ident++;
519 	otmp->onamelth = 0;
520 	obj->quan = num;
521 	obj->owt = weight(obj);
522 	otmp->quan -= num;
523 	otmp->owt = weight(otmp);	/* -= obj->owt ? */
524 	obj->nobj = otmp;
525 	if(obj->unpaid) splitbill(obj,otmp);
526 	return(otmp);
527 }
528 
529 void
more_experienced(int exp,int rexp)530 more_experienced(int exp, int rexp)
531 {
532 	extern char pl_character[];
533 
534 	u.uexp += exp;
535 	u.urexp += 4*exp + rexp;
536 	if(exp) flags.botl = 1;
537 	if(u.urexp >= ((pl_character[0] == 'W') ? 1000 : 2000))
538 		flags.beginner = 0;
539 }
540 
541 void
set_wounded_legs(long side,int timex)542 set_wounded_legs(long side, int timex)
543 {
544 	if(!Wounded_legs || (Wounded_legs & TIMEOUT))
545 		Wounded_legs |= side + timex;
546 	else
547 		Wounded_legs |= side;
548 }
549 
550 void
heal_legs(void)551 heal_legs(void)
552 {
553 	if(Wounded_legs) {
554 		if((Wounded_legs & BOTH_SIDES) == BOTH_SIDES)
555 			pline("Your legs feel somewhat better.");
556 		else
557 			pline("Your leg feels somewhat better.");
558 		Wounded_legs = 0;
559 	}
560 }
561