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