xref: /netbsd-src/games/larn/display.c (revision 2980e352a13e8f0b545a366830c411e7a542ada8)
1 /*	$NetBSD: display.c,v 1.8 2008/02/03 19:20:41 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.8 2008/02/03 19:20:41 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 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 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], d_flag;	/* template for the screen */
284 void
285 drawscreen()
286 {
287 	int    i, j, kk;
288 	int             lastx, lasty;	/* variables used to optimize the
289 					 * object printing */
290 	if (d_xmin == 0 && d_xmax == MAXX && d_ymin == 0 && d_ymax == MAXY) {
291 		d_flag = 1;
292 		clear();	/* clear the screen */
293 	} else {
294 		d_flag = 0;
295 		cursor(1, 1);
296 	}
297 	if (d_xmin < 0)
298 		d_xmin = 0;	/* d_xmin=-1 means display all without
299 				 * bottomline */
300 
301 	for (i = d_ymin; i < d_ymax; i++)
302 		for (j = d_xmin; j < d_xmax; j++)
303 			if (know[j][i] == 0)
304 				screen[j][i] = ' ';
305 			else if ((kk = mitem[j][i]) != 0)
306 				screen[j][i] = monstnamelist[kk];
307 			else if ((kk = item[j][i]) == OWALL)
308 				screen[j][i] = '#';
309 			else
310 				screen[j][i] = ' ';
311 
312 	for (i = d_ymin; i < d_ymax; i++) {
313 		j = d_xmin;
314 		while ((screen[j][i] == ' ') && (j < d_xmax))
315 			j++;
316 		/* was m=0 */
317 		if (j >= d_xmax)
318 			m = d_xmin;	/* don't search backwards if blank
319 					 * line */
320 		else {		/* search backwards for end of line */
321 			m = d_xmax - 1;
322 			while ((screen[m][i] == ' ') && (m > d_xmin))
323 				--m;
324 			if (j <= m)
325 				cursor(j + 1, i + 1);
326 			else
327 				continue;
328 		}
329 		while (j <= m) {
330 			if (j <= m - 3) {
331 				for (kk = j; kk <= j + 3; kk++)
332 					if (screen[kk][i] != ' ')
333 						kk = 1000;
334 				if (kk < 1000) {
335 					while (screen[j][i] == ' ' && j <= m)
336 						j++;
337 					cursor(j + 1, i + 1);
338 				}
339 			}
340 			lprc(screen[j++][i]);
341 		}
342 	}
343 	setbold();		/* print out only bold objects now */
344 
345 	for (lastx = lasty = 127, i = d_ymin; i < d_ymax; i++)
346 		for (j = d_xmin; j < d_xmax; j++) {
347 			if ((kk = item[j][i]) != 0)
348 				if (kk != OWALL)
349 					if ((know[j][i]) && (mitem[j][i] == 0))
350 						if (objnamelist[kk] != ' ') {
351 							if (lasty != i + 1 || lastx != j)
352 								cursor(lastx = j + 1, lasty = i + 1);
353 							else
354 								lastx++;
355 							lprc(objnamelist[kk]);
356 						}
357 		}
358 
359 	resetbold();
360 	if (d_flag) {
361 		always = 1;
362 		botside();
363 		always = 1;
364 		bot_linex();
365 	}
366 	oldx = 99;
367 	d_xmin = 0, d_xmax = MAXX, d_ymin = 0, d_ymax = MAXY;	/* for limited screen
368 								 * drawing */
369 }
370 
371 
372 /*
373 	showcell(x,y)
374 
375 	subroutine to display a cell location on the screen
376  */
377 void
378 showcell(x, y)
379 	int             x, y;
380 {
381 	int    i, j, kk, mm;
382 	if (c[BLINDCOUNT])
383 		return;		/* see nothing if blind		 */
384 	if (c[AWARENESS]) {
385 		minx = x - 3;
386 		maxx = x + 3;
387 		miny = y - 3;
388 		maxy = y + 3;
389 	} else {
390 		minx = x - 1;
391 		maxx = x + 1;
392 		miny = y - 1;
393 		maxy = y + 1;
394 	}
395 
396 	if (minx < 0)
397 		minx = 0;
398 	if (maxx > MAXX - 1)
399 		maxx = MAXX - 1;
400 	if (miny < 0)
401 		miny = 0;
402 	if (maxy > MAXY - 1)
403 		maxy = MAXY - 1;
404 
405 	for (j = miny; j <= maxy; j++)
406 		for (mm = minx; mm <= maxx; mm++)
407 			if (know[mm][j] == 0) {
408 				cursor(mm + 1, j + 1);
409 				x = maxx;
410 				while (know[x][j])
411 					--x;
412 				for (i = mm; i <= x; i++) {
413 					if ((kk = mitem[i][j]) != 0)
414 						lprc(monstnamelist[kk]);
415 					else
416 						switch (kk = item[i][j]) {
417 						case OWALL:
418 						case 0:
419 						case OIVTELETRAP:
420 						case OTRAPARROWIV:
421 						case OIVDARTRAP:
422 						case OIVTRAPDOOR:
423 							lprc(objnamelist[kk]);
424 							break;
425 
426 						default:
427 							setbold();
428 							lprc(objnamelist[kk]);
429 							resetbold();
430 						};
431 					know[i][j] = 1;
432 				}
433 				mm = maxx;
434 			}
435 }
436 
437 /*
438 	this routine shows only the spot that is given it.  the spaces around
439 	these coordinated are not shown
440 	used in godirect() in monster.c for missile weapons display
441  */
442 void
443 show1cell(x, y)
444 	int             x, y;
445 {
446 	if (c[BLINDCOUNT])
447 		return;		/* see nothing if blind		 */
448 	cursor(x + 1, y + 1);
449 	if ((k = mitem[x][y]) != 0)
450 		lprc(monstnamelist[k]);
451 	else
452 		switch (k = item[x][y]) {
453 		case OWALL:
454 		case 0:
455 		case OIVTELETRAP:
456 		case OTRAPARROWIV:
457 		case OIVDARTRAP:
458 		case OIVTRAPDOOR:
459 			lprc(objnamelist[k]);
460 			break;
461 
462 		default:
463 			setbold();
464 			lprc(objnamelist[k]);
465 			resetbold();
466 		};
467 	know[x][y] |= 1;	/* we end up knowing about it */
468 }
469 
470 /*
471 	showplayer()
472 
473 	subroutine to show where the player is on the screen
474 	cursor values start from 1 up
475  */
476 void
477 showplayer()
478 {
479 	cursor(playerx + 1, playery + 1);
480 	oldx = playerx;
481 	oldy = playery;
482 }
483 
484 /*
485 	moveplayer(dir)
486 
487 	subroutine to move the player from one room to another
488 	returns 0 if can't move in that direction or hit a monster or on an object
489 	else returns 1
490 	nomove is set to 1 to stop the next move (inadvertent monsters hitting
491 	players when walking into walls) if player walks off screen or into wall
492  */
493 short           diroffx[] = {0, 0, 1, 0, -1, 1, -1, 1, -1};
494 short           diroffy[] = {0, 1, 0, -1, 0, -1, -1, 1, 1};
495 int
496 moveplayer(dir)
497 	int             dir;	/* from = present room #  direction =
498 				 * [1-north] [2-east] [3-south] [4-west]
499 				 * [5-northeast] [6-northwest] [7-southeast]
500 				 * [8-southwest] if direction=0, don't
501 				 * move--just show where he is */
502 {
503 	int    kk, mm, i, j;
504 	if (c[CONFUSE])
505 		if (c[LEVEL] < rnd(30))
506 			dir = rund(9);	/* if confused any dir */
507 	kk = playerx + diroffx[dir];
508 	mm = playery + diroffy[dir];
509 	if (kk < 0 || kk >= MAXX || mm < 0 || mm >= MAXY) {
510 		nomove = 1;
511 		return (yrepcount = 0);
512 	}
513 	i = item[kk][mm];
514 	j = mitem[kk][mm];
515 	if (i == OWALL && c[WTW] == 0) {
516 		nomove = 1;
517 		return (yrepcount = 0);
518 	}			/* hit a wall	 */
519 	if (kk == 33 && mm == MAXY - 1 && level == 1) {
520 		newcavelevel(0);
521 		for (kk = 0; kk < MAXX; kk++)
522 			for (mm = 0; mm < MAXY; mm++)
523 				if (item[kk][mm] == OENTRANCE) {
524 					playerx = kk;
525 					playery = mm;
526 					positionplayer();
527 					drawscreen();
528 					return (0);
529 				}
530 	}
531 	if (j > 0) {
532 		hitmonster(kk, mm);
533 		return (yrepcount = 0);
534 	}			/* hit a monster */
535 	lastpx = playerx;
536 	lastpy = playery;
537 	playerx = kk;
538 	playery = mm;
539 	if (i && i != OTRAPARROWIV && i != OIVTELETRAP && i != OIVDARTRAP && i != OIVTRAPDOOR)
540 		return (yrepcount = 0);
541 	else
542 		return (1);
543 }
544 
545 
546 /*
547  *	function to show what magic items have been discovered thus far
548  *	enter with -1 for just spells, anything else will give scrolls & potions
549  */
550 static int      lincount, count;
551 void
552 seemagic(arg)
553 	int             arg;
554 {
555 	int    i, number = 0;
556 	count = lincount = 0;
557 	nosignal = 1;
558 
559 	if (arg == -1) {	/* if display spells while casting one */
560 		for (number = i = 0; i < SPNUM; i++)
561 			if (spelknow[i])
562 				number++;
563 		number = (number + 2) / 3 + 4;	/* # lines needed to display */
564 		cl_up(79, number);
565 		cursor(1, 1);
566 	} else {
567 		resetscroll();
568 		clear();
569 	}
570 
571 	lprcat("The magic spells you have discovered thus far:\n\n");
572 	for (i = 0; i < SPNUM; i++)
573 		if (spelknow[i]) {
574 			lprintf("%s %-20s ", spelcode[i], spelname[i]);
575 			seepage();
576 		}
577 	if (arg == -1) {
578 		seepage();
579 		more();
580 		nosignal = 0;
581 		draws(0, MAXX, 0, number);
582 		return;
583 	}
584 	lincount += 3;
585 	if (count != 0) {
586 		count = 2;
587 		seepage();
588 	}
589 	lprcat("\nThe magic scrolls you have found to date are:\n\n");
590 	count = 0;
591 	for (i = 0; i < MAXSCROLL; i++)
592 		if (scrollname[i][0])
593 			if (scrollname[i][1] != ' ') {
594 				lprintf("%-26s", &scrollname[i][1]);
595 				seepage();
596 			}
597 	lincount += 3;
598 	if (count != 0) {
599 		count = 2;
600 		seepage();
601 	}
602 	lprcat("\nThe magic potions you have found to date are:\n\n");
603 	count = 0;
604 	for (i = 0; i < MAXPOTION; i++)
605 		if (potionname[i][0])
606 			if (potionname[i][1] != ' ') {
607 				lprintf("%-26s", &potionname[i][1]);
608 				seepage();
609 			}
610 	if (lincount != 0)
611 		more();
612 	nosignal = 0;
613 	setscroll();
614 	drawscreen();
615 }
616 
617 /*
618  *	subroutine to paginate the seemagic function
619  */
620 static void
621 seepage(void)
622 {
623 	if (++count == 3) {
624 		lincount++;
625 		count = 0;
626 		lprc('\n');
627 		if (lincount > 17) {
628 			lincount = 0;
629 			more();
630 			clear();
631 		}
632 	}
633 }
634