RogueLife/src/render.c

159 lines
4.0 KiB
C

#include "render.h"
#include <gint/display.h>
//---
// 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);
}
}
}