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.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. /*
  2. * maps.c
  3. * Duality
  4. *
  5. * Copyright (C) 2025 Thomas Buck <thomas@xythobuz.de>
  6. *
  7. * Based on examples from gbdk-2020:
  8. * https://github.com/gbdk-2020/gbdk-2020/blob/develop/gbdk-lib/examples/cross-platform/large_map
  9. *
  10. * This program is free software: you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License as published by
  12. * the Free Software Foundation, either version 3 of the License, or
  13. * (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU General Public License for more details.
  19. *
  20. * See <http://www.gnu.org/licenses/>.
  21. */
  22. #include <assert.h>
  23. #include "banks.h"
  24. #include "title_map.h"
  25. #include "bg_map.h"
  26. #include "util.h"
  27. #include "maps.h"
  28. // currently this assumption is hard-coded
  29. static_assert(bg_map_WIDTH == 256, "bg_map needs to be 256x256");
  30. static_assert(bg_map_HEIGHT == 256, "bg_map needs to be 256x256");
  31. #define POS_SCALE_BG 6
  32. // define this to disable mirrored map scaling support
  33. #define WRAP_BG // TODO
  34. #define bg_map_mapWidth (bg_map_WIDTH / bg_map_TILE_W)
  35. #define bg_map_mapHeight (bg_map_HEIGHT / bg_map_TILE_H)
  36. #define camera_max_x ((bg_map_mapWidth - DEVICE_SCREEN_WIDTH) * 8)
  37. #define camera_max_y ((bg_map_mapHeight - DEVICE_SCREEN_HEIGHT) * 8)
  38. #define MAP_FLIP_NONE 0x00
  39. #define MAP_FLIP_X (0x20 | 0x01)
  40. #define MAP_FLIP_Y (0x40 | 0x02)
  41. #define MAP_FLIP_XY (MAP_FLIP_X | MAP_FLIP_Y)
  42. // current unscaled ship position
  43. static uint16_t abs_x, abs_y;
  44. // current and old positions of the camera in pixels
  45. static uint16_t old_camera_x, old_camera_y;
  46. // current and old position of the map in tiles
  47. static uint8_t old_map_pos_x, old_map_pos_y;
  48. void map_title(void) NONBANKED {
  49. START_ROM_BANK(BANK(title_map)) {
  50. set_bkg_palette(OAMF_CGB_PAL0, title_map_PALETTE_COUNT, title_map_palettes);
  51. set_bkg_data(0, title_map_TILE_COUNT, title_map_tiles);
  52. if (title_map_MAP_ATTRIBUTES != NULL) {
  53. set_bkg_attributes(0, 0,
  54. title_map_WIDTH / title_map_TILE_W,
  55. title_map_HEIGHT / title_map_TILE_H,
  56. title_map_MAP_ATTRIBUTES);
  57. } else {
  58. VBK_REG = VBK_ATTRIBUTES;
  59. fill_bkg_rect(0, 0,
  60. title_map_WIDTH / title_map_TILE_W,
  61. title_map_HEIGHT / title_map_TILE_H,
  62. 0x00);
  63. VBK_REG = VBK_TILES;
  64. }
  65. set_bkg_tiles(0, 0,
  66. title_map_WIDTH / title_map_TILE_W,
  67. title_map_HEIGHT / title_map_TILE_H,
  68. title_map_map);
  69. } END_ROM_BANK
  70. move_bkg(0, 0);
  71. }
  72. static inline void set_bkg_sub_attr(uint8_t x, uint8_t y,
  73. uint8_t w, uint8_t h,
  74. const uint8_t *attr,
  75. uint8_t attr_val,
  76. uint8_t map_w) {
  77. if (attr) {
  78. set_bkg_submap_attributes(x, y, w, h, attr, map_w);
  79. } else {
  80. VBK_REG = VBK_ATTRIBUTES;
  81. fill_bkg_rect(x, y, w, h, attr_val);
  82. VBK_REG = VBK_TILES;
  83. }
  84. }
  85. static inline void set_bkg_sub(uint8_t x, uint8_t y,
  86. uint8_t w, uint8_t h,
  87. const uint8_t *map, const uint8_t *attr,
  88. uint8_t attr_val,
  89. uint8_t map_w) {
  90. START_ROM_BANK(BANK(bg_map)) {
  91. set_bkg_submap(x, y, w, h, map, map_w);
  92. set_bkg_sub_attr(x, y, w, h, attr, attr_val, map_w);
  93. } END_ROM_BANK
  94. }
  95. void map_game(void) NONBANKED {
  96. START_ROM_BANK(BANK(bg_map)) {
  97. set_bkg_palette(OAMF_CGB_PAL0, bg_map_PALETTE_COUNT, bg_map_palettes);
  98. set_bkg_data(0, bg_map_TILE_COUNT, bg_map_tiles);
  99. } END_ROM_BANK
  100. #ifdef WRAP_BG
  101. if (bg_map_MAP_ATTRIBUTES != NULL) {
  102. set_bkg_attributes(0, 0,
  103. bg_map_WIDTH / bg_map_TILE_W, bg_map_HEIGHT / bg_map_TILE_H,
  104. bg_map_MAP_ATTRIBUTES);
  105. } else {
  106. VBK_REG = VBK_ATTRIBUTES;
  107. fill_bkg_rect(0, 0,
  108. bg_map_WIDTH / bg_map_TILE_W, bg_map_HEIGHT / bg_map_TILE_H,
  109. 0x00);
  110. VBK_REG = VBK_TILES;
  111. }
  112. set_bkg_tiles(0, 0, bg_map_WIDTH / bg_map_TILE_W, bg_map_HEIGHT / bg_map_TILE_H, bg_map_map);
  113. #else // WRAP_BG
  114. abs_x = 0;
  115. abs_y = 0;
  116. old_camera_x = 0;
  117. old_camera_y = 0;
  118. old_map_pos_x = 0;
  119. old_map_pos_y = 0;
  120. move_bkg(0, 0);
  121. // Draw the initial map view for the whole screen
  122. set_bkg_sub(0, 0,
  123. MIN(DEVICE_SCREEN_WIDTH + 1u, bg_map_mapWidth),
  124. MIN(DEVICE_SCREEN_HEIGHT + 1u, bg_map_mapHeight),
  125. bg_map_map, bg_map_MAP_ATTRIBUTES, MAP_FLIP_NONE, bg_map_mapWidth);
  126. #endif // WRAP_BG
  127. }
  128. static inline void set(uint8_t dst_x, uint8_t dst_y,
  129. uint8_t src_x, uint8_t src_y,
  130. uint8_t attr) {
  131. START_ROM_BANK(BANK(bg_map)) {
  132. set_bkg_tile_xy(dst_x, dst_y, bg_map_map[src_x + (src_y * (bg_map_WIDTH / bg_map_TILE_W))]);
  133. set_bkg_attribute_xy(dst_x, dst_y, attr);
  134. } END_ROM_BANK
  135. }
  136. void map_dbg_reset(void) NONBANKED {
  137. #ifndef WRAP_BG
  138. uint16_t camera_x = abs_x >> POS_SCALE_BG;
  139. uint16_t camera_y = abs_y >> POS_SCALE_BG;
  140. uint8_t map_pos_x = camera_x >> 3;
  141. uint8_t map_pos_y = camera_y >> 3;
  142. for (uint8_t x = 0; x < DEVICE_SCREEN_WIDTH; x++) {
  143. for (uint8_t y = 0; y < DEVICE_SCREEN_HEIGHT; y++) {
  144. uint8_t is_flipped_x = ((camera_x >> 3) + x) & 0x10;
  145. uint8_t is_flipped_y = ((camera_y >> 3) + y) & 0x10;
  146. uint8_t attr = is_flipped_y ? (is_flipped_x ? MAP_FLIP_XY : MAP_FLIP_Y)
  147. : (is_flipped_x ? MAP_FLIP_X : MAP_FLIP_NONE);
  148. set(x, y,
  149. is_flipped_x ? bg_map_mapWidth - map_pos_x : map_pos_x,
  150. is_flipped_y ? bg_map_mapHeight - map_pos_y : map_pos_y,
  151. attr);
  152. }
  153. }
  154. #endif // ! WRAP_BG
  155. }
  156. void map_move(int16_t delta_x, int16_t delta_y) NONBANKED {
  157. abs_x += delta_x;
  158. abs_y += delta_y;
  159. uint16_t camera_x = abs_x >> POS_SCALE_BG;
  160. uint16_t camera_y = abs_y >> POS_SCALE_BG;
  161. move_bkg(camera_x, camera_y);
  162. #ifndef WRAP_BG
  163. uint8_t map_pos_x = camera_x >> 3;
  164. uint8_t map_pos_y = camera_y >> 3;
  165. uint8_t is_flipped_x_left = (camera_x >> 3) & 0x10;
  166. uint8_t is_flipped_x_right = ((camera_x >> 3) + DEVICE_SCREEN_WIDTH) & 0x10;
  167. uint8_t is_flipped_y_top = (camera_y >> 3) & 0x10;
  168. uint8_t is_flipped_y_bottom = ((camera_y >> 3) + DEVICE_SCREEN_HEIGHT) & 0x10;
  169. if (map_pos_x != old_map_pos_x) {
  170. old_map_pos_x = map_pos_x;
  171. if (camera_x < old_camera_x) {
  172. // moving left
  173. set_bkg_sub(map_pos_x, map_pos_y,
  174. 1, MIN(DEVICE_SCREEN_HEIGHT + 1, bg_map_mapHeight - map_pos_y),
  175. bg_map_map, bg_map_MAP_ATTRIBUTES, MAP_FLIP_X, bg_map_mapWidth);
  176. } else if ((bg_map_mapWidth - DEVICE_SCREEN_WIDTH) > map_pos_x) {
  177. // moving right
  178. /*
  179. set_bkg_sub(map_pos_x + DEVICE_SCREEN_WIDTH, map_pos_y,
  180. 1, MIN(DEVICE_SCREEN_HEIGHT + 1, bg_map_mapHeight - map_pos_y),
  181. bg_map_map, bg_map_MAP_ATTRIBUTES, MAP_FLIP_NONE, bg_map_mapWidth);
  182. */
  183. for (uint8_t i = 0; i < DEVICE_SCREEN_HEIGHT; i++) {
  184. uint8_t is_flipped_y = (map_pos_y + i) & 0x04;
  185. uint8_t attr = is_flipped_y ? (is_flipped_x_right ? MAP_FLIP_XY : MAP_FLIP_Y)
  186. : (is_flipped_x_right ? MAP_FLIP_X : MAP_FLIP_NONE);
  187. set(map_pos_x + DEVICE_SCREEN_WIDTH, map_pos_y + i,
  188. is_flipped_x_right ? bg_map_mapWidth - map_pos_x : map_pos_x,
  189. is_flipped_y ? bg_map_mapHeight - map_pos_y : map_pos_y,
  190. attr);
  191. }
  192. }
  193. }
  194. if (map_pos_y != old_map_pos_y) {
  195. old_map_pos_y = map_pos_y;
  196. if (camera_y < old_camera_y) {
  197. // moving up
  198. set_bkg_sub(map_pos_x, map_pos_y,
  199. MIN(DEVICE_SCREEN_WIDTH + 1, bg_map_mapWidth - map_pos_x), 1,
  200. bg_map_map, bg_map_MAP_ATTRIBUTES, MAP_FLIP_Y, bg_map_mapWidth);
  201. } else if ((bg_map_mapHeight - DEVICE_SCREEN_HEIGHT) > map_pos_y) {
  202. // moving down
  203. set_bkg_sub(map_pos_x, map_pos_y + DEVICE_SCREEN_HEIGHT,
  204. MIN(DEVICE_SCREEN_WIDTH + 1, bg_map_mapWidth - map_pos_x), 1,
  205. bg_map_map, bg_map_MAP_ATTRIBUTES, MAP_FLIP_NONE, bg_map_mapWidth);
  206. }
  207. }
  208. // set old camera position to current camera position
  209. old_camera_x = camera_x;
  210. old_camera_y = camera_y;
  211. #endif // ! WRAP_BG
  212. }