xref: /netbsd-src/games/larn/display.c (revision a5847cc334d9a7029f6352b847e9e8d71a0f9e0c)
1 /*	$NetBSD: display.c,v 1.9 2009/08/12 08:04:05 dholland Exp $	*/
2 
3 /* display.c		Larn is copyrighted 1986 by Noah Morgan. */
4 #include <sys/cdefs.h>
5 #ifndef lint
6 __RCSID("$NetBSD: display.c,v 1.9 2009/08/12 08:04:05 dholland Exp $");
7 #endif /* not lint */
8 
9 #include "header.h"
10 #include "extern.h"
11 
12 #define makecode(_a,_b,_c) (((_a)<<16) + ((_b)<<8) + (_c))
13 
14 static void bot_hpx(void);
15 static void bot_spellx(void);
16 static void botside(void);
17 static void botsub(int, const char *);
18 static void seepage(void);
19 
20 static int      minx, maxx, miny, maxy, k, m;
21 static char     bot1f = 0, bot2f = 0, bot3f = 0;
22 static char always = 0;
23 /*
24 	bottomline()
25 
26 	now for the bottom line of the display
27  */
28 void
29 bottomline()
30 {
31 	recalc();
32 	bot1f = 1;
33 }
34 
35 void
36 bottomhp()
37 {
38 	bot2f = 1;
39 }
40 
41 void
42 bottomspell()
43 {
44 	bot3f = 1;
45 }
46 
47 void
48 bottomdo()
49 {
50 	if (bot1f) {
51 		bot3f = bot1f = bot2f = 0;
52 		bot_linex();
53 		return;
54 	}
55 	if (bot2f) {
56 		bot2f = 0;
57 		bot_hpx();
58 	}
59 	if (bot3f) {
60 		bot3f = 0;
61 		bot_spellx();
62 	}
63 }
64 
65 void
66 bot_linex()
67 {
68 	int    i;
69 	if (cbak[SPELLS] <= -50 || (always)) {
70 		cursor(1, 18);
71 		if (c[SPELLMAX] > 99)
72 			lprintf("Spells:%3ld(%3ld)", (long) c[SPELLS], (long) c[SPELLMAX]);
73 		else
74 			lprintf("Spells:%3ld(%2ld) ", (long) c[SPELLS], (long) c[SPELLMAX]);
75 		lprintf(" AC: %-3ld  WC: %-3ld  Level", (long) c[AC], (long) c[WCLASS]);
76 		if (c[LEVEL] > 99)
77 			lprintf("%3ld", (long) c[LEVEL]);
78 		else
79 			lprintf(" %-2ld", (long) c[LEVEL]);
80 		lprintf(" Exp: %-9ld %s\n", (long) c[EXPERIENCE], class[c[LEVEL] - 1]);
81 		lprintf("HP: %3ld(%3ld) STR=%-2ld INT=%-2ld ",
82 			(long) c[HP], (long) c[HPMAX], (long) (c[STRENGTH] + c[STREXTRA]), (long) c[INTELLIGENCE]);
83 		lprintf("WIS=%-2ld CON=%-2ld DEX=%-2ld CHA=%-2ld LV:",
84 			(long) c[WISDOM], (long) c[CONSTITUTION], (long) c[DEXTERITY], (long) c[CHARISMA]);
85 
86 		if ((level == 0) || (wizard))
87 			c[TELEFLAG] = 0;
88 		if (c[TELEFLAG])
89 			lprcat(" ?");
90 		else
91 			lprcat(levelname[level]);
92 		lprintf("  Gold: %-6ld", (long) c[GOLD]);
93 		always = 1;
94 		botside();
95 		c[TMP] = c[STRENGTH] + c[STREXTRA];
96 		for (i = 0; i < 100; i++)
97 			cbak[i] = c[i];
98 		return;
99 	}
100 	botsub(makecode(SPELLS, 8, 18), "%3ld");
101 	if (c[SPELLMAX] > 99)
102 		botsub(makecode(SPELLMAX, 12, 18), "%3ld)");
103 	else
104 		botsub(makecode(SPELLMAX, 12, 18), "%2ld) ");
105 	botsub(makecode(HP, 5, 19), "%3ld");
106 	botsub(makecode(HPMAX, 9, 19), "%3ld");
107 	botsub(makecode(AC, 21, 18), "%-3ld");
108 	botsub(makecode(WCLASS, 30, 18), "%-3ld");
109 	botsub(makecode(EXPERIENCE, 49, 18), "%-9ld");
110 	if (c[LEVEL] != cbak[LEVEL]) {
111 		cursor(59, 18);
112 		lprcat(class[c[LEVEL] - 1]);
113 	}
114 	if (c[LEVEL] > 99)
115 		botsub(makecode(LEVEL, 40, 18), "%3ld");
116 	else
117 		botsub(makecode(LEVEL, 40, 18), " %-2ld");
118 	c[TMP] = c[STRENGTH] + c[STREXTRA];
119 	botsub(makecode(TMP, 18, 19), "%-2ld");
120 	botsub(makecode(INTELLIGENCE, 25, 19), "%-2ld");
121 	botsub(makecode(WISDOM, 32, 19), "%-2ld");
122 	botsub(makecode(CONSTITUTION, 39, 19), "%-2ld");
123 	botsub(makecode(DEXTERITY, 46, 19), "%-2ld");
124 	botsub(makecode(CHARISMA, 53, 19), "%-2ld");
125 	if ((level != cbak[CAVELEVEL]) || (c[TELEFLAG] != cbak[TELEFLAG])) {
126 		if ((level == 0) || (wizard))
127 			c[TELEFLAG] = 0;
128 		cbak[TELEFLAG] = c[TELEFLAG];
129 		cbak[CAVELEVEL] = level;
130 		cursor(59, 19);
131 		if (c[TELEFLAG])
132 			lprcat(" ?");
133 		else
134 			lprcat(levelname[level]);
135 	}
136 	botsub(makecode(GOLD, 69, 19), "%-6ld");
137 	botside();
138 }
139 
140 /*
141 	special subroutine to update only the gold number on the bottomlines
142 	called from ogold()
143  */
144 void
145 bottomgold()
146 {
147 	botsub(makecode(GOLD, 69, 19), "%-6ld");
148 	/* botsub(GOLD,"%-6ld",69,19); */
149 }
150 
151 /*
152 	special routine to update hp and level fields on bottom lines
153 	called in monster.c hitplayer() and spattack()
154  */
155 static void
156 bot_hpx(void)
157 {
158 	if (c[EXPERIENCE] != cbak[EXPERIENCE]) {
159 		recalc();
160 		bot_linex();
161 	} else
162 		botsub(makecode(HP, 5, 19), "%3ld");
163 }
164 
165 /*
166 	special routine to update number of spells called from regen()
167  */
168 static void
169 bot_spellx(void)
170 {
171 	botsub(makecode(SPELLS, 9, 18), "%2ld");
172 }
173 
174 /*
175 	common subroutine for a more economical bottomline()
176  */
177 static struct bot_side_def {
178 	int             typ;
179 	const char     *string;
180 }
181                 bot_data[] =
182 {
183 	{ STEALTH, "stealth"},
184 	{ UNDEADPRO, "undead pro" },
185 	{ SPIRITPRO, "spirit pro" },
186 	{ CHARMCOUNT, "Charm"},
187 	{ TIMESTOP, "Time Stop" },
188 	{ HOLDMONST, "Hold Monst" },
189 	{ GIANTSTR, "Giant Str"},
190 	{ FIRERESISTANCE, "Fire Resit" },
191 	{ DEXCOUNT, "Dexterity" },
192 	{ STRCOUNT, "Strength"},
193 	{ SCAREMONST, "Scare" },
194 	{ HASTESELF, "Haste Self" },
195 	{ CANCELLATION, "Cancel"},
196 	{ INVISIBILITY, "Invisible" },
197 	{ ALTPRO, "Protect 3" },
198 	{ PROTECTIONTIME, "Protect 2"},
199 	{ WTW, "Wall-Walk" }
200 };
201 
202 static void
203 botside(void)
204 {
205 	int    i, idx;
206 	for (i = 0; i < 17; i++) {
207 		idx = bot_data[i].typ;
208 		if ((always) || (c[idx] != cbak[idx])) {
209 			if ((always) || (cbak[idx] == 0)) {
210 				if (c[idx]) {
211 					cursor(70, i + 1);
212 					lprcat(bot_data[i].string);
213 				}
214 			} else if (c[idx] == 0) {
215 				cursor(70, i + 1);
216 				lprcat("          ");
217 			}
218 			cbak[idx] = c[idx];
219 		}
220 	}
221 	always = 0;
222 }
223 
224 static void
225 botsub(int idx, const char *str)
226 {
227 	int    x, y;
228 	y = idx & 0xff;
229 	x = (idx >> 8) & 0xff;
230 	idx >>= 16;
231 	if (c[idx] != cbak[idx]) {
232 		cbak[idx] = c[idx];
233 		cursor(x, y);
234 		lprintf(str, (long) c[idx]);
235 	}
236 }
237 
238 /*
239  *	subroutine to draw only a section of the screen
240  *	only the top section of the screen is updated.
241  *	If entire lines are being drawn, then they will be cleared first.
242  */
243 /* for limited screen drawing */
244 static int d_xmin = 0, d_xmax = MAXX, d_ymin = 0, d_ymax = MAXY;
245 
246 void
247 draws(xmin, xmax, ymin, ymax)
248 	int             xmin, xmax, ymin, ymax;
249 {
250 	int    i, idx;
251 	if (xmin == 0 && xmax == MAXX) {	/* clear section of screen as
252 						 * needed */
253 		if (ymin == 0)
254 			cl_up(79, ymax);
255 		else
256 			for (i = ymin; i < ymin; i++)
257 				cl_line(1, i + 1);
258 		xmin = -1;
259 	}
260 	d_xmin = xmin;
261 	d_xmax = xmax;
262 	d_ymin = ymin;
263 	d_ymax = ymax;		/* for limited screen drawing */
264 	drawscreen();
265 	if (xmin <= 0 && xmax == MAXX) {	/* draw stuff on right side
266 						 * of screen as needed */
267 		for (i = ymin; i < ymax; i++) {
268 			idx = bot_data[i].typ;
269 			if (c[idx]) {
270 				cursor(70, i + 1);
271 				lprcat(bot_data[i].string);
272 			}
273 			cbak[idx] = c[idx];
274 		}
275 	}
276 }
277 
278 /*
279 	drawscreen()
280 
281 	subroutine to redraw the whole screen as the player knows it
282  */
283 u_char            screen[MAXX][MAXY];	/* template for the screen */
284 static u_char d_flag;
285 void
286 drawscreen()
287 {
288 	int    i, j, kk;
289 	int             lastx, lasty;	/* variables used to optimize the
290 					 * object printing */
291 	if (d_xmin == 0 && d_xmax == MAXX && d_ymin == 0 && d_ymax == MAXY) {
292 		d_flag = 1;
293 		clear();	/* clear the screen */
294 	} else {
295 		d_flag = 0;
296 		cursor(1, 1);
297 	}
298 	if (d_xmin < 0)
299 		d_xmin = 0;	/* d_xmin=-1 means display all without
300 				 * bottomline */
301 
302 	for (i = d_ymin; i < d_ymax; i++)
303 		for (j = d_xmin; j < d_xmax; j++)
304 			if (know[j][i] == 0)
305 				screen[j][i] = ' ';
306 			else if ((kk = mitem[j][i]) != 0)
307 				screen[j][i] = monstnamelist[kk];
308 			else if ((kk = item[j][i]) == OWALL)
309 				screen[j][i] = '#';
310 			else
311 				screen[j][i] = ' ';
312 
313 	for (i = d_ymin; i < d_ymax; i++) {
314 		j = d_xmin;
315 		while ((screen[j][i] == ' ') && (j < d_xmax))
316 			j++;
317 		/* was m=0 */
318 		if (j >= d_xmax)
319 			m = d_xmin;	/* don't search backwards if blank
320 					 * line */
321 		else {		/* search backwards for end of line */
322 			m = d_xmax - 1;
323 			while ((screen[m][i] == ' ') && (m > d_xmin))
324 				--m;
325 			if (j <= m)
326 				cursor(j + 1, i + 1);
327 			else
328 				continue;
329 		}
330 		while (j <= m) {
331 			if (j <= m - 3) {
332 				for (kk = j; kk <= j + 3; kk++)
333 					if (screen[kk][i] != ' ')
334 						kk = 1000;
335 				if (kk < 1000) {
336 					while (screen[j][i] == ' ' && j <= m)
337 						j++;
338 					cursor(j + 1, i + 1);
339 				}
340 			}
341 			lprc(screen[j++][i]);
342 		}
343 	}
344 	setbold();		/* print out only bold objects now */
345 
346 	for (lastx = lasty = 127, i = d_ymin; i < d_ymax; i++)
347 		for (j = d_xmin; j < d_xmax; j++) {
348 			if ((kk = item[j][i]) != 0)
349 				if (kk != OWALL)
350 					if ((know[j][i]) && (mitem[j][i] == 0))
351 						if (objnamelist[kk] != ' ') {
352 							if (lasty != i + 1 || lastx != j)
353 								cursor(lastx = j + 1, lasty = i + 1);
354 							else
355 								lastx++;
356 							lprc(objnamelist[kk]);
357 						}
358 		}
359 
360 	resetbold();
361 	if (d_flag) {
362 		always = 1;
363 		botside();
364 		always = 1;
365 		bot_linex();
366 	}
367 	oldx = 99;
368 	d_xmin = 0, d_xmax = MAXX, d_ymin = 0, d_ymax = MAXY;	/* for limited screen
369 								 * drawing */
370 }
371 
372 
373 /*
374 	showcell(x,y)
375 
376 	subroutine to display a cell location on the screen
377  */
378 void
379 showcell(x, y)
380 	int             x, y;
381 {
382 	int    i, j, kk, mm;
383 	if (c[BLINDCOUNT])
384 		return;		/* see nothing if blind		 */
385 	if (c[AWARENESS]) {
386 		minx = x - 3;
387 		maxx = x + 3;
388 		miny = y - 3;
389 		maxy = y + 3;
390 	} else {
391 		minx = x - 1;
392 		maxx = x + 1;
393 		miny = y - 1;
394 		maxy = y + 1;
395 	}
396 
397 	if (minx < 0)
398 		minx = 0;
399 	if (maxx > MAXX - 1)
400 		maxx = MAXX - 1;
401 	if (miny < 0)
402 		miny = 0;
403 	if (maxy > MAXY - 1)
404 		maxy = MAXY - 1;
405 
406 	for (j = miny; j <= maxy; j++)
407 		for (mm = minx; mm <= maxx; mm++)
408 			if (know[mm][j] == 0) {
409 				cursor(mm + 1, j + 1);
410 				x = maxx;
411 				while (know[x][j])
412 					--x;
413 				for (i = mm; i <= x; i++) {
414 					if ((kk = mitem[i][j]) != 0)
415 						lprc(monstnamelist[kk]);
416 					else
417 						switch (kk = item[i][j]) {
418 						case OWALL:
419 						case 0:
420 						case OIVTELETRAP:
421 						case OTRAPARROWIV:
422 						case OIVDARTRAP:
423 						case OIVTRAPDOOR:
424 							lprc(objnamelist[kk]);
425 							break;
426 
427 						default:
428 							setbold();
429 							lprc(objnamelist[kk]);
430 							resetbold();
431 						};
432 					know[i][j] = 1;
433 				}
434 				mm = maxx;
435 			}
436 }
437 
438 /*
439 	this routine shows only the spot that is given it.  the spaces around
440 	these coordinated are not shown
441 	used in godirect() in monster.c for missile weapons display
442  */
443 void
444 show1cell(x, y)
445 	int             x, y;
446 {
447 	if (c[BLINDCOUNT])
448 		return;		/* see nothing if blind		 */
449 	cursor(x + 1, y + 1);
450 	if ((k = mitem[x][y]) != 0)
451 		lprc(monstnamelist[k]);
452 	else
453 		switch (k = item[x][y]) {
454 		case OWALL:
455 		case 0:
456 		case OIVTELETRAP:
457 		case OTRAPARROWIV:
458 		case OIVDARTRAP:
459 		case OIVTRAPDOOR:
460 			lprc(objnamelist[k]);
461 			break;
462 
463 		default:
464 			setbold();
465 			lprc(objnamelist[k]);
466 			resetbold();
467 		};
468 	know[x][y] |= 1;	/* we end up knowing about it */
469 }
470 
471 /*
472 	showplayer()
473 
474 	subroutine to show where the player is on the screen
475 	cursor values start from 1 up
476  */
477 void
478 showplayer()
479 {
480 	cursor(playerx + 1, playery + 1);
481 	oldx = playerx;
482 	oldy = playery;
483 }
484 
485 /*
486 	moveplayer(dir)
487 
488 	subroutine to move the player from one room to another
489 	returns 0 if can't move in that direction or hit a monster or on an object
490 	else returns 1
491 	nomove is set to 1 to stop the next move (inadvertent monsters hitting
492 	players when walking into walls) if player walks off screen or into wall
493  */
494 short           diroffx[] = {0, 0, 1, 0, -1, 1, -1, 1, -1};
495 short           diroffy[] = {0, 1, 0, -1, 0, -1, -1, 1, 1};
496 int
497 moveplayer(dir)
498 	int             dir;	/* from = present room #  direction =
499 				 * [1-north] [2-east] [3-south] [4-west]
500 				 * [5-northeast] [6-northwest] [7-southeast]
501 				 * [8-southwest] if direction=0, don't
502 				 * move--just show where he is */
503 {
504 	int    kk, mm, i, j;
505 	if (c[CONFUSE])
506 		if (c[LEVEL] < rnd(30))
507 			dir = rund(9);	/* if confused any dir */
508 	kk = playerx + diroffx[dir];
509 	mm = playery + diroffy[dir];
510 	if (kk < 0 || kk >= MAXX || mm < 0 || mm >= MAXY) {
511 		nomove = 1;
512 		return (yrepcount = 0);
513 	}
514 	i = item[kk][mm];
515 	j = mitem[kk][mm];
516 	if (i == OWALL && c[WTW] == 0) {
517 		nomove = 1;
518 		return (yrepcount = 0);
519 	}			/* hit a wall	 */
520 	if (kk == 33 && mm == MAXY - 1 && level == 1) {
521 		newcavelevel(0);
522 		for (kk = 0; kk < MAXX; kk++)
523 			for (mm = 0; mm < MAXY; mm++)
524 				if (item[kk][mm] == OENTRANCE) {
525 					playerx = kk;
526 					playery = mm;
527 					positionplayer();
528 					drawscreen();
529 					return (0);
530 				}
531 	}
532 	if (j > 0) {
533 		hitmonster(kk, mm);
534 		return (yrepcount = 0);
535 	}			/* hit a monster */
536 	lastpx = playerx;
537 	lastpy = playery;
538 	playerx = kk;
539 	playery = mm;
540 	if (i && i != OTRAPARROWIV && i != OIVTELETRAP && i != OIVDARTRAP && i != OIVTRAPDOOR)
541 		return (yrepcount = 0);
542 	else
543 		return (1);
544 }
545 
546 
547 /*
548  *	function to show what magic items have been discovered thus far
549  *	enter with -1 for just spells, anything else will give scrolls & potions
550  */
551 static int      lincount, count;
552 void
553 seemagic(arg)
554 	int             arg;
555 {
556 	int    i, number = 0;
557 	count = lincount = 0;
558 	nosignal = 1;
559 
560 	if (arg == -1) {	/* if display spells while casting one */
561 		for (number = i = 0; i < SPNUM; i++)
562 			if (spelknow[i])
563 				number++;
564 		number = (number + 2) / 3 + 4;	/* # lines needed to display */
565 		cl_up(79, number);
566 		cursor(1, 1);
567 	} else {
568 		resetscroll();
569 		clear();
570 	}
571 
572 	lprcat("The magic spells you have discovered thus far:\n\n");
573 	for (i = 0; i < SPNUM; i++)
574 		if (spelknow[i]) {
575 			lprintf("%s %-20s ", spelcode[i], spelname[i]);
576 			seepage();
577 		}
578 	if (arg == -1) {
579 		seepage();
580 		more();
581 		nosignal = 0;
582 		draws(0, MAXX, 0, number);
583 		return;
584 	}
585 	lincount += 3;
586 	if (count != 0) {
587 		count = 2;
588 		seepage();
589 	}
590 	lprcat("\nThe magic scrolls you have found to date are:\n\n");
591 	count = 0;
592 	for (i = 0; i < MAXSCROLL; i++)
593 		if (scrollname[i][0])
594 			if (scrollname[i][1] != ' ') {
595 				lprintf("%-26s", &scrollname[i][1]);
596 				seepage();
597 			}
598 	lincount += 3;
599 	if (count != 0) {
600 		count = 2;
601 		seepage();
602 	}
603 	lprcat("\nThe magic potions you have found to date are:\n\n");
604 	count = 0;
605 	for (i = 0; i < MAXPOTION; i++)
606 		if (potionname[i][0])
607 			if (potionname[i][1] != ' ') {
608 				lprintf("%-26s", &potionname[i][1]);
609 				seepage();
610 			}
611 	if (lincount != 0)
612 		more();
613 	nosignal = 0;
614 	setscroll();
615 	drawscreen();
616 }
617 
618 /*
619  *	subroutine to paginate the seemagic function
620  */
621 static void
622 seepage(void)
623 {
624 	if (++count == 3) {
625 		lincount++;
626 		count = 0;
627 		lprc('\n');
628 		if (lincount > 17) {
629 			lincount = 0;
630 			more();
631 			clear();
632 		}
633 	}
634 }
635