From 3d70de3b9e4a749f5dc9e969f82ec569e77fc8cb Mon Sep 17 00:00:00 2001 From: Mauro Junior <45118493+jetrotal@users.noreply.github.com> Date: Thu, 13 Feb 2025 17:25:59 -0300 Subject: [PATCH] New Command 2059 - EasyRPG Zoom Zooms in and Out the screen. `The syntax always comes in pairs. The first parameter of each pair indicates whether you are using a direct value or a variable/indirect variable, through ValueOrVariable().` ------------------------ #### Syntax (TPC): ```js @raw 2059, "", // Not Used. 0, 0, // Scale value in percentage (Default is 100%). 0, 1, // Origin Type (0 = absolute pixel || 1 =percentage of screen area ) 0, 50, // Origin X 0, 50, // Origin Y ``` #### Example: ```js @raw 2059, "", 1, 20, 0, 0, 1, 21, 1, 22 // size is var20, absolute pixel, originX is var21, originY is var22 ``` ----------------------------------- This command differs from maniacs patch zoom, which is executed per picture layer, and it has a interpolation between zoom states and a "wait for completion" check. --- src/game_interpreter.cpp | 23 ++++++++++ src/game_interpreter.h | 1 + src/graphics.cpp | 8 ++++ src/graphics.h | 29 +++++++++++- src/platform/sdl/sdl2_ui.cpp | 85 +++++++++++++++++++++++++++++++++--- 5 files changed, 140 insertions(+), 6 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 5a5b6dbed7..177e0f71bb 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -46,6 +46,7 @@ #include "game_screen.h" #include "game_interpreter_control_variables.h" #include "game_windows.h" +#include "graphics.h" #include "json_helper.h" #include "maniac_patch.h" #include "spriteset_map.h" @@ -794,6 +795,8 @@ bool Game_Interpreter::ExecuteCommand(lcf::rpg::EventCommand const& com) { return CommandEasyRpgCloneMapEvent(com); case Cmd::EasyRpg_DestroyMapEvent: return CommandEasyRpgDestroyMapEvent(com); + case static_cast(2059): + return CommandEasyRpgZoom(com); default: return true; } @@ -5491,6 +5494,26 @@ bool Game_Interpreter::CommandEasyRpgDestroyMapEvent(lcf::rpg::EventCommand cons return true; } +bool Game_Interpreter::CommandEasyRpgZoom(lcf::rpg::EventCommand const& com) { + if (!Player::HasEasyRpgExtensions()) { + //return true; + } + + int Scale = ValueOrVariable(com.parameters[0], com.parameters[1]); + bool OriginAsPercentage = static_cast(ValueOrVariable(com.parameters[2], com.parameters[3])); + int OriginX = ValueOrVariable(com.parameters[4], com.parameters[5]); + int OriginY = ValueOrVariable(com.parameters[6], com.parameters[7]); + + Graphics::GetZoomData().SetScale(Scale); + Graphics::GetZoomData().SetOriginAsPercentage(OriginAsPercentage); + Graphics::GetZoomData().SetOriginX(OriginX); + Graphics::GetZoomData().SetOriginY(OriginY); + + //Output::Warning("Zoom: {} Scale: {}, OriginAsPercentage: {}, OriginX: {}, OriginY: {}", Graphics::GetZoomData().GetScale(), Graphics::GetZoomData().IsOriginPercentage(), Graphics::GetZoomData().GetOriginX(), Graphics::GetZoomData().GetOriginY()); + + return true; +} + Game_Interpreter& Game_Interpreter::GetForegroundInterpreter() { return Game_Battle::IsBattleRunning() ? Game_Battle::GetInterpreter() diff --git a/src/game_interpreter.h b/src/game_interpreter.h index 04bf3afda3..837ca4873a 100644 --- a/src/game_interpreter.h +++ b/src/game_interpreter.h @@ -301,6 +301,7 @@ class Game_Interpreter : public Game_BaseInterpreterContext bool CommandEasyRpgProcessJson(lcf::rpg::EventCommand const& com); bool CommandEasyRpgCloneMapEvent(lcf::rpg::EventCommand const& com); bool CommandEasyRpgDestroyMapEvent(lcf::rpg::EventCommand const& com); + bool CommandEasyRpgZoom(lcf::rpg::EventCommand const& com); bool CommandManiacGetGameInfo(lcf::rpg::EventCommand const& com); void SetSubcommandIndex(int indent, int idx); diff --git a/src/graphics.cpp b/src/graphics.cpp index 7b661f91d2..c32f95a57f 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -34,6 +34,14 @@ using namespace std::chrono_literals; namespace Graphics { + namespace { + static ZoomData viewport_scale_instance; + } + + ZoomData& GetZoomData() { + return viewport_scale_instance; + } + void UpdateTitle(); std::shared_ptr current_scene; diff --git a/src/graphics.h b/src/graphics.h index 6bf5ec9889..5281ba6a15 100644 --- a/src/graphics.h +++ b/src/graphics.h @@ -18,7 +18,7 @@ #ifndef EP_GRAPHICS_H #define EP_GRAPHICS_H -// Headers +#include #include #include "bitmap.h" #include "drawable.h" @@ -33,6 +33,33 @@ class Scene; * Handles screen drawing. */ namespace Graphics { + /** + * Controls viewport scaling and positioning + */ + class ZoomData { + private: + int scale_percentage = 100; + bool origin_type = true; // true = percentage, false = absolute pixels + int origin_x = 50; // center point for scaling + int origin_y = 50; // center point for scaling + + public: + int GetScale() const { return scale_percentage; } + void SetScale(int percentage) { scale_percentage = percentage; } + + bool IsOriginPercentage() const { return origin_type; } + void SetOriginAsPercentage(bool is_relative) { origin_type = is_relative; } + + int GetOriginX() const { return origin_x; } + void SetOriginX(int x) { origin_x = x; } + + int GetOriginY() const { return origin_y; } + void SetOriginY(int y) { origin_y = y; } + }; + + // Global instance declaration + extern ZoomData& GetZoomData(); + /** * Initializes Graphics. */ diff --git a/src/platform/sdl/sdl2_ui.cpp b/src/platform/sdl/sdl2_ui.cpp index 355f6184be..96dfb164ef 100644 --- a/src/platform/sdl/sdl2_ui.cpp +++ b/src/platform/sdl/sdl2_ui.cpp @@ -20,6 +20,7 @@ #include "game_config.h" #include "system.h" #include "sdl2_ui.h" +#include "scene.h" #ifdef _WIN32 # include @@ -590,6 +591,25 @@ void Sdl2Ui::ToggleVsync() { } void Sdl2Ui::UpdateDisplay() { + + struct zoom_params { + int zoom; + bool is_anchor_percent; + int x; + int y; + }; + + zoom_params params{100,true,50,50}; + + auto st = Scene::instance->type; + if (st == Scene::Battle || st == Scene::Map) { + params.zoom = Graphics::GetZoomData().GetScale(); + params.is_anchor_percent = Graphics::GetZoomData().IsOriginPercentage(); + params.x = Graphics::GetZoomData().GetOriginX(); + params.y = Graphics::GetZoomData().GetOriginY(); + + } + #ifdef __WIIU__ if (vcfg.scaling_mode.Get() == ConfigEnum::ScalingMode::Bilinear && window.scale > 0.f) { // Workaround WiiU bug: Bilinear uses a render target and for these the format is not converted @@ -676,7 +696,7 @@ void Sdl2Ui::UpdateDisplay() { } SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); sdl_texture_scaled = SDL_CreateTexture(sdl_renderer, texture_format, SDL_TEXTUREACCESS_TARGET, - static_cast(ceilf(window.scale)) * main_surface->width(), static_cast(ceilf(window.scale)) * main_surface->height()); + static_cast(ceilf(window.scale)) * main_surface->width(), static_cast(ceilf(window.scale)) * main_surface->height()); SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest"); if (!sdl_texture_scaled) { Output::Debug("SDL_CreateTexture failed : {}", SDL_GetError()); @@ -686,18 +706,73 @@ void Sdl2Ui::UpdateDisplay() { SDL_RenderClear(sdl_renderer); if (vcfg.scaling_mode.Get() == ConfigEnum::ScalingMode::Bilinear && window.scale > 0.f) { + // Calculate zoom parameters + float zoom_scale = params.zoom / 100.0f; + + // Calculate source rectangle (the area we want to show) + SDL_Rect src_rect; + int zoomed_width = static_cast(main_surface->width() * zoom_scale); + int zoomed_height = static_cast(main_surface->height() * zoom_scale); + + // Calculate anchor points + int anchor_pixel_x = params.is_anchor_percent ? + (params.x * main_surface->width() / 100) : + params.x; + int anchor_pixel_y = params.is_anchor_percent ? + (params.y * main_surface->height() / 100) : + params.y; + + // Calculate the top-left corner of the source rectangle + src_rect.x = anchor_pixel_x - (main_surface->width() / (2 * zoom_scale)); + src_rect.y = anchor_pixel_y - (main_surface->height() / (2 * zoom_scale)); + src_rect.w = main_surface->width() / zoom_scale; + src_rect.h = main_surface->height() / zoom_scale; + + // Clamp source rectangle to texture bounds + if (src_rect.x < 0) src_rect.x = 0; + if (src_rect.y < 0) src_rect.y = 0; + if (src_rect.x + src_rect.w > main_surface->width()) + src_rect.x = main_surface->width() - src_rect.w; + if (src_rect.y + src_rect.h > main_surface->height()) + src_rect.y = main_surface->height() - src_rect.h; + // Render game texture on the scaled texture SDL_SetRenderTarget(sdl_renderer, sdl_texture_scaled); SDL_RenderClear(sdl_renderer); - SDL_RenderCopy(sdl_renderer, sdl_texture_game, nullptr, nullptr); + SDL_RenderCopy(sdl_renderer, sdl_texture_game, &src_rect, nullptr); SDL_SetRenderTarget(sdl_renderer, nullptr); SDL_RenderCopy(sdl_renderer, sdl_texture_scaled, nullptr, nullptr); - } else { - SDL_RenderCopy(sdl_renderer, sdl_texture_game, nullptr, nullptr); + } + else { + // Handle non-bilinear case with zoom + float zoom_scale = params.zoom / 100.0f; + + SDL_Rect src_rect; + int anchor_pixel_x = params.is_anchor_percent ? + (params.x * main_surface->width() / 100) : + params.x; + int anchor_pixel_y = params.is_anchor_percent ? + (params.y * main_surface->height() / 100) : + params.y; + + src_rect.x = anchor_pixel_x - (main_surface->width() / (2 * zoom_scale)); + src_rect.y = anchor_pixel_y - (main_surface->height() / (2 * zoom_scale)); + src_rect.w = main_surface->width() / zoom_scale; + src_rect.h = main_surface->height() / zoom_scale; + + // Clamp source rectangle + if (src_rect.x < 0) src_rect.x = 0; + if (src_rect.y < 0) src_rect.y = 0; + if (src_rect.x + src_rect.w > main_surface->width()) + src_rect.x = main_surface->width() - src_rect.w; + if (src_rect.y + src_rect.h > main_surface->height()) + src_rect.y = main_surface->height() - src_rect.h; + + SDL_RenderCopy(sdl_renderer, sdl_texture_game, &src_rect, nullptr); } SDL_RenderPresent(sdl_renderer); -} + } void Sdl2Ui::SetTitle(const std::string &title) { SDL_SetWindowTitle(sdl_window, title.c_str());