/*
* Copyright (C) 2013 Patrick "P. J." McDermott
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program. If not, see
* .
*/
#include
#include "area.h"
#include "viewport.h"
#include "resources/map.h"
#include "resources/tileset.h"
#include "logging.h"
static void init_map_layers(struct area *);
static void blit_map_layers(struct area *);
static void blit_map_layer(struct area *, enum map_layer, enum area_layer);
struct area *
area_new(struct map *map, SDL_Palette *pal)
{
struct area *a;
if (map == NULL) {
return NULL;
}
a = malloc(sizeof(*a));
if (a == NULL) {
return NULL;
}
a->map = map;
init_map_layers(a);
set_area_palette(a, pal);
blit_map_layers(a);
return a;
}
void
set_area_palette(struct area *area, SDL_Palette *pal)
{
enum area_layer l;
for (l = 0; l < AREA_LAYERS_MAX; ++l) {
SDL_SetPalette(area->map_layers[l], SDL_LOGPAL,
pal->colors, 0, pal->ncolors);
}
}
static void
init_map_layers(struct area *area)
{
SDL_Rect dstrect;
enum area_layer l;
dstrect.x = 0;
dstrect.y = 0;
dstrect.w = area->map->width * area->map->tilewidth;
dstrect.h = area->map->height * area->map->tileheight;
for (l = 0; l < AREA_LAYERS_MAX; ++l) {
area->map_layers[l] = SDL_CreateRGBSurface(
SDL_HWSURFACE | SDL_SRCCOLORKEY,
area->map->width * area->map->tilewidth,
area->map->height * area->map->tileheight,
8, 0x00, 0x00, 0x00, 0x00);
SDL_FillRect(area->map_layers[l], &dstrect,
SDL_MapRGB(area->map_layers[l]->format,
0xFC, 0x00, 0xFF));
SDL_SetColorKey(area->map_layers[l], SDL_SRCCOLORKEY,
SDL_MapRGB(area->map_layers[l]->format,
0xFC, 0x00, 0xFF));
}
}
static void
blit_map_layers(struct area *area)
{
blit_map_layer(area, MAP_LAYER_GROUND, AREA_LAYER_BOT);
blit_map_layer(area, MAP_LAYER_OBJ_LOW, AREA_LAYER_BOT);
blit_map_layer(area, MAP_LAYER_OBJ_MID, AREA_LAYER_MID);
blit_map_layer(area, MAP_LAYER_OBJ_HIGH, AREA_LAYER_TOP);
}
static void
blit_map_layer(struct area *area, enum map_layer map_layer,
enum area_layer area_layer)
{
int i;
Uint32 gid;
int tile_found;
struct map_tileset *mts;
Uint32 mts_lastgid;
SDL_Rect tilerect, layerrect;
for (i = 0; i < area->map->width * area->map->height; ++i) {
gid = area->map->layers[map_layer].tiles[i];
if (gid == 0) {
continue;
}
tile_found = 0;
for (mts = area->map->tilesets_head;
mts != NULL; mts = mts->next) {
mts_lastgid = mts->firstgid +
mts->tileset->width * mts->tileset->height;
if (gid >= mts->firstgid && gid < mts_lastgid) {
gid -= mts->firstgid;
tilerect.x = gid % mts->tileset->width;
tilerect.y = gid / mts->tileset->width;
tilerect.w = mts->tileset->tilewidth;
tilerect.h = mts->tileset->tileheight;
tilerect.x *= tilerect.w;
tilerect.y *= tilerect.h;
layerrect.x = i % area->map->width;
layerrect.y = i / area->map->width;
layerrect.w = mts->tileset->tilewidth;
layerrect.h = mts->tileset->tileheight;
layerrect.x *= layerrect.w;
layerrect.y *= layerrect.h;
#ifdef DEBUG_RENDER
debug("Blitting %dx%d tile at (%d,%d) "
"onto layer at (%d,%d)...",
tilerect.w, tilerect.h,
tilerect.x, tilerect.y,
layerrect.x, layerrect.y);
#endif
SDL_BlitSurface(mts->tileset->image->image,
&tilerect,
area->map_layers[area_layer],
&layerrect);
tile_found = 1;
break;
}
}
if (!tile_found) {
warn("Tile with gid 0x%8.8x not found", gid);
}
}
}
void
render_area_to_viewport(struct area *area, struct viewport *vp)
{
SDL_Rect srcrect;
SDL_Rect dstrect;
if (area == NULL || vp == NULL) {
return;
}
srcrect.x = vp->x;
srcrect.y = vp->y;
srcrect.w = vp->w;
srcrect.h = vp->h;
dstrect.x = 0;
dstrect.y = 0;
dstrect.w = vp->w;
dstrect.h = vp->h;
render_area_to_surface(area, &srcrect, vp->screen, &dstrect);
}
void
render_area_to_surface(struct area *area, SDL_Rect *arearect,
SDL_Surface *surface, SDL_Rect *surfacerect)
{
SDL_Rect srcrect, dstrect;
/* For demo */
SDL_Rect tmprect;
if (area == NULL || arearect == NULL) {
return;
}
if (surface == NULL || surfacerect == NULL) {
return;
}
memcpy(&srcrect, arearect, sizeof(srcrect));
memcpy(&dstrect, surfacerect, sizeof(dstrect));
/* Fast fill with black to avoid artifacts in off-map pixels. */
/* NB: This is gray, at least temporarily. */
SDL_FillRect(surface, &dstrect,
SDL_MapRGB(surface->format, 0, 0, 0));
/* For demo */
tmprect.x = dstrect.x + 160 - 8;
tmprect.y = dstrect.y + 120 - 12;
tmprect.w = 16;
tmprect.h = 20;
/* Avoid fun glitches due to negative coordinates in source
* rectangles. */
if (srcrect.x < 0) {
srcrect.x = 0;
dstrect.x = surfacerect->x - arearect->x;
}
if (srcrect.y < 0) {
srcrect.y = 0;
dstrect.y = surfacerect->y - arearect->y;
}
#ifdef DEBUG_RENDER
debug("Rendering %dx%d area rect at (%d,%d) "
"onto %dx%d surface rect at (%d,%d)...",
srcrect.w, srcrect.h, srcrect.x, srcrect.y,
dstrect.w, dstrect.h, dstrect.x, dstrect.y);
#endif
/* Blit bottom layer. */
SDL_BlitSurface(area->map_layers[AREA_LAYER_BOT], &srcrect,
surface, &dstrect);
/* return;*/
/* TODO: Blit low sprites. */
/* For demo */
SDL_FillRect(surface, &tmprect,
SDL_MapRGB(surface->format, 255, 0, 255));
/* Blit middle layer. */
SDL_BlitSurface(area->map_layers[AREA_LAYER_MID], &srcrect,
surface, &dstrect);
/* TODO: Blit high sprites. */
/* Blit top layer. */
SDL_BlitSurface(area->map_layers[AREA_LAYER_TOP], &srcrect,
surface, &dstrect);
}