mirror of
https://github.com/libretro/easyrpg-libretro.git
synced 2026-11-10 02:41:35 -10:00
That solves some errors related out of bounds access. Sometimes it happens when drawing a tile, others when checking passability. It may differ from how maniacs deals with this (I suspect maniacs have an extra blank tile, with locked passability, just for OOB tiles).
338 lines
10 KiB
C++
338 lines
10 KiB
C++
/*
|
|
* This file is part of EasyRPG Player.
|
|
*
|
|
* EasyRPG Player 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.
|
|
*
|
|
* EasyRPG Player 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 EasyRPG Player. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
// Headers
|
|
#include "spriteset_map.h"
|
|
#include "cache.h"
|
|
#include "game_dynrpg.h"
|
|
#include "game_map.h"
|
|
#include "main_data.h"
|
|
#include "sprite_airshipshadow.h"
|
|
#include "sprite_character.h"
|
|
#include "game_character.h"
|
|
#include "game_player.h"
|
|
#include "game_vehicle.h"
|
|
#include "game_screen.h"
|
|
#include "bitmap.h"
|
|
#include "player.h"
|
|
#include "drawable_list.h"
|
|
#include "map_data.h"
|
|
|
|
Spriteset_Map::Spriteset_Map() {
|
|
panorama = std::make_unique<Plane>();
|
|
panorama->SetZ(Priority_Background);
|
|
|
|
timer1 = std::make_unique<Sprite_Timer>(0);
|
|
timer2 = std::make_unique<Sprite_Timer>(1);
|
|
|
|
screen = std::make_unique<Screen>();
|
|
|
|
if (Player::IsRPG2k3()) {
|
|
frame = std::make_unique<Frame>();
|
|
}
|
|
|
|
ParallaxUpdated();
|
|
|
|
Refresh();
|
|
|
|
Update();
|
|
}
|
|
|
|
void Spriteset_Map::Refresh() {
|
|
CalculateMapRenderOffset();
|
|
|
|
tilemap = std::make_unique<Tilemap>();
|
|
tilemap->SetWidth(Game_Map::GetTilesX());
|
|
tilemap->SetHeight(Game_Map::GetTilesY());
|
|
tilemap->SetRenderOx(map_render_ox);
|
|
tilemap->SetRenderOy(map_render_oy);
|
|
|
|
airship_shadows.clear();
|
|
character_sprites.clear();
|
|
|
|
ChipsetUpdated();
|
|
|
|
need_x_clone = Game_Map::LoopHorizontal();
|
|
need_y_clone = Game_Map::LoopVertical();
|
|
|
|
for (Game_Event& ev : Game_Map::GetEvents()) {
|
|
CreateSprite(&ev, need_x_clone, need_y_clone);
|
|
}
|
|
|
|
CreateAirshipShadowSprite(need_x_clone, need_y_clone);
|
|
|
|
CreateSprite(Main_Data::game_player.get(), need_x_clone, need_y_clone);
|
|
|
|
for (bool& v: vehicle_loaded) {
|
|
v = false;
|
|
}
|
|
}
|
|
|
|
// Update
|
|
void Spriteset_Map::Update() {
|
|
Tone new_tone = Main_Data::game_screen->GetTone();
|
|
|
|
tilemap->SetOx(Game_Map::GetDisplayX() / (SCREEN_TILE_SIZE / TILE_SIZE));
|
|
tilemap->SetOy(Game_Map::GetDisplayY() / (SCREEN_TILE_SIZE / TILE_SIZE));
|
|
tilemap->SetTone(new_tone);
|
|
|
|
for (const auto& character_sprite : character_sprites) {
|
|
character_sprite->Update();
|
|
character_sprite->SetTone(new_tone);
|
|
}
|
|
|
|
panorama->SetOx(Game_Map::Parallax::GetX());
|
|
panorama->SetOy(Game_Map::Parallax::GetY());
|
|
panorama->SetTone(new_tone);
|
|
|
|
Game_Vehicle* vehicle;
|
|
int map_id = Game_Map::GetMapId();
|
|
for (int i = 1; i <= 3; ++i) {
|
|
vehicle = Game_Map::GetVehicle((Game_Vehicle::Type) i);
|
|
|
|
if (!vehicle_loaded[i - 1] && vehicle->GetMapId() == map_id) {
|
|
vehicle_loaded[i - 1] = true;
|
|
CreateSprite(vehicle, need_x_clone, need_y_clone);
|
|
}
|
|
}
|
|
|
|
for (auto& shadow : airship_shadows) {
|
|
shadow->SetTone(new_tone);
|
|
shadow->Update();
|
|
}
|
|
|
|
Main_Data::game_dynrpg->Update();
|
|
}
|
|
|
|
void Spriteset_Map::ChipsetUpdated() {
|
|
if (!Game_Map::GetChipsetName().empty()) {
|
|
FileRequestAsync* request = AsyncHandler::RequestFile("ChipSet", Game_Map::GetChipsetName());
|
|
tilemap_request_id = request->Bind(&Spriteset_Map::OnTilemapSpriteReady, this);
|
|
request->SetImportantFile(true);
|
|
request->SetGraphicFile(true);
|
|
request->Start();
|
|
}
|
|
else {
|
|
OnTilemapSpriteReady(NULL);
|
|
}
|
|
|
|
for (auto& sprite: character_sprites) {
|
|
sprite->ChipsetUpdated();
|
|
}
|
|
}
|
|
|
|
void Spriteset_Map::ParallaxUpdated() {
|
|
std::string name = Game_Map::Parallax::GetName();
|
|
if (name != panorama_name) {
|
|
panorama_name = name;
|
|
if (!name.empty()) {
|
|
FileRequestAsync* request = AsyncHandler::RequestFile("Panorama", panorama_name);
|
|
request->SetGraphicFile(true);
|
|
request->SetImportantFile(true);
|
|
panorama_request_id = request->Bind(&Spriteset_Map::OnPanoramaSpriteReady, this);
|
|
request->Start();
|
|
}
|
|
}
|
|
|
|
if (name.empty()) {
|
|
panorama->SetBitmap(BitmapRef());
|
|
Game_Map::Parallax::Initialize(0, 0);
|
|
}
|
|
}
|
|
|
|
void Spriteset_Map::SystemGraphicUpdated() {
|
|
for (auto& shadow : airship_shadows) {
|
|
shadow->RecreateShadow();
|
|
}
|
|
}
|
|
|
|
void Spriteset_Map::SubstituteDown(int old_id, int new_id) {
|
|
int num_subst = Game_Map::SubstituteDown(old_id, new_id);
|
|
if (num_subst) {
|
|
tilemap->OnSubstituteDown();
|
|
}
|
|
}
|
|
|
|
void Spriteset_Map::SubstituteUp(int old_id, int new_id) {
|
|
int num_subst = Game_Map::SubstituteUp(old_id, new_id);
|
|
if (num_subst) {
|
|
tilemap->OnSubstituteUp();
|
|
}
|
|
}
|
|
|
|
void Spriteset_Map::ReplaceDownAt(int x, int y, int tile_index, bool disable_autotile) {
|
|
if (tile_index >= BLOCK_F_INDEX) tile_index = BLOCK_F_INDEX - 1;
|
|
|
|
auto tile_id = IndexToChipId(tile_index);
|
|
tilemap->SetMapTileDataDownAt(x, y, tile_id, disable_autotile);
|
|
}
|
|
|
|
void Spriteset_Map::ReplaceUpAt(int x, int y, int tile_index) {
|
|
tile_index += BLOCK_F_INDEX;
|
|
if (tile_index >= NUM_UPPER_TILES + BLOCK_F_INDEX) tile_index = BLOCK_F_INDEX;
|
|
|
|
auto tile_id = IndexToChipId(tile_index);
|
|
tilemap->SetMapTileDataUpAt(x, y, tile_id);
|
|
}
|
|
|
|
bool Spriteset_Map::RequireClear(DrawableList& drawable_list) {
|
|
if (drawable_list.empty()) {
|
|
return true;
|
|
}
|
|
|
|
// When using a custom resolution that is not divisible by 16, clear to avoid
|
|
// artifacts at the borders
|
|
if (Player::screen_width % TILE_SIZE != 0 || Player::screen_height % TILE_SIZE != 0) {
|
|
return true;
|
|
}
|
|
|
|
// Speed optimisation:
|
|
// When there is nothing below the tilemap it can be drawn opaque (faster)
|
|
tilemap->SetFastBlitDown(false);
|
|
|
|
if (!panorama_name.empty()) {
|
|
// Map has a panorama -> No opaque tilemap blit possible
|
|
// but the panorama is drawn opaque -> clearing the screen is not needed
|
|
return false;
|
|
}
|
|
|
|
// The list is about to be drawn, so we can just sort it now if needed.
|
|
if (drawable_list.IsDirty()) {
|
|
drawable_list.Sort();
|
|
}
|
|
|
|
// Only if there is nothing below the tileset, can we do fast blitting.
|
|
if ((*drawable_list.begin())->GetZ() >= Priority_TilesetBelow) {
|
|
tilemap->SetFastBlitDown(true);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Spriteset_Map::CreateSprite(Game_Character* character, bool create_x_clone, bool create_y_clone) {
|
|
auto add_sprite = [&](auto&& chara) {
|
|
chara->SetRenderOx(map_render_ox);
|
|
chara->SetRenderOy(map_render_oy);
|
|
character_sprites.push_back(std::forward<decltype(chara)>(chara));
|
|
};
|
|
|
|
add_sprite(std::make_unique<Sprite_Character>(character));
|
|
if (create_x_clone) {
|
|
add_sprite(std::make_unique<Sprite_Character>(character, -map_tiles_x, 0));
|
|
add_sprite(std::make_unique<Sprite_Character>(character, map_tiles_x, 0));
|
|
}
|
|
if (create_y_clone) {
|
|
add_sprite(std::make_unique<Sprite_Character>(character, 0, -map_tiles_y));
|
|
add_sprite(std::make_unique<Sprite_Character>(character, 0, map_tiles_y));
|
|
}
|
|
if (create_x_clone && create_y_clone) {
|
|
add_sprite(std::make_unique<Sprite_Character>(character, map_tiles_x, map_tiles_y));
|
|
add_sprite(std::make_unique<Sprite_Character>(character, -map_tiles_x, map_tiles_y));
|
|
add_sprite(std::make_unique<Sprite_Character>(character, map_tiles_x, -map_tiles_y));
|
|
add_sprite(std::make_unique<Sprite_Character>(character, -map_tiles_x, -map_tiles_y));
|
|
}
|
|
}
|
|
|
|
void Spriteset_Map::CreateAirshipShadowSprite(bool create_x_clone, bool create_y_clone) {
|
|
auto add_sprite = [&](auto&& chara) {
|
|
chara->SetRenderOx(map_render_ox);
|
|
chara->SetRenderOy(map_render_oy);
|
|
airship_shadows.push_back(std::forward<decltype(chara)>(chara));
|
|
};
|
|
|
|
add_sprite(std::make_unique<Sprite_AirshipShadow>());
|
|
if (create_x_clone) {
|
|
add_sprite(std::make_unique<Sprite_AirshipShadow>(-map_tiles_x, 0));
|
|
add_sprite(std::make_unique<Sprite_AirshipShadow>(map_tiles_x, 0));
|
|
}
|
|
if (create_y_clone) {
|
|
add_sprite(std::make_unique<Sprite_AirshipShadow>(0, -map_tiles_y));
|
|
add_sprite(std::make_unique<Sprite_AirshipShadow>(0, map_tiles_y));
|
|
}
|
|
if (create_x_clone && create_y_clone) {
|
|
add_sprite(std::make_unique<Sprite_AirshipShadow>(map_tiles_x, map_tiles_y));
|
|
add_sprite(std::make_unique<Sprite_AirshipShadow>(-map_tiles_x, map_tiles_y));
|
|
add_sprite(std::make_unique<Sprite_AirshipShadow>(map_tiles_x, -map_tiles_y));
|
|
add_sprite(std::make_unique<Sprite_AirshipShadow>(-map_tiles_x, -map_tiles_y));
|
|
}
|
|
}
|
|
|
|
void Spriteset_Map::OnTilemapSpriteReady(FileRequestResult*) {
|
|
if (!Game_Map::GetChipsetName().empty()) {
|
|
tilemap->SetChipset(Cache::Chipset(Game_Map::GetChipsetName()));
|
|
}
|
|
else {
|
|
tilemap->SetChipset(Bitmap::Create(480, 256));
|
|
}
|
|
|
|
tilemap->SetMapDataDown(Game_Map::GetMapDataDown());
|
|
tilemap->SetMapDataUp(Game_Map::GetMapDataUp());
|
|
tilemap->SetPassableDown(Game_Map::GetPassagesDown());
|
|
tilemap->SetPassableUp(Game_Map::GetPassagesUp());
|
|
tilemap->SetAnimationType(Game_Map::GetAnimationType());
|
|
tilemap->SetAnimationSpeed(Game_Map::GetAnimationSpeed());
|
|
}
|
|
|
|
void Spriteset_Map::OnPanoramaSpriteReady(FileRequestResult* result) {
|
|
BitmapRef panorama_bmp = Cache::Panorama(result->file);
|
|
panorama->SetBitmap(panorama_bmp);
|
|
Game_Map::Parallax::Initialize(panorama_bmp->GetWidth(), panorama_bmp->GetHeight());
|
|
CalculatePanoramaRenderOffset();
|
|
}
|
|
|
|
void Spriteset_Map::CalculateMapRenderOffset() {
|
|
map_render_ox = 0;
|
|
map_render_oy = 0;
|
|
|
|
// Smallest possible map. Smaller maps are hacked
|
|
map_tiles_x = std::max<int>(Game_Map::GetTilesX(), 20) * TILE_SIZE;
|
|
map_tiles_y = std::max<int>(Game_Map::GetTilesY(), 15) * TILE_SIZE;
|
|
|
|
panorama->SetRenderOx(0);
|
|
panorama->SetRenderOy(0);
|
|
screen->SetViewport(Rect());
|
|
|
|
if (Player::game_config.fake_resolution.Get()) {
|
|
// Resolution hack for tiles and sprites
|
|
map_tiles_x = std::max<int>(Game_Map::GetTilesX(), 20) * TILE_SIZE;
|
|
map_tiles_y = std::max<int>(Game_Map::GetTilesY(), 15) * TILE_SIZE;
|
|
|
|
if (map_tiles_x < Player::screen_width) {
|
|
map_render_ox = (Player::screen_width - map_tiles_x) / 2;
|
|
}
|
|
if (map_tiles_y < Player::screen_height) {
|
|
map_render_oy = (Player::screen_height - map_tiles_y) / 2;
|
|
}
|
|
|
|
CalculatePanoramaRenderOffset();
|
|
|
|
screen->SetViewport({map_render_ox, map_render_oy, map_tiles_x, map_tiles_y});
|
|
}
|
|
}
|
|
|
|
void Spriteset_Map::CalculatePanoramaRenderOffset() {
|
|
// Resolution hack for Panorama
|
|
if (Player::game_config.fake_resolution.Get()) {
|
|
if (Game_Map::Parallax::FakeXPosition()) {
|
|
panorama->SetRenderOx((Player::screen_width - SCREEN_TARGET_WIDTH) / 2);
|
|
}
|
|
if (Game_Map::Parallax::FakeYPosition()) {
|
|
panorama->SetRenderOy((Player::screen_height - SCREEN_TARGET_HEIGHT) / 2);
|
|
}
|
|
}
|
|
}
|