limit tickrate to half refresh rate
[d2d-md.git] / src / map.c
blob4edade76a38076c71d8158c81058a8496d036f5a
1 #include <genesis.h>
2 #include "map.h"
3 #include "tiles.h"
4 #include "player.h"
5 #include "switch.h"
6 #include "maps.h"
8 u8 g_cells[MAPH*MAPW] = { 0 };
10 map_t *g_map = NULL;
11 cam_t g_cam[MAX_PLAYERS] = {
12 // first player's camera is always at y offset 0
13 { .planeidx = BG_A, .sy = 0, .sty = 0, .sh = VIEWPIXH, .sth = VIEWH },
14 // second player's camera is in the bottom half of the screen
15 { .planeidx = BG_B, .sy = (SCREENPIXH >> 1) + 16, .sty = (SCREENH >> 1) + 2, .sh = VIEWPIXH, .sth = VIEWH },
18 static const u16 bg_basetile = TILE_ATTR_FULL(PAL0, 0, 0, 0, TILE_USERINDEX);
19 static const u16 fg_basetile = TILE_ATTR_FULL(PAL0, 1, 0, 0, TILE_USERINDEX);
20 const u16 tile_rowoff[MAPW] = {
21 0, 100, 200, 300, 400, 500, 600, 700, 800, 900,
22 1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700, 1800, 1900,
23 2000, 2100, 2200, 2300, 2400, 2500, 2600, 2700, 2800, 2900,
24 3000, 3100, 3200, 3300, 3400, 3500, 3600, 3700, 3800, 3900,
25 4000, 4100, 4200, 4300, 4400, 4500, 4600, 4700, 4800, 4900,
26 5000, 5100, 5200, 5300, 5400, 5500, 5600, 5700, 5800, 5900,
27 6000, 6100, 6200, 6300, 6400, 6500, 6600, 6700, 6800, 6900,
28 7000, 7100, 7200, 7300, 7400, 7500, 7600, 7700, 7800, 7900,
29 8000, 8100, 8200, 8300, 8400, 8500, 8600, 8700, 8800, 8900,
30 9000, 9100, 9200, 9300, 9400, 9500, 9600, 9700, 9800, 9900,
33 static u16 empty_row[VDPW] = { 0 };
35 void map_startup(void) {
36 for (u16 i = 0; i < MAX_PLAYERS; ++i) {
37 if (g_cam[i].planeidx == BG_A)
38 g_cam[i].plane = VDP_BG_A;
39 else
40 g_cam[i].plane = VDP_BG_B;
44 void map_load(map_t *map) {
45 tiles_load(map->tilemap);
46 g_map = map;
47 memcpy(g_cells, map->map, sizeof(g_cells));
48 sw_load(map->sw, map->numsw);
49 things_load(map->th, map->numth);
51 if (num_players > 1) {
52 // player 1's camera is only half height if there's multiple cameras
53 g_cam[0].sh = VIEWPIXH;
54 g_cam[0].sth = VIEWH;
55 } else {
56 // if there's only a single camera, it takes up the whole screen
57 g_cam[0].sh = SCREENPIXH - 16;
58 g_cam[0].sth = SCREENH - 2;
62 static inline void cam_clear_row(cam_t *cam, const u16 vty) {
63 DMA_queueDmaFast(DMA_VRAM, empty_row, cam->plane + (vty << (6 + 1)), VDPW, 2);
66 static inline void cam_upload_row(cam_t *cam, const u16 vty, const u16 ty) {
67 u16 *buf = DMA_allocateTemp(VDPW);
68 DMA_queueDmaFast(DMA_VRAM, buf, cam->plane + (vty << (6 + 1)), VDPW, 2);
69 register u16 *datap = g_map->tiles + tile_rowoff[ty] + cam->tx;
70 register u8 *cellp = g_cells + tile_rowoff[ty] + cam->tx;
71 register u16 rx = cam->tx;
72 for (register u16 i = VDPW; i; --i) {
73 rx &= (VDPW - 1);
74 buf[rx++] = (*cellp & MAP_FORCECLEAR) ? bg_basetile : bg_basetile + *datap;
75 ++datap;
76 ++cellp;
80 static inline void cam_upload_column(cam_t *cam, const u16 vtx, const u16 tx) {
81 u16 *buf = DMA_allocateTemp(cam->sth);
82 // the column might be split by the plane border
83 const u16 vty0 = (cam->sty + cam->ty) & (VDPH - 1);
84 const u16 vty1 = vty0 + cam->sth;
85 if (vty1 > VDPH) {
86 // it's split, have to do two DMAs
87 const u16 vth0 = VDPH - vty0;
88 const u16 vth1 = vty1 - VDPH;
89 DMA_queueDmaFast(DMA_VRAM, buf + 0, cam->plane + (((vty0 << 6) + vtx) << 1), vth0, VDPW << 1);
90 DMA_queueDmaFast(DMA_VRAM, buf + vth0, cam->plane + (vtx << 1), vth1, VDPW << 1);
91 } else {
92 // it's in the middle of the plane, do one DMA
93 DMA_queueDmaFast(DMA_VRAM, buf, cam->plane + (((vty0 << 6) + vtx) << 1), cam->sth, VDPW << 1);
95 // fill the buffer
96 register u16 *datap = g_map->tiles + tile_rowoff[cam->ty] + tx;
97 register u8 *cellp = g_cells + tile_rowoff[cam->ty] + tx;
98 register u16 i;
99 for (i = 0; i < cam->sth; ++i) {
100 buf[i] = (*cellp & MAP_FORCECLEAR) ? bg_basetile : bg_basetile + *datap;
101 datap += MAPW;
102 cellp += MAPW;
106 static inline void view_update(cam_t *cam, thing_t *plr) {
107 if (plr) {
108 register s16 cx = plr->x - 160;
109 register s16 cy = plr->y - 64;
110 if (cx < 0) cx = 0;
111 else if (cx > MAPPIXW - VIEWPIXW) cx = MAPPIXW - VIEWPIXW;
112 if (cy < 0) cy = 0;
113 else if (cy > MAPPIXH - cam->sh) cy = MAPPIXH - cam->sh;
114 if (cam->x != cx || cam->y != cy) {
115 cam->x = cx;
116 cam->y = cy;
117 cam->tx = cx >> 3;
118 cam->ty = cy >> 3;
123 static inline void cam_update(cam_t *cam, thing_t *plr) {
124 u16 otx = cam->tx;
125 u16 oty = cam->ty;
127 view_update(cam, plr);
129 u16 tx = cam->tx;
130 u16 ty = cam->ty;
132 s16 dx = tx - otx;
133 s16 dy = ty - oty;
135 if (dx == 0 && dy == 0)
136 return;
138 // can only scroll a maximum of one screen + borders
139 if (dx > VIEWW) {
140 otx += dx - VIEWW;
141 dx = VIEWW;
142 dy = 0; // full screen update, skip rows
143 } else if (dx < -VIEWW) {
144 otx += dx + VIEWW;
145 dx = -VIEWW;
146 dy = 0; // full screen update, skip rows
147 } else if (dy > cam->sth) {
148 oty += dy - cam->sth;
149 dy = cam->sth;
150 dx = 0; // full screen update, skip cols
151 } else if (dy < -cam->sth) {
152 oty += dy + cam->sth;
153 dy = -cam->sth;
154 dx = 0; // full screen update, skip cols
157 if (dx > 0) {
158 // upload on the right, columns need no clearing
159 tx = otx + VIEWW;
160 while (dx--) {
161 // cam_clear_column(cam, otx & (VDPW - 1));
162 cam_upload_column(cam, tx & (VDPW - 1), tx);
163 ++tx;
164 // ++otx;
166 } else {
167 // upload on the left, columns need no clearing
168 // tx = otx + VIEWW;
169 while (dx++) {
170 // --tx;
171 --otx;
172 cam_upload_column(cam, otx & (VDPW - 1), otx);
173 // cam_clear_column(cam, tx & (VDPW - 1));
177 if (dy > 0) {
178 // upload on the bottom, clear on the top
179 ty = oty + cam->sth;
180 while (dy--) {
181 cam_clear_row(cam, (cam->sty + oty) & (VDPH - 1));
182 cam_upload_row(cam, (cam->sty + ty) & (VDPH - 1), ty);
183 ++ty;
184 ++oty;
186 } else {
187 // upload on the top, clear on the bottom
188 ty = oty + cam->sth;
189 while (dy++) {
190 --ty;
191 --oty;
192 cam_upload_row(cam, (cam->sty + oty) & (VDPH - 1), oty);
193 cam_clear_row(cam, (cam->sty + ty) & (VDPH - 1));
198 void map_update(void) {
199 for (u16 i = 0; i < num_players; ++i)
200 cam_update(&g_cam[i], th_player[i]);
203 static inline s16 tile_in_camera(const cam_t *cam, const u16 tx, const u16 ty) {
204 return (tx >= cam->tx && tx <= cam->tx + VIEWW &&
205 ty >= cam->ty && ty <= cam->ty + cam->sth);
208 void map_clear_tile(u16 tx, u16 ty) {
209 for (u16 i = 0; i < num_players; ++i)
210 if (tile_in_camera(&g_cam[i], tx, ty))
211 VDP_setTileMapXY(g_cam[i].planeidx, bg_basetile, tx & (VDPW - 1), (ty + g_cam[i].sty) & (VDPH - 1));
214 void map_set_tile(u16 tx, u16 ty, u16 tile) {
215 for (u16 i = 0; i < num_players; ++i)
216 if (tile_in_camera(&g_cam[i], tx, ty))
217 VDP_setTileMapXY(g_cam[i].planeidx, fg_basetile + tile, tx & (VDPW - 1), (ty + g_cam[i].sty) & (VDPH - 1));
220 void map_clear_tile_rect(s16 tx, s16 ty, s16 w, s16 h) {
221 // TODO: just clip the rect and do a fill
222 for (; tx < tx + w; ++tx)
223 for (; ty < ty + h; ++ty)
224 map_clear_tile(tx, ty);
227 void map_set_tile_rect(s16 tx, s16 ty, s16 w, s16 h, u16 tile) {
228 // TODO: just clip the rect and do a fill
229 for (; tx < tx + w; ++tx)
230 for (; ty < ty + h; ++ty)
231 map_set_tile(tx, ty, tile);