#pragma once #include "game/game.hpp" #include "variable_value.hpp" #include "vector.hpp" #include namespace scripting { class entity; class array; class function; class value_wrap; namespace { std::array var_typename = { "undefined", "object", "string", "localized string", "vector", "float", "int", "codepos", "precodepos", "function", "builtin function", "builtin method", "stack", "animation", "pre animation", "thread", "thread", "thread", "thread", "struct", "removed entity", "entity", "array", "removed thread", "", "thread list", "endon list", }; std::string get_typename(const game::VariableValue& value) { if (value.type == game::VAR_POINTER) { const auto type = game::scr_VarGlob->objectVariableValue[value.u.uintValue].w.type; return var_typename[type]; } else { return var_typename[value.type]; } } template std::string get_c_typename() { auto& info = typeid(T); if (info == typeid(std::string)) { return "string"; } if (info == typeid(const char*)) { return "string"; } if (info == typeid(entity)) { return "entity"; } if (info == typeid(array)) { return "array"; } if (info == typeid(function)) { return "function"; } if (info == typeid(vector)) { return "vector"; } return info.name(); } } class script_value { public: script_value() = default; script_value(const game::VariableValue& value); script_value(const value_wrap& value); script_value(int value); script_value(unsigned int value); script_value(bool value); script_value(float value); script_value(double value); script_value(const char* value); script_value(const std::string& value); script_value(const entity& value); script_value(const array& value); script_value(const function& value); script_value(const vector& value); std::string type_name() const { return get_typename(this->get_raw()); } std::string to_string() const; template bool is() const; template T as() const { if (!this->is()) { const auto type = get_typename(this->get_raw()); const auto c_type = get_c_typename(); throw std::runtime_error(std::format("has type '{}' but should be '{}'", type, c_type)); } return get(); } template T* as_ptr() { const auto value = this->as(); if (!value) { throw std::runtime_error("is null"); } return reinterpret_cast(value); } const game::VariableValue& get_raw() const; variable_value value_{}; private: template T get() const; }; class value_wrap { public: value_wrap(const scripting::script_value& value, int argument_index); std::string to_string() const { return this->value_.to_string(); } std::string type_name() const { return this->value_.type_name(); } template T as() const { try { return this->value_.as(); } catch (const std::exception& e) { throw std::runtime_error(utils::string::va("parameter %d %s", this->argument_index_, e.what())); } } template T* as_ptr() { try { return this->value_.as_ptr(); } catch (const std::exception& e) { throw std::runtime_error(utils::string::va("parameter %d %s", this->argument_index_, e.what())); } } template T is() const { return this->value_.is(); } const game::VariableValue& get_raw() const { return this->value_.get_raw(); } int argument_index_{}; scripting::script_value value_; }; }