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