diff --git a/.github/labeler.yml b/.github/labeler.yml
index 972b28d350..2e50dbdd35 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -9,7 +9,6 @@ Building:
- .github/**
- CMakeLists.txt
- builds/**
- - '!builds/android/app/**'
- Makefile.am
- configure.ac
@@ -37,6 +36,22 @@ Audio:
- changed-files:
- any-glob-to-any-file: [ src/**/*audio* ]
+Battle:
+- changed-files:
+ - any-glob-to-any-file:
+ - src/**/scene_battle*
+ - src/**/window_battle*
+ - src/**/game_battle.*
+ - src/**/game_battlealgorithm.*
+
+Bitmaps:
+- changed-files:
+ - any-glob-to-any-file:
+ - src/**/bitmap.*
+ - src/**/bitmap_*
+ - src/**/sprite.*
+ - src/**/sprite_*
+
FileFinder:
- changed-files:
- any-glob-to-any-file: [ src/**/filefinder*, src/**/filesystem* ]
@@ -51,10 +66,26 @@ Fonts:
- src/**/*font*
- src/generated/bitmapfont_*
+Input:
+- changed-files:
+ - any-glob-to-any-file: [ src/**/*input* ]
+
+Messages:
+- changed-files:
+ - any-glob-to-any-file: [ src/**/*message* ]
+
MIDI:
- changed-files:
- any-glob-to-any-file: [ src/**/*midi* ]
+Settings:
+- changed-files:
+ - any-glob-to-any-file: [ src/**/*config* ]
+
+Translation:
+- changed-files:
+ - any-glob-to-any-file: [ src/**/translation* ]
+
# platforms
3DS:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 48b917b7a4..f63d4ffa55 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -73,6 +73,7 @@ add_library(${PROJECT_NAME} OBJECT
src/bitmap_hslrgb.h
src/cache.cpp
src/cache.h
+ src/callback.h
src/cmdline_parser.cpp
src/cmdline_parser.h
src/color.h
diff --git a/Makefile.am b/Makefile.am
index 7ee65f6792..b61c8d6fb8 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -51,6 +51,7 @@ libeasyrpg_player_a_SOURCES = \
src/bitmap_hslrgb.h \
src/cache.cpp \
src/cache.h \
+ src/callback.h \
src/cmdline_parser.cpp \
src/cmdline_parser.h \
src/color.h \
diff --git a/src/callback.h b/src/callback.h
new file mode 100644
index 0000000000..1840fd38d1
--- /dev/null
+++ b/src/callback.h
@@ -0,0 +1,157 @@
+/*
+ * 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 EP_CALLBACK_H
+#define EP_CALLBACK_H
+
+#include
+#include
+#include
+#include
+#include
+
+#if 0
+Where to place a callback:
+When it is related to something global put the callback where it is suitable.
+When it is related to an instance of a class/struct add it as a public variable.
+
+Adding a new callback:
+
+Assuming you want to add a notification when a variable is changed.
+
+In the header of Game_Variables (game_variables.h) add a public field
+
+```cpp
+Callback OnVariableChanged;
+```
+
+The /* names */ are for increated readability.
+Functions that take two integer arguments can be bound to this callback.
+
+Signal the callback at a suitable location.
+Here this would be in SetOp in game_variables.cpp:
+
+```cpp
+OnVariableChanged.Call(variable_id, v);
+```
+
+This invocation will trigger all functions attached with Bind.
+
+Attaching to the callback:
+
+```cpp
+// Attaching with a scope guard (the callback will automatically Unbind when the
+// guard is not in scope anymore).
+// This prevents stale handlers that crash.
+auto guard = Main_Data::game_variables->OnVariableChanged.Bind([](int var_id, int val) {
+ Output::Warning("{} = {}", var_id, val);
+});
+
+// In case of a class the scope guard should be a field.
+// It is automatically unbound when the instance is destroyed.
+class MyClass {
+public:
+ MyClass() {
+ guard = Main_Data::game_variables->OnVariableChanged.Bind([](int var_id, int val) {
+ Output::Warning("{} = {}", var_id, val);
+ });
+
+ // Alternative: Binding a method instead of a lambda
+ guard = Main_Data::game_variables->OnVariableChanged.Bind(&MyClass::VariableChanged, this);
+ }
+
+ void VariableChanged(int var_id, int val) {
+ Output::Warning("{} = {}", var_id, val);
+ }
+
+private:
+ BindingScopeGuard guard;
+}
+```
+
+Not recommended:
+
+If you want to (for whatever reason) manually manage the lifetime of the binding
+use BindUnmanaged.
+
+This works the same as described above but the function returns an integer.
+
+And you must that integer to Unbind(id) to remove the handler.
+#endif
+
+using BindingScopeGuard = lcf::ScopeGuard>;
+
+template*typename Ret, */typename ...Args>
+class Callback {
+public:
+ using Ret = void;
+ using Fn = std::function;
+
+ template
+ int BindUnmanaged(Ret (T::*func)(Args...), T* that, Args... args) {
+ Fn f = std::bind(std::mem_fn(func), that, std::placeholders::_1, args...);
+ return BindUnmanaged(f);
+ }
+
+ int BindUnmanaged(Fn func) {
+ listeners.push_back({next_id, func});
+ return next_id++;
+ }
+
+ template
+ BindingScopeGuard Bind(Ret (T::*func)(Args...), T* that, Args... args) {
+ int id = BindUnmanaged(func, that, args...);
+
+ return lcf::ScopeGuard>([this, id=id]() {
+ Unbind(id);
+ });
+ }
+
+ BindingScopeGuard Bind(Fn func) {
+ int id = BindUnmanaged(func);
+
+ return lcf::ScopeGuard>([this, id]() {
+ Unbind(id);
+ });
+ }
+
+ void Unbind(int id) {
+ auto it = std::find_if(listeners.begin(), listeners.end(), [id](auto& listener){
+ return listener.first == id;
+ });
+ assert(it != listeners.end());
+
+ listeners.erase(it);
+ }
+
+ void Call(Args... args) {
+ for (auto& listener: listeners) {
+ listener.second(std::forward(args)...);
+ }
+ }
+
+ void Clear() {
+ listeners.clear();
+ }
+
+private:
+ std::vector> listeners;
+
+ int next_id = 1;
+};
+
+#endif
diff --git a/src/player.cpp b/src/player.cpp
index 494f09a052..eedb575968 100644
--- a/src/player.cpp
+++ b/src/player.cpp
@@ -16,7 +16,6 @@
*/
// Headers
-
#include
#include
#include
@@ -81,6 +80,7 @@
#include "instrumentation.h"
#include "transition.h"
#include
+#include
#include "baseui.h"
#include "game_clock.h"
#include "message_overlay.h"
@@ -152,6 +152,10 @@ namespace {
}
void Player::Init(std::vector args) {
+ lcf::LogHandler::SetHandler([](lcf::LogHandler::Level level, StringView message, lcf::LogHandler::UserData) {
+ Output::Debug("lcf ({}): {}", lcf::LogHandler::kLevelTags.tag(level), message);
+ });
+
frames = 0;
// Must be called before the first call to Output