170 lines
4.8 KiB
C++
170 lines
4.8 KiB
C++
#include "std_include.hpp"
|
|
#include "game/game.hpp"
|
|
#include "utils/string.hpp"
|
|
#include "functions.hpp"
|
|
#include "stack_isolation.hpp"
|
|
#include "safe_executer.hpp"
|
|
#include "context.hpp"
|
|
|
|
namespace game::scripting
|
|
{
|
|
executer::executer(context* context) : context_(context)
|
|
{
|
|
}
|
|
|
|
int executer::get_field_id(const int classnum, const std::string& field) const
|
|
{
|
|
const auto field_name = utils::string::to_lower(field);
|
|
const auto class_id = native::g_classMap[classnum].id;
|
|
const auto field_str = native::SL_GetString(field_name.data(), 1);
|
|
const auto _ = gsl::finally([field_str]()
|
|
{
|
|
native::RemoveRefToValue(native::SCRIPT_STRING, {int(field_str)});
|
|
});
|
|
|
|
const auto offset = native::FindVariable(class_id, field_str);
|
|
if (offset)
|
|
{
|
|
const auto index = 4 * (offset + 0xC800 * (class_id & 1));
|
|
return PINT(SELECT_VALUE(0x1A3BC80, 0x1EFE180, 0x1DC8800))[index];
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
void executer::set_entity_field(const std::string& field, const unsigned int entity_id,
|
|
const chaiscript::Boxed_Value& value)
|
|
{
|
|
const auto entref = native::Scr_GetEntityIdRef(entity_id);
|
|
const int id = get_field_id(entref.raw.classnum, field);
|
|
|
|
if (id != -1)
|
|
{
|
|
stack_isolation _;
|
|
this->context_->get_parameters()->push(value);
|
|
|
|
native::scr_VmPub->outparamcount = native::scr_VmPub->inparamcount;
|
|
native::scr_VmPub->inparamcount = 0;
|
|
|
|
if (!safe_executer::set_entity_field(entref, id))
|
|
{
|
|
throw std::runtime_error("Failed to set value for field '" + field + "'");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this->entity_fields_[entity_id][field] = value;
|
|
}
|
|
}
|
|
|
|
chaiscript::Boxed_Value executer::get_entity_field(const std::string& field, const unsigned int entity_id)
|
|
{
|
|
const auto entref = native::Scr_GetEntityIdRef(entity_id);
|
|
const auto id = this->get_field_id(entref.raw.classnum, field);
|
|
|
|
if (id != -1)
|
|
{
|
|
stack_isolation _;
|
|
|
|
native::VariableValue value{};
|
|
if (!safe_executer::get_entity_field(entref, id, &value))
|
|
{
|
|
throw std::runtime_error("Failed to get value for field '" + field + "'");
|
|
}
|
|
|
|
const auto $ = gsl::finally([value]()
|
|
{
|
|
native::RemoveRefToValue(value.type, value.u);
|
|
});
|
|
|
|
return this->context_->get_parameters()->load(value);
|
|
}
|
|
else
|
|
{
|
|
const auto& map = this->entity_fields_[entity_id];
|
|
const auto value = map.find(field);
|
|
if (value != map.end())
|
|
{
|
|
return value->second;
|
|
}
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
void executer::notify(const std::string& event, const unsigned int entity_id,
|
|
std::vector<chaiscript::Boxed_Value> arguments) const
|
|
{
|
|
stack_isolation _;
|
|
|
|
std::reverse(arguments.begin(), arguments.end());
|
|
for (auto argument : arguments)
|
|
{
|
|
this->context_->get_parameters()->push(argument);
|
|
}
|
|
|
|
const auto event_id = native::SL_GetString(event.data(), 0);
|
|
native::Scr_NotifyId(entity_id, event_id, native::scr_VmPub->inparamcount);
|
|
}
|
|
|
|
chaiscript::Boxed_Value executer::call(const std::string& function, const unsigned int entity_id,
|
|
std::vector<chaiscript::Boxed_Value> arguments) const
|
|
{
|
|
const auto function_index = find_function_index(function, entity_id == 0);
|
|
if (function_index < 0)
|
|
{
|
|
throw std::runtime_error("No function found for name '" + function + "'");
|
|
}
|
|
|
|
const auto entity = function_index > 0x1C7
|
|
? native::Scr_GetEntityIdRef(entity_id)
|
|
: native::scr_entref_t{~0u};
|
|
|
|
const auto function_ptr = native::Scr_GetFunc(function_index);
|
|
|
|
stack_isolation _;
|
|
|
|
std::reverse(arguments.begin(), arguments.end());
|
|
for (const auto& argument : arguments)
|
|
{
|
|
this->context_->get_parameters()->push(argument);
|
|
}
|
|
|
|
native::scr_VmPub->outparamcount = native::scr_VmPub->inparamcount;
|
|
native::scr_VmPub->inparamcount = 0;
|
|
|
|
if (!safe_executer::call(function_ptr, entity))
|
|
{
|
|
throw std::runtime_error("Error executing function '" + function + "'");
|
|
}
|
|
|
|
return this->context_->get_parameters()->get_return_value();
|
|
}
|
|
|
|
int executer::find_function_index(const std::string& function, const bool prefer_global)
|
|
{
|
|
const auto target = utils::string::to_lower(function);
|
|
|
|
const auto primary_map = prefer_global
|
|
? &global_function_map
|
|
: &instance_function_map;
|
|
const auto secondary_map = !prefer_global
|
|
? &global_function_map
|
|
: &instance_function_map;
|
|
|
|
auto function_entry = primary_map->find(target);
|
|
if (function_entry != primary_map->end())
|
|
{
|
|
return function_entry->second;
|
|
}
|
|
|
|
function_entry = secondary_map->find(target);
|
|
if (function_entry != secondary_map->end())
|
|
{
|
|
return function_entry->second;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
}
|