#include "render.h" #include //--- // Camera management //--- void camera_init(struct camera *c, struct map *m) { c->zoom = 1; c->limits.x_min = fix(-CAMERA_BORDER); c->limits.x_max = fix(m->width + CAMERA_BORDER); c->limits.y_min = fix(-CAMERA_BORDER); c->limits.y_max = fix(m->height + CAMERA_BORDER); /* Fullscreen */ c->viewport.x_min = 0; c->viewport.x_max = DWIDTH; c->viewport.y_min = 0; c->viewport.y_max = DHEIGHT; c->width = fix(c->viewport.x_max - c->viewport.x_min) / TILE_WIDTH; c->height = fix(c->viewport.y_max - c->viewport.y_min) / TILE_HEIGHT; c->x = (c->limits.x_min + c->limits.x_max) / 2; c->y = (c->limits.y_min + c->limits.y_max) / 2; } void camera_map2screen(struct camera *c, map_coord_t map_x, map_coord_t map_y, int *screen_x, int *screen_y) { /* Start from center point */ int sx = DWIDTH / 2; int sy = DHEIGHT / 2; map_x -= c->x; map_y -= c->y; sx += fround(map_x * TILE_WIDTH * c->zoom); sy += fround(map_y * TILE_HEIGHT * c->zoom); if(screen_x) *screen_x = sx; if(screen_y) *screen_y = sy; } /* Translate screen coordinates to map coordinates */ void camera_screen2map(struct camera *c, int screen_x, int screen_y, map_coord_t *map_x, map_coord_t *map_y) { /* Start from center point */ map_coord_t mx = c->x; map_coord_t my = c->y; screen_x -= DWIDTH / 2; screen_y -= DHEIGHT / 2; mx += fix(screen_x) / TILE_WIDTH / c->zoom; my += fix(screen_y) / TILE_HEIGHT / c->zoom; if(map_x) *map_x = mx; if(map_y) *map_y = my; } /* Lock the camera at the center if set by settings. */ static bool camera_lock(struct camera *c) { if(c->zoom == 1 && CAMERA_LOCK_AT_x1) { c->x = (c->limits.x_min + c->limits.x_max) / 2; c->y = (c->limits.y_min + c->limits.y_max) / 2; return true; } return false; } /* Bound the camera to the map limits */ static void camera_bound(struct camera *c) { /* Project viewport onto the map */ map_coord_t vx_min, vx_max; map_coord_t vy_min, vy_max; camera_screen2map(c,c->viewport.x_min,c->viewport.y_min,&vx_min,&vy_min); camera_screen2map(c,c->viewport.x_max,c->viewport.y_max,&vx_max,&vy_max); /* Bound viewport to map limits */ if(vx_min < c->limits.x_min) c->x += (c->limits.x_min - vx_min); else if(vx_max > c->limits.x_max) c->x += (c->limits.x_max - vx_max); if(vy_min < c->limits.y_min) c->y += (c->limits.y_min - vy_min); else if(vy_max > c->limits.y_max) c->y += (c->limits.y_max - vy_max); } void camera_move(struct camera *c, map_coord_t dx, map_coord_t dy) { if(camera_lock(c)) return; c->x += dx; c->y += dy; camera_bound(c); } void camera_zoom(struct camera *c, int zoom) { if(zoom < ZOOM_MIN) zoom = ZOOM_MIN; if(zoom > ZOOM_MAX) zoom = ZOOM_MAX; c->zoom = zoom; if(camera_lock(c)) return; camera_bound(c); } //--- // Rendering //--- static uint16_t darker2(uint16_t color) { return (color & 0xf7de) >> 1; } static uint16_t darker1(uint16_t color) { int r = (color >> 11); int g = (color >> 6) & 0x1f; int b = color & 0x1f; return C_RGB(2*r/3, 2*g/3, 2*b/3); } void render_map(struct map *m, struct camera *c) { for(int row = 0; row < m->height; row++) for(int col = 0; col < m->width; col++) { struct tile *t = map_tile(m, col, row); if(!t) continue; int x, y; camera_map2screen(c, fix(col), fix(row), &x, &y); /* TODO: render_map: use images */ uint16_t color_ground = darker1(t->color); uint16_t color_wall = darker2(darker2(t->color)); int tile_w = c->zoom * TILE_WIDTH; int tile_h = c->zoom * TILE_HEIGHT; int wall_h = c->zoom * WALL_HEIGHT; drect(x, y, x+tile_w-1, y+tile_h-1, color_ground); struct tile *above = map_tile(m, col, row-1); if(above && !t->solid && above->solid) { drect(x, y-wall_h, x+tile_w-1, y-1, color_wall); } } }