xref: /netbsd-src/games/larn/create.c (revision c34afa686bf074a6dcc7f17157faae72111dde9a)
1 /* $NetBSD: create.c,v 1.12 2012/06/19 05:30:43 dholland Exp $	 */
2 
3 /* create.c		Larn is copyrighted 1986 by Noah Morgan. */
4 
5 #include <sys/cdefs.h>
6 #ifndef lint
7 __RCSID("$NetBSD: create.c,v 1.12 2012/06/19 05:30:43 dholland Exp $");
8 #endif				/* not lint */
9 
10 #include "header.h"
11 #include "extern.h"
12 #include <unistd.h>
13 
14 static void makemaze(int);
15 static int cannedlevel(int);
16 static void treasureroom(int);
17 static void troom(int, int, int, int, int, int);
18 static void makeobject(int);
19 static void fillmroom(int, int, int);
20 static void froom(int, int, int);
21 static void fillroom(int, int);
22 static void sethp(int);
23 static void checkgen(void);
24 
25 /*
26 	makeplayer()
27 
28 	subroutine to create the player and the players attributes
29 	this is called at the beginning of a game and at no other time
30  */
31 void
makeplayer(void)32 makeplayer(void)
33 {
34 	int i;
35 	scbr();
36 	clear();
37 	c[HPMAX] = c[HP] = 10;	/* start player off with 15 hit points	 */
38 	c[LEVEL] = 1;		/* player starts at level one		 */
39 	c[SPELLMAX] = c[SPELLS] = 1;	/* total # spells starts off as 3 */
40 	c[REGENCOUNTER] = 16;
41 	c[ECOUNTER] = 96;	/* start regeneration correctly */
42 	c[SHIELD] = c[WEAR] = c[WIELD] = -1;
43 	for (i = 0; i < 26; i++)
44 		iven[i] = 0;
45 	spelknow[0] = spelknow[1] = 1;	/* he knows protection, magic missile */
46 	if (c[HARDGAME] <= 0) {
47 		iven[0] = OLEATHER;
48 		iven[1] = ODAGGER;
49 		ivenarg[1] = ivenarg[0] = c[WEAR] = 0;
50 		c[WIELD] = 1;
51 	}
52 	playerx = rnd(MAXX - 2);
53 	playery = rnd(MAXY - 2);
54 	oldx = 0;
55 	oldy = 25;
56 	gltime = 0;		/* time clock starts at zero	 */
57 	cbak[SPELLS] = -50;
58 	for (i = 0; i < 6; i++)
59 		c[i] = 12;	/* make the attributes, ie str, int, etc. */
60 	recalc();
61 }
62 
63 
64 /*
65 	newcavelevel(level)
66 	int level;
67 
68 	function to enter a new level.  This routine must be called anytime the
69 	player changes levels.  If that level is unknown it will be created.
70 	A new set of monsters will be created for a new level, and existing
71 	levels will get a few more monsters.
72 	Note that it is here we remove genocided monsters from the present level.
73  */
74 void
newcavelevel(int x)75 newcavelevel(int x)
76 {
77 	int             i, j;
78 	if (beenhere[level])
79 		savelevel();	/* put the level back into storage	 */
80 	level = x;		/* get the new level and put in working
81 				 * storage */
82 	if (beenhere[x]) {
83 		getlevel();
84 		sethp(0);
85 		checkgen();
86 		return;
87 	}
88 
89 	/* fill in new level */
90 	for (i = 0; i < MAXY; i++)
91 		for (j = 0; j < MAXX; j++)
92 			know[j][i] = mitem[j][i] = 0;
93 	makemaze(x);
94 	makeobject(x);
95 	beenhere[x] = 1;
96 	sethp(1);
97 	checkgen();		/* wipe out any genocided monsters */
98 
99 #if WIZID
100 	if (wizard || x == 0)
101 #else
102 	if (x == 0)
103 #endif
104 		for (j = 0; j < MAXY; j++)
105 			for (i = 0; i < MAXX; i++)
106 				know[i][j] = 1;
107 }
108 
109 /*
110 	makemaze(level)
111 	int level;
112 
113 	subroutine to make the caverns for a given level.  only walls are made.
114  */
115 static int      mx, mxl, mxh, my, myl, myh, tmp2;
116 
117 static void
makemaze(int k)118 makemaze(int k)
119 {
120 	int             i, j, tmp;
121 	int             z;
122 	if (k > 1 && (rnd(17) <= 4 || k == MAXLEVEL - 1 || k == MAXLEVEL + MAXVLEVEL - 1)) {
123 		if (cannedlevel(k))
124 			return;		/* read maze from data file */
125 	}
126 	if (k == 0)
127 		tmp = 0;
128 	else
129 		tmp = OWALL;
130 	for (i = 0; i < MAXY; i++)
131 		for (j = 0; j < MAXX; j++)
132 			item[j][i] = tmp;
133 	if (k == 0)
134 		return;
135 	eat(1, 1);
136 	if (k == 1)
137 		item[33][MAXY - 1] = 0;	/* exit from dungeon */
138 
139 	/* now for open spaces -- not on level 10	 */
140 	if (k != MAXLEVEL - 1) {
141 		tmp2 = rnd(3) + 3;
142 		for (tmp = 0; tmp < tmp2; tmp++) {
143 			my = rnd(11) + 2;
144 			myl = my - rnd(2);
145 			myh = my + rnd(2);
146 			if (k < MAXLEVEL) {
147 				mx = rnd(44) + 5;
148 				mxl = mx - rnd(4);
149 				mxh = mx + rnd(12) + 3;
150 				z = 0;
151 			} else {
152 				mx = rnd(60) + 3;
153 				mxl = mx - rnd(2);
154 				mxh = mx + rnd(2);
155 				z = makemonst(k);
156 			}
157 			for (i = mxl; i < mxh; i++)
158 				for (j = myl; j < myh; j++) {
159 					item[i][j] = 0;
160 					if ((mitem[i][j] = z))
161 						hitp[i][j] = monster[z].hitpoints;
162 				}
163 		}
164 	}
165 	if (k != MAXLEVEL - 1) {
166 		my = rnd(MAXY - 2);
167 		for (i = 1; i < MAXX - 1; i++)
168 			item[i][my] = 0;
169 	}
170 	if (k > 1)
171 		treasureroom(k);
172 }
173 
174 /*
175 	function to eat away a filled in maze
176  */
177 void
eat(int xx,int yy)178 eat(int xx, int yy)
179 {
180 	int             dir, try;
181 	dir = rnd(4);
182 	try = 2;
183 	while (try) {
184 		switch (dir) {
185 		case 1:
186 			if (xx <= 2)
187 				break;	/* west	 */
188 			if ((item[xx - 1][yy] != OWALL) || (item[xx - 2][yy] != OWALL))
189 				break;
190 			item[xx - 1][yy] = item[xx - 2][yy] = 0;
191 			eat(xx - 2, yy);
192 			break;
193 
194 		case 2:
195 			if (xx >= MAXX - 3)
196 				break;	/* east	 */
197 			if ((item[xx + 1][yy] != OWALL) || (item[xx + 2][yy] != OWALL))
198 				break;
199 			item[xx + 1][yy] = item[xx + 2][yy] = 0;
200 			eat(xx + 2, yy);
201 			break;
202 
203 		case 3:
204 			if (yy <= 2)
205 				break;	/* south	 */
206 			if ((item[xx][yy - 1] != OWALL) || (item[xx][yy - 2] != OWALL))
207 				break;
208 			item[xx][yy - 1] = item[xx][yy - 2] = 0;
209 			eat(xx, yy - 2);
210 			break;
211 
212 		case 4:
213 			if (yy >= MAXY - 3)
214 				break;	/* north	 */
215 			if ((item[xx][yy + 1] != OWALL) || (item[xx][yy + 2] != OWALL))
216 				break;
217 			item[xx][yy + 1] = item[xx][yy + 2] = 0;
218 			eat(xx, yy + 2);
219 			break;
220 		};
221 		if (++dir > 4) {
222 			dir = 1;
223 			--try;
224 		}
225 	}
226 }
227 
228 /*
229  *	function to read in a maze from a data file
230  *
231  *	Format of maze data file:  1st character = # of mazes in file (ascii digit)
232  *				For each maze: 18 lines (1st 17 used) 67 characters per line
233  *
234  *	Special characters in maze data file:
235  *
236  *		#	wall			D	door			.	random monster
237  *		~	eye of larn		!	cure dianthroritis
238  *		-	random object
239  */
240 static int
cannedlevel(int k)241 cannedlevel(int k)
242 {
243 	char           *row;
244 	int             i, j;
245 	int             it, arg, mit, marg;
246 	if (lopen(larnlevels) < 0) {
247 		write(1, "Can't open the maze data file\n", 30);
248 		died(-282);
249 		return (0);
250 	}
251 	i = lgetc();
252 	if (i <= '0') {
253 		died(-282);
254 		return (0);
255 	}
256 	for (i = 18 * rund(i - '0'); i > 0; i--)
257 		lgetl();	/* advance to desired maze */
258 	for (i = 0; i < MAXY; i++) {
259 		row = lgetl();
260 		for (j = 0; j < MAXX; j++) {
261 			it = mit = arg = marg = 0;
262 			switch (*row++) {
263 			case '#':
264 				it = OWALL;
265 				break;
266 			case 'D':
267 				it = OCLOSEDDOOR;
268 				arg = rnd(30);
269 				break;
270 			case '~':
271 				if (k != MAXLEVEL - 1)
272 					break;
273 				it = OLARNEYE;
274 				mit = rund(8) + DEMONLORD;
275 				marg = monster[mit].hitpoints;
276 				break;
277 			case '!':
278 				if (k != MAXLEVEL + MAXVLEVEL - 1)
279 					break;
280 				it = OPOTION;
281 				arg = 21;
282 				mit = DEMONLORD + 7;
283 				marg = monster[mit].hitpoints;
284 				break;
285 			case '.':
286 				if (k < MAXLEVEL)
287 					break;
288 				mit = makemonst(k + 1);
289 				marg = monster[mit].hitpoints;
290 				break;
291 			case '-':
292 				it = newobject(k + 1, &arg);
293 				break;
294 			};
295 			item[j][i] = it;
296 			iarg[j][i] = arg;
297 			mitem[j][i] = mit;
298 			hitp[j][i] = marg;
299 
300 #if WIZID
301 			know[j][i] = (wizard) ? 1 : 0;
302 #else
303 			know[j][i] = 0;
304 #endif
305 		}
306 	}
307 	lrclose();
308 	return (1);
309 }
310 
311 /*
312 	function to make a treasure room on a level
313 	level 10's treasure room has the eye in it and demon lords
314 	level V3 has potion of cure dianthroritis and demon prince
315  */
316 static void
treasureroom(int lv)317 treasureroom(int lv)
318 {
319 	int             tx, ty, xsize, ysize;
320 
321 	for (tx = 1 + rnd(10); tx < MAXX - 10; tx += 10)
322 		if ((lv == MAXLEVEL - 1) || (lv == MAXLEVEL + MAXVLEVEL - 1) || rnd(13) == 2) {
323 			xsize = rnd(6) + 3;
324 			ysize = rnd(3) + 3;
325 			ty = rnd(MAXY - 9) + 1;	/* upper left corner of room */
326 			if (lv == MAXLEVEL - 1 || lv == MAXLEVEL + MAXVLEVEL - 1)
327 				troom(lv, xsize, ysize, tx = tx + rnd(MAXX - 24), ty, rnd(3) + 6);
328 			else
329 				troom(lv, xsize, ysize, tx, ty, rnd(9));
330 		}
331 }
332 
333 /*
334  *	subroutine to create a treasure room of any size at a given location
335  *	room is filled with objects and monsters
336  *	the coordinate given is that of the upper left corner of the room
337  */
338 static void
troom(int lv,int xsize,int ysize,int tx,int ty,int glyph)339 troom(int lv, int xsize, int ysize, int tx, int ty, int glyph)
340 {
341 	int             i, j;
342 	int             tp1, tp2;
343 	for (j = ty - 1; j <= ty + ysize; j++)
344 		for (i = tx - 1; i <= tx + xsize; i++)	/* clear out space for
345 							 * room */
346 			item[i][j] = 0;
347 	for (j = ty; j < ty + ysize; j++)
348 		for (i = tx; i < tx + xsize; i++) {	/* now put in the walls */
349 			item[i][j] = OWALL;
350 			mitem[i][j] = 0;
351 		}
352 	for (j = ty + 1; j < ty + ysize - 1; j++)
353 		for (i = tx + 1; i < tx + xsize - 1; i++)	/* now clear out
354 								 * interior */
355 			item[i][j] = 0;
356 
357 	switch (rnd(2)) {	/* locate the door on the treasure room */
358 	case 1:
359 		item[i = tx + rund(xsize)][j = ty + (ysize - 1) * rund(2)] = OCLOSEDDOOR;
360 		iarg[i][j] = glyph;	/* on horizontal walls */
361 		break;
362 	case 2:
363 		item[i = tx + (xsize - 1) * rund(2)][j = ty + rund(ysize)] = OCLOSEDDOOR;
364 		iarg[i][j] = glyph;	/* on vertical walls */
365 		break;
366 	};
367 
368 	tp1 = playerx;
369 	tp2 = playery;
370 	playery = ty + (ysize >> 1);
371 	if (c[HARDGAME] < 2)
372 		for (playerx = tx + 1; playerx <= tx + xsize - 2; playerx += 2)
373 			for (i = 0, j = rnd(6); i <= j; i++) {
374 				something(lv + 2);
375 				createmonster(makemonst(lv + 1));
376 			}
377 	else
378 		for (playerx = tx + 1; playerx <= tx + xsize - 2; playerx += 2)
379 			for (i = 0, j = rnd(4); i <= j; i++) {
380 				something(lv + 2);
381 				createmonster(makemonst(lv + 3));
382 			}
383 
384 	playerx = tp1;
385 	playery = tp2;
386 }
387 
388 
389 /*
390 	***********
391 	MAKE_OBJECT
392 	***********
393 	subroutine to create the objects in the maze for the given level
394  */
395 static void
makeobject(int j)396 makeobject(int j)
397 {
398 	int             i;
399 	if (j == 0) {
400 		fillroom(OENTRANCE, 0);	/* entrance to dungeon			 */
401 		fillroom(ODNDSTORE, 0);	/* the DND STORE				 */
402 		fillroom(OSCHOOL, 0);	/* college of Larn				 */
403 		fillroom(OBANK, 0);	/* 1st national bank of larn 	 */
404 		fillroom(OVOLDOWN, 0);	/* volcano shaft to temple 	 */
405 		fillroom(OHOME, 0);	/* the players home & family 	 */
406 		fillroom(OTRADEPOST, 0);	/* the trading post			 */
407 		fillroom(OLRS, 0);	/* the larn revenue service 	 */
408 		return;
409 	}
410 	if (j == MAXLEVEL)
411 		fillroom(OVOLUP, 0);	/* volcano shaft up from the temple */
412 
413 	/* make the fixed objects in the maze STAIRS	 */
414 	if ((j > 0) && (j != MAXLEVEL - 1) && (j != MAXLEVEL + MAXVLEVEL - 1))
415 		fillroom(OSTAIRSDOWN, 0);
416 	if ((j > 1) && (j != MAXLEVEL))
417 		fillroom(OSTAIRSUP, 0);
418 
419 	/* make the random objects in the maze		 */
420 
421 	fillmroom(rund(3), OBOOK, j);
422 	fillmroom(rund(3), OALTAR, 0);
423 	fillmroom(rund(3), OSTATUE, 0);
424 	fillmroom(rund(3), OPIT, 0);
425 	fillmroom(rund(3), OFOUNTAIN, 0);
426 	fillmroom(rnd(3) - 2, OIVTELETRAP, 0);
427 	fillmroom(rund(2), OTHRONE, 0);
428 	fillmroom(rund(2), OMIRROR, 0);
429 	fillmroom(rund(2), OTRAPARROWIV, 0);
430 	fillmroom(rnd(3) - 2, OIVDARTRAP, 0);
431 	fillmroom(rund(3), OCOOKIE, 0);
432 	if (j == 1)
433 		fillmroom(1, OCHEST, j);
434 	else
435 		fillmroom(rund(2), OCHEST, j);
436 	if ((j != MAXLEVEL - 1) && (j != MAXLEVEL + MAXVLEVEL - 1))
437 		fillmroom(rund(2), OIVTRAPDOOR, 0);
438 	if (j <= 10) {
439 		fillmroom((rund(2)), ODIAMOND, rnd(10 * j + 1) + 10);
440 		fillmroom(rund(2), ORUBY, rnd(6 * j + 1) + 6);
441 		fillmroom(rund(2), OEMERALD, rnd(4 * j + 1) + 4);
442 		fillmroom(rund(2), OSAPPHIRE, rnd(3 * j + 1) + 2);
443 	}
444 	for (i = 0; i < rnd(4) + 3; i++)
445 		fillroom(OPOTION, newpotion());	/* make a POTION	 */
446 	for (i = 0; i < rnd(5) + 3; i++)
447 		fillroom(OSCROLL, newscroll());	/* make a SCROLL	 */
448 	for (i = 0; i < rnd(12) + 11; i++)
449 		fillroom(OGOLDPILE, 12 * rnd(j + 1) + (j << 3) + 10);	/* make GOLD	 */
450 	if (j == 5)
451 		fillroom(OBANK2, 0);	/* branch office of the bank */
452 	froom(2, ORING, 0);	/* a ring mail 			 */
453 	froom(1, OSTUDLEATHER, 0);	/* a studded leather	 */
454 	froom(3, OSPLINT, 0);	/* a splint mail		 */
455 	froom(5, OSHIELD, rund(3));	/* a shield				 */
456 	froom(2, OBATTLEAXE, rund(3));	/* a battle axe			 */
457 	froom(5, OLONGSWORD, rund(3));	/* a long sword			 */
458 	froom(5, OFLAIL, rund(3));	/* a flail				 */
459 	froom(4, OREGENRING, rund(3));	/* ring of regeneration */
460 	froom(1, OPROTRING, rund(3));	/* ring of protection	 */
461 	froom(2, OSTRRING, 4);	/* ring of strength + 4 */
462 	froom(7, OSPEAR, rnd(5));	/* a spear				 */
463 	froom(3, OORBOFDRAGON, 0);	/* orb of dragon slaying */
464 	froom(4, OSPIRITSCARAB, 0);	/* scarab of negate spirit */
465 	froom(4, OCUBEofUNDEAD, 0);	/* cube of undead control	 */
466 	froom(2, ORINGOFEXTRA, 0);	/* ring of extra regen		 */
467 	froom(3, ONOTHEFT, 0);	/* device of antitheft 		 */
468 	froom(2, OSWORDofSLASHING, 0);	/* sword of slashing */
469 	if (c[BESSMANN] == 0) {
470 		froom(4, OHAMMER, 0);	/* Bessman's flailing hammer */
471 		c[BESSMANN] = 1;
472 	}
473 	if (c[HARDGAME] < 3 || (rnd(4) == 3)) {
474 		if (j > 3) {
475 			froom(3, OSWORD, 3);	/* sunsword + 3  		 */
476 			froom(5, O2SWORD, rnd(4));	/* a two handed sword	 */
477 			froom(3, OBELT, 4);	/* belt of striking		 */
478 			froom(3, OENERGYRING, 3);	/* energy ring			 */
479 			froom(4, OPLATE, 5);	/* platemail + 5 		 */
480 		}
481 	}
482 }
483 
484 /*
485 	subroutine to fill in a number of objects of the same kind
486  */
487 
488 static void
fillmroom(int n,int what_i,int arg)489 fillmroom(int n, int what_i, int arg)
490 {
491 	int             i;
492 	char            what;
493 
494 	/* truncate to char width (just in case it matters) */
495 	what = (char)what_i;
496 	for (i = 0; i < n; i++)
497 		fillroom(what, arg);
498 }
499 
500 static void
froom(int n,int theitem,int arg)501 froom(int n, int theitem, int arg)
502 {
503 	if (rnd(151) < n)
504 		fillroom(theitem, arg);
505 }
506 
507 /*
508 	subroutine to put an object into an empty room
509  *	uses a random walk
510  */
511 static void
fillroom(int what_i,int arg)512 fillroom(int what_i, int arg)
513 {
514 	int             x, y;
515 	char            what;
516 
517 	/* truncate to char width (just in case it matters) */
518 	what = (char)what_i;
519 
520 #ifdef EXTRA
521 	c[FILLROOM]++;
522 #endif
523 
524 	x = rnd(MAXX - 2);
525 	y = rnd(MAXY - 2);
526 	while (item[x][y]) {
527 
528 #ifdef EXTRA
529 		c[RANDOMWALK]++;/* count up these random walks */
530 #endif
531 
532 		x += rnd(3) - 2;
533 		y += rnd(3) - 2;
534 		if (x > MAXX - 2)
535 			x = 1;
536 		if (x < 1)
537 			x = MAXX - 2;
538 		if (y > MAXY - 2)
539 			y = 1;
540 		if (y < 1)
541 			y = MAXY - 2;
542 	}
543 	item[x][y] = what;
544 	iarg[x][y] = arg;
545 }
546 
547 /*
548 	subroutine to put monsters into an empty room without walls or other
549 	monsters
550  */
551 int
fillmonst(int what)552 fillmonst(int what)
553 {
554 	int             x, y, trys;
555 	for (trys = 5; trys > 0; --trys) {	/* max # of creation attempts */
556 		x = rnd(MAXX - 2);
557 		y = rnd(MAXY - 2);
558 		if ((item[x][y] == 0) && (mitem[x][y] == 0) && ((playerx != x) || (playery != y))) {
559 			mitem[x][y] = what;
560 			know[x][y] = 0;
561 			hitp[x][y] = monster[what].hitpoints;
562 			return (0);
563 		}
564 	}
565 	return (-1);		/* creation failure */
566 }
567 
568 /*
569 	creates an entire set of monsters for a level
570 	must be done when entering a new level
571 	if sethp(1) then wipe out old monsters else leave them there
572  */
573 static void
sethp(int flg)574 sethp(int flg)
575 {
576 	int             i, j;
577 	if (flg)
578 		for (i = 0; i < MAXY; i++)
579 			for (j = 0; j < MAXX; j++)
580 				stealth[j][i] = 0;
581 	if (level == 0) {
582 		c[TELEFLAG] = 0;
583 		return;
584 	}			/* if teleported and found level 1 then know
585 				 * level we are on */
586 	if (flg)
587 		j = rnd(12) + 2 + (level >> 1);
588 	else
589 		j = (level >> 1) + 1;
590 	for (i = 0; i < j; i++)
591 		fillmonst(makemonst(level));
592 	positionplayer();
593 }
594 
595 /*
596  *	Function to destroy all genocided monsters on the present level
597  */
598 static void
checkgen(void)599 checkgen(void)
600 {
601 	int             x, y;
602 	for (y = 0; y < MAXY; y++)
603 		for (x = 0; x < MAXX; x++)
604 			if (monster[mitem[x][y]].genocided)
605 				mitem[x][y] = 0;	/* no more monster */
606 }
607