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