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