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 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  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 <string.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. #include "git.h"
  28. #include "main.h"
  29. #include "maps.h"
  30. #define MAX_DIGITS 7
  31. #define LINE_WIDTH 10
  32. // TODO inverted score color not visible on DMG
  33. BANKREF(maps)
  34. const unsigned char num_attr_1[104] = {
  35. 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,
  36. 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,
  37. 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,
  38. 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,
  39. 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,
  40. 0x81,0x81,0x81,0x81,
  41. };
  42. const unsigned char num_attr_2[104] = {
  43. 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,
  44. 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,
  45. 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,
  46. 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,
  47. 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,
  48. 0x82,0x82,0x82,0x82,
  49. };
  50. const palette_color_t num_pal_inv[4] = {
  51. //RGB8( 0, 0, 0), RGB8(248,252,248), RGB8( 0, 0, 0), RGB8( 0, 0, 0)
  52. RGB8( 0, 0, 0), RGB8( 0, 0, 0), RGB8(248,252,248), RGB8( 0, 0, 0)
  53. };
  54. static uint8_t fnt_off = 0;
  55. void map_title(void) NONBANKED {
  56. SWITCH_ROM(BANK(title_map));
  57. set_bkg_palette(OAMF_CGB_PAL0, title_map_PALETTE_COUNT, title_map_palettes);
  58. set_bkg_data(0, title_map_TILE_COUNT, title_map_tiles);
  59. set_bkg_attributes(0, 0, title_map_MAP_ATTRIBUTES_WIDTH, title_map_MAP_ATTRIBUTES_HEIGHT, title_map_MAP_ATTRIBUTES);
  60. set_bkg_tiles(0, 0, title_map_WIDTH / title_map_TILE_W, title_map_HEIGHT / title_map_TILE_H, title_map_map);
  61. }
  62. void map_game(void) NONBANKED {
  63. SWITCH_ROM(BANK(bg_map));
  64. set_bkg_palette(OAMF_CGB_PAL0, bg_map_PALETTE_COUNT, bg_map_palettes);
  65. set_bkg_data(0, bg_map_TILE_COUNT, bg_map_tiles);
  66. set_bkg_attributes(0, 0, bg_map_MAP_ATTRIBUTES_WIDTH, bg_map_MAP_ATTRIBUTES_HEIGHT, bg_map_MAP_ATTRIBUTES);
  67. set_bkg_tiles(0, 0, bg_map_WIDTH / bg_map_TILE_W, bg_map_HEIGHT / bg_map_TILE_H, bg_map_map);
  68. }
  69. void win_init(uint8_t is_splash) NONBANKED {
  70. fnt_off = is_splash ? title_map_TILE_COUNT : bg_map_TILE_COUNT;
  71. SWITCH_ROM(BANK(numbers_fnt));
  72. set_bkg_palette(OAMF_CGB_PAL0 + bg_map_PALETTE_COUNT, numbers_fnt_PALETTE_COUNT, numbers_fnt_palettes);
  73. set_win_data(fnt_off, numbers_fnt_TILE_COUNT, numbers_fnt_tiles);
  74. SWITCH_ROM(BANK(maps));
  75. set_bkg_palette(OAMF_CGB_PAL0 + bg_map_PALETTE_COUNT + numbers_fnt_PALETTE_COUNT, numbers_fnt_PALETTE_COUNT, num_pal_inv);
  76. if (is_splash) {
  77. SWITCH_ROM(BANK(text_fnt));
  78. set_win_data(fnt_off + numbers_fnt_TILE_COUNT, text_fnt_TILE_COUNT, text_fnt_tiles);
  79. }
  80. }
  81. static void set_win_based(uint8_t x, uint8_t y, uint8_t w, uint8_t h,
  82. const uint8_t *tiles, uint8_t base_tile, uint8_t tile_bank,
  83. const uint8_t *attributes, uint8_t attr_bank) NONBANKED {
  84. SWITCH_ROM(attr_bank);
  85. VBK_REG = VBK_ATTRIBUTES;
  86. set_win_tiles(x, y, w, h, attributes);
  87. SWITCH_ROM(tile_bank);
  88. VBK_REG = VBK_TILES;
  89. set_win_based_tiles(x, y, w, h, tiles, base_tile);
  90. }
  91. static void character(uint8_t c, uint8_t pos, uint8_t x_off, uint8_t y_off, uint8_t is_black) NONBANKED {
  92. uint8_t off = c * text_fnt_WIDTH / text_fnt_TILE_W;
  93. set_win_based(x_off + (pos * text_fnt_WIDTH / text_fnt_TILE_W), y_off,
  94. text_fnt_WIDTH / text_fnt_TILE_W, 1,
  95. text_fnt_map + off, fnt_off + numbers_fnt_TILE_COUNT, BANK(text_fnt),
  96. (is_black ? num_attr_2 : num_attr_1) + off, BANK(maps));
  97. set_win_based(x_off + (pos * text_fnt_WIDTH / text_fnt_TILE_W), y_off + 1,
  98. text_fnt_WIDTH / text_fnt_TILE_W, 1,
  99. text_fnt_map + off + (sizeof(text_fnt_map) / 2), fnt_off + numbers_fnt_TILE_COUNT, BANK(text_fnt),
  100. (is_black ? num_attr_2 : num_attr_1) + off, BANK(maps));
  101. }
  102. static void str3(uint16_t name, uint8_t x_off, uint8_t y_off,
  103. uint8_t is_black_a, uint8_t is_black_b, uint8_t is_black_c) NONBANKED {
  104. character((name >> 10) & 0x1F, 0, x_off, y_off, is_black_a);
  105. character((name >> 5) & 0x1F, 1, x_off, y_off, is_black_b);
  106. character((name >> 0) & 0x1F, 2, x_off, y_off, is_black_c);
  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. uint8_t off = val * numbers_fnt_WIDTH / numbers_fnt_TILE_W;
  110. set_win_based(x_off + (pos * numbers_fnt_WIDTH / numbers_fnt_TILE_W), y_off,
  111. numbers_fnt_WIDTH / numbers_fnt_TILE_W, 1,
  112. numbers_fnt_map + off, fnt_off, BANK(numbers_fnt),
  113. (is_black ? num_attr_2 : num_attr_1) + off, BANK(maps));
  114. set_win_based(x_off + (pos * numbers_fnt_WIDTH / numbers_fnt_TILE_W), y_off + 1,
  115. numbers_fnt_WIDTH / numbers_fnt_TILE_W, 1,
  116. numbers_fnt_map + off + (sizeof(numbers_fnt_map) / 2), fnt_off, BANK(numbers_fnt),
  117. (is_black ? num_attr_2 : num_attr_1) + off, BANK(maps));
  118. }
  119. static void str_l(const char *s, uint8_t len, uint8_t x_off, uint8_t y_off, uint8_t is_black) NONBANKED {
  120. for (uint8_t n = 0; (*s) && (n < LINE_WIDTH) && (n < len); n++) {
  121. char c = *(s++);
  122. if ((c >= 'A') && (c <= 'Z')) {
  123. c = c - 'A' + 'a';
  124. }
  125. if ((c >= '0') && (c <= '9')) {
  126. digit(c - '0', n, x_off, y_off, is_black);
  127. } else if ((c >= 'a') && (c <= 'z')) {
  128. character(c - 'a', n, x_off, y_off, is_black);
  129. }
  130. }
  131. }
  132. static void str(const char *s, uint8_t x_off, uint8_t y_off, uint8_t is_black) NONBANKED {
  133. str_l(s, 0xFF, x_off, y_off, is_black);
  134. }
  135. static void str_center(const char *s, uint8_t y_off, uint8_t is_black) NONBANKED {
  136. uint8_t n = strlen(s);
  137. if (n > LINE_WIDTH) n = LINE_WIDTH;
  138. str(s, LINE_WIDTH - n, y_off, is_black);
  139. }
  140. static void str_lines(const char *s, uint8_t y_off, uint8_t is_black) NONBANKED {
  141. if (strlen(s) > 10) {
  142. str(s, 0, y_off, is_black);
  143. str_center(s + 10, y_off + 2, is_black);
  144. } else {
  145. str_center(s, y_off, is_black);
  146. }
  147. }
  148. static uint8_t number(int32_t score, uint8_t x_off, uint8_t y_off, uint8_t is_black) NONBANKED {
  149. // TODO can not set numbers larger than int16 max?!
  150. //score = 32767 + 1; // wtf?!
  151. uint8_t len = 0;
  152. uint8_t digits[MAX_DIGITS];
  153. do {
  154. digits[len++] = score % 10L;
  155. score = score / 10L;
  156. if (len >= MAX_DIGITS) {
  157. break;
  158. }
  159. } while (score > 0);
  160. // if the number was too large for our buffer don't draw anything
  161. if (score > 0) {
  162. return 0;
  163. }
  164. uint8_t off = (x_off == 0xFF) ? (LINE_WIDTH - len) : ((x_off == 0xFE) ? ((LINE_WIDTH * 2) - (len * 2)) : x_off);
  165. for (uint8_t i = 0; i < len; i++) {
  166. digit(digits[len - i - 1], i, off, y_off, is_black);
  167. }
  168. return 8 * len * 2;
  169. }
  170. static void fill_win(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t tile, uint8_t attr) NONBANKED {
  171. VBK_REG = VBK_ATTRIBUTES;
  172. fill_win_rect(x, y, w, h, attr);
  173. VBK_REG = VBK_TILES;
  174. fill_win_rect(x, y, w, h, tile);
  175. }
  176. void win_splash_draw(int32_t lowest, int32_t highest) NONBANKED {
  177. // reuse full black and white tiles at 0 and 1 from splash bg
  178. fill_win(0, 0, 10, 2, 0, 0x00);
  179. fill_win(10, 0, 10, 2, 1, 0x00);
  180. number(lowest, 0, 0, 1);
  181. number(highest, 0xFE, 0, 0);
  182. }
  183. void win_score_clear(uint8_t is_black) NONBANKED {
  184. set_win_based(0, 0,
  185. title_map_WIDTH / title_map_TILE_W, title_map_HEIGHT / title_map_TILE_H,
  186. title_map_map, 0, BANK(title_map), title_map_MAP_ATTRIBUTES, BANK(title_map));
  187. str_center(is_black ? "black" : "white", 1, is_black);
  188. }
  189. void win_score_draw(struct scores score, uint8_t off, uint8_t is_black) NONBANKED {
  190. str3(score.name, 0, 4 + off * 3, is_black, is_black, is_black);
  191. number(is_black ? -score.score : score.score, 7, 4 + off * 3, is_black);
  192. }
  193. void win_about(void) NONBANKED {
  194. set_win_based(0, 0,
  195. title_map_WIDTH / title_map_TILE_W, title_map_HEIGHT / title_map_TILE_H,
  196. title_map_map, 0, BANK(title_map), title_map_MAP_ATTRIBUTES, BANK(title_map));
  197. str_center("Duality", 0, 1);
  198. str_center("xythobuz", 2, 1);
  199. SWITCH_ROM(BANK(git));
  200. char line_buff[2 * LINE_WIDTH + 1] = {0};
  201. strncpy(line_buff, git_version, 2 * LINE_WIDTH);
  202. str_lines(line_buff, 7, 0);
  203. str_l(&__DATE__[7], 4, 0, 14, 1); // year (4)
  204. str_l(&__DATE__[0], 3, (4 * 2) + 1, 14, 1); // month (3)
  205. str_l(&__DATE__[4], 2, (7 * 2) + 2, 14, 1); // day (2)
  206. str(__TIME__, 4, 16, 0);
  207. }
  208. void win_debug(void) NONBANKED {
  209. set_win_based(0, 0,
  210. title_map_WIDTH / title_map_TILE_W, title_map_HEIGHT / title_map_TILE_H,
  211. title_map_map, 0, BANK(title_map), title_map_MAP_ATTRIBUTES, BANK(title_map));
  212. // TODO paging when more options added
  213. str_center("Debug Menu", 0, 0);
  214. char name_buff[DEBUG_ENTRY_NAME_LEN + 2 + 1] = {0};
  215. for (uint8_t i = 0; (i < DEBUG_ENTRY_COUNT) && (i < 7); i++) {
  216. SWITCH_ROM(BANK(main));
  217. strncpy(name_buff, debug_entries[i].name, DEBUG_ENTRY_NAME_LEN + 1);
  218. uint8_t n_len = strlen(name_buff);
  219. name_buff[n_len] = ' ';
  220. name_buff[n_len + 1] = (debug_flags & debug_entries[i].flag) ? '1' : '0';
  221. name_buff[n_len + 2] = '\0';
  222. n_len += 2;
  223. str(name_buff, (LINE_WIDTH - n_len) * 2, (i * 2) + 3, (debug_menu_index == i) ? 1 : 0);
  224. }
  225. }
  226. void win_name(int32_t score) NONBANKED {
  227. set_win_based(0, 0,
  228. title_map_WIDTH / title_map_TILE_W, title_map_HEIGHT / title_map_TILE_H,
  229. title_map_map, 0, BANK(title_map), title_map_MAP_ATTRIBUTES, BANK(title_map));
  230. str_center("score", 1, score < 0);
  231. number(score < 0 ? -score : score, 0xFF, 3, score < 0);
  232. str_center("enter", 6, score < 0);
  233. str_center("name", 8, score < 0);
  234. str_center("start ok", 16, score < 0);
  235. }
  236. void win_name_draw(uint16_t name, uint8_t is_black, uint8_t pos) NONBANKED {
  237. str3(name, LINE_WIDTH - 3, 12,
  238. (pos == 0) ? !is_black : is_black,
  239. (pos == 1) ? !is_black : is_black,
  240. (pos == 2) ? !is_black : is_black);
  241. }
  242. uint8_t win_game_draw(int32_t score) NONBANKED {
  243. fill_win(0, 0, 10, 2, fnt_off + numbers_fnt_TILE_COUNT, 0x81);
  244. uint8_t is_black = 0;
  245. if (score < 0) {
  246. score = -score;
  247. is_black = 1;
  248. }
  249. return number(score, 0, 0, is_black);
  250. }