1 /*
2  * xrick/src/draw.c
3  *
4  * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net). All rights reserved.
5  *
6  * The use and distribution terms for this software are contained in the file
7  * named README, which can be found in the root of this distribution. By
8  * using this software in any fashion, you are agreeing to be bound by the
9  * terms of this license.
10  *
11  * You must not remove this notice, or any other, from this software.
12  */
13 
14 /*
15  * NOTES
16  *
17  * This is the only file which accesses the video. Anything calling d_*
18  * function should be video-independant.
19  *
20  * draw.c draws into a 320x200 or 0x0140x0xc8 8-bits depth frame buffer,
21  * using the CGA 2 bits color codes. It is up to the video to figure out
22  * how to display the frame buffer. Whatever draw.c does, does not show
23  * until the screen is explicitely refreshed.
24  *
25  * The "screen" is the whole 0x0140 by 0x00c8 screen, coordinates go from
26  * 0x0000,0x0000 to 0x013f,0x00c7.
27  *
28  * The "map" is a 0x0100 by 0x0140 rectangle that represents the active
29  * game area.
30  *
31  * Relative to the screen, the "map" is located at 0x0020,-0x0040 : the
32  * "map" is composed of two hidden 0x0100 by 0x0040 rectangles (one at the
33  * top and one at the bottom) and one visible 0x0100 by 0x00c0 rectangle (in
34  * the middle).
35  *
36  * The "map screen" is the visible rectangle ; it is a 0x0100 by 0xc0
37  * rectangle located at 0x0020,0x00.
38  *
39  * Coordinates can be relative to the screen, the map, or the map screen.
40  *
41  * Coordinates can be expressed in pixels. When relative to the map or the
42  * map screen, they can also be expressed in tiles, the map being composed
43  * of rows of 0x20 tiles of 0x08 by 0x08 pixels.
44  */
45 
46 #include "system.h"
47 #include "game.h"
48 #include "draw.h"
49 
50 #include "sysvid.h"
51 #include "sprites.h"
52 #include "tiles.h"
53 #include "maps.h"
54 #include "rects.h"
55 #include "img.h"
56 
57 
58 /*
59  * counters positions (pixels, screen)
60  */
61 #ifdef GFXPC
62 #define DRAW_STATUS_SCORE_X 0x28
63 #define DRAW_STATUS_LIVES_X 0xE8
64 #define DRAW_STATUS_Y 0x08
65 #endif
66 #define DRAW_STATUS_BULLETS_X 0x68
67 #define DRAW_STATUS_BOMBS_X 0xA8
68 #ifdef GFXST
69 #define DRAW_STATUS_SCORE_X 0x20
70 #define DRAW_STATUS_LIVES_X 0xF0
71 #define DRAW_STATUS_Y 0
72 #endif
73 
74 
75 /*
76  * public vars
77  */
78 U8 *draw_tllst;    /* pointer to tiles list */
79 #ifdef GFXPC
80 U16 draw_filter;   /* CGA colors filter */
81 #endif
82 U8 draw_tilesBank; /* tile number offset */
83 rect_t draw_STATUSRECT = {
84   DRAW_STATUS_SCORE_X, DRAW_STATUS_Y,
85   DRAW_STATUS_LIVES_X + 6 * 8 - DRAW_STATUS_SCORE_X, 8,
86   NULL
87 };
88 rect_t draw_SCREENRECT = { 0, 0, SYSVID_WIDTH, SYSVID_HEIGHT, NULL };
89 
90 
91 /*
92  * private vars
93  */
94 static U8 *fb;     /* frame buffer pointer */
95 
96 
97 /*
98  * Set the frame buffer pointer
99  *
100  * x, y: position (pixels, screen)
101  */
102 void
draw_setfb(U16 x,U16 y)103 draw_setfb(U16 x, U16 y)
104 {
105   fb = sysvid_fb + x + y * SYSVID_WIDTH;
106 }
107 
108 
109 /*
110  * Clip to map screen
111  *
112  * x, y: position (pixels, map) CHANGED clipped
113  * width, height: dimension CHANGED clipped
114  * return: TRUE if fully clipped, FALSE if still (at least partly) visible
115  */
116 U8
draw_clipms(S16 * x,S16 * y,U16 * width,U16 * height)117 draw_clipms(S16 *x, S16 *y, U16 *width, U16 *height)
118 {
119   if (*x < 0) {
120     if (*x + *width < 0)
121       return TRUE;
122     else {
123       *width += *x;
124       *x = 0;
125     }
126   }
127   else {
128     if (*x > 0x0100)
129       return TRUE;
130     else if (*x + *width > 0x0100) {
131       *width = 0x0100 - *x;
132     }
133   }
134 
135   if (*y < DRAW_XYMAP_SCRTOP) {
136     if ((*y + *height) < DRAW_XYMAP_SCRTOP)
137       return TRUE;
138     else {
139       *height += *y - DRAW_XYMAP_SCRTOP;
140       *y = DRAW_XYMAP_SCRTOP;
141     }
142   }
143   else {
144     if (*y >= DRAW_XYMAP_HBTOP)
145       return TRUE;
146     else if (*y + *height > DRAW_XYMAP_HBTOP)
147       *height = DRAW_XYMAP_HBTOP - *y;
148   }
149 
150   return FALSE;
151 }
152 
153 
154 /*
155  * Draw a list of tiles onto the frame buffer
156  * start at position indicated by fb ; at the end of each (sub)list,
157  * perform a "carriage return + line feed" i.e. go back to the initial
158  * position then go down one tile row (8 pixels)
159  *
160  * ASM 1e33
161  * fb: CHANGED (see above)
162  * draw_tllst: CHANGED points to the element following 0xfe/0xff end code
163  */
164 void
draw_tilesList(void)165 draw_tilesList(void)
166 {
167   U8 *t;
168 
169   t = fb;
170   while (draw_tilesSubList() != 0xFE) {  /* draw sub-list */
171     t += 8 * SYSVID_WIDTH;  /* go down one tile i.e. 8 lines */
172     fb = t;
173   }
174 }
175 
176 
177 /*
178  * Draw a list of tiles onto the frame buffer -- same as draw_tilesList,
179  * but accept an immediate string as parameter. Note that the string needs
180  * to be properly terminated with 0xfe (\376) and 0xff (\377) chars.
181  */
182 void
draw_tilesListImm(U8 * list)183 draw_tilesListImm(U8 *list)
184 {
185   draw_tllst = list;
186   draw_tilesList();
187 }
188 
189 
190 /*
191  * Draw a sub-list of tiles onto the frame buffer
192  * start at position indicated by fb ; leave fb pointing to the next
193  * tile to the right of the last tile drawn
194  *
195  * ASM 1e41
196  * fpb: CHANGED (see above)
197  * draw_tllst: CHANGED points to the element following 0xfe/0xff end code
198  * returns: end code (0xfe : end of list ; 0xff : end of sub-list)
199  */
200 U8
draw_tilesSubList()201 draw_tilesSubList()
202 {
203   U8 i;
204 
205   i = *(draw_tllst++);
206   while (i != 0xFF && i != 0xFE) {  /* while not end */
207     draw_tile(i);  /* draw tile */
208     i = *(draw_tllst++);
209   }
210   return i;
211 }
212 
213 
214 /*
215  * Draw a tile
216  * at position indicated by fb ; leave fb pointing to the next tile
217  * to the right of the tile drawn
218  *
219  * ASM 1e6c
220  * tlnbr: tile number
221  * draw_filter: CGA colors filter
222  * fb: CHANGED (see above)
223  */
224 void
draw_tile(U8 tileNumber)225 draw_tile(U8 tileNumber)
226 {
227   U8 i, k, *f;
228 
229 #ifdef GFXPC
230   U16 x;
231 #endif
232 
233 #ifdef GFXST
234   U32 x;
235 #endif
236 
237   f = fb;  /* frame buffer */
238   for (i = 0; i < 8; i++) {  /* for all 8 pixel lines */
239 
240 #ifdef GFXPC
241     x = tiles_data[draw_tilesBank][tileNumber][i] & draw_filter;
242     /*
243      * tiles / perform the transformation from CGA 2 bits
244      * per pixel to frame buffer 8 bits per pixels
245      */
246     for (k = 8; k--; x >>= 2)
247       f[k] = x & 3;
248     f += SYSVID_WIDTH;  /* next line */
249 #endif
250 
251 #ifdef GFXST
252   x = tiles_data[draw_tilesBank][tileNumber][i];
253   /*
254    * tiles / perform the transformation from ST 4 bits
255    * per pixel to frame buffer 8 bits per pixels
256    */
257   for (k = 8; k--; x >>= 4)
258     f[k] = x & 0x0F;
259   f += SYSVID_WIDTH;  /* next line */
260 #endif
261 
262   }
263 
264   fb += 8;  /* next tile */
265 }
266 
267 /*
268  * Draw a sprite
269  *
270  * ASM 1a09
271  * nbr: sprite number
272  * x, y: sprite position (pixels, screen)
273  * fb: CHANGED
274  */
275 #ifdef GFXPC
276 void
draw_sprite(U8 nbr,U16 x,U16 y)277 draw_sprite(U8 nbr, U16 x, U16 y)
278 {
279   U8 i, j, k, *f;
280   U16 xm = 0, xp = 0;
281 
282   draw_setfb(x, y);
283 
284   for (i = 0; i < 4; i++) {  /* for each tile column */
285     f = fb;  /* frame buffer */
286     for (j = 0; j < 0x15; j++) {  /* for each pixel row */
287       xm = sprites_data[nbr][i][j].mask;  /* mask */
288       xp = sprites_data[nbr][i][j].pict;  /* picture */
289       /*
290        * sprites / perform the transformation from CGA 2 bits
291        * per pixel to frame buffer 8 bits per pixels
292        */
293       for (k = 8; k--; xm >>= 2, xp >>= 2)
294 	f[k] = (f[k] & (xm & 3)) | (xp & 3);
295       f += SYSVID_WIDTH;
296     }
297     fb += 8;
298   }
299 }
300 #endif
301 
302 
303 /*
304  * Draw a sprite
305  *
306  * foobar
307  */
308 #ifdef GFXST
309 void
draw_sprite(U8 number,U16 x,U16 y)310 draw_sprite(U8 number, U16 x, U16 y)
311 {
312   U8 i, j, k, *f;
313   U16 g;
314   U32 d;
315 
316   draw_setfb(x, y);
317   g = 0;
318   for (i = 0; i < 0x15; i++) { /* rows */
319     f = fb;
320     for (j = 0; j < 4; j++) { /* cols */
321       d = sprites_data[number][g++];
322       for (k = 8; k--; d >>= 4)
323 	if (d & 0x0F) f[k] = (f[k] & 0xF0) | (d & 0x0F);
324       f += 8;
325     }
326     fb += SYSVID_WIDTH;
327   }
328 }
329 #endif
330 
331 
332 /*
333  * Draw a sprite
334  *
335  * NOTE re-using original ST graphics format
336  */
337 #ifdef GFXST
338 void
draw_sprite2(U8 number,U16 x,U16 y,U8 front)339 draw_sprite2(U8 number, U16 x, U16 y, U8 front)
340 {
341   U32 d = 0;   /* sprite data */
342   S16 x0, y0;  /* clipped x, y */
343   U16 w, h;    /* width, height */
344   S16 g,       /* sprite data offset*/
345     r, c,      /* row, column */
346     i,         /* frame buffer shifter */
347     im;        /* tile flag shifter */
348   U8 flg;      /* tile flag */
349 
350   x0 = x;
351   y0 = y;
352   w = 0x20;
353   h = 0x15;
354 
355   if (draw_clipms(&x0, &y0, &w, &h))  /* return if not visible */
356     return;
357 
358   g = 0;
359   draw_setfb(x0 - DRAW_XYMAP_SCRLEFT, y0 - DRAW_XYMAP_SCRTOP + 8);
360 
361   for (r = 0; r < 0x15; r++) {
362     if (r >= h || y + r < y0) continue;
363 
364     i = 0x1f;
365     im = x - (x & 0xfff8);
366     flg = map_eflg[map_map[(y + r) >> 3][(x + 0x1f)>> 3]];
367 
368 #ifdef ENABLE_CHEATS
369 #define LOOP(N, C0, C1) \
370     d = sprites_data[number][g + N]; \
371     for (c = C0; c >= C1; c--, i--, d >>= 4, im--) { \
372       if (im == 0) { \
373 	flg = map_eflg[map_map[(y + r) >> 3][(x + c) >> 3]]; \
374 	im = 8; \
375       } \
376       if (c >= w || x + c < x0) continue; \
377       if (!front && !game_cheat3 && (flg & MAP_EFLG_FGND)) continue; \
378       if (d & 0x0F) fb[i] = (fb[i] & 0xF0) | (d & 0x0F); \
379       if (game_cheat3) fb[i] |= 0x10; \
380     }
381 #else
382 #define LOOP(N, C0, C1) \
383     d = sprites_data[number][g + N]; \
384     for (c = C0; c >= C1; c--, i--, d >>= 4, im--) { \
385       if (im == 0) { \
386 	flg = map_eflg[map_map[(y + r) >> 3][(x + c) >> 3]]; \
387 	im = 8; \
388       } \
389       if (!front && (flg & MAP_EFLG_FGND)) continue; \
390       if (c >= w || x + c < x0) continue; \
391       if (d & 0x0F) fb[i] = (fb[i] & 0xF0) | (d & 0x0F); \
392     }
393 #endif
394     LOOP(3, 0x1f, 0x18);
395     LOOP(2, 0x17, 0x10);
396     LOOP(1, 0x0f, 0x08);
397     LOOP(0, 0x07, 0x00);
398 
399 #undef LOOP
400 
401     fb += SYSVID_WIDTH;
402     g += 4;
403   }
404 }
405 
406 #endif
407 
408 
409 /*
410  * Draw a sprite
411  * align to tile column, determine plane automatically, and clip
412  *
413  * nbr: sprite number
414  * x, y: sprite position (pixels, map).
415  * fb: CHANGED
416  */
417 #ifdef GFXPC
418 void
draw_sprite2(U8 number,U16 x,U16 y,U8 front)419 draw_sprite2(U8 number, U16 x, U16 y, U8 front)
420 {
421   U8 k, *f, c, r, dx;
422   U16 cmax, rmax;
423   U16 xm = 0, xp = 0;
424   S16 xmap, ymap;
425 
426   /* align to tile column, prepare map coordinate and clip */
427   xmap = x & 0xFFF8;
428   ymap = y;
429   cmax = 0x20;  /* width, 4 tile columns, 8 pixels each */
430   rmax = 0x15;  /* height, 15 pixels */
431   dx = (x - xmap) * 2;
432   if (draw_clipms(&xmap, &ymap, &cmax, &rmax))  /* return if not visible */
433     return;
434 
435   /* get back to screen */
436   draw_setfb(xmap - DRAW_XYMAP_SCRLEFT, ymap - DRAW_XYMAP_SCRTOP);
437   xmap >>= 3;
438   cmax >>= 3;
439 
440   /* draw */
441   for (c = 0; c < cmax; c++) {  /* for each tile column */
442     f = fb;
443     for (r = 0; r < rmax; r++) {  /* for each pixel row */
444       /* check that tile is not hidden behind foreground */
445 #ifdef ENABLE_CHEATS
446       if (front || game_cheat3 ||
447 	  !(map_eflg[map_map[(ymap + r) >> 3][xmap + c]] & MAP_EFLG_FGND)) {
448 #else
449       if (front ||
450 	  !(map_eflg[map_map[(ymap + r) >> 3][xmap + c]] & MAP_EFLG_FGND)) {
451 #endif
452 	xp = xm = 0;
453 	if (c > 0) {
454 	  xm |= sprites_data[number][c - 1][r].mask << (16 - dx);
455 	  xp |= sprites_data[number][c - 1][r].pict << (16 - dx);
456 	}
457 	else
458 	  xm |= 0xFFFF << (16 - dx);
459 	if (c < cmax) {
460 	  xm |= sprites_data[number][c][r].mask >> dx;
461 	  xp |= sprites_data[number][c][r].pict >> dx;
462 	}
463 	else
464 	  xm |= 0xFFFF >> dx;
465 	/*
466 	 * sprites / perform the transformation from CGA 2 bits
467 	 * per pixel to frame buffer 8 bits per pixels
468 	 */
469 	for (k = 8; k--; xm >>= 2, xp >>= 2) {
470 	  f[k] = ((f[k] & (xm & 3)) | (xp & 3));
471 #ifdef ENABLE_CHEATS
472 	  if (game_cheat3) f[k] |= 4;
473 #endif
474 	}
475       }
476       f += SYSVID_WIDTH;
477     }
478     fb += 8;
479   }
480 }
481 #endif
482 
483 
484 /*
485  * Redraw the map behind a sprite
486  * align to tile column and row, and clip
487  *
488  * x, y: sprite position (pixels, map).
489  */
490 void
491 draw_spriteBackground(U16 x, U16 y)
492 {
493   U8 r, c;
494   U16 rmax, cmax;
495   S16 xmap, ymap;
496   U16 xs, ys;
497 
498   /* aligne to column and row, prepare map coordinate, and clip */
499   xmap = x & 0xFFF8;
500   ymap = y & 0xFFF8;
501   cmax = (x - xmap == 0 ? 0x20 : 0x28);  /* width, 4 tl cols, 8 pix each */
502   rmax = (y & 0x04) ? 0x20 : 0x18;  /* height, 3 or 4 tile rows */
503   if (draw_clipms(&xmap, &ymap, &cmax, &rmax))  /* don't draw if fully clipped */
504     return;
505 
506   /* get back to screen */
507   xs = xmap - DRAW_XYMAP_SCRLEFT;
508   ys = ymap - DRAW_XYMAP_SCRTOP;
509   xmap >>= 3;
510   ymap >>= 3;
511   cmax >>= 3;
512   rmax >>= 3;
513 
514   /* draw */
515   for (r = 0; r < rmax; r++) {  /* for each row */
516 #ifdef GFXPC
517     draw_setfb(xs, ys + r * 8);
518 #endif
519 #ifdef GFXST
520     draw_setfb(xs, 8 + ys + r * 8);
521 #endif
522     for (c = 0; c < cmax; c++) {  /* for each column */
523       draw_tile(map_map[ymap + r][xmap + c]);
524     }
525   }
526 }
527 
528 
529 /*
530  * Draw entire map screen background tiles onto frame buffer.
531  *
532  * ASM 0af5, 0a54
533  */
534 void
535 draw_map(void)
536 {
537   U8 i, j;
538 
539   draw_tilesBank = map_tilesBank;
540 
541   for (i = 0; i < 0x18; i++) {  /* 0x18 rows */
542 #ifdef GFXPC
543     draw_setfb(0x20, (i * 8));
544 #endif
545 #ifdef GFXST
546     draw_setfb(0x20, 8 + (i * 8));
547 #endif
548     for (j = 0; j < 0x20; j++)  /* 0x20 tiles per row */
549       draw_tile(map_map[i + 8][j]);
550   }
551 }
552 
553 
554 /*
555  * Draw status indicators
556  *
557  * ASM 0309
558  */
559 void
560 draw_drawStatus(void)
561 {
562   S8 i;
563   U32 sv;
564   static U8 s[7] = {0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xfe};
565 
566   draw_tilesBank = 0;
567 
568   for (i = 5, sv = game_score; i >= 0; i--) {
569     s[i] = 0x30 + (U8)(sv % 10);
570     sv /= 10;
571   }
572   draw_tllst = s;
573 
574   draw_setfb(DRAW_STATUS_SCORE_X, DRAW_STATUS_Y);
575   draw_tilesList();
576 
577   draw_setfb(DRAW_STATUS_BULLETS_X, DRAW_STATUS_Y);
578   for (i = 0; i < game_bullets; i++)
579     draw_tile(TILES_BULLET);
580 
581   draw_setfb(DRAW_STATUS_BOMBS_X, DRAW_STATUS_Y);
582   for (i = 0; i < game_bombs; i++)
583     draw_tile(TILES_BOMB);
584 
585   draw_setfb(DRAW_STATUS_LIVES_X, DRAW_STATUS_Y);
586   for (i = 0; i < game_lives; i++)
587     draw_tile(TILES_RICK);
588 }
589 
590 
591 /*
592  * Draw info indicators
593  */
594 #ifdef ENABLE_CHEATS
595 void
596 draw_infos(void)
597 {
598   draw_tilesBank = 0;
599 
600 #ifdef GFXPC
601   draw_filter = 0xffff;
602 #endif
603 
604   draw_setfb(0x00, DRAW_STATUS_Y);
605   draw_tile(game_cheat1 ? 'T' : '@');
606   draw_setfb(0x08, DRAW_STATUS_Y);
607   draw_tile(game_cheat2 ? 'N' : '@');
608   draw_setfb(0x10, DRAW_STATUS_Y);
609   draw_tile(game_cheat3 ? 'V' : '@');
610 }
611 #endif
612 
613 
614 /*
615  * Clear status indicators
616  */
617 void
618 draw_clearStatus(void)
619 {
620   U8 i;
621 
622 #ifdef GFXPC
623   draw_tilesBank = map_tilesBank;
624 #endif
625 #ifdef GFXST
626   draw_tilesBank = 0;
627 #endif
628   draw_setfb(DRAW_STATUS_SCORE_X, DRAW_STATUS_Y);
629   for (i = 0; i < DRAW_STATUS_LIVES_X/8 + 6 - DRAW_STATUS_SCORE_X/8; i++) {
630 #ifdef GFXPC
631     draw_tile(map_map[MAP_ROW_SCRTOP + (DRAW_STATUS_Y / 8)][i]);
632 #endif
633 #ifdef GFXST
634     draw_tile('@');
635 #endif
636   }
637 }
638 
639 /*
640  * Draw a picture
641  */
642 #ifdef GFXST
643 void
644 draw_pic(U16 x, U16 y, U16 w, U16 h, U32 *pic)
645 {
646   U8 *f;
647   U16 i, j, k, pp;
648   U32 v;
649 
650   draw_setfb(x, y);
651   pp = 0;
652 
653   for (i = 0; i < h; i++) { /* rows */
654     f = fb;
655     for (j = 0; j < w; j += 8) {  /* cols */
656       v = pic[pp++];
657       for (k = 8; k--; v >>=4)
658 	f[k] = v & 0x0F;
659       f += 8;
660     }
661     fb += SYSVID_WIDTH;
662   }
663 }
664 #endif
665 
666 
667 /*
668  * Draw a bitmap
669  */
670 void
671 draw_img(img_t *i)
672 {
673   U16 k;
674 
675   draw_setfb(0, 0);
676   if (i->ncolors > 0)
677     sysvid_setPalette(i->colors, i->ncolors);
678   for (k = 0; k < SYSVID_WIDTH * SYSVID_HEIGHT; k++)
679     fb[k] = i->pixels[k];
680 }
681 
682 
683 /* eof */
684