From 0f8d6b5f9d85fdecbb0c72c8737f71b92af05b3c Mon Sep 17 00:00:00 2001 From: Ghabry Date: Fri, 13 Jan 2017 00:31:11 +0100 Subject: [PATCH 1/2] DynRPG: Add TextPlugin --- CMakeLists.txt | 2 + Makefile.am | 2 + src/dynrpg.cpp | 8 +- src/dynrpg_textplugin.cpp | 645 ++++++++++++++++++++++++++++++++++++++ src/dynrpg_textplugin.h | 36 +++ src/scene_map.cpp | 2 + 6 files changed, 692 insertions(+), 3 deletions(-) create mode 100644 src/dynrpg_textplugin.cpp create mode 100644 src/dynrpg_textplugin.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 727c850679..cac05d3c57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,6 +105,8 @@ add_library(${PROJECT_NAME} OBJECT src/dynrpg.h src/dynrpg_easyrpg.cpp src/dynrpg_easyrpg.h + src/dynrpg_textplugin.cpp + src/dynrpg_textplugin.h src/enemyai.cpp src/enemyai.h src/exe_reader.cpp diff --git a/Makefile.am b/Makefile.am index 4c4774892c..0edc1f0704 100644 --- a/Makefile.am +++ b/Makefile.am @@ -86,6 +86,8 @@ libeasyrpg_player_a_SOURCES = \ src/dynrpg.h \ src/dynrpg_easyrpg.cpp \ src/dynrpg_easyrpg.h \ + src/dynrpg_textplugin.h \ + src/dynrpg_textplugin.cpp \ src/enemyai.cpp \ src/enemyai.h \ src/exe_reader.cpp \ diff --git a/src/dynrpg.cpp b/src/dynrpg.cpp index b611a1d3a9..8be59c4fb9 100644 --- a/src/dynrpg.cpp +++ b/src/dynrpg.cpp @@ -24,12 +24,13 @@ #include "output.h" #include "player.h" -#include "dynrpg_easyrpg.h" - #include #include #include +#include "dynrpg_easyrpg.h" +#include "dynrpg_textplugin.h" + enum DynRpg_ParseMode { ParseMode_Function, ParseMode_WaitForComma, @@ -184,7 +185,8 @@ static std::string ParseToken(const std::string& token, const std::string& funct void create_all_plugins() { plugins.emplace_back(new DynRpg::EasyRpgPlugin()); - + plugins.emplace_back(new DynRpg::TextPlugin()); + for (auto& plugin : plugins) { plugin->RegisterFunctions(); } diff --git a/src/dynrpg_textplugin.cpp b/src/dynrpg_textplugin.cpp new file mode 100644 index 0000000000..5279a12f48 --- /dev/null +++ b/src/dynrpg_textplugin.cpp @@ -0,0 +1,645 @@ +/* + * 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 . + */ + +// Headers +#include + +#include "dynrpg_textplugin.h" +#include "baseui.h" +#include "bitmap.h" +#include "drawable.h" +#include "drawable_mgr.h" +#include "game_actors.h" +#include "game_map.h" +#include "game_party.h" +#include "game_pictures.h" +#include "game_screen.h" +#include "game_variables.h" +#include "graphics.h" +#include "main_data.h" +#include "player.h" + +class DynRpgText; + +namespace { + std::map> graphics; +} + +class DynRpgText : public Drawable { +public: + DynRpgText(int pic_id, int x, int y, const std::string& text) : Drawable(0), pic_id(pic_id), x(x), y(y) { + DrawableMgr::Register(this); + + AddLine(text); + } + + DynRpgText(int pic_id, int x, int y, const std::vector& text) : Drawable(0), pic_id(pic_id), x(x), y(y) { + DrawableMgr::Register(this); + + for (auto& s : text) { + AddLine(s); + } + } + + void AddLine(const std::string& text) { + texts.push_back(text); + + Refresh(); + } + + void AddText(const std::string& text) { + if (texts.empty()) { + texts.push_back(text); + } else { + texts.back() += text; + } + + Refresh(); + } + + void ClearText() { + texts.clear(); + + Refresh(); + } + + void SetPosition(int new_x, int new_y) { + x = new_x; + y = new_y; + } + + void SetColor(int new_color) { + color = new_color; + + Refresh(); + } + + void SetPictureId(int new_pic_id) { + pic_id = new_pic_id; + + Refresh(); + } + + void SetFixed(bool fixed) { + this->fixed = fixed; + } + + void Draw(Bitmap& dst) override { + if (!bitmap) { + return; + } + + const Sprite* sprite = Main_Data::game_pictures->GetPicture(pic_id).GetSprite(); + if (!sprite) { + return; + } + + if (fixed) { + dst.Blit(x - Game_Map::GetDisplayX() / 16, y - Game_Map::GetDisplayY() / 16, *bitmap, bitmap->GetRect(), sprite->GetOpacity()); + } else { + dst.Blit(x, y, *bitmap, bitmap->GetRect(), sprite->GetOpacity()); + } + }; + + void Update() { + const Sprite *sprite = Main_Data::game_pictures->GetPicture(pic_id).GetSprite(); + + if (sprite) { + if (z != sprite->GetZ()) { + z = sprite->GetZ() + 1; + SetZ(z); + } + } + } + + std::vector Save(const std::string& id) { + std::stringstream ss; + ss << x << "," << y << ","; + for (int i = 0; i < texts.size(); ++i) { + std::string t = texts[i]; + // Replace , with a sentinel 0x01 to not mess up the tokenizer + std::replace(t.begin(), t.end(), ',', '\1'); + ss << t; + if (i < texts.size() - 1) { + ss << "\n"; + } + + } + ss << "," << color << "," << id; + + ss << "," << 255 << "," << (fixed ? "1" : "0") << "," << pic_id; + + std::vector data; + + std::string s = ss.str(); + size_t slen = s.size(); + + data.resize(slen); + data.insert(data.end(), s.begin(), s.end()); + + return data; + } + + static int ParseParameter(bool& is_valid, std::u32string::iterator& text_index, std::u32string::iterator& end) { + ++text_index; + + if (text_index == end || + *text_index != '[') { + --text_index; + is_valid = false; + return 0; + } + + ++text_index; // Skip the [ + + bool null_at_start = false; + std::stringstream ss; + for (;;) { + if (text_index == end) { + break; + } else if (*text_index == '\n') { + --text_index; + break; + } + else if (*text_index == '0') { + // Truncate 0 at the start + if (!ss.str().empty()) { + ss << "0"; + } else { + null_at_start = true; + } + } + else if (*text_index >= '1' && + *text_index <= '9') { + ss << std::string(text_index, std::next(text_index)); + } else if (*text_index == ']') { + break; + } else { + // End of number + // Search for ] or line break + while (text_index != end) { + if (*text_index == '\n') { + --text_index; + break; + } else if (*text_index == ']') { + break; + } + ++text_index; + } + break; + } + ++text_index; + } + + if (ss.str().empty()) { + if (null_at_start) { + ss << "0"; + } else { + is_valid = false; + return 0; + } + } + + int num; + ss >> num; + is_valid = true; + return num; + } + + static std::string ParseCommandCode(bool& success, std::u32string::iterator& text_index, std::u32string::iterator& end) { + int parameter; + bool is_valid; + uint32_t cmd_char = *text_index; + success = true; + + switch (cmd_char) { + case 'n': + case 'N': + // Output Hero name + parameter = ParseParameter(is_valid, text_index, end); + if (is_valid) { + Game_Actor* actor = NULL; + if (parameter == 0) { + // Party hero + actor = Main_Data::game_party->GetActors()[0]; + } else { + actor = Game_Actors::GetActor(parameter); + } + if (actor != NULL) { + return actor->GetName(); + } + } + break; + case 'v': + case 'V': + // Show Variable value + parameter = ParseParameter(is_valid, text_index, end); + if (is_valid && Main_Data::game_variables->IsValid(parameter)) { + std::stringstream ss; + ss << Main_Data::game_variables->Get(parameter); + return ss.str(); + } else { + // Invalid Var is always 0 + return "0"; + } + case 'i': + parameter = ParseParameter(is_valid, text_index, end); + if (is_valid && parameter > 0 && parameter <= Data::items.size()) { + return Data::items[parameter - 1].name; + } + return ""; + case 'I': + parameter = ParseParameter(is_valid, text_index, end); + if (is_valid && parameter > 0 && parameter <= Data::items.size()) { + return Data::items[parameter - 1].description; + } + return ""; + case 't': + parameter = ParseParameter(is_valid, text_index, end); + if (is_valid && parameter > 0 && parameter <= Data::skills.size()) { + return Data::skills[parameter - 1].name; + } + return ""; + case 'T': + parameter = ParseParameter(is_valid, text_index, end); + if (is_valid && parameter > 0 && parameter <= Data::skills.size()) { + return Data::skills[parameter - 1].description; + } + return ""; + case 'x': + case 'X': + // Take text of ID referenced by X (if exists) TODO + { + + } + return ""; + default:; + // When this happens text_index was not on a \ during calling + } + success = false; + return ""; + } + + static std::string Substitute(const std::string& text) { + std::u32string::iterator text_index, end; + std::u32string utext; + + utext = Utils::DecodeUTF32(text); + text_index = utext.end(); + end = utext.end(); + + uint32_t escape_char = Utils::DecodeUTF32(Player::escape_symbol).front(); + + if (!utext.empty()) { + // Move on first valid char + --text_index; + + // Apply commands that insert text + while (std::distance(text_index, utext.begin()) <= -1) { + switch (tolower(*text_index--)) { + case 'n': + case 'v': + case 'i': + case 't': + case 'x': + { + if (*text_index != escape_char) { + continue; + } + ++text_index; + + auto start_code = text_index - 1; + bool success; + std::u32string command_result = Utils::DecodeUTF32(ParseCommandCode(success, text_index, end)); + if (!success) { + text_index = start_code - 2; + continue; + } + utext.replace(start_code, text_index + 1, command_result); + // Start from the beginning, the inserted text might add new commands + text_index = utext.end(); + + // Move on first valid char + --text_index; + + break; + } + default: + break; + } + } + } + + return Utils::EncodeUTF(utext); + } + +private: + void Refresh() { + if (texts.empty()) { + bitmap.reset(); + return; + } + + int width = 0; + int height = 0; + + const FontRef& font = Font::Default(); + + for (auto& t : texts) { + t = Substitute(t); + + Rect r = font->GetSize(t); + width = std::max(width, r.width); + height += r.height + 2; + } + + bitmap = Bitmap::Create(width, height, true); + + height = 0; + for (auto& t : texts) { + bitmap->TextDraw(0, height, color, t); + height += font->GetSize(t).height + 2; + } + } + + std::vector texts; + BitmapRef bitmap; + int x = 0; + int y = 0; + int z = 0; + int pic_id = 1; + int color = 0; + bool fixed = false; +}; + +DynRpgText* get_text(const std::string& id, bool silent = false) { + std::string new_id = DynRpgText::Substitute(id); + + auto it = graphics.find(new_id); + if (it == graphics.end()) { + if (!silent) { + Output::Warning("No text with ID %s found", new_id.c_str()); + } + return nullptr; + } + + return (*it).second.get(); +} + +static bool WriteText(const dyn_arg_list& args) { + DYNRPG_FUNCTION("write_text") + + DYNRPG_CHECK_ARG_LENGTH(4); + + DYNRPG_GET_STR_ARG(0, id) + DYNRPG_GET_INT_ARG(1, x) + DYNRPG_GET_INT_ARG(2, y) + DYNRPG_GET_STR_ARG(3, text) + + const std::string new_id = DynRpgText::Substitute(id); + graphics[new_id] = std::unique_ptr(new DynRpgText(1, x, y + 2, text)); + + if (args.size() > 4) { + DYNRPG_GET_STR_ARG(4, fixed) + graphics[new_id]->SetFixed(fixed == "fixed"); + } + + if (args.size() > 5) { + DYNRPG_GET_INT_ARG(5, color) + graphics[new_id]->SetColor(color); + } + + if (args.size() > 6) { + DYNRPG_GET_INT_ARG(6, pic_id) + graphics[new_id]->SetPictureId(pic_id); + } + + return true; +} + +static bool AppendLine(const dyn_arg_list& args) { + DYNRPG_FUNCTION("append_line") + + DYNRPG_CHECK_ARG_LENGTH(2); + + DYNRPG_GET_STR_ARG(0, id) + DYNRPG_GET_STR_ARG(1, text) + + DynRpgText* handle = get_text(id); + + if (!handle) { + return true; + } + + handle->AddLine(text); + + return true; +} + +static bool AppendText(const dyn_arg_list& args) { + DYNRPG_FUNCTION("append_text") + + DYNRPG_CHECK_ARG_LENGTH(2); + + DYNRPG_GET_STR_ARG(0, id) + DYNRPG_GET_STR_ARG(1, text) + + DynRpgText* handle = get_text(id); + + if (!handle) { + return true; + } + + handle->AddText(text); + + return true; +} + +static bool ChangeText(const dyn_arg_list& args) { + DYNRPG_FUNCTION("change_text") + + DYNRPG_CHECK_ARG_LENGTH(3); + + DYNRPG_GET_STR_ARG(0, id) + DYNRPG_GET_STR_ARG(1, text) + DYNRPG_GET_INT_ARG(2, color) + + DynRpgText* handle = get_text(id); + + if (!handle) { + return true; + } + + handle->ClearText(); + handle->SetColor(color); + handle->AddText(text); + + return true; +} + +static bool ChangePosition(const dyn_arg_list& args) { + DYNRPG_FUNCTION("change_position") + + DYNRPG_CHECK_ARG_LENGTH(3); + + DYNRPG_GET_STR_ARG(0, id) + DYNRPG_GET_INT_ARG(1, x) + DYNRPG_GET_INT_ARG(2, y) + + DynRpgText* handle = get_text(id); + + if (!handle) { + return true; + } + + // Offset is somehow wrong compared to RPG_RT + handle->SetPosition(x, y + 2); + + return true; +} + +static bool RemoveText(const dyn_arg_list& args) { + DYNRPG_FUNCTION("remove_text") + + DYNRPG_CHECK_ARG_LENGTH(2); + + DYNRPG_GET_STR_ARG(0, id) + DYNRPG_GET_STR_ARG(1, nothing) + + DynRpgText* handle = get_text(id, true); + + if (!handle) { + return true; + } + + handle->ClearText(); + + return true; +} + +static bool RemoveAll(const dyn_arg_list& args) { + DYNRPG_FUNCTION("remove_all") + + DYNRPG_CHECK_ARG_LENGTH(0); + + graphics.clear(); + + return true; +} + +std::string DynRpg::TextPlugin::GetIdentifier() { + return "DynTextPlugin"; +} + +void DynRpg::TextPlugin::RegisterFunctions() { + DynRpg::RegisterFunction("write_text", WriteText); + DynRpg::RegisterFunction("append_line", AppendLine); + DynRpg::RegisterFunction("append_text", AppendText); + DynRpg::RegisterFunction("change_text", ChangeText); + DynRpg::RegisterFunction("change_position", ChangePosition); + DynRpg::RegisterFunction("remove_text", RemoveText); + DynRpg::RegisterFunction("remove_all", RemoveAll); +} + +void DynRpg::TextPlugin::Update() { + for (auto& g : graphics) { + g.second->Update(); + } +} + +DynRpg::TextPlugin::~TextPlugin() { + graphics.clear(); +} + +void DynRpg::TextPlugin::Load(const std::vector& in_buffer) { + size_t counter = 0; + + std::string str((char*)in_buffer.data(), in_buffer.size()); + + std::vector tokens = Utils::Tokenize(str, [&] (char32_t c) { return c == ','; }); + + int x = 0; + int y = 0; + std::vector texts; + int color = 0; + std::string id = ""; + bool fixed = false; + int pic_id = 1; + + for (auto& t : tokens) { + switch (counter) { + case 0: + x = atoi(t.c_str()); + break; + case 1: + y = atoi(t.c_str()); + break; + case 2: + { + // Replace sentinel \1 with , + std::replace(t.begin(), t.end(), '\1', ','); + + texts = Utils::Tokenize(t, [&] (char32_t c) { return c == '\n'; }); + } + break; + case 3: + color = atoi(t.c_str()); + break; + case 4: + // transparency, but isn't that from the picture? + break; + case 5: + fixed = t == "1"; + break; + case 6: + pic_id = atoi(t.c_str()); + break; + case 7: + id = t.c_str(); + break; + default: + break; + } + + ++counter; + + if (counter == 8) { + counter = 0; + + graphics[id] = std::unique_ptr(new DynRpgText(pic_id, x, y + 2, texts)); + texts.clear(); + graphics[id]->SetColor(color); + graphics[id]->SetFixed(fixed); + } + } +} + +std::vector DynRpg::TextPlugin::Save() { + std::vector save_data; + std::stringstream ss; + + for (auto& g : graphics) { + std::vector res = g.second->Save(g.first); + save_data.reserve(save_data.size() + res.size() + 1); + save_data.insert(save_data.end(), res.begin(), res.end()); + + save_data.push_back(','); + } + save_data.pop_back(); + + return DynRpgPlugin::Save(); +} diff --git a/src/dynrpg_textplugin.h b/src/dynrpg_textplugin.h new file mode 100644 index 0000000000..de3fd3ca7b --- /dev/null +++ b/src/dynrpg_textplugin.h @@ -0,0 +1,36 @@ +/* + * 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 . + */ + +#ifndef _EASYRPG_DYNRPG_TEXTPLUGIN_H_ +#define _EASYRPG_DYNRPG_TEXTPLUGIN_H_ + +#include "dynrpg.h" + +namespace DynRpg { + class TextPlugin : public DynRpgPlugin { + public: + ~TextPlugin(); + + std::string GetIdentifier(); + void RegisterFunctions(); + void Update(); + void Load(const std::vector&); + std::vector Save(); + }; +} + +#endif diff --git a/src/scene_map.cpp b/src/scene_map.cpp index 55b9f394d3..6a921b5fe8 100644 --- a/src/scene_map.cpp +++ b/src/scene_map.cpp @@ -249,6 +249,8 @@ void Scene_Map::vUpdate() { UpdateInn(); return; } + + DynRpg::Update(); MapUpdateAsyncContext actx; UpdateStage1(actx); } From 3dcacc53f9320bf0ae7ff3a41c39e2eed93f0916 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Mon, 28 Sep 2020 18:23:09 +0200 Subject: [PATCH 2/2] DynRPG TextPlugin: Update to latest Player code --- src/dynrpg_textplugin.cpp | 430 +++++++++++++------------------------- src/dynrpg_textplugin.h | 14 +- src/game_message.cpp | 85 +++++++- src/game_message.h | 15 +- src/game_pictures.h | 4 +- src/pending_message.cpp | 78 ++++--- src/pending_message.h | 12 +- src/scene_map.cpp | 1 - src/sprite_picture.cpp | 4 +- 9 files changed, 299 insertions(+), 344 deletions(-) diff --git a/src/dynrpg_textplugin.cpp b/src/dynrpg_textplugin.cpp index 5279a12f48..8654cd164f 100644 --- a/src/dynrpg_textplugin.cpp +++ b/src/dynrpg_textplugin.cpp @@ -17,21 +17,21 @@ // Headers #include +#include +#include #include "dynrpg_textplugin.h" #include "baseui.h" #include "bitmap.h" #include "drawable.h" #include "drawable_mgr.h" -#include "game_actors.h" #include "game_map.h" -#include "game_party.h" +#include "game_message.h" #include "game_pictures.h" -#include "game_screen.h" #include "game_variables.h" -#include "graphics.h" #include "main_data.h" -#include "player.h" +#include "pending_message.h" +#include "text.h" class DynRpgText; @@ -91,7 +91,16 @@ class DynRpgText : public Drawable { void SetPictureId(int new_pic_id) { pic_id = new_pic_id; - Refresh(); + const Game_Pictures::Picture* pic = Main_Data::game_pictures->GetPicturePtr(pic_id); + if (!pic) { + return; + } + const Sprite_Picture* sprite = pic->sprite.get(); + if (!sprite) { + return; + } + + SetZ(sprite->GetZ() + 1); } void SetFixed(bool fixed) { @@ -103,26 +112,26 @@ class DynRpgText : public Drawable { return; } - const Sprite* sprite = Main_Data::game_pictures->GetPicture(pic_id).GetSprite(); + const Game_Pictures::Picture* pic = Main_Data::game_pictures->GetPicturePtr(pic_id); + if (!pic) { + return; + } + const Sprite_Picture* sprite = pic->sprite.get(); if (!sprite) { return; } + // For unknown reasons the official plugin has an y-offset of 2 if (fixed) { - dst.Blit(x - Game_Map::GetDisplayX() / 16, y - Game_Map::GetDisplayY() / 16, *bitmap, bitmap->GetRect(), sprite->GetOpacity()); + dst.Blit(x - Game_Map::GetDisplayX() / 16, y - Game_Map::GetDisplayY() / 16 + 2, *bitmap, bitmap->GetRect(), sprite->GetOpacity()); } else { - dst.Blit(x, y, *bitmap, bitmap->GetRect(), sprite->GetOpacity()); + dst.Blit(x, y + 2, *bitmap, bitmap->GetRect(), sprite->GetOpacity()); } }; void Update() { - const Sprite *sprite = Main_Data::game_pictures->GetPicture(pic_id).GetSprite(); - - if (sprite) { - if (z != sprite->GetZ()) { - z = sprite->GetZ() + 1; - SetZ(z); - } + if (GetZ() == 0) { + SetPictureId(pic_id); } } @@ -154,197 +163,58 @@ class DynRpgText : public Drawable { return data; } - static int ParseParameter(bool& is_valid, std::u32string::iterator& text_index, std::u32string::iterator& end) { - ++text_index; - - if (text_index == end || - *text_index != '[') { - --text_index; - is_valid = false; - return 0; - } - - ++text_index; // Skip the [ - - bool null_at_start = false; - std::stringstream ss; - for (;;) { - if (text_index == end) { - break; - } else if (*text_index == '\n') { - --text_index; - break; - } - else if (*text_index == '0') { - // Truncate 0 at the start - if (!ss.str().empty()) { - ss << "0"; - } else { - null_at_start = true; - } - } - else if (*text_index >= '1' && - *text_index <= '9') { - ss << std::string(text_index, std::next(text_index)); - } else if (*text_index == ']') { - break; - } else { - // End of number - // Search for ] or line break - while (text_index != end) { - if (*text_index == '\n') { - --text_index; - break; - } else if (*text_index == ']') { - break; - } - ++text_index; - } - break; - } - ++text_index; - } + static DynRpgText* GetTextHandle(const std::string& id, bool silent = false) { + PendingMessage pm; + pm.PushLine(id, DynRpgText::CommandCodeInserter); + std::string new_id = pm.GetLines().front(); - if (ss.str().empty()) { - if (null_at_start) { - ss << "0"; - } else { - is_valid = false; - return 0; + auto it = graphics.find(new_id); + if (it == graphics.end()) { + if (!silent) { + Output::Warning("No text with ID %s found", new_id.c_str()); } + return nullptr; } - int num; - ss >> num; - is_valid = true; - return num; + return (*it).second.get(); } - static std::string ParseCommandCode(bool& success, std::u32string::iterator& text_index, std::u32string::iterator& end) { - int parameter; - bool is_valid; - uint32_t cmd_char = *text_index; - success = true; - - switch (cmd_char) { - case 'n': - case 'N': - // Output Hero name - parameter = ParseParameter(is_valid, text_index, end); - if (is_valid) { - Game_Actor* actor = NULL; - if (parameter == 0) { - // Party hero - actor = Main_Data::game_party->GetActors()[0]; - } else { - actor = Game_Actors::GetActor(parameter); - } - if (actor != NULL) { - return actor->GetName(); - } - } - break; - case 'v': - case 'V': - // Show Variable value - parameter = ParseParameter(is_valid, text_index, end); - if (is_valid && Main_Data::game_variables->IsValid(parameter)) { - std::stringstream ss; - ss << Main_Data::game_variables->Get(parameter); - return ss.str(); - } else { - // Invalid Var is always 0 - return "0"; - } - case 'i': - parameter = ParseParameter(is_valid, text_index, end); - if (is_valid && parameter > 0 && parameter <= Data::items.size()) { - return Data::items[parameter - 1].name; - } + static std::optional CommandCodeInserter(char ch, const char** iter, const char* end, uint32_t escape_char) { + if (ch == 'I' || ch == 'i') { + auto parse_ret = Game_Message::ParseParam('I', 'i', *iter, end, escape_char, true); + *iter = parse_ret.next; + int value = parse_ret.value; + const auto* item = lcf::ReaderUtil::GetElement(lcf::Data::items, value); + if (!item) { + Output::Warning("Invalid Item Id {} in DynTextPlugin text", value); return ""; - case 'I': - parameter = ParseParameter(is_valid, text_index, end); - if (is_valid && parameter > 0 && parameter <= Data::items.size()) { - return Data::items[parameter - 1].description; - } - return ""; - case 't': - parameter = ParseParameter(is_valid, text_index, end); - if (is_valid && parameter > 0 && parameter <= Data::skills.size()) { - return Data::skills[parameter - 1].name; - } - return ""; - case 'T': - parameter = ParseParameter(is_valid, text_index, end); - if (is_valid && parameter > 0 && parameter <= Data::skills.size()) { - return Data::skills[parameter - 1].description; - } + } else{ + return ToString(ch == 'i' ? item->name : item->description); + } + } else if (ch == 'T' || ch == 't') { + auto parse_ret = Game_Message::ParseParam('T', 't', *iter, end, escape_char, true); + *iter = parse_ret.next; + int value = parse_ret.value; + const auto* skill = lcf::ReaderUtil::GetElement(lcf::Data::skills, value); + if (!skill) { + Output::Warning("Invalid Item Id {} in DynTextPlugin text", value); return ""; - case 'x': - case 'X': - // Take text of ID referenced by X (if exists) TODO - { - - } + } else{ + return ToString(ch == 't' ? skill->name : skill->description); + } + } else if (ch == 'x' || ch == 'X') { + auto parse_ret = Game_Message::ParseStringParam('X', 'x', *iter, end, escape_char, true); + *iter = parse_ret.next; + std::string value = parse_ret.value; + auto* handle = GetTextHandle(value); + if (handle) { + return handle->texts[0]; + } else { return ""; - default:; - // When this happens text_index was not on a \ during calling - } - success = false; - return ""; - } - - static std::string Substitute(const std::string& text) { - std::u32string::iterator text_index, end; - std::u32string utext; - - utext = Utils::DecodeUTF32(text); - text_index = utext.end(); - end = utext.end(); - - uint32_t escape_char = Utils::DecodeUTF32(Player::escape_symbol).front(); - - if (!utext.empty()) { - // Move on first valid char - --text_index; - - // Apply commands that insert text - while (std::distance(text_index, utext.begin()) <= -1) { - switch (tolower(*text_index--)) { - case 'n': - case 'v': - case 'i': - case 't': - case 'x': - { - if (*text_index != escape_char) { - continue; - } - ++text_index; - - auto start_code = text_index - 1; - bool success; - std::u32string command_result = Utils::DecodeUTF32(ParseCommandCode(success, text_index, end)); - if (!success) { - text_index = start_code - 2; - continue; - } - utext.replace(start_code, text_index + 1, command_result); - // Start from the beginning, the inserted text might add new commands - text_index = utext.end(); - - // Move on first valid char - --text_index; - - break; - } - default: - break; - } } } - return Utils::EncodeUTF(utext); + return PendingMessage::DefaultCommandInserter(ch, iter, end, escape_char); } private: @@ -360,9 +230,11 @@ class DynRpgText : public Drawable { const FontRef& font = Font::Default(); for (auto& t : texts) { - t = Substitute(t); + PendingMessage pm; + pm.PushLine(t, CommandCodeInserter); + t = pm.GetLines().front(); - Rect r = font->GetSize(t); + Rect r = Text::GetSize(*font, t); width = std::max(width, r.width); height += r.height + 2; } @@ -372,179 +244,161 @@ class DynRpgText : public Drawable { height = 0; for (auto& t : texts) { bitmap->TextDraw(0, height, color, t); - height += font->GetSize(t).height + 2; + height += Text::GetSize(*font, t).height + 2; } + + SetPictureId(pic_id); } std::vector texts; BitmapRef bitmap; int x = 0; int y = 0; - int z = 0; int pic_id = 1; int color = 0; bool fixed = false; }; -DynRpgText* get_text(const std::string& id, bool silent = false) { - std::string new_id = DynRpgText::Substitute(id); +static bool WriteText(dyn_arg_list args) { + auto func = "write_text"; + bool okay; + std::string id, text; + int x, y; - auto it = graphics.find(new_id); - if (it == graphics.end()) { - if (!silent) { - Output::Warning("No text with ID %s found", new_id.c_str()); - } - return nullptr; - } - - return (*it).second.get(); -} - -static bool WriteText(const dyn_arg_list& args) { - DYNRPG_FUNCTION("write_text") - - DYNRPG_CHECK_ARG_LENGTH(4); - - DYNRPG_GET_STR_ARG(0, id) - DYNRPG_GET_INT_ARG(1, x) - DYNRPG_GET_INT_ARG(2, y) - DYNRPG_GET_STR_ARG(3, text) + std::tie(id, x, y, text) = DynRpg::ParseArgs(func, args, &okay); + if (!okay) + return true; - const std::string new_id = DynRpgText::Substitute(id); - graphics[new_id] = std::unique_ptr(new DynRpgText(1, x, y + 2, text)); + PendingMessage pm; + pm.PushLine(id, DynRpgText::CommandCodeInserter); + std::string new_id = pm.GetLines().front(); + graphics[new_id] = std::make_unique(1, x, y, text); if (args.size() > 4) { - DYNRPG_GET_STR_ARG(4, fixed) + std::string fixed = std::get<0>(DynRpg::ParseArgs(func, args.subspan(4), &okay)); + if (!okay) + return true; graphics[new_id]->SetFixed(fixed == "fixed"); } if (args.size() > 5) { - DYNRPG_GET_INT_ARG(5, color) + int color = std::get<0>(DynRpg::ParseArgs(func, args.subspan(5), &okay)); + if (!okay) + return true; graphics[new_id]->SetColor(color); } if (args.size() > 6) { - DYNRPG_GET_INT_ARG(6, pic_id) + int pic_id = std::get<0>(DynRpg::ParseArgs(func, args.subspan(6), &okay)); + if (!okay) + return true; graphics[new_id]->SetPictureId(pic_id); } return true; } -static bool AppendLine(const dyn_arg_list& args) { - DYNRPG_FUNCTION("append_line") +static bool AppendLine(dyn_arg_list args) { + auto func = "append_line"; + bool okay; + std::string id, text; - DYNRPG_CHECK_ARG_LENGTH(2); - - DYNRPG_GET_STR_ARG(0, id) - DYNRPG_GET_STR_ARG(1, text) - - DynRpgText* handle = get_text(id); + std::tie(id, text) = DynRpg::ParseArgs(func, args, &okay); + if (!okay) + return true; + DynRpgText* handle = DynRpgText::GetTextHandle(id); if (!handle) { return true; } handle->AddLine(text); - return true; } -static bool AppendText(const dyn_arg_list& args) { - DYNRPG_FUNCTION("append_text") - - DYNRPG_CHECK_ARG_LENGTH(2); +static bool AppendText(dyn_arg_list args) { + auto func = "append_line"; + bool okay; + std::string id, text; - DYNRPG_GET_STR_ARG(0, id) - DYNRPG_GET_STR_ARG(1, text) - - DynRpgText* handle = get_text(id); + std::tie(id, text) = DynRpg::ParseArgs(func, args, &okay); + if (!okay) + return true; + DynRpgText* handle = DynRpgText::GetTextHandle(id); if (!handle) { return true; } handle->AddText(text); - return true; } -static bool ChangeText(const dyn_arg_list& args) { - DYNRPG_FUNCTION("change_text") - - DYNRPG_CHECK_ARG_LENGTH(3); +static bool ChangeText(dyn_arg_list args) { + auto func = "change_text"; + bool okay; + std::string id, text, color; - DYNRPG_GET_STR_ARG(0, id) - DYNRPG_GET_STR_ARG(1, text) - DYNRPG_GET_INT_ARG(2, color) - - DynRpgText* handle = get_text(id); + // Color can be a string (usually "end") or a number + std::tie(id, text, color) = DynRpg::ParseArgs(func, args, &okay); + if (!okay) + return true; + DynRpgText* handle = DynRpgText::GetTextHandle(id); if (!handle) { return true; } handle->ClearText(); - handle->SetColor(color); + if (color != "end") { + handle->SetColor(atoi(color.c_str())); + } handle->AddText(text); - return true; } -static bool ChangePosition(const dyn_arg_list& args) { - DYNRPG_FUNCTION("change_position") - - DYNRPG_CHECK_ARG_LENGTH(3); +static bool ChangePosition(dyn_arg_list args) { + auto func = "change_position"; + bool okay; + std::string id; + int x, y; - DYNRPG_GET_STR_ARG(0, id) - DYNRPG_GET_INT_ARG(1, x) - DYNRPG_GET_INT_ARG(2, y) - - DynRpgText* handle = get_text(id); + std::tie(id, x, y) = DynRpg::ParseArgs(func, args, &okay); + if (!okay) + return true; + DynRpgText* handle = DynRpgText::GetTextHandle(id); if (!handle) { return true; } - // Offset is somehow wrong compared to RPG_RT - handle->SetPosition(x, y + 2); - + handle->SetPosition(x, y); return true; } -static bool RemoveText(const dyn_arg_list& args) { - DYNRPG_FUNCTION("remove_text") - - DYNRPG_CHECK_ARG_LENGTH(2); +static bool RemoveText(dyn_arg_list args) { + auto func = "remove_text"; + bool okay; + std::string id; - DYNRPG_GET_STR_ARG(0, id) - DYNRPG_GET_STR_ARG(1, nothing) - - DynRpgText* handle = get_text(id, true); + std::tie(id) = DynRpg::ParseArgs(func, args, &okay); + if (!okay) + return true; + DynRpgText* handle = DynRpgText::GetTextHandle(id, true); if (!handle) { return true; } handle->ClearText(); - return true; } -static bool RemoveAll(const dyn_arg_list& args) { - DYNRPG_FUNCTION("remove_all") - - DYNRPG_CHECK_ARG_LENGTH(0); - +static bool RemoveAll(dyn_arg_list) { graphics.clear(); - return true; } -std::string DynRpg::TextPlugin::GetIdentifier() { - return "DynTextPlugin"; -} - void DynRpg::TextPlugin::RegisterFunctions() { DynRpg::RegisterFunction("write_text", WriteText); DynRpg::RegisterFunction("append_line", AppendLine); @@ -600,7 +454,7 @@ void DynRpg::TextPlugin::Load(const std::vector& in_buffer) { color = atoi(t.c_str()); break; case 4: - // transparency, but isn't that from the picture? + // ignore transparency, the picture defines this break; case 5: fixed = t == "1"; @@ -620,7 +474,7 @@ void DynRpg::TextPlugin::Load(const std::vector& in_buffer) { if (counter == 8) { counter = 0; - graphics[id] = std::unique_ptr(new DynRpgText(pic_id, x, y + 2, texts)); + graphics[id] = std::make_unique(pic_id, x, y, texts); texts.clear(); graphics[id]->SetColor(color); graphics[id]->SetFixed(fixed); diff --git a/src/dynrpg_textplugin.h b/src/dynrpg_textplugin.h index de3fd3ca7b..14c2767218 100644 --- a/src/dynrpg_textplugin.h +++ b/src/dynrpg_textplugin.h @@ -15,21 +15,21 @@ * along with EasyRPG Player. If not, see . */ -#ifndef _EASYRPG_DYNRPG_TEXTPLUGIN_H_ -#define _EASYRPG_DYNRPG_TEXTPLUGIN_H_ +#ifndef EP_DYNRPG_TEXTPLUGIN_H +#define EP_DYNRPG_TEXTPLUGIN_H #include "dynrpg.h" namespace DynRpg { class TextPlugin : public DynRpgPlugin { public: + TextPlugin() : DynRpgPlugin("DynTextPlugin") {} ~TextPlugin(); - std::string GetIdentifier(); - void RegisterFunctions(); - void Update(); - void Load(const std::vector&); - std::vector Save(); + void RegisterFunctions() override; + void Update() override; + void Load(const std::vector&) override; + std::vector Save() override; }; } diff --git a/src/game_message.cpp b/src/game_message.cpp index f94c3fc4a5..d2a0245236 100644 --- a/src/game_message.cpp +++ b/src/game_message.cpp @@ -154,10 +154,9 @@ bool Game_Message::CanShowMessage(bool foreground) { return window ? window->GetAllowNextMessage(foreground) : false; } - -static Game_Message::ParseParamResult ParseParamImpl( - const char upper, - const char lower, +Game_Message::ParseParamResult Game_Message::ParseParam( + char upper, + char lower, const char* iter, const char* end, uint32_t escape_char, @@ -216,7 +215,7 @@ static Game_Message::ParseParamResult ParseParamImpl( if (iter != end && (*iter == 'V' || *iter == 'v')) { ++iter; - auto ret = ParseParamImpl('V', 'v', iter, end, escape_char, true, max_recursion - 1); + auto ret = ParseParam('V', 'v', iter, end, escape_char, true, max_recursion - 1); iter = ret.next; int var_val = Main_Data::game_variables->Get(ret.value); @@ -254,20 +253,84 @@ static Game_Message::ParseParamResult ParseParamImpl( return { iter, value }; } +Game_Message::ParseParamStringResult Game_Message::ParseStringParam( + char upper, + char lower, + const char* iter, + const char* end, + uint32_t escape_char, + bool skip_prefix, + int max_recursion) +{ + if (!skip_prefix) { + const auto begin = iter; + if (iter == end) { + return { begin, "" }; + } + auto ret = Utils::UTF8Next(iter, end); + // Invalid commands + if (ret.ch != escape_char) { + return { begin, "" }; + } + iter = ret.next; + if (iter == end || (*iter != upper && *iter != lower)) { + return { begin, "" }; + } + ++iter; + } + + // If no bracket, return empty string + if (iter == end || *iter != '[') { + return { iter, "" }; + } + + std::string value; + ++iter; + + while (iter != end && *iter != ']') { + // Fast inline isdigit() + value += *iter; + + if (max_recursion > 0) { + auto ret = Utils::UTF8Next(iter, end); + auto ch = ret.ch; + iter = ret.next; + + // Recursive variable case. + if (ch == escape_char) { + if (iter != end && (*iter == 'V' || *iter == 'v')) { + ++iter; + + auto ret = ParseParam('V', 'v', iter, end, escape_char, true, max_recursion - 1); + iter = ret.next; + int var_val = Main_Data::game_variables->Get(ret.value); + + value += std::to_string(var_val); + continue; + } + } + } + } + + if (iter != end) { + ++iter; + } + + return { iter, value }; +} + Game_Message::ParseParamResult Game_Message::ParseVariable(const char* iter, const char* end, uint32_t escape_char, bool skip_prefix, int max_recursion) { - return ParseParamImpl('V', 'v', iter, end, escape_char, skip_prefix, max_recursion - 1); + return ParseParam('V', 'v', iter, end, escape_char, skip_prefix, max_recursion - 1); } Game_Message::ParseParamResult Game_Message::ParseColor(const char* iter, const char* end, uint32_t escape_char, bool skip_prefix, int max_recursion) { - return ParseParamImpl('C', 'c', iter, end, escape_char, skip_prefix, max_recursion); + return ParseParam('C', 'c', iter, end, escape_char, skip_prefix, max_recursion); } Game_Message::ParseParamResult Game_Message::ParseSpeed(const char* iter, const char* end, uint32_t escape_char, bool skip_prefix, int max_recursion) { - return ParseParamImpl('S', 's', iter, end, escape_char, skip_prefix, max_recursion); + return ParseParam('S', 's', iter, end, escape_char, skip_prefix, max_recursion); } Game_Message::ParseParamResult Game_Message::ParseActor(const char* iter, const char* end, uint32_t escape_char, bool skip_prefix, int max_recursion) { - return ParseParamImpl('N', 'n', iter, end, escape_char, skip_prefix, max_recursion); + return ParseParam('N', 'n', iter, end, escape_char, skip_prefix, max_recursion); } - - diff --git a/src/game_message.h b/src/game_message.h index 933e6d2bcb..8c7ad39a1d 100644 --- a/src/game_message.h +++ b/src/game_message.h @@ -68,7 +68,7 @@ namespace Game_Message { * given font (except in cases when breaking by spaces * can't produce a short line), and calls the callback * for each resulting line. - * + * * Font::Default() will be used to determine the word breaking. * The caller is responsible for ensuring that Font::Default() * either does not change between calling this function and @@ -111,6 +111,14 @@ namespace Game_Message { int value = 0; }; + /** Struct returned by parameter parsing methods */ + struct ParseParamStringResult { + /** iterator to the next character after parsed content */ + const char* next = nullptr; + /** value that was parsed */ + std::string value; + }; + /** Parse a \v[] variable string * * @param iter start of utf8 string @@ -158,7 +166,10 @@ namespace Game_Message { * @return \refer ParseParamResult */ ParseParamResult ParseActor(const char* iter, const char* end, uint32_t escape_char, bool skip_prefix = false, int max_recursion = default_max_recursion); -} + Game_Message::ParseParamResult ParseParam(char upper, char lower, const char* iter, const char* end, uint32_t escape_char, bool skip_prefix = false, int max_recursion = default_max_recursion); + // same as ParseParam but the parameter is of structure \x[some_word] instead of \x[1] + Game_Message::ParseParamStringResult ParseStringParam(char upper, char lower, const char* iter, const char* end, uint32_t escape_char, bool skip_prefix = false, int max_recursion = default_max_recursion); +} #endif diff --git a/src/game_pictures.h b/src/game_pictures.h index ad147e97b6..cb4ab567d5 100644 --- a/src/game_pictures.h +++ b/src/game_pictures.h @@ -93,8 +93,8 @@ class Game_Pictures { void OnMapScrolled(int dx, int dy); struct Picture { - Picture(int id) { data.ID = id; } - Picture(lcf::rpg::SavePicture data); + explicit Picture(int id) { data.ID = id; } + explicit Picture(lcf::rpg::SavePicture data); std::unique_ptr sprite; lcf::rpg::SavePicture data; diff --git a/src/pending_message.cpp b/src/pending_message.cpp index ffd46497e9..bd3dc38971 100644 --- a/src/pending_message.cpp +++ b/src/pending_message.cpp @@ -1,3 +1,20 @@ +/* + * 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 . + */ + #include "pending_message.h" #include "game_variables.h" #include "game_actors.h" @@ -17,26 +34,26 @@ static void RemoveControlChars(std::string& s) { s.erase(iter, s.end()); } -int PendingMessage::PushLineImpl(std::string msg) { +int PendingMessage::PushLineImpl(std::string msg, CommandInserter cmd_fn) { RemoveControlChars(msg); - msg = ApplyTextInsertingCommands(std::move(msg), Player::escape_char); + msg = ApplyTextInsertingCommands(std::move(msg), Player::escape_char, cmd_fn); texts.push_back(std::move(msg)); return texts.size(); } -int PendingMessage::PushLine(std::string msg) { +int PendingMessage::PushLine(std::string msg, CommandInserter cmd_fn) { assert(!HasChoices()); assert(!HasNumberInput()); - return PushLineImpl(std::move(msg)); + return PushLineImpl(std::move(msg), cmd_fn); } -int PendingMessage::PushChoice(std::string msg, bool enabled) { +int PendingMessage::PushChoice(std::string msg, bool enabled, CommandInserter cmd_fn) { assert(!HasNumberInput()); if (!HasChoices()) { choice_start = NumLines(); } choice_enabled[GetNumChoices()] = enabled; - return PushLineImpl(std::move(msg)); + return PushLineImpl(std::move(msg), cmd_fn); } int PendingMessage::PushNumInput(int variable_id, int num_digits) { @@ -69,7 +86,7 @@ void PendingMessage::SetChoiceResetColors(bool value) { choice_reset_color = value; } -std::string PendingMessage::ApplyTextInsertingCommands(std::string input, uint32_t escape_char) { +std::string PendingMessage::ApplyTextInsertingCommands(std::string input, uint32_t escape_char, CommandInserter cmd_fn) { if (input.empty()) { return input; } @@ -103,27 +120,9 @@ std::string PendingMessage::ApplyTextInsertingCommands(std::string input, uint32 const auto ch = *iter; ++iter; - if (ch == 'N' || ch == 'n') { - auto parse_ret = Game_Message::ParseActor(iter, end, escape_char, true); - iter = parse_ret.next; - int value = parse_ret.value; - - const auto* actor = Main_Data::game_actors->GetActor(value); - if (!actor) { - Output::Warning("Invalid Actor Id {} in message text", value); - } else{ - output.append(ToString(actor->GetName())); - } - - start_copy = iter; - } else if (ch == 'V' || ch == 'v') { - auto parse_ret = Game_Message::ParseVariable(iter, end, escape_char, true); - iter = parse_ret.next; - int value = parse_ret.value; - - int variable_value = Main_Data::game_variables->Get(value); - output.append(std::to_string(variable_value)); - + auto fn_res = cmd_fn(ch, &iter, end, escape_char); + if (fn_res) { + output.append(*fn_res); start_copy = iter; } } @@ -138,4 +137,27 @@ std::string PendingMessage::ApplyTextInsertingCommands(std::string input, uint32 return output; } +std::optional PendingMessage::DefaultCommandInserter(char ch, const char** iter, const char* end, uint32_t escape_char) { + if (ch == 'N' || ch == 'n') { + auto parse_ret = Game_Message::ParseActor(*iter, end, escape_char, true); + *iter = parse_ret.next; + int value = parse_ret.value; + + const auto* actor = Main_Data::game_actors->GetActor(value); + if (!actor) { + Output::Warning("Invalid Actor Id {} in message text", value); + return ""; + } else { + return ToString(actor->GetName()); + } + } else if (ch == 'V' || ch == 'v') { + auto parse_ret = Game_Message::ParseVariable(*iter, end, escape_char, true); + *iter = parse_ret.next; + int value = parse_ret.value; + + int variable_value = Main_Data::game_variables->Get(value); + return std::to_string(variable_value); + } + return std::nullopt; +}; diff --git a/src/pending_message.h b/src/pending_message.h index ef391d866f..b34b3681ad 100644 --- a/src/pending_message.h +++ b/src/pending_message.h @@ -22,14 +22,17 @@ #include #include #include +#include #include "async_op.h" class PendingMessage { public: using ChoiceContinuation = std::function; + using CommandInserter = std::function(char,const char**,const char*,uint32_t)>; + static std::optional DefaultCommandInserter(char ch, const char** iter, const char* end, uint32_t escape_char); - int PushLine(std::string msg); - int PushChoice(std::string msg, bool enabled = true); + int PushLine(std::string msg, CommandInserter cmd_fn = DefaultCommandInserter); + int PushChoice(std::string msg, bool enabled = true, CommandInserter cmd_fn = DefaultCommandInserter); int PushNumInput(int variable_id, int num_digits); void PushPageEnd(); @@ -64,10 +67,11 @@ class PendingMessage { void SetIsEventMessage(bool value) { is_event_message = value; } bool IsEventMessage() const { return is_event_message; } + private: - int PushLineImpl(std::string msg); + int PushLineImpl(std::string msg, CommandInserter cmd_fn); - std::string ApplyTextInsertingCommands(std::string input, uint32_t escape_char); + std::string ApplyTextInsertingCommands(std::string input, uint32_t escape_char, CommandInserter cmd_fn); private: ChoiceContinuation choice_continuation; diff --git a/src/scene_map.cpp b/src/scene_map.cpp index 6a921b5fe8..6d05f4383c 100644 --- a/src/scene_map.cpp +++ b/src/scene_map.cpp @@ -250,7 +250,6 @@ void Scene_Map::vUpdate() { return; } - DynRpg::Update(); MapUpdateAsyncContext actx; UpdateStage1(actx); } diff --git a/src/sprite_picture.cpp b/src/sprite_picture.cpp index 0f8b102c8c..47de160f81 100644 --- a/src/sprite_picture.cpp +++ b/src/sprite_picture.cpp @@ -53,7 +53,9 @@ void Sprite_Picture::OnPictureShow() { priority = Drawable::GetPriorityForMapLayer(pic.data.map_layer); } if (priority > 0) { - SetZ(priority + pic_id); + // Small offset (10) to ensure there is space for graphics that are + // drawn at the image position (e.g. DynRPG Text Plugin) + SetZ(priority + pic_id * 10); } } }