/*
Copyright © 2017 Justin Jacobs
This file is part of FLARE.
FLARE is free software: you can redistribute it and/or modify it under the terms
of the GNU General Public License as published by the Free Software Foundation,
either version 3 of the License, or (at your option) any later version.
FLARE 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 General Public License for more details.
You should have received a copy of the GNU General Public License along with
FLARE. If not, see http://www.gnu.org/licenses/
*/
#include "FileParser.h"
#include "MapParallax.h"
#include "RenderDevice.h"
#include "Settings.h"
#include "SharedResources.h"
#include "UtilsParsing.h"
#include <math.h>
MapParallax::MapParallax()
: current_layer(0)
, loaded(false)
, current_filename("")
{
}
MapParallax::~MapParallax() {
clear();
}
void MapParallax::clear() {
for (size_t i = 0; i < layers.size(); ++i) {
delete layers[i].sprite;
}
layers.clear();
loaded = false;
}
void MapParallax::load(const std::string& filename) {
current_filename = filename;
if (loaded)
clear();
if (!settings->parallax_layers)
return;
// @CLASS MapParallax|Description of maps/parallax/
FileParser infile;
if (infile.open(filename, FileParser::MOD_FILE, FileParser::ERROR_NORMAL)) {
while (infile.next()) {
if (infile.new_section && infile.section == "layer") {
layers.resize(layers.size()+1);
}
if (layers.empty())
continue;
if (infile.key == "image") {
// @ATTR layer.image|filename|Image file to use as a scrolling background.
Image *graphics = render_device->loadImage(infile.val, RenderDevice::ERROR_NORMAL);
if (graphics) {
layers.back().sprite = graphics->createSprite();
graphics->unref();
}
}
else if (infile.key == "speed") {
// @ATTR layer.speed|float|Speed at which the background will move relative to the camera.
layers.back().speed = (Settings::LOGIC_FPS * Parse::toFloat(infile.val)) / settings->max_frames_per_sec;
}
else if (infile.key == "fixed_speed") {
// @ATTR layer.fixed_speed|float, float : X speed, Y speed|Speed at which the background will move independent of the camera movement.
layers.back().fixed_speed.x = (Settings::LOGIC_FPS * Parse::toFloat(Parse::popFirstString(infile.val))) / settings->max_frames_per_sec;
layers.back().fixed_speed.y = (Settings::LOGIC_FPS * Parse::toFloat(Parse::popFirstString(infile.val))) / settings->max_frames_per_sec;
}
else if (infile.key == "map_layer") {
// @ATTR layer.map_layer|string|The tile map layer that this parallax layer will be rendered on top of.
layers.back().map_layer = infile.val;
}
}
infile.close();
}
loaded = true;
}
void MapParallax::setMapCenter(int x, int y) {
map_center.x = static_cast<float>(x) + 0.5f;
map_center.y = static_cast<float>(y) + 0.5f;
}
void MapParallax::render(const FPoint& cam, const std::string& map_layer) {
if (!settings->parallax_layers) {
if (loaded)
clear();
return;
}
else if (!loaded) {
load(current_filename);
}
if (map_layer.empty())
current_layer = 0;
for (size_t i = current_layer; i < layers.size(); ++i) {
if (layers[i].map_layer != map_layer)
continue;
int width = layers[i].sprite->getGraphicsWidth();
int height = layers[i].sprite->getGraphicsHeight();
layers[i].fixed_offset.x += layers[i].fixed_speed.x;
layers[i].fixed_offset.y += layers[i].fixed_speed.y;
if (layers[i].fixed_offset.x > static_cast<float>(width))
layers[i].fixed_offset.x -= static_cast<float>(width);
if (layers[i].fixed_offset.x < static_cast<float>(-width))
layers[i].fixed_offset.x += static_cast<float>(width);
if (layers[i].fixed_offset.y > static_cast<float>(height))
layers[i].fixed_offset.y -= static_cast<float>(height);
if (layers[i].fixed_offset.y < static_cast<float>(-height))
layers[i].fixed_offset.y += static_cast<float>(height);
FPoint dp;
dp.x = map_center.x - cam.x;
dp.y = map_center.y - cam.y;
Point center_tile = Utils::mapToScreen(map_center.x + (dp.x * layers[i].speed) + layers[i].fixed_offset.x, map_center.y + (dp.y * layers[i].speed) + layers[i].fixed_offset.y, cam.x, cam.y);
center_tile.x -= width/2;
center_tile.y -= height/2;
Point draw_pos;
draw_pos.x = center_tile.x - static_cast<int>(ceilf(static_cast<float>(settings->view_w_half + center_tile.x) / static_cast<float>(width))) * width;
draw_pos.y = center_tile.y - static_cast<int>(ceilf(static_cast<float>(settings->view_h_half + center_tile.y) / static_cast<float>(height))) * height;
Point start_pos = draw_pos;
while (draw_pos.x < settings->view_w) {
draw_pos.y = start_pos.y;
while (draw_pos.y < settings->view_h) {
layers[i].sprite->setDest(draw_pos.x, draw_pos.y);
render_device->render(layers[i].sprite);
draw_pos.y += height;
}
draw_pos.x += width;
}
current_layer++;
}
}