Always check the limits of array index variables before using them.
[dragonfly.git] / games / larn / display.c
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 }