#pragma once #include "globalincs/pstypes.h" #include "ade_api.h" #include "scripting.h" #include "utils/tuples.h" namespace scripting { namespace detail { ade_odata_setter convert_arg_type(object* objp); template T&& convert_arg_type(T&& arg) { return std::forward(arg); } template struct HookParameterInstance { SCP_string name; char type = '\0'; T value; HookParameterInstance(SCP_string name_, char type_, T value_) : name(std::move(name_)), type(type_), value(std::move(value_)) { } }; struct SetSingleHookVarHelper { SCP_vector& paramNames; SetSingleHookVarHelper(SCP_vector& paramNames_) : paramNames(paramNames_) {} template void operator()(HookParameterInstance&& instance) { paramNames.push_back(instance.name); Script_system.SetHookVar(instance.name.c_str(), instance.type, detail::convert_arg_type(std::move(instance.value))); } }; template struct HookParameterInstanceList { std::tuple...> params; HookParameterInstanceList(HookParameterInstance... params_) : params(params_...) {} void setHookVars(SCP_vector& paramNames) { util::tuples::for_each<0, SetSingleHookVarHelper, HookParameterInstance...>( std::move(params), SetSingleHookVarHelper(paramNames)); } }; } // namespace detail template detail::HookParameterInstance hook_param(SCP_string name_, char type_, T value_) { return detail::HookParameterInstance(std::move(name_), type_, std::move(value_)); } template detail::HookParameterInstanceList hook_param_list(detail::HookParameterInstance... params) { return detail::HookParameterInstanceList(std::move(params)...); } struct HookVariableDocumentation { const char* name = nullptr; scripting::ade_type_info type; const char* description = nullptr; HookVariableDocumentation(const char* name_, scripting::ade_type_info type_, const char* description_); }; class HookBase { public: HookBase(SCP_string hookName, SCP_string description, SCP_vector parameters, int32_t hookId = -1); virtual ~HookBase(); const SCP_string& getHookName() const; const SCP_string& getDescription() const; const SCP_vector& getParameters() const; int32_t getHookId() const; virtual bool isOverridable() const = 0; protected: SCP_string _hookName; SCP_string _description; SCP_vector _parameters; int32_t _hookId = 0; bool hasParameter(const SCP_string& param) const { return std::find_if(_parameters.begin(), _parameters.end(), [¶m](const HookVariableDocumentation& test) { return test.name == param; }) != _parameters.end(); } }; class Hook : public HookBase { public: Hook(SCP_string hookName, SCP_string description, SCP_vector parameters, int32_t hookId); ~Hook() override; static std::shared_ptr Factory(SCP_string hookName, SCP_string description, SCP_vector parameters, int32_t hookId = -1); static std::shared_ptr Factory(SCP_string hookName, int32_t hookId); bool isOverridable() const override; template int run(detail::HookParameterInstanceList argsList = hook_param_list(), object* objp = nullptr, int more_data = 0) const { SCP_vector paramNames; argsList.setHookVars(paramNames); #ifndef NDEBUG std::for_each(paramNames.begin(), paramNames.end(), [this](const SCP_string& param) { Assertion(hasParameter(param), "Hook '%s' does not accept parameter '%s'.", _hookName.c_str(), param.c_str()); }); #endif const auto num_run = Script_system.RunCondition(_hookId, objp, more_data); for (const auto& param : paramNames) { Script_system.RemHookVar(param.c_str()); } return num_run; } }; class OverridableHook : public Hook { public: OverridableHook(SCP_string hookName, SCP_string description, SCP_vector parameters, int32_t hookId); ~OverridableHook() override; static std::shared_ptr Factory(SCP_string hookName, SCP_string description, SCP_vector parameters, int32_t hookId = -1); bool isOverridable() const override; template bool isOverride(detail::HookParameterInstanceList argsList = hook_param_list(), object* objp = nullptr) const { SCP_vector paramNames; argsList.setHookVars(paramNames); #ifndef NDEBUG std::for_each(paramNames.begin(), paramNames.end(), [this](const SCP_string& param) { Assertion(hasParameter(param), "Hook '%s' does not accept parameter '%s'.", _hookName.c_str(), param.c_str()); }); #endif const auto ret_val = Script_system.IsConditionOverride(_hookId, objp); for (const auto& param : paramNames) { Script_system.RemHookVar(param.c_str()); } return ret_val; } }; const SCP_vector& getHooks(); } // namespace scripting