Add asset & entity list windows
This commit is contained in:
parent
29c5af9cb6
commit
5c31d10e1f
102
src/client/component/asset_list.cpp
Normal file
102
src/client/component/asset_list.cpp
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
#include "loader/component_loader.hpp"
|
||||||
|
|
||||||
|
#include "game/game.hpp"
|
||||||
|
#include "game/dvars.hpp"
|
||||||
|
|
||||||
|
#include "scheduler.hpp"
|
||||||
|
#include "command.hpp"
|
||||||
|
#include "gui.hpp"
|
||||||
|
|
||||||
|
#include <utils/string.hpp>
|
||||||
|
#include <utils/hook.hpp>
|
||||||
|
|
||||||
|
namespace asset_list
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
void enum_assets(const game::XAssetType type, const std::function<void(game::XAssetHeader)>& callback, const bool includeOverride)
|
||||||
|
{
|
||||||
|
game::DB_EnumXAssets_Internal(type, static_cast<void(*)(game::XAssetHeader, void*)>([](game::XAssetHeader header, void* data)
|
||||||
|
{
|
||||||
|
const auto& cb = *static_cast<const std::function<void(game::XAssetHeader)>*>(data);
|
||||||
|
cb(header);
|
||||||
|
}), &callback, includeOverride);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_frame()
|
||||||
|
{
|
||||||
|
if (!gui::enabled_menus["asset_list"])
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool shown_assets[game::XAssetType::ASSET_TYPE_COUNT];
|
||||||
|
|
||||||
|
{
|
||||||
|
ImGui::Begin("Asset list", &gui::enabled_menus["asset_list"]);
|
||||||
|
|
||||||
|
static char filter[0x200]{};
|
||||||
|
ImGui::InputText("asset type", filter, IM_ARRAYSIZE(filter));
|
||||||
|
for (auto i = 0; i < game::XAssetType::ASSET_TYPE_COUNT; i++)
|
||||||
|
{
|
||||||
|
const auto name = game::g_assetNames[i];
|
||||||
|
const auto type = static_cast<game::XAssetType>(i);
|
||||||
|
|
||||||
|
if (strstr(name, filter))
|
||||||
|
{
|
||||||
|
ImGui::Checkbox(name, &shown_assets[type]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto i = 0; i < game::XAssetType::ASSET_TYPE_COUNT; i++)
|
||||||
|
{
|
||||||
|
const auto name = game::g_assetNames[i];
|
||||||
|
const auto type = static_cast<game::XAssetType>(i);
|
||||||
|
|
||||||
|
if (!shown_assets[type])
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SetNextWindowSizeConstraints(ImVec2(500, 500), ImVec2(1000, 1000));
|
||||||
|
ImGui::Begin(name, &shown_assets[type]);
|
||||||
|
|
||||||
|
static char filter[0x200]{};
|
||||||
|
ImGui::InputText("asset name", filter, IM_ARRAYSIZE(filter));
|
||||||
|
|
||||||
|
enum_assets(type, [type](const game::XAssetHeader header)
|
||||||
|
{
|
||||||
|
const auto asset = game::XAsset{type, header};
|
||||||
|
const auto* const asset_name = game::DB_GetXAssetName(&asset);
|
||||||
|
|
||||||
|
if (!strstr(asset_name, filter))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button(asset_name))
|
||||||
|
{
|
||||||
|
utils::string::set_clipboard_data(asset_name);
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class component final : public component_interface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void post_unpack() override
|
||||||
|
{
|
||||||
|
gui::on_frame(on_frame);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_COMPONENT(asset_list::component)
|
697
src/client/component/entity_list.cpp
Normal file
697
src/client/component/entity_list.cpp
Normal file
@ -0,0 +1,697 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
#include "loader/component_loader.hpp"
|
||||||
|
|
||||||
|
#include "game/game.hpp"
|
||||||
|
#include "game/dvars.hpp"
|
||||||
|
|
||||||
|
#include "scheduler.hpp"
|
||||||
|
#include "command.hpp"
|
||||||
|
#include "gui.hpp"
|
||||||
|
#include "scripting.hpp"
|
||||||
|
|
||||||
|
#include "game/scripting/execution.hpp"
|
||||||
|
|
||||||
|
#include <utils/string.hpp>
|
||||||
|
#include <utils/hook.hpp>
|
||||||
|
#include <utils/concurrency.hpp>
|
||||||
|
|
||||||
|
namespace entity_list
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
enum entity_type
|
||||||
|
{
|
||||||
|
type_any,
|
||||||
|
actor,
|
||||||
|
spawner,
|
||||||
|
weapon,
|
||||||
|
node,
|
||||||
|
count,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum entity_team
|
||||||
|
{
|
||||||
|
team_any,
|
||||||
|
neutral,
|
||||||
|
allies,
|
||||||
|
axis,
|
||||||
|
team3
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unordered_map<entity_team, std::string> team_names =
|
||||||
|
{
|
||||||
|
{entity_team::neutral, "neutral"},
|
||||||
|
{entity_team::allies, "allies"},
|
||||||
|
{entity_team::axis, "axis"},
|
||||||
|
{entity_team::team3, "team3"},
|
||||||
|
};
|
||||||
|
|
||||||
|
struct entity_info_t
|
||||||
|
{
|
||||||
|
unsigned int id;
|
||||||
|
unsigned int num;
|
||||||
|
std::unordered_map<std::string, std::string> fields;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct filters_t
|
||||||
|
{
|
||||||
|
bool filter_by_range{};
|
||||||
|
float range{};
|
||||||
|
entity_team team{};
|
||||||
|
entity_type type{};
|
||||||
|
std::vector<std::pair<std::string, std::string>> fields;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct data_t
|
||||||
|
{
|
||||||
|
bool auto_update{};
|
||||||
|
bool force_update{};
|
||||||
|
filters_t filters;
|
||||||
|
std::chrono::milliseconds interval{};
|
||||||
|
std::chrono::high_resolution_clock::time_point last_call{};
|
||||||
|
std::vector<entity_info_t> entity_info{};
|
||||||
|
std::unordered_map<std::string, bool> selected_fields =
|
||||||
|
{
|
||||||
|
{"code_classname", false},
|
||||||
|
{"classname", true},
|
||||||
|
{"origin", true},
|
||||||
|
{"model", false},
|
||||||
|
{"spawnflags", false},
|
||||||
|
{"target", false},
|
||||||
|
{"targetname", false},
|
||||||
|
{"count", false},
|
||||||
|
{"health", false},
|
||||||
|
{"dmg", false},
|
||||||
|
{"angles", true},
|
||||||
|
{"script_linkname", false},
|
||||||
|
{"script_noteworthy", false},
|
||||||
|
{"maxhealth", false},
|
||||||
|
{"anglelerprate", false},
|
||||||
|
{"activator", false},
|
||||||
|
{"slidevelocity", false},
|
||||||
|
{"disableplayeradsloscheck", false},
|
||||||
|
{"type", false},
|
||||||
|
{"accuracy", false},
|
||||||
|
{"lookforward", false},
|
||||||
|
{"lookright", false},
|
||||||
|
{"lookup", false},
|
||||||
|
{"fovcosine", false},
|
||||||
|
{"fovcosinebusy", false},
|
||||||
|
{"fovcosinez", false},
|
||||||
|
{"upaimlimit", false},
|
||||||
|
{"downaimlimit", false},
|
||||||
|
{"rightaimlimit", false},
|
||||||
|
{"leftaimlimit", false},
|
||||||
|
{"maxsightdistsqrd", false},
|
||||||
|
{"sightlatency", false},
|
||||||
|
{"defaultsightlatency", false},
|
||||||
|
{"ignoreclosefoliage", false},
|
||||||
|
{"interval", false},
|
||||||
|
{"teammovewaittime", false},
|
||||||
|
{"damagetaken", false},
|
||||||
|
{"damagedir", false},
|
||||||
|
{"damageyaw", false},
|
||||||
|
{"damagelocation", false},
|
||||||
|
{"damageweapon", false},
|
||||||
|
{"damagemod", false},
|
||||||
|
{"proneok", false},
|
||||||
|
{"walkdistfacingmotion", false},
|
||||||
|
{"walkdist", false},
|
||||||
|
{"desiredangle", false},
|
||||||
|
{"pacifist", false},
|
||||||
|
{"pacifistwait", false},
|
||||||
|
{"footstepdetectdist", false},
|
||||||
|
{"footstepdetectdistwalk", false},
|
||||||
|
{"footstepdetectdistsprint", false},
|
||||||
|
{"reactiontargetpos", false},
|
||||||
|
{"newenemyreactiondistsq", false},
|
||||||
|
{"ignoreexplosionevents", false},
|
||||||
|
{"ignoresuppression", false},
|
||||||
|
{"suppressionwait", false},
|
||||||
|
{"suppressionduration", false},
|
||||||
|
{"suppressionstarttime", false},
|
||||||
|
{"suppressionmeter", false},
|
||||||
|
{"ignoreplayersuppression", false},
|
||||||
|
{"name", false},
|
||||||
|
{"weapon", false},
|
||||||
|
{"dontavoidplayer", false},
|
||||||
|
{"grenadeawareness", false},
|
||||||
|
{"grenade", false},
|
||||||
|
{"grenadeweapon", false},
|
||||||
|
{"grenadeammo", false},
|
||||||
|
{"grenadetargetpos", false},
|
||||||
|
{"grenadetargetvalid", false},
|
||||||
|
{"grenadetossvel", false},
|
||||||
|
{"favoriteenemy", false},
|
||||||
|
{"highlyawareradius", false},
|
||||||
|
{"minpaindamage", false},
|
||||||
|
{"allowpain", false},
|
||||||
|
{"allowdeath", false},
|
||||||
|
{"delayeddeath", false},
|
||||||
|
{"diequietly", false},
|
||||||
|
{"forceragdollimmediate", false},
|
||||||
|
{"providecoveringfire", false},
|
||||||
|
{"doingambush", false},
|
||||||
|
{"combatmode", false},
|
||||||
|
{"alertlevel", false},
|
||||||
|
{"alertlevelint", false},
|
||||||
|
{"useable", false},
|
||||||
|
{"ignoretriggers", false},
|
||||||
|
{"pushable", false},
|
||||||
|
{"script_pushable", false},
|
||||||
|
{"dropweapon", false},
|
||||||
|
{"drawoncompass", false},
|
||||||
|
{"groundtype", false},
|
||||||
|
{"anim_pose", false},
|
||||||
|
{"goalradius", false},
|
||||||
|
{"goalheight", false},
|
||||||
|
{"goalpos", false},
|
||||||
|
{"nodeoffsetpos", false},
|
||||||
|
{"ignoreforfixednodesafecheck", false},
|
||||||
|
{"fixednode", false},
|
||||||
|
{"fixednodesaferadius", false},
|
||||||
|
{"pathgoalpos", false},
|
||||||
|
{"pathrandompercent", false},
|
||||||
|
{"usechokepoints", false},
|
||||||
|
{"stopanimdistsq", false},
|
||||||
|
{"lastenemysightpos", false},
|
||||||
|
{"pathenemylookahead", false},
|
||||||
|
{"pathenemyfightdist", false},
|
||||||
|
{"meleeattackdist", false},
|
||||||
|
{"movemode", false},
|
||||||
|
{"script_move_distance_override", false},
|
||||||
|
{"usecombatscriptatcover", false},
|
||||||
|
{"safetochangescript", false},
|
||||||
|
{"keepclaimednode", false},
|
||||||
|
{"keepclaimednodeifvalid", false},
|
||||||
|
{"keepnodeduringscriptedanim", false},
|
||||||
|
{"dodangerreact", false},
|
||||||
|
{"dangerreactduration", false},
|
||||||
|
{"nododgemove", false},
|
||||||
|
{"noteammove", false},
|
||||||
|
{"leanamount", false},
|
||||||
|
{"pitchamount", false},
|
||||||
|
{"turnrate", false},
|
||||||
|
{"turnanimactive", false},
|
||||||
|
{"badplaceawareness", false},
|
||||||
|
{"damageshield", false},
|
||||||
|
{"nogrenadereturnthrow", false},
|
||||||
|
{"noattackeraccuracymod", false},
|
||||||
|
{"frontshieldanglecos", false},
|
||||||
|
{"lookaheaddir", false},
|
||||||
|
{"lookaheaddist", false},
|
||||||
|
{"lookaheadhitsstairs", false},
|
||||||
|
{"velocity", false},
|
||||||
|
{"prevanimdelta", false},
|
||||||
|
{"exposedduration", false},
|
||||||
|
{"requestarrivalnotify", false},
|
||||||
|
{"scriptedarrivalent", false},
|
||||||
|
{"goingtoruntopos", false},
|
||||||
|
{"engagemindist", false},
|
||||||
|
{"engageminfalloffdist", false},
|
||||||
|
{"engagemaxdist", false},
|
||||||
|
{"engagemaxfalloffdist", false},
|
||||||
|
{"usingcovermoveup", false},
|
||||||
|
{"finalaccuracy", false},
|
||||||
|
{"facemotion", false},
|
||||||
|
{"gunblockedbywall", false},
|
||||||
|
{"relativedir", false},
|
||||||
|
{"lockorientation", false},
|
||||||
|
{"maxfaceenemydist", false},
|
||||||
|
{"stairsstate", false},
|
||||||
|
{"script", false},
|
||||||
|
{"prevscript", false},
|
||||||
|
{"headicon", false},
|
||||||
|
{"headiconteam", false},
|
||||||
|
{"coversearchinterval", false},
|
||||||
|
{"threatupdateinterval", false},
|
||||||
|
{"canclimbladders", false},
|
||||||
|
{"swimmer", false},
|
||||||
|
{"space", false},
|
||||||
|
{"doghandler", false},
|
||||||
|
{"sharpturnlookaheaddist", false},
|
||||||
|
{"postsharpturnlookaheaddist", false},
|
||||||
|
{"sharpturntooclosetodestdist", false},
|
||||||
|
{"usepathsmoothingvalues", false},
|
||||||
|
{"pathlookaheaddist", false},
|
||||||
|
{"maxturnspeed", false},
|
||||||
|
{"sharpturn", false},
|
||||||
|
{"disablesightandthreatupdate", false},
|
||||||
|
{"team", false},
|
||||||
|
{"threatbias", false},
|
||||||
|
{"threatbiasgroup", false},
|
||||||
|
{"node", false},
|
||||||
|
{"prevnode", false},
|
||||||
|
{"enemy", false},
|
||||||
|
{"syncedmeleetarget", false},
|
||||||
|
{"lastattacker", false},
|
||||||
|
{"lastpusher", false},
|
||||||
|
{"ignoreme", false},
|
||||||
|
{"ignoreall", false},
|
||||||
|
{"maxvisibledist", false},
|
||||||
|
{"surprisedbymedistsq", false},
|
||||||
|
{"attackeraccuracy", false},
|
||||||
|
{"ignorerandombulletdamage", false},
|
||||||
|
{"dodamagetoall", false},
|
||||||
|
{"turretinvulnerability", false},
|
||||||
|
{"useorcaavoidance", false},
|
||||||
|
{"reciprocality", false},
|
||||||
|
{"avoidanceboundshalfsize", false},
|
||||||
|
{"onlygoodnearestnodes", false},
|
||||||
|
{"playername", false},
|
||||||
|
{"deathinvulnerabletime", false},
|
||||||
|
{"criticalbulletdamagedist", false},
|
||||||
|
{"attackercount", false},
|
||||||
|
{"damagemultiplier", false},
|
||||||
|
{"laststand", false},
|
||||||
|
{"motiontrackerenabled", false},
|
||||||
|
{"veh_speed", false},
|
||||||
|
{"veh_pathspeed", false},
|
||||||
|
{"veh_transmission", false},
|
||||||
|
{"veh_pathdir", false},
|
||||||
|
{"veh_pathtype", false},
|
||||||
|
{"veh_topspeed", false},
|
||||||
|
{"veh_brake", false},
|
||||||
|
{"veh_throttle", false},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
utils::concurrency::container<data_t> data_;
|
||||||
|
|
||||||
|
bool verify_entity(unsigned int id)
|
||||||
|
{
|
||||||
|
const auto type = game::scr_VarGlob->objectVariableValue[id].w.type;
|
||||||
|
return type == game::SCRIPT_ENTITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<scripting::array> get_entity_array(const entity_type type, const entity_team team)
|
||||||
|
{
|
||||||
|
const auto value = scripting::call("getentarray");
|
||||||
|
if (!value.is<scripting::array>())
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto all = value.as<scripting::array>();
|
||||||
|
|
||||||
|
if (type == entity_type::type_any)
|
||||||
|
{
|
||||||
|
return {all};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == entity_type::actor)
|
||||||
|
{
|
||||||
|
scripting::array result{};
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < all.size(); i++)
|
||||||
|
{
|
||||||
|
const auto raw = all[i].get_raw();
|
||||||
|
if (raw.type != game::SCRIPT_OBJECT)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!verify_entity(raw.u.uintValue))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto entity = all[i].as<scripting::entity>();
|
||||||
|
|
||||||
|
const auto classname_value = entity.get("classname");
|
||||||
|
if (!classname_value.is<std::string>())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto team_value = entity.get("team");
|
||||||
|
if (!team_value.is<std::string>())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto classname = classname_value.as<std::string>();
|
||||||
|
const auto team_ = team_value.as<std::string>();
|
||||||
|
if (strstr(classname.data(), "actor_") && (team == entity_team::team_any || team_ == team_names[team]))
|
||||||
|
{
|
||||||
|
result.push(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == entity_type::spawner && team == entity_team::team_any)
|
||||||
|
{
|
||||||
|
return scripting::call("getspawnerarray").as<scripting::array>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == entity_type::spawner)
|
||||||
|
{
|
||||||
|
return scripting::call("getspawnerteamarray", {team_names[team]}).as<scripting::array>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == entity_type::weapon)
|
||||||
|
{
|
||||||
|
return scripting::call("getweaponarray").as<scripting::array>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == entity_type::node)
|
||||||
|
{
|
||||||
|
return scripting::call("getnodearray").as<scripting::array>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_entity_list()
|
||||||
|
{
|
||||||
|
data_.access([](data_t& data)
|
||||||
|
{
|
||||||
|
const auto now = std::chrono::high_resolution_clock::now();
|
||||||
|
if (!data.force_update && (!data.auto_update || (now - data.last_call < data.interval)))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.last_call = now;
|
||||||
|
data.force_update = false;
|
||||||
|
|
||||||
|
const auto value = get_entity_array(data.filters.type, data.filters.team);
|
||||||
|
if (!value.has_value())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.entity_info.clear();
|
||||||
|
|
||||||
|
const auto array = value.value();
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < array.size(); i++)
|
||||||
|
{
|
||||||
|
const auto raw = array[i].get_raw();
|
||||||
|
if (raw.type != game::SCRIPT_OBJECT)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!verify_entity(raw.u.uintValue))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto entity = array[i].as<scripting::entity>();
|
||||||
|
entity_info_t info{};
|
||||||
|
|
||||||
|
info.id = raw.u.uintValue;
|
||||||
|
info.num = entity.get_entity_reference().entnum;
|
||||||
|
|
||||||
|
if (data.filters.filter_by_range)
|
||||||
|
{
|
||||||
|
const auto player = scripting::call("getentbynum", {0}).as<scripting::entity>();
|
||||||
|
const auto distance = scripting::call("distance", {player.get("origin"), entity.get("origin")}).as<float>();
|
||||||
|
|
||||||
|
if (distance > data.filters.range)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto match_count = 0;
|
||||||
|
for (const auto& field : data.selected_fields)
|
||||||
|
{
|
||||||
|
if (!field.second)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const auto field_value = entity.get(field.first);
|
||||||
|
const auto value_string = field_value.to_string();
|
||||||
|
info.fields[field.first] = value_string;
|
||||||
|
|
||||||
|
for (const auto& filter : data.filters.fields)
|
||||||
|
{
|
||||||
|
if (field_value.is<std::string>() &&
|
||||||
|
strstr(field.first.data(), utils::string::to_lower(filter.first).data()) &&
|
||||||
|
strstr(value_string.data(), filter.second.data()))
|
||||||
|
{
|
||||||
|
match_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match_count == data.filters.fields.size())
|
||||||
|
{
|
||||||
|
data.entity_info.push_back(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void teleport_to(unsigned int id)
|
||||||
|
{
|
||||||
|
scheduler::once([id]()
|
||||||
|
{
|
||||||
|
if (!verify_entity(id))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto dest = scripting::entity{id};
|
||||||
|
const auto value = scripting::call("getentbynum", {0});
|
||||||
|
if (value.get_raw().type != game::SCRIPT_OBJECT)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto player = value.as<scripting::entity>();
|
||||||
|
player.call("setorigin", {dest.get("origin")});
|
||||||
|
}, scheduler::pipeline::server);
|
||||||
|
}
|
||||||
|
|
||||||
|
void teleport_to_reverse(unsigned int id)
|
||||||
|
{
|
||||||
|
scheduler::once([id]()
|
||||||
|
{
|
||||||
|
if (!verify_entity(id))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto dest = scripting::entity{id};
|
||||||
|
const auto value = scripting::call("getentbynum", {0});
|
||||||
|
if (value.get_raw().type != game::SCRIPT_OBJECT)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto player = value.as<scripting::entity>();
|
||||||
|
dest.set("origin", player.get("origin"));
|
||||||
|
}, scheduler::pipeline::server);
|
||||||
|
}
|
||||||
|
|
||||||
|
void delete_entity(unsigned int id)
|
||||||
|
{
|
||||||
|
scheduler::once([id]()
|
||||||
|
{
|
||||||
|
if (!verify_entity(id))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto target = scripting::entity{id};
|
||||||
|
target.call("delete");
|
||||||
|
}, scheduler::pipeline::server);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_frame()
|
||||||
|
{
|
||||||
|
if (!gui::enabled_menus["entity_list"])
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data_.access([](data_t& data)
|
||||||
|
{
|
||||||
|
ImGui::Begin("Entity list", &gui::enabled_menus["entity_list"]);
|
||||||
|
|
||||||
|
if (ImGui::Button("Update list"))
|
||||||
|
{
|
||||||
|
data.force_update = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Checkbox("Auto update", &data.auto_update);
|
||||||
|
|
||||||
|
auto interval = static_cast<int>(data.interval.count());
|
||||||
|
if (data.auto_update && ImGui::SliderInt("Interval", &interval, 0, 1000 * 30))
|
||||||
|
{
|
||||||
|
data.interval = std::chrono::milliseconds(interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
if (ImGui::CollapsingHeader("Filters"))
|
||||||
|
{
|
||||||
|
ImGui::Checkbox("Filter by distance", &data.filters.filter_by_range);
|
||||||
|
|
||||||
|
if (data.filters.filter_by_range)
|
||||||
|
{
|
||||||
|
if (ImGui::SliderFloat("range", &data.filters.range, 0.f, 10000.f))
|
||||||
|
{
|
||||||
|
data.force_update = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::TreeNode("Entity type"))
|
||||||
|
{
|
||||||
|
auto result = 0;
|
||||||
|
|
||||||
|
result += ImGui::RadioButton("any", reinterpret_cast<int*>(&data.filters.type), entity_type::type_any);
|
||||||
|
result += ImGui::RadioButton("actor", reinterpret_cast<int*>(&data.filters.type), entity_type::actor);
|
||||||
|
result += ImGui::RadioButton("spawner", reinterpret_cast<int*>(&data.filters.type), entity_type::spawner);
|
||||||
|
result += ImGui::RadioButton("weapon", reinterpret_cast<int*>(&data.filters.type), entity_type::weapon);
|
||||||
|
result += ImGui::RadioButton("node", reinterpret_cast<int*>(&data.filters.type), entity_type::node);
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
data.force_update = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::TreePop();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::TreeNode("Entity team"))
|
||||||
|
{
|
||||||
|
auto result = 0;
|
||||||
|
|
||||||
|
result += ImGui::RadioButton("any", reinterpret_cast<int*>(&data.filters.team), entity_team::team_any);
|
||||||
|
result += ImGui::RadioButton("neutral", reinterpret_cast<int*>(&data.filters.team), entity_team::neutral);
|
||||||
|
result += ImGui::RadioButton("allies", reinterpret_cast<int*>(&data.filters.team), entity_team::allies);
|
||||||
|
result += ImGui::RadioButton("axis", reinterpret_cast<int*>(&data.filters.team), entity_team::axis);
|
||||||
|
result += ImGui::RadioButton("team3", reinterpret_cast<int*>(&data.filters.team), entity_team::team3);
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
data.force_update = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::TreePop();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Text("Fields");
|
||||||
|
|
||||||
|
auto index = 0;
|
||||||
|
for (auto i = data.filters.fields.begin(); i != data.filters.fields.end(); ++i)
|
||||||
|
{
|
||||||
|
if (ImGui::TreeNode(utils::string::va("Filter #%i", index++)))
|
||||||
|
{
|
||||||
|
ImGui::InputText("name", &i->first);
|
||||||
|
ImGui::InputText("value", &i->second);
|
||||||
|
|
||||||
|
if (ImGui::Button("Erase"))
|
||||||
|
{
|
||||||
|
data.filters.fields.erase(i);
|
||||||
|
--i;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::TreePop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("Add field filter"))
|
||||||
|
{
|
||||||
|
data.filters.fields.push_back({});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
for (const auto& info : data.entity_info)
|
||||||
|
{
|
||||||
|
if (ImGui::TreeNode(utils::string::va("Entity num %i id %i", info.num, info.id)))
|
||||||
|
{
|
||||||
|
ImGui::Text("Commands");
|
||||||
|
|
||||||
|
if (ImGui::Button("Teleport to"))
|
||||||
|
{
|
||||||
|
teleport_to(info.id);
|
||||||
|
data.force_update = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("Teleport to you"))
|
||||||
|
{
|
||||||
|
teleport_to_reverse(info.id);
|
||||||
|
data.force_update = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.num != 0 && ImGui::Button("Delete"))
|
||||||
|
{
|
||||||
|
delete_entity(info.id);
|
||||||
|
data.force_update = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Text("Fields");
|
||||||
|
|
||||||
|
for (const auto& field : info.fields)
|
||||||
|
{
|
||||||
|
if (field.second.empty())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button(field.first.data()))
|
||||||
|
{
|
||||||
|
utils::string::set_clipboard_data(field.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
if (ImGui::Button(field.second.data()))
|
||||||
|
{
|
||||||
|
utils::string::set_clipboard_data(field.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::TreePop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
|
||||||
|
ImGui::SetNextWindowSizeConstraints(ImVec2(500, 500), ImVec2(1000, 1000));
|
||||||
|
ImGui::Begin("Selected fields");
|
||||||
|
|
||||||
|
static char field_filter[0x100]{};
|
||||||
|
ImGui::InputText("field name", field_filter, IM_ARRAYSIZE(field_filter));
|
||||||
|
for (auto& field : data.selected_fields)
|
||||||
|
{
|
||||||
|
if (strstr(field.first.data(), field_filter) && ImGui::Checkbox(field.first.data(), &field.second))
|
||||||
|
{
|
||||||
|
data.force_update = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class component final : public component_interface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void post_unpack() override
|
||||||
|
{
|
||||||
|
gui::on_frame(on_frame);
|
||||||
|
scheduler::loop(update_entity_list, scheduler::pipeline::server, 0ms);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_COMPONENT(entity_list::component)
|
@ -5,16 +5,22 @@
|
|||||||
#include "game/dvars.hpp"
|
#include "game/dvars.hpp"
|
||||||
|
|
||||||
#include "scheduler.hpp"
|
#include "scheduler.hpp"
|
||||||
|
#include "gui.hpp"
|
||||||
|
|
||||||
#include <utils/string.hpp>
|
#include <utils/string.hpp>
|
||||||
#include <utils/hook.hpp>
|
#include <utils/hook.hpp>
|
||||||
|
#include <utils/concurrency.hpp>
|
||||||
|
|
||||||
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||||
|
|
||||||
namespace gui
|
namespace gui
|
||||||
{
|
{
|
||||||
|
std::unordered_map<std::string, bool> enabled_menus;
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
utils::concurrency::container<std::vector<std::function<void()>>> on_frame_callbacks;
|
||||||
|
|
||||||
ID3D11Device* device;
|
ID3D11Device* device;
|
||||||
ID3D11DeviceContext* device_context;
|
ID3D11DeviceContext* device_context;
|
||||||
bool initialized = false;
|
bool initialized = false;
|
||||||
@ -47,12 +53,43 @@ namespace gui
|
|||||||
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
|
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
|
||||||
}
|
}
|
||||||
|
|
||||||
void gui_draw()
|
void toggle_menu(const std::string& name)
|
||||||
{
|
{
|
||||||
|
enabled_menus[name] = !enabled_menus[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_frame()
|
void gui_draw()
|
||||||
|
{
|
||||||
|
on_frame_callbacks.access([](std::vector<std::function<void()>>& callbacks)
|
||||||
|
{
|
||||||
|
for (const auto& callback : callbacks)
|
||||||
|
{
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (ImGui::BeginMainMenuBar())
|
||||||
|
{
|
||||||
|
if (ImGui::BeginMenu("Windows"))
|
||||||
|
{
|
||||||
|
if (ImGui::MenuItem("Asset list"))
|
||||||
|
{
|
||||||
|
toggle_menu("asset_list");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::MenuItem("Entity list"))
|
||||||
|
{
|
||||||
|
toggle_menu("entity_list");
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndMainMenuBar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gui_on_frame()
|
||||||
{
|
{
|
||||||
if (!initialized)
|
if (!initialized)
|
||||||
{
|
{
|
||||||
@ -83,11 +120,19 @@ namespace gui
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
utils::hook::detour dxgi_swap_chain_present_hook;
|
void dxgi_swap_chain_present_stub(utils::hook::assembler& a)
|
||||||
void* dxgi_swap_chain_present_stub()
|
|
||||||
{
|
{
|
||||||
on_frame();
|
a.pushad64();
|
||||||
return dxgi_swap_chain_present_hook.invoke<void*>();
|
a.call_aligned(gui_on_frame);
|
||||||
|
a.popad64();
|
||||||
|
|
||||||
|
a.mov(r8d, esi);
|
||||||
|
a.mov(edx, r15d);
|
||||||
|
a.mov(rcx, rdi);
|
||||||
|
a.call_aligned(rbx);
|
||||||
|
a.mov(ecx, eax);
|
||||||
|
|
||||||
|
a.jmp(0x7A14D1_b);
|
||||||
}
|
}
|
||||||
|
|
||||||
utils::hook::detour wnd_proc_hook;
|
utils::hook::detour wnd_proc_hook;
|
||||||
@ -110,6 +155,12 @@ namespace gui
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (key == game::K_ESCAPE && down && toggled)
|
||||||
|
{
|
||||||
|
toggled = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return !toggled;
|
return !toggled;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,6 +174,19 @@ namespace gui
|
|||||||
return !toggled;
|
return !toggled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void on_frame(const std::function<void()>& callback)
|
||||||
|
{
|
||||||
|
on_frame_callbacks.access([callback](std::vector<std::function<void()>>& callbacks)
|
||||||
|
{
|
||||||
|
callbacks.push_back(callback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_menu_open(const std::string& name)
|
||||||
|
{
|
||||||
|
return enabled_menus[name];
|
||||||
|
}
|
||||||
|
|
||||||
class component final : public component_interface
|
class component final : public component_interface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -138,7 +202,7 @@ namespace gui
|
|||||||
|
|
||||||
void post_unpack() override
|
void post_unpack() override
|
||||||
{
|
{
|
||||||
dxgi_swap_chain_present_hook.create(0x7A13A0_b, dxgi_swap_chain_present_stub);
|
utils::hook::jump(0x7A14C4_b, utils::hook::assemble(dxgi_swap_chain_present_stub), true);
|
||||||
wnd_proc_hook.create(0x650F10_b, wnd_proc_stub);
|
wnd_proc_hook.create(0x650F10_b, wnd_proc_stub);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,12 @@
|
|||||||
|
|
||||||
namespace gui
|
namespace gui
|
||||||
{
|
{
|
||||||
|
extern std::unordered_map<std::string, bool> enabled_menus;
|
||||||
|
|
||||||
bool gui_key_event(const int local_client_num, const int key, const int down);
|
bool gui_key_event(const int local_client_num, const int key, const int down);
|
||||||
bool gui_char_event(const int local_client_num, const int key);
|
bool gui_char_event(const int local_client_num, const int key);
|
||||||
bool gui_mouse_event(const int local_client_num, int x, int y);
|
bool gui_mouse_event(const int local_client_num, int x, int y);
|
||||||
|
|
||||||
|
void on_frame(const std::function<void()>& callback);
|
||||||
|
bool is_menu_open(const std::string& name);
|
||||||
}
|
}
|
339
src/client/game/scripting/array.cpp
Normal file
339
src/client/game/scripting/array.cpp
Normal file
@ -0,0 +1,339 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
#include "array.hpp"
|
||||||
|
#include "script_value.hpp"
|
||||||
|
#include "execution.hpp"
|
||||||
|
|
||||||
|
namespace scripting
|
||||||
|
{
|
||||||
|
array_value::array_value(unsigned int parent_id, unsigned int id)
|
||||||
|
: id_(id)
|
||||||
|
, parent_id_(parent_id)
|
||||||
|
{
|
||||||
|
if (!this->id_)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto value = game::scr_VarGlob->childVariableValue[this->id_ + 0xA000 * (this->parent_id_ & 3)];
|
||||||
|
game::VariableValue variable;
|
||||||
|
variable.u = value.u.u;
|
||||||
|
variable.type = (game::scriptType_e)value.type;
|
||||||
|
|
||||||
|
this->value_ = variable;
|
||||||
|
}
|
||||||
|
|
||||||
|
void array_value::operator=(const script_value& _value)
|
||||||
|
{
|
||||||
|
if (!this->id_)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto value = _value.get_raw();
|
||||||
|
|
||||||
|
const auto variable = &game::scr_VarGlob->childVariableValue[this->id_ + 0xA000 * (this->parent_id_ & 3)];
|
||||||
|
game::AddRefToValue(value.type, value.u);
|
||||||
|
game::RemoveRefToValue(variable->type, variable->u.u);
|
||||||
|
|
||||||
|
variable->type = (char)value.type;
|
||||||
|
variable->u.u = value.u;
|
||||||
|
|
||||||
|
this->value_ = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
array::array(const unsigned int id)
|
||||||
|
: id_(id)
|
||||||
|
{
|
||||||
|
this->add();
|
||||||
|
}
|
||||||
|
|
||||||
|
array::array(const array& other) : array(other.id_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
array::array(array&& other) noexcept
|
||||||
|
{
|
||||||
|
this->id_ = other.id_;
|
||||||
|
other.id_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
array::array()
|
||||||
|
{
|
||||||
|
this->id_ = make_array();
|
||||||
|
}
|
||||||
|
|
||||||
|
array::array(std::vector<script_value> values)
|
||||||
|
{
|
||||||
|
this->id_ = make_array();
|
||||||
|
|
||||||
|
for (const auto& value : values)
|
||||||
|
{
|
||||||
|
this->push(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
array::array(std::unordered_map<std::string, script_value> values)
|
||||||
|
{
|
||||||
|
this->id_ = make_array();
|
||||||
|
|
||||||
|
for (const auto& value : values)
|
||||||
|
{
|
||||||
|
this->set(value.first, value.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
array::~array()
|
||||||
|
{
|
||||||
|
this->release();
|
||||||
|
}
|
||||||
|
|
||||||
|
array& array::operator=(const array& other)
|
||||||
|
{
|
||||||
|
if (&other != this)
|
||||||
|
{
|
||||||
|
this->release();
|
||||||
|
this->id_ = other.id_;
|
||||||
|
this->add();
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
array& array::operator=(array&& other) noexcept
|
||||||
|
{
|
||||||
|
if (&other != this)
|
||||||
|
{
|
||||||
|
this->release();
|
||||||
|
this->id_ = other.id_;
|
||||||
|
other.id_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void array::add() const
|
||||||
|
{
|
||||||
|
if (this->id_)
|
||||||
|
{
|
||||||
|
game::AddRefToValue(game::SCRIPT_OBJECT, {static_cast<int>(this->id_)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void array::release() const
|
||||||
|
{
|
||||||
|
if (this->id_)
|
||||||
|
{
|
||||||
|
game::RemoveRefToValue(game::SCRIPT_OBJECT, {static_cast<int>(this->id_)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<script_value> array::get_keys() const
|
||||||
|
{
|
||||||
|
std::vector<script_value> result;
|
||||||
|
|
||||||
|
const auto offset = 0xA000 * (this->id_ & 3);
|
||||||
|
auto current = game::scr_VarGlob->objectVariableChildren[this->id_].firstChild;
|
||||||
|
|
||||||
|
for (auto i = offset + current; current; i = offset + current)
|
||||||
|
{
|
||||||
|
const auto var = game::scr_VarGlob->childVariableValue[i];
|
||||||
|
|
||||||
|
if (var.type == game::SCRIPT_NONE)
|
||||||
|
{
|
||||||
|
current = var.nextSibling;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto string_value = (game::scr_string_t)((unsigned __int8)var.name_lo + (var.k.keys.name_hi << 8));
|
||||||
|
const auto* str = game::SL_ConvertToString(string_value);
|
||||||
|
|
||||||
|
script_value key;
|
||||||
|
if (string_value < 0x40000 && str)
|
||||||
|
{
|
||||||
|
key = str;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
key = (string_value - 0x800000) & 0xFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push_back(key);
|
||||||
|
|
||||||
|
current = var.nextSibling;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int array::size() const
|
||||||
|
{
|
||||||
|
return game::scr_VarGlob->objectVariableValue[this->id_].u.f.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int array::push(script_value value) const
|
||||||
|
{
|
||||||
|
this->set(this->size(), value);
|
||||||
|
return this->size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void array::erase(const unsigned int index) const
|
||||||
|
{
|
||||||
|
const auto variable_id = game::FindVariable(this->id_, (index - 0x800000) & 0xFFFFFF);
|
||||||
|
if (variable_id)
|
||||||
|
{
|
||||||
|
game::RemoveVariableValue(this->id_, variable_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void array::erase(const std::string& key) const
|
||||||
|
{
|
||||||
|
const auto string_value = game::SL_GetString(key.data(), 0);
|
||||||
|
const auto variable_id = game::FindVariable(this->id_, string_value);
|
||||||
|
if (variable_id)
|
||||||
|
{
|
||||||
|
game::RemoveVariableValue(this->id_, variable_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value array::pop() const
|
||||||
|
{
|
||||||
|
const auto value = this->get(this->size() - 1);
|
||||||
|
this->erase(this->size() - 1);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value array::get(const script_value& key) const
|
||||||
|
{
|
||||||
|
if (key.is<int>())
|
||||||
|
{
|
||||||
|
return this->get(key.as<int>());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return this->get(key.as<std::string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value array::get(const std::string& key) const
|
||||||
|
{
|
||||||
|
const auto string_value = game::SL_GetString(key.data(), 0);
|
||||||
|
const auto variable_id = game::FindVariable(this->id_, string_value);
|
||||||
|
|
||||||
|
if (!variable_id)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto value = game::scr_VarGlob->childVariableValue[variable_id + 0xA000 * (this->id_ & 3)];
|
||||||
|
game::VariableValue variable;
|
||||||
|
variable.u = value.u.u;
|
||||||
|
variable.type = (game::scriptType_e)value.type;
|
||||||
|
|
||||||
|
return variable;
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value array::get(const unsigned int index) const
|
||||||
|
{
|
||||||
|
const auto variable_id = game::FindVariable(this->id_, (index - 0x800000) & 0xFFFFFF);
|
||||||
|
|
||||||
|
if (!variable_id)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto value = game::scr_VarGlob->childVariableValue[variable_id + 0xA000 * (this->id_ & 3)];
|
||||||
|
game::VariableValue variable;
|
||||||
|
variable.u = value.u.u;
|
||||||
|
variable.type = (game::scriptType_e)value.type;
|
||||||
|
|
||||||
|
return variable;
|
||||||
|
}
|
||||||
|
|
||||||
|
void array::set(const script_value& key, const script_value& value) const
|
||||||
|
{
|
||||||
|
if (key.is<int>())
|
||||||
|
{
|
||||||
|
this->set(key.as<int>(), value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->set(key.as<std::string>(), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void array::set(const std::string& key, const script_value& _value) const
|
||||||
|
{
|
||||||
|
const auto value = _value.get_raw();
|
||||||
|
|
||||||
|
const auto string_value = game::SL_GetString(key.data(), 0);
|
||||||
|
const auto variable_id = this->get_value_id(key);
|
||||||
|
|
||||||
|
if (!variable_id)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto variable = &game::scr_VarGlob->childVariableValue[variable_id + 0xA000 * (this->id_ & 3)];
|
||||||
|
|
||||||
|
game::AddRefToValue(value.type, value.u);
|
||||||
|
game::RemoveRefToValue(variable->type, variable->u.u);
|
||||||
|
|
||||||
|
variable->type = (char)value.type;
|
||||||
|
variable->u.u = value.u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void array::set(const unsigned int index, const script_value& _value) const
|
||||||
|
{
|
||||||
|
const auto value = _value.get_raw();
|
||||||
|
const auto variable_id = this->get_value_id(index);
|
||||||
|
|
||||||
|
if (!variable_id)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto variable = &game::scr_VarGlob->childVariableValue[variable_id + 0xA000 * (this->id_ & 3)];
|
||||||
|
|
||||||
|
game::AddRefToValue(value.type, value.u);
|
||||||
|
game::RemoveRefToValue(variable->type, variable->u.u);
|
||||||
|
|
||||||
|
variable->type = (char)value.type;
|
||||||
|
variable->u.u = value.u;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int array::get_entity_id() const
|
||||||
|
{
|
||||||
|
return this->id_;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int array::get_value_id(const std::string& key) const
|
||||||
|
{
|
||||||
|
const auto string_value = game::SL_GetString(key.data(), 0);
|
||||||
|
const auto variable_id = game::FindVariable(this->id_, string_value);
|
||||||
|
|
||||||
|
if (!variable_id)
|
||||||
|
{
|
||||||
|
return game::GetNewVariable(this->id_, string_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return variable_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int array::get_value_id(const unsigned int index) const
|
||||||
|
{
|
||||||
|
const auto variable_id = game::FindVariable(this->id_, (index - 0x800000) & 0xFFFFFF);
|
||||||
|
if (!variable_id)
|
||||||
|
{
|
||||||
|
return game::GetNewArrayVariable(this->id_, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
return variable_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
entity array::get_raw() const
|
||||||
|
{
|
||||||
|
return entity(this->id_);
|
||||||
|
}
|
||||||
|
}
|
87
src/client/game/scripting/array.hpp
Normal file
87
src/client/game/scripting/array.hpp
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "game/game.hpp"
|
||||||
|
#include "script_value.hpp"
|
||||||
|
|
||||||
|
namespace scripting
|
||||||
|
{
|
||||||
|
class array_value : public script_value
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
array_value(unsigned int, unsigned int);
|
||||||
|
void operator=(const script_value&);
|
||||||
|
private:
|
||||||
|
unsigned int id_;
|
||||||
|
unsigned int parent_id_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class array final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
array();
|
||||||
|
array(const unsigned int);
|
||||||
|
|
||||||
|
array(std::vector<script_value>);
|
||||||
|
array(std::unordered_map<std::string, script_value>);
|
||||||
|
|
||||||
|
array(const array& other);
|
||||||
|
array(array&& other) noexcept;
|
||||||
|
|
||||||
|
~array();
|
||||||
|
|
||||||
|
array& operator=(const array& other);
|
||||||
|
array& operator=(array&& other) noexcept;
|
||||||
|
|
||||||
|
std::vector<script_value> get_keys() const;
|
||||||
|
unsigned int size() const;
|
||||||
|
|
||||||
|
unsigned int push(script_value) const;
|
||||||
|
void erase(const unsigned int) const;
|
||||||
|
void erase(const std::string&) const;
|
||||||
|
script_value pop() const;
|
||||||
|
|
||||||
|
script_value get(const script_value&) const;
|
||||||
|
script_value get(const std::string&) const;
|
||||||
|
script_value get(const unsigned int) const;
|
||||||
|
|
||||||
|
void set(const script_value&, const script_value&) const;
|
||||||
|
void set(const std::string&, const script_value&) const;
|
||||||
|
void set(const unsigned int, const script_value&) const;
|
||||||
|
|
||||||
|
unsigned int get_entity_id() const;
|
||||||
|
|
||||||
|
unsigned int get_value_id(const std::string&) const;
|
||||||
|
unsigned int get_value_id(const unsigned int) const;
|
||||||
|
|
||||||
|
entity get_raw() const;
|
||||||
|
|
||||||
|
array_value operator[](const int index) const
|
||||||
|
{
|
||||||
|
return {this->id_, this->get_value_id(index)};
|
||||||
|
}
|
||||||
|
|
||||||
|
array_value operator[](const std::string& key) const
|
||||||
|
{
|
||||||
|
return {this->id_, this->get_value_id(key)};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename I = int, typename S = std::string>
|
||||||
|
array_value operator[](const script_value& key) const
|
||||||
|
{
|
||||||
|
if (key.is<I>())
|
||||||
|
{
|
||||||
|
return { this->id_, this->get_value_id(key.as<I>()) };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key.is<S>())
|
||||||
|
{
|
||||||
|
return { this->id_, this->get_value_id(key.as<S>()) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void add() const;
|
||||||
|
void release() const;
|
||||||
|
|
||||||
|
unsigned int id_;
|
||||||
|
};
|
||||||
|
}
|
@ -235,4 +235,15 @@ namespace scripting
|
|||||||
|
|
||||||
return get_custom_field(entity, field);
|
return get_custom_field(entity, field);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned int make_array()
|
||||||
|
{
|
||||||
|
unsigned int index = 0;
|
||||||
|
const auto variable = game::AllocVariable(&index);
|
||||||
|
variable->w.type = game::SCRIPT_ARRAY;
|
||||||
|
variable->u.f.prev = 0;
|
||||||
|
variable->u.f.next = 0;
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "game/game.hpp"
|
#include "game/game.hpp"
|
||||||
#include "entity.hpp"
|
#include "entity.hpp"
|
||||||
|
#include "array.hpp"
|
||||||
#include "script_value.hpp"
|
#include "script_value.hpp"
|
||||||
|
|
||||||
namespace scripting
|
namespace scripting
|
||||||
@ -35,4 +36,6 @@ namespace scripting
|
|||||||
script_value get_entity_field(const entity& entity, const std::string& field);
|
script_value get_entity_field(const entity& entity, const std::string& field);
|
||||||
|
|
||||||
void notify(const entity& entity, const std::string& event, const std::vector<script_value>& arguments);
|
void notify(const entity& entity, const std::string& event, const std::vector<script_value>& arguments);
|
||||||
|
|
||||||
|
unsigned int make_array();
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
#include <std_include.hpp>
|
#include <std_include.hpp>
|
||||||
#include "script_value.hpp"
|
#include "script_value.hpp"
|
||||||
#include "entity.hpp"
|
#include "entity.hpp"
|
||||||
|
#include "array.hpp"
|
||||||
|
|
||||||
|
#include <utils/string.hpp>
|
||||||
|
|
||||||
namespace scripting
|
namespace scripting
|
||||||
{
|
{
|
||||||
@ -78,6 +81,15 @@ namespace scripting
|
|||||||
this->value_ = variable;
|
this->value_ = variable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
script_value::script_value(const array& value)
|
||||||
|
{
|
||||||
|
game::VariableValue variable{};
|
||||||
|
variable.type = game::SCRIPT_OBJECT;
|
||||||
|
variable.u.pointerValue = value.get_entity_id();
|
||||||
|
|
||||||
|
this->value_ = variable;
|
||||||
|
}
|
||||||
|
|
||||||
script_value::script_value(const vector& value)
|
script_value::script_value(const vector& value)
|
||||||
{
|
{
|
||||||
game::VariableValue variable{};
|
game::VariableValue variable{};
|
||||||
@ -250,6 +262,30 @@ namespace scripting
|
|||||||
return entity(this->get_raw().u.pointerValue);
|
return entity(this->get_raw().u.pointerValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***************************************************************
|
||||||
|
* Array
|
||||||
|
**************************************************************/
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool script_value::is<array>() const
|
||||||
|
{
|
||||||
|
if (this->get_raw().type != game::SCRIPT_OBJECT)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto id = this->get_raw().u.uintValue;
|
||||||
|
const auto type = game::scr_VarGlob->objectVariableValue[id].w.type;
|
||||||
|
|
||||||
|
return type == game::SCRIPT_ARRAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
array script_value::get() const
|
||||||
|
{
|
||||||
|
return array(this->get_raw().u.uintValue);
|
||||||
|
}
|
||||||
|
|
||||||
/***************************************************************
|
/***************************************************************
|
||||||
* Vector
|
* Vector
|
||||||
**************************************************************/
|
**************************************************************/
|
||||||
@ -274,4 +310,34 @@ namespace scripting
|
|||||||
{
|
{
|
||||||
return this->value_.get();
|
return this->value_.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string script_value::to_string() const
|
||||||
|
{
|
||||||
|
if (this->is<int>())
|
||||||
|
{
|
||||||
|
return utils::string::va("%i", this->as<int>());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->is<float>())
|
||||||
|
{
|
||||||
|
return utils::string::va("%f", this->as<float>());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->is<std::string>())
|
||||||
|
{
|
||||||
|
return this->as<std::string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->is<vector>())
|
||||||
|
{
|
||||||
|
const auto vec = this->as<vector>();
|
||||||
|
return utils::string::va("(%g, %g, %g)",
|
||||||
|
vec.get_x(),
|
||||||
|
vec.get_y(),
|
||||||
|
vec.get_z()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
namespace scripting
|
namespace scripting
|
||||||
{
|
{
|
||||||
class entity;
|
class entity;
|
||||||
|
class array;
|
||||||
|
|
||||||
class script_value
|
class script_value
|
||||||
{
|
{
|
||||||
@ -24,6 +25,7 @@ namespace scripting
|
|||||||
script_value(const std::string& value);
|
script_value(const std::string& value);
|
||||||
|
|
||||||
script_value(const entity& value);
|
script_value(const entity& value);
|
||||||
|
script_value(const array& value);
|
||||||
|
|
||||||
script_value(const vector& value);
|
script_value(const vector& value);
|
||||||
|
|
||||||
@ -43,10 +45,13 @@ namespace scripting
|
|||||||
|
|
||||||
const game::VariableValue& get_raw() const;
|
const game::VariableValue& get_raw() const;
|
||||||
|
|
||||||
|
std::string to_string() const;
|
||||||
|
|
||||||
|
variable_value value_{};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T get() const;
|
T get() const;
|
||||||
|
|
||||||
variable_value value_{};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -676,6 +676,7 @@ namespace game
|
|||||||
SCRIPT_END = 8,
|
SCRIPT_END = 8,
|
||||||
SCRIPT_FUNCTION = 9,
|
SCRIPT_FUNCTION = 9,
|
||||||
SCRIPT_STRUCT = 19,
|
SCRIPT_STRUCT = 19,
|
||||||
|
SCRIPT_ENTITY = 21,
|
||||||
SCRIPT_ARRAY = 22,
|
SCRIPT_ARRAY = 22,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ namespace game
|
|||||||
WEAK symbol<void(int type, VariableUnion u)> AddRefToValue{0x5C0EB0};
|
WEAK symbol<void(int type, VariableUnion u)> AddRefToValue{0x5C0EB0};
|
||||||
WEAK symbol<void(unsigned int id)> AddRefToObject{0x5C0EA0};
|
WEAK symbol<void(unsigned int id)> AddRefToObject{0x5C0EA0};
|
||||||
WEAK symbol<unsigned int(unsigned int id)> AllocThread{0x5C1200};
|
WEAK symbol<unsigned int(unsigned int id)> AllocThread{0x5C1200};
|
||||||
|
WEAK symbol<ObjectVariableValue*(unsigned int* id)> AllocVariable{0x5C1260};
|
||||||
WEAK symbol<void(int type, VariableUnion u)> RemoveRefToValue{0x5C29B0};
|
WEAK symbol<void(int type, VariableUnion u)> RemoveRefToValue{0x5C29B0};
|
||||||
WEAK symbol<void(unsigned int id)> RemoveRefToObject{0x5C28A0};
|
WEAK symbol<void(unsigned int id)> RemoveRefToObject{0x5C28A0};
|
||||||
|
|
||||||
@ -58,6 +59,11 @@ namespace game
|
|||||||
WEAK symbol<unsigned int (unsigned int parentId, unsigned int name)> FindVariable{0x5C1D50};
|
WEAK symbol<unsigned int (unsigned int parentId, unsigned int name)> FindVariable{0x5C1D50};
|
||||||
WEAK symbol<unsigned int(int entnum, unsigned int classnum)> FindEntityId{0x5C1C50};
|
WEAK symbol<unsigned int(int entnum, unsigned int classnum)> FindEntityId{0x5C1C50};
|
||||||
WEAK symbol<void(VariableValue* result, unsigned int classnum, int entnum, int offset)> GetEntityFieldValue{0x5C6100};
|
WEAK symbol<void(VariableValue* result, unsigned int classnum, int entnum, int offset)> GetEntityFieldValue{0x5C6100};
|
||||||
|
WEAK symbol<unsigned int(unsigned int parentId, unsigned int unsignedValue)> GetVariable{0x5C2690};
|
||||||
|
WEAK symbol<unsigned int(unsigned int parentId, unsigned int unsignedValue)> GetNewVariable{0x5C22B0};
|
||||||
|
WEAK symbol<unsigned int(unsigned int parentId, unsigned int unsignedValue)> GetNewArrayVariable{0x5C2130};
|
||||||
|
WEAK symbol<void(unsigned int parentId, unsigned int id, VariableValue* value)> SetNewVariableValue{0x5C5EA0};
|
||||||
|
WEAK symbol<void(unsigned int parentId, unsigned int index)> RemoveVariableValue{0x5C2A50};
|
||||||
|
|
||||||
WEAK symbol<unsigned int(const char* name)> G_GetWeaponForName{0x51B260};
|
WEAK symbol<unsigned int(const char* name)> G_GetWeaponForName{0x51B260};
|
||||||
WEAK symbol<int(void* ps, unsigned int weapon, int a3, int a4, __int64 a5, int a6)>
|
WEAK symbol<int(void* ps, unsigned int weapon, int a3, int a4, __int64 a5, int a6)>
|
||||||
@ -80,6 +86,7 @@ namespace game
|
|||||||
WEAK symbol<scr_entref_t(unsigned int entId)> Scr_GetEntityIdRef{0x5C56C0};
|
WEAK symbol<scr_entref_t(unsigned int entId)> Scr_GetEntityIdRef{0x5C56C0};
|
||||||
WEAK symbol<int(unsigned int classnum, int entnum, int offset)> Scr_SetObjectField{0x512190};
|
WEAK symbol<int(unsigned int classnum, int entnum, int offset)> Scr_SetObjectField{0x512190};
|
||||||
WEAK symbol<void(unsigned int id, scr_string_t stringValue, unsigned int paramcount)> Scr_NotifyId{0x5C8240};
|
WEAK symbol<void(unsigned int id, scr_string_t stringValue, unsigned int paramcount)> Scr_NotifyId{0x5C8240};
|
||||||
|
WEAK symbol<unsigned int(unsigned int threadId)> Scr_GetSelf{0x5C57C0};
|
||||||
|
|
||||||
WEAK symbol<unsigned int(unsigned int localId, const char* pos, unsigned int paramcount)> VM_Execute{0x5C8DB0};
|
WEAK symbol<unsigned int(unsigned int localId, const char* pos, unsigned int paramcount)> VM_Execute{0x5C8DB0};
|
||||||
|
|
||||||
|
@ -105,6 +105,22 @@ namespace utils::string
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_clipboard_data(const std::string& text)
|
||||||
|
{
|
||||||
|
const auto len = text.size() + 1;
|
||||||
|
const auto mem = GlobalAlloc(GMEM_MOVEABLE, len);
|
||||||
|
|
||||||
|
memcpy(GlobalLock(mem), text.data(), len);
|
||||||
|
GlobalUnlock(mem);
|
||||||
|
|
||||||
|
if (OpenClipboard(nullptr))
|
||||||
|
{
|
||||||
|
EmptyClipboard();
|
||||||
|
SetClipboardData(CF_TEXT, mem);
|
||||||
|
CloseClipboard();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void strip(const char* in, char* out, int max)
|
void strip(const char* in, char* out, int max)
|
||||||
{
|
{
|
||||||
if (!in || !out) return;
|
if (!in || !out) return;
|
||||||
|
@ -90,6 +90,7 @@ namespace utils::string
|
|||||||
std::string dump_hex(const std::string& data, const std::string& separator = " ");
|
std::string dump_hex(const std::string& data, const std::string& separator = " ");
|
||||||
|
|
||||||
std::string get_clipboard_data();
|
std::string get_clipboard_data();
|
||||||
|
void set_clipboard_data(const std::string& text);
|
||||||
|
|
||||||
void strip(const char* in, char* out, int max);
|
void strip(const char* in, char* out, int max);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user