GameBoy (Color) port of the GTA San Andreas arcade game Duality
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

maps.c 8.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. /*
  2. * maps.c
  3. * Duality
  4. *
  5. * Copyright (C) 2025 Thomas Buck <thomas@xythobuz.de>
  6. *
  7. * This program is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation, either version 3 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * See <http://www.gnu.org/licenses/>.
  18. */
  19. #include <gbdk/platform.h>
  20. #include "maps.h"
  21. #include "gb/gb.h"
  22. #include "score.h"
  23. #include "title_map.h"
  24. #include "bg_map.h"
  25. #include "numbers_fnt.h"
  26. #include "text_fnt.h"
  27. #define MAX_DIGITS 7
  28. // TODO inverted score color not visible on DMG
  29. const unsigned char num_attr_1[104] = {
  30. 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,
  31. 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,
  32. 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,
  33. 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,
  34. 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,
  35. 0x81,0x81,0x81,0x81,
  36. };
  37. const unsigned char num_attr_2[104] = {
  38. 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,
  39. 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,
  40. 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,
  41. 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,
  42. 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,
  43. 0x82,0x82,0x82,0x82,
  44. };
  45. const palette_color_t num_pal_inv[4] = {
  46. //RGB8( 0, 0, 0), RGB8(248,252,248), RGB8( 0, 0, 0), RGB8( 0, 0, 0)
  47. RGB8( 0, 0, 0), RGB8( 0, 0, 0), RGB8(248,252,248), RGB8( 0, 0, 0)
  48. };
  49. static uint8_t fnt_off = 0;
  50. void map_title(void) NONBANKED {
  51. SWITCH_ROM(BANK(title_map));
  52. set_bkg_palette(OAMF_CGB_PAL0, title_map_PALETTE_COUNT, title_map_palettes);
  53. set_bkg_data(0, title_map_TILE_COUNT, title_map_tiles);
  54. set_bkg_attributes(0, 0, title_map_MAP_ATTRIBUTES_WIDTH, title_map_MAP_ATTRIBUTES_HEIGHT, title_map_MAP_ATTRIBUTES);
  55. set_bkg_tiles(0, 0, title_map_WIDTH / title_map_TILE_W, title_map_HEIGHT / title_map_TILE_H, title_map_map);
  56. }
  57. void map_game(void) NONBANKED {
  58. SWITCH_ROM(BANK(bg_map));
  59. set_bkg_palette(OAMF_CGB_PAL0, bg_map_PALETTE_COUNT, bg_map_palettes);
  60. set_bkg_data(0, bg_map_TILE_COUNT, bg_map_tiles);
  61. set_bkg_attributes(0, 0, bg_map_MAP_ATTRIBUTES_WIDTH, bg_map_MAP_ATTRIBUTES_HEIGHT, bg_map_MAP_ATTRIBUTES);
  62. set_bkg_tiles(0, 0, bg_map_WIDTH / bg_map_TILE_W, bg_map_HEIGHT / bg_map_TILE_H, bg_map_map);
  63. }
  64. void win_init(uint8_t is_splash) NONBANKED {
  65. fnt_off = is_splash ? title_map_TILE_COUNT : bg_map_TILE_COUNT;
  66. SWITCH_ROM(BANK(numbers_fnt));
  67. set_bkg_palette(OAMF_CGB_PAL0 + bg_map_PALETTE_COUNT, numbers_fnt_PALETTE_COUNT, numbers_fnt_palettes);
  68. set_bkg_palette(OAMF_CGB_PAL0 + bg_map_PALETTE_COUNT + numbers_fnt_PALETTE_COUNT, numbers_fnt_PALETTE_COUNT, num_pal_inv);
  69. set_win_data(fnt_off, numbers_fnt_TILE_COUNT, numbers_fnt_tiles);
  70. if (is_splash) {
  71. SWITCH_ROM(BANK(text_fnt));
  72. set_win_data(fnt_off + numbers_fnt_TILE_COUNT, text_fnt_TILE_COUNT, text_fnt_tiles);
  73. }
  74. }
  75. static void set_win_based(uint8_t x, uint8_t y, uint8_t w, uint8_t h,
  76. const uint8_t *tiles, uint8_t base_tile, const uint8_t *attributes) NONBANKED {
  77. VBK_REG = VBK_ATTRIBUTES;
  78. set_win_tiles(x, y, w, h, attributes);
  79. VBK_REG = VBK_TILES;
  80. set_win_based_tiles(x, y, w, h, tiles, base_tile);
  81. }
  82. static void character(uint8_t c, uint8_t pos, uint8_t x_off, uint8_t y_off, uint8_t is_black) NONBANKED {
  83. SWITCH_ROM(BANK(text_fnt));
  84. uint8_t off = c * text_fnt_WIDTH / text_fnt_TILE_W;
  85. set_win_based(x_off + (pos * text_fnt_WIDTH / text_fnt_TILE_W), y_off,
  86. text_fnt_WIDTH / text_fnt_TILE_W, 1,
  87. text_fnt_map + off, fnt_off + numbers_fnt_TILE_COUNT,
  88. (is_black ? num_attr_2 : num_attr_1) + off);
  89. set_win_based(x_off + (pos * text_fnt_WIDTH / text_fnt_TILE_W), y_off + 1,
  90. text_fnt_WIDTH / text_fnt_TILE_W, 1,
  91. text_fnt_map + off + (sizeof(text_fnt_map) / 2), fnt_off + numbers_fnt_TILE_COUNT,
  92. (is_black ? num_attr_2 : num_attr_1) + off);
  93. }
  94. static void str3(uint16_t name, uint8_t x_off, uint8_t y_off, uint8_t is_black) NONBANKED {
  95. character((name >> 10) & 0x1F, 0, x_off, y_off, is_black);
  96. character((name >> 5) & 0x1F, 1, x_off, y_off, is_black);
  97. character((name >> 0) & 0x1F, 2, x_off, y_off, is_black);
  98. }
  99. static void str(const char *s, uint8_t x_off, uint8_t y_off, uint8_t is_black) NONBANKED {
  100. uint8_t n = 0;
  101. while (*s) {
  102. char c = *(s++);
  103. if ((c >= 'A') && (c <= 'Z')) c = c - 'A' + 'a';
  104. if ((c < 'a') || (c > 'z')) c = 'x';
  105. character(c - 'a', n++, x_off, y_off, is_black);
  106. }
  107. }
  108. static void digit(uint8_t val, uint8_t pos, uint8_t x_off, uint8_t y_off, uint8_t is_black) NONBANKED {
  109. SWITCH_ROM(BANK(numbers_fnt));
  110. uint8_t off = val * numbers_fnt_WIDTH / numbers_fnt_TILE_W;
  111. set_win_based(x_off + (pos * numbers_fnt_WIDTH / numbers_fnt_TILE_W), y_off,
  112. numbers_fnt_WIDTH / numbers_fnt_TILE_W, 1,
  113. numbers_fnt_map + off, fnt_off,
  114. (is_black ? num_attr_2 : num_attr_1) + off);
  115. set_win_based(x_off + (pos * numbers_fnt_WIDTH / numbers_fnt_TILE_W), y_off + 1,
  116. numbers_fnt_WIDTH / numbers_fnt_TILE_W, 1,
  117. numbers_fnt_map + off + (sizeof(numbers_fnt_map) / 2), fnt_off,
  118. (is_black ? num_attr_2 : num_attr_1) + off);
  119. }
  120. static uint8_t number(int32_t score, uint8_t x_off, uint8_t y_off, uint8_t is_black) NONBANKED {
  121. // TODO can not set numbers larger than int16 max?!
  122. //score = 32767 + 1; // wtf?!
  123. uint8_t len = 0;
  124. uint8_t digits[MAX_DIGITS];
  125. do {
  126. digits[len++] = score % 10L;
  127. score = score / 10L;
  128. if (len >= MAX_DIGITS) {
  129. break;
  130. }
  131. } while (score > 0);
  132. // if the number was too large for our buffer don't draw anything
  133. if (score > 0) {
  134. return 0;
  135. }
  136. uint8_t off = (x_off == 0xFF) ? (10 - len) : ((x_off == 0xFE) ? (20 - (len * 2)) : x_off);
  137. for (uint8_t i = 0; i < len; i++) {
  138. digit(digits[len - i - 1], i, off, y_off, is_black);
  139. }
  140. return 8 * len * 2;
  141. }
  142. static void fill_win(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t tile, uint8_t attr) NONBANKED {
  143. VBK_REG = VBK_ATTRIBUTES;
  144. fill_win_rect(x, y, w, h, attr);
  145. VBK_REG = VBK_TILES;
  146. fill_win_rect(x, y, w, h, tile);
  147. }
  148. void win_splash_draw(int32_t lowest, int32_t highest) NONBANKED {
  149. // reuse full black and white tiles at 0 and 1 from splash bg
  150. fill_win(0, 0, 10, 2, 0, 0x00);
  151. fill_win(10, 0, 10, 2, 1, 0x00);
  152. number(lowest, 0, 0, 1);
  153. number(highest, 0xFE, 0, 0);
  154. }
  155. void win_score_clear(uint8_t is_black) NONBANKED {
  156. SWITCH_ROM(BANK(title_map));
  157. set_win_based(0, 0,
  158. title_map_WIDTH / title_map_TILE_W, title_map_HEIGHT / title_map_TILE_H,
  159. title_map_map, 0, title_map_MAP_ATTRIBUTES);
  160. str(is_black ? "black" : "white", 10 - 5, 1, is_black);
  161. }
  162. void win_score_draw(struct scores score, uint8_t off, uint8_t is_black) NONBANKED {
  163. str3(score.name, 0, 4 + off * 3, is_black);
  164. number(is_black ? -score.score : score.score, 7, 4 + off * 3, is_black);
  165. }
  166. uint8_t win_game_draw(int32_t score) NONBANKED {
  167. fill_win(0, 0, 10, 2, fnt_off + numbers_fnt_TILE_COUNT, 0x81);
  168. uint8_t is_black = 0;
  169. if (score < 0) {
  170. score = -score;
  171. is_black = 1;
  172. }
  173. return number(score, 0, 0, is_black);
  174. }