2021-12-19 19:53:01 -05:00
|
|
|
#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>
|
|
|
|
|
2022-03-27 13:29:28 -04:00
|
|
|
namespace gui::entity_list
|
2021-12-19 19:53:01 -05:00
|
|
|
{
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
enum entity_type
|
|
|
|
{
|
2022-01-04 11:57:09 -05:00
|
|
|
entity,
|
2021-12-19 19:53:01 -05:00
|
|
|
actor,
|
|
|
|
spawner,
|
|
|
|
weapon,
|
2022-01-04 11:57:09 -05:00
|
|
|
vehicle,
|
2021-12-19 19:53:01 -05:00
|
|
|
node,
|
2022-01-04 11:57:09 -05:00
|
|
|
vehicle_node,
|
2021-12-19 19:53:01 -05:00
|
|
|
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;
|
2022-01-04 11:57:09 -05:00
|
|
|
game::scr_entref_t entref;
|
2021-12-19 19:53:01 -05:00
|
|
|
std::unordered_map<std::string, std::string> fields;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct filters_t
|
|
|
|
{
|
2021-12-28 11:14:47 -05:00
|
|
|
bool filter_by_range;
|
|
|
|
float range;
|
|
|
|
entity_team team;
|
|
|
|
entity_type type;
|
2021-12-19 19:53:01 -05:00
|
|
|
std::vector<std::pair<std::string, std::string>> fields;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct data_t
|
|
|
|
{
|
2021-12-28 11:14:47 -05:00
|
|
|
bool auto_update;
|
|
|
|
bool force_update;
|
2021-12-19 19:53:01 -05:00
|
|
|
filters_t filters;
|
2021-12-28 11:14:47 -05:00
|
|
|
std::chrono::milliseconds interval;
|
|
|
|
std::chrono::high_resolution_clock::time_point last_call;
|
|
|
|
std::vector<entity_info_t> entity_info;
|
2021-12-20 03:59:57 -05:00
|
|
|
std::vector<std::function<void()>> tasks;
|
2021-12-19 19:53:01 -05:00
|
|
|
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},
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2021-12-28 11:14:47 -05:00
|
|
|
utils::concurrency::container<data_t> data_{};
|
2022-01-04 11:57:09 -05:00
|
|
|
game::scr_entref_t selected_entity{};
|
2021-12-20 03:59:57 -05:00
|
|
|
bool set_field_window{};
|
2021-12-22 16:25:41 -05:00
|
|
|
bool selected_fields_window{};
|
2021-12-20 03:59:57 -05:00
|
|
|
int selected_type = game::SCRIPT_INTEGER;
|
|
|
|
|
2021-12-22 16:25:41 -05:00
|
|
|
std::string field_filter{};
|
|
|
|
std::string field_name_buffer{};
|
|
|
|
std::string field_value_buffer{};
|
|
|
|
std::string vector_input[3]{};
|
2021-12-19 19:53:01 -05:00
|
|
|
|
|
|
|
std::optional<scripting::array> get_entity_array(const entity_type type, const entity_team team)
|
|
|
|
{
|
2022-01-04 11:57:09 -05:00
|
|
|
if (type == entity_type::entity)
|
2021-12-19 19:53:01 -05:00
|
|
|
{
|
2022-01-04 11:57:09 -05:00
|
|
|
return {scripting::call("getentarray").as<scripting::array>()};
|
2021-12-19 19:53:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (type == entity_type::actor)
|
|
|
|
{
|
|
|
|
scripting::array result{};
|
|
|
|
|
2022-01-04 11:57:09 -05:00
|
|
|
const auto actors = scripting::call("getaiarray").as<scripting::array>();
|
|
|
|
for (auto i = 0; i < actors.size(); i++)
|
2021-12-19 19:53:01 -05:00
|
|
|
{
|
2022-01-04 11:57:09 -05:00
|
|
|
const auto entity = actors[i].as<scripting::entity>();
|
|
|
|
const auto team_string = entity.get("team").as<std::string>();
|
2021-12-19 19:53:01 -05:00
|
|
|
|
2022-01-04 11:57:09 -05:00
|
|
|
if (team == entity_team::team_any || team_string == team_names[team])
|
2021-12-19 19:53:01 -05:00
|
|
|
{
|
|
|
|
result.push(entity);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-04 11:57:09 -05:00
|
|
|
return {result};
|
2021-12-19 19:53:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (type == entity_type::spawner && team == entity_team::team_any)
|
|
|
|
{
|
2022-01-04 11:57:09 -05:00
|
|
|
return {scripting::call("getspawnerarray").as<scripting::array>()};
|
2021-12-19 19:53:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (type == entity_type::spawner)
|
|
|
|
{
|
2022-01-04 11:57:09 -05:00
|
|
|
return {scripting::call("getspawnerteamarray", {team_names[team]}).as<scripting::array>()};
|
2021-12-19 19:53:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (type == entity_type::weapon)
|
|
|
|
{
|
2022-01-04 11:57:09 -05:00
|
|
|
return {scripting::call("getweaponarray").as<scripting::array>()};
|
2021-12-19 19:53:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (type == entity_type::node)
|
|
|
|
{
|
2022-01-04 11:57:09 -05:00
|
|
|
return {scripting::call("getallnodes").as<scripting::array>()};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == entity_type::vehicle_node)
|
|
|
|
{
|
|
|
|
return {scripting::call("getallvehiclenodes").as<scripting::array>()};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == entity_type::vehicle)
|
|
|
|
{
|
|
|
|
return {scripting::call("vehicle_getarray").as<scripting::array>()};
|
2021-12-19 19:53:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
void update_entity_list()
|
|
|
|
{
|
|
|
|
data_.access([](data_t& data)
|
|
|
|
{
|
2021-12-20 03:59:57 -05:00
|
|
|
for (const auto& task : data.tasks)
|
|
|
|
{
|
|
|
|
task();
|
|
|
|
}
|
|
|
|
|
|
|
|
data.tasks = {};
|
|
|
|
|
2021-12-19 19:53:01 -05:00
|
|
|
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();
|
2022-01-04 11:57:09 -05:00
|
|
|
const scripting::entity player{{0, 0}};
|
2021-12-19 19:53:01 -05:00
|
|
|
|
2022-01-03 15:05:46 -05:00
|
|
|
for (auto i = 0; i < array.size(); i++)
|
2021-12-19 19:53:01 -05:00
|
|
|
{
|
|
|
|
const auto entity = array[i].as<scripting::entity>();
|
|
|
|
entity_info_t info{};
|
|
|
|
|
2022-01-04 11:57:09 -05:00
|
|
|
info.id = entity.get_entity_id();
|
|
|
|
info.entref = entity.get_entity_reference();
|
2021-12-19 19:53:01 -05:00
|
|
|
|
|
|
|
if (data.filters.filter_by_range)
|
|
|
|
{
|
|
|
|
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>() &&
|
2021-12-21 09:21:50 -05:00
|
|
|
utils::string::find_lower(field.first, filter.first) &&
|
|
|
|
utils::string::find_lower(value_string, filter.second))
|
2021-12-19 19:53:01 -05:00
|
|
|
{
|
|
|
|
match_count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (match_count == data.filters.fields.size())
|
|
|
|
{
|
|
|
|
data.entity_info.push_back(info);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-01-04 11:57:09 -05:00
|
|
|
void teleport_to(data_t& data, game::scr_entref_t entref)
|
2021-12-19 19:53:01 -05:00
|
|
|
{
|
2022-01-04 11:57:09 -05:00
|
|
|
data.tasks.push_back([entref]()
|
2021-12-19 19:53:01 -05:00
|
|
|
{
|
2022-01-02 10:53:17 -05:00
|
|
|
try
|
2021-12-19 19:53:01 -05:00
|
|
|
{
|
2022-01-04 11:57:09 -05:00
|
|
|
const scripting::entity entity{entref};
|
|
|
|
const scripting::entity player{{0, 0}};
|
2022-01-02 10:53:17 -05:00
|
|
|
player.call("setorigin", {entity.get("origin")});
|
2021-12-19 19:53:01 -05:00
|
|
|
}
|
2022-01-02 10:53:17 -05:00
|
|
|
catch (...)
|
2021-12-19 19:53:01 -05:00
|
|
|
{
|
2022-01-04 11:57:09 -05:00
|
|
|
gui::notification("Error", utils::string::va("^1error teleporting player to entity num %i!", entref.entnum));
|
2021-12-19 19:53:01 -05:00
|
|
|
}
|
2021-12-20 03:59:57 -05:00
|
|
|
});
|
2021-12-19 19:53:01 -05:00
|
|
|
}
|
|
|
|
|
2022-01-04 11:57:09 -05:00
|
|
|
void teleport_to_reverse(data_t& data, game::scr_entref_t entref)
|
2021-12-19 19:53:01 -05:00
|
|
|
{
|
2022-01-04 11:57:09 -05:00
|
|
|
data.tasks.push_back([entref]()
|
2021-12-19 19:53:01 -05:00
|
|
|
{
|
2022-01-02 10:53:17 -05:00
|
|
|
try
|
2021-12-19 19:53:01 -05:00
|
|
|
{
|
2022-01-04 11:57:09 -05:00
|
|
|
const scripting::entity entity{entref};
|
|
|
|
const scripting::entity player{{0, 0}};
|
2022-01-02 10:53:17 -05:00
|
|
|
entity.set("origin", player.get("origin"));
|
2021-12-19 19:53:01 -05:00
|
|
|
}
|
2022-01-02 10:53:17 -05:00
|
|
|
catch (...)
|
2021-12-19 19:53:01 -05:00
|
|
|
{
|
2022-01-04 11:57:09 -05:00
|
|
|
gui::notification("Error", utils::string::va("^1error teleporting entity num %i to player!", entref.entnum));
|
2021-12-19 19:53:01 -05:00
|
|
|
}
|
2021-12-20 03:59:57 -05:00
|
|
|
});
|
2021-12-19 19:53:01 -05:00
|
|
|
}
|
|
|
|
|
2022-01-04 11:57:09 -05:00
|
|
|
void delete_entity(data_t& data, game::scr_entref_t entref)
|
2021-12-19 19:53:01 -05:00
|
|
|
{
|
2022-01-04 11:57:09 -05:00
|
|
|
data.tasks.push_back([entref]()
|
2021-12-19 19:53:01 -05:00
|
|
|
{
|
2022-01-02 10:53:17 -05:00
|
|
|
try
|
2021-12-19 19:53:01 -05:00
|
|
|
{
|
2022-01-04 11:57:09 -05:00
|
|
|
const scripting::entity entity{entref};
|
2022-01-02 10:53:17 -05:00
|
|
|
entity.call("delete");
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
2022-01-04 11:57:09 -05:00
|
|
|
gui::notification("Error", utils::string::va("^1error deleting entity num %i!", entref.entnum));
|
2021-12-19 19:53:01 -05:00
|
|
|
}
|
2021-12-20 03:59:57 -05:00
|
|
|
});
|
2021-12-19 19:53:01 -05:00
|
|
|
}
|
|
|
|
|
2022-01-04 11:57:09 -05:00
|
|
|
void set_entity_field(data_t& data, game::scr_entref_t entref,
|
2021-12-20 03:59:57 -05:00
|
|
|
const std::string& name, const std::string& string_value, int type)
|
2021-12-19 19:53:01 -05:00
|
|
|
{
|
2022-01-04 11:57:09 -05:00
|
|
|
data.tasks.push_back([entref, name, type, string_value, &data]()
|
2021-12-19 19:53:01 -05:00
|
|
|
{
|
2021-12-20 03:59:57 -05:00
|
|
|
scripting::script_value value{};
|
|
|
|
|
|
|
|
if (type == game::SCRIPT_INTEGER)
|
|
|
|
{
|
|
|
|
value = atoi(string_value.data());
|
|
|
|
}
|
2021-12-19 19:53:01 -05:00
|
|
|
|
2021-12-20 03:59:57 -05:00
|
|
|
if (type == game::SCRIPT_FLOAT)
|
2021-12-19 19:53:01 -05:00
|
|
|
{
|
2021-12-20 03:59:57 -05:00
|
|
|
value = atof(string_value.data());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == game::SCRIPT_STRING)
|
|
|
|
{
|
|
|
|
value = string_value;
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2022-01-04 11:57:09 -05:00
|
|
|
const scripting::entity entity{entref};
|
2022-01-02 10:53:17 -05:00
|
|
|
scripting::set_entity_field(entity, name, value);
|
2021-12-19 19:53:01 -05:00
|
|
|
data.force_update = true;
|
|
|
|
}
|
2021-12-20 03:59:57 -05:00
|
|
|
catch (...)
|
|
|
|
{
|
2021-12-20 12:58:23 -05:00
|
|
|
gui::notification("Error", utils::string::va("^1error setting field '%s'!", name.data()));
|
2021-12-20 03:59:57 -05:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2021-12-19 19:53:01 -05:00
|
|
|
|
2022-01-04 11:57:09 -05:00
|
|
|
void set_entity_field_vector(data_t& data, game::scr_entref_t entref,
|
2021-12-20 03:59:57 -05:00
|
|
|
const std::string& name, const scripting::vector& value)
|
|
|
|
{
|
2022-01-04 11:57:09 -05:00
|
|
|
data.tasks.push_back([entref, name, value, &data]()
|
2021-12-20 03:59:57 -05:00
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2022-01-04 11:57:09 -05:00
|
|
|
const scripting::entity entity{entref};
|
2022-01-02 10:53:17 -05:00
|
|
|
scripting::set_entity_field(entity, name, value);
|
2021-12-20 03:59:57 -05:00
|
|
|
data.force_update = true;
|
|
|
|
}
|
|
|
|
catch (...)
|
2021-12-19 19:53:01 -05:00
|
|
|
{
|
2021-12-20 12:58:23 -05:00
|
|
|
gui::notification("Error", utils::string::va("^1error setting field '%s'!", name.data()));
|
2021-12-19 19:53:01 -05:00
|
|
|
}
|
2021-12-20 03:59:57 -05:00
|
|
|
});
|
|
|
|
}
|
2021-12-19 19:53:01 -05:00
|
|
|
|
2021-12-20 03:59:57 -05:00
|
|
|
void show_set_field_window(data_t& data)
|
|
|
|
{
|
|
|
|
ImGui::SetNextWindowSizeConstraints(ImVec2(300, 300), ImVec2(300, 300));
|
|
|
|
ImGui::Begin("Set entity field", &set_field_window);
|
|
|
|
ImGui::SetWindowSize(ImVec2(300, 300));
|
|
|
|
ImGui::Text(utils::string::va("Entity id %i", selected_entity));
|
|
|
|
|
|
|
|
if (ImGui::TreeNode("Type"))
|
|
|
|
{
|
|
|
|
ImGui::RadioButton("integer", &selected_type, game::SCRIPT_INTEGER);
|
|
|
|
ImGui::RadioButton("float", &selected_type, game::SCRIPT_FLOAT);
|
|
|
|
ImGui::RadioButton("vector", &selected_type, game::SCRIPT_VECTOR);
|
|
|
|
ImGui::RadioButton("string", &selected_type, game::SCRIPT_STRING);
|
2021-12-19 19:53:01 -05:00
|
|
|
|
2021-12-20 03:59:57 -05:00
|
|
|
ImGui::TreePop();
|
|
|
|
}
|
|
|
|
|
2021-12-22 16:25:41 -05:00
|
|
|
ImGui::InputText("name", &field_name_buffer);
|
2021-12-20 03:59:57 -05:00
|
|
|
if (selected_type == game::SCRIPT_VECTOR)
|
|
|
|
{
|
2021-12-22 16:25:41 -05:00
|
|
|
ImGui::InputText("x", &vector_input[0]);
|
|
|
|
ImGui::InputText("y", &vector_input[1]);
|
|
|
|
ImGui::InputText("z", &vector_input[2]);
|
2021-12-20 03:59:57 -05:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-12-22 16:25:41 -05:00
|
|
|
ImGui::InputText("value", &field_value_buffer);
|
2021-12-20 03:59:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ImGui::Button("set", ImVec2(300, 0)))
|
|
|
|
{
|
|
|
|
if (selected_type == game::SCRIPT_VECTOR)
|
2021-12-19 19:53:01 -05:00
|
|
|
{
|
2021-12-20 03:59:57 -05:00
|
|
|
const scripting::vector vector
|
|
|
|
{
|
2021-12-22 16:25:41 -05:00
|
|
|
static_cast<float>(atof(vector_input[0].data())),
|
|
|
|
static_cast<float>(atof(vector_input[1].data())),
|
|
|
|
static_cast<float>(atof(vector_input[2].data()))
|
2021-12-20 03:59:57 -05:00
|
|
|
};
|
2021-12-19 19:53:01 -05:00
|
|
|
|
2021-12-22 16:25:41 -05:00
|
|
|
set_entity_field_vector(data, selected_entity,
|
|
|
|
field_name_buffer, vector);
|
2021-12-20 03:59:57 -05:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-12-22 16:25:41 -05:00
|
|
|
set_entity_field(data, selected_entity, field_name_buffer,
|
|
|
|
field_value_buffer, selected_type);
|
2021-12-20 03:59:57 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::End();
|
|
|
|
}
|
|
|
|
|
|
|
|
void show_entity_list_window(data_t& data)
|
|
|
|
{
|
2022-03-27 13:29:28 -04:00
|
|
|
static auto* enabled = &gui::enabled_menus["entity_list"];
|
2021-12-20 03:59:57 -05:00
|
|
|
ImGui::SetNextWindowSizeConstraints(ImVec2(500, 500), ImVec2(1000, 1000));
|
2022-03-27 13:29:28 -04:00
|
|
|
ImGui::Begin("Entity list", enabled);
|
2021-12-20 03:59:57 -05:00
|
|
|
|
|
|
|
if (ImGui::Button("Update list"))
|
|
|
|
{
|
|
|
|
data.force_update = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::Checkbox("Auto update", &data.auto_update);
|
2021-12-22 16:25:41 -05:00
|
|
|
ImGui::Checkbox("Field list", &selected_fields_window);
|
2021-12-20 03:59:57 -05:00
|
|
|
|
|
|
|
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))
|
2021-12-19 19:53:01 -05:00
|
|
|
{
|
2021-12-20 03:59:57 -05:00
|
|
|
data.force_update = true;
|
2021-12-19 19:53:01 -05:00
|
|
|
}
|
2021-12-20 03:59:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ImGui::TreeNode("Entity type"))
|
|
|
|
{
|
|
|
|
auto result = 0;
|
2021-12-19 19:53:01 -05:00
|
|
|
|
2022-01-04 11:57:09 -05:00
|
|
|
result += ImGui::RadioButton("entity", reinterpret_cast<int*>(&data.filters.type), entity_type::entity);
|
2021-12-20 03:59:57 -05:00
|
|
|
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);
|
2022-01-04 11:57:09 -05:00
|
|
|
result += ImGui::RadioButton("vehicle", reinterpret_cast<int*>(&data.filters.type), entity_type::vehicle);
|
|
|
|
result += ImGui::RadioButton("path node", reinterpret_cast<int*>(&data.filters.type), entity_type::node);
|
|
|
|
result += ImGui::RadioButton("vehicle node", reinterpret_cast<int*>(&data.filters.type), entity_type::vehicle_node);
|
2021-12-20 03:59:57 -05:00
|
|
|
|
|
|
|
if (result)
|
2021-12-19 19:53:01 -05:00
|
|
|
{
|
2021-12-20 03:59:57 -05:00
|
|
|
data.force_update = true;
|
|
|
|
}
|
2021-12-19 19:53:01 -05:00
|
|
|
|
2021-12-20 03:59:57 -05:00
|
|
|
ImGui::TreePop();
|
|
|
|
}
|
2021-12-19 19:53:01 -05:00
|
|
|
|
2022-01-04 11:57:09 -05:00
|
|
|
if ((data.filters.type == entity_type::actor || data.filters.type == entity_type::spawner)
|
|
|
|
&& ImGui::TreeNode("Entity team"))
|
2021-12-20 03:59:57 -05:00
|
|
|
{
|
|
|
|
auto result = 0;
|
2021-12-19 19:53:01 -05:00
|
|
|
|
2021-12-20 03:59:57 -05:00
|
|
|
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);
|
2021-12-19 19:53:01 -05:00
|
|
|
|
2021-12-20 03:59:57 -05:00
|
|
|
if (result)
|
2021-12-19 19:53:01 -05:00
|
|
|
{
|
2021-12-20 03:59:57 -05:00
|
|
|
data.force_update = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::TreePop();
|
|
|
|
}
|
2021-12-19 19:53:01 -05:00
|
|
|
|
2021-12-20 03:59:57 -05:00
|
|
|
ImGui::Text("Fields");
|
2021-12-19 19:53:01 -05:00
|
|
|
|
2021-12-20 03:59:57 -05:00
|
|
|
auto index = 0;
|
2022-01-07 19:28:46 -05:00
|
|
|
for (auto i = data.filters.fields.begin(); i != data.filters.fields.end();)
|
2021-12-20 03:59:57 -05:00
|
|
|
{
|
|
|
|
if (ImGui::TreeNode(utils::string::va("Filter #%i", index++)))
|
|
|
|
{
|
|
|
|
ImGui::InputText("name", &i->first);
|
|
|
|
ImGui::InputText("value", &i->second);
|
|
|
|
|
|
|
|
if (ImGui::Button("Erase"))
|
2021-12-19 19:53:01 -05:00
|
|
|
{
|
2022-01-07 19:28:46 -05:00
|
|
|
i = data.filters.fields.erase(i);
|
|
|
|
ImGui::TreePop();
|
|
|
|
continue;
|
2021-12-19 19:53:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::TreePop();
|
|
|
|
}
|
2022-01-07 19:28:46 -05:00
|
|
|
|
|
|
|
++i;
|
2021-12-20 03:59:57 -05:00
|
|
|
}
|
2021-12-19 19:53:01 -05:00
|
|
|
|
2021-12-20 03:59:57 -05:00
|
|
|
if (ImGui::Button("Add field filter"))
|
|
|
|
{
|
|
|
|
data.filters.fields.push_back({});
|
|
|
|
}
|
|
|
|
}
|
2021-12-19 19:53:01 -05:00
|
|
|
|
2021-12-20 03:59:57 -05:00
|
|
|
ImGui::Separator();
|
2021-12-22 16:25:41 -05:00
|
|
|
ImGui::BeginChild("entity list");
|
2021-12-19 19:53:01 -05:00
|
|
|
|
2021-12-20 03:59:57 -05:00
|
|
|
for (const auto& info : data.entity_info)
|
|
|
|
{
|
2022-01-04 11:57:09 -05:00
|
|
|
if (ImGui::TreeNode(utils::string::va("Entity num %i class %i",
|
|
|
|
info.entref.entnum, info.entref.classnum)))
|
2021-12-20 03:59:57 -05:00
|
|
|
{
|
2021-12-22 16:25:41 -05:00
|
|
|
ImGui::Text("Info");
|
|
|
|
|
2022-01-04 11:57:09 -05:00
|
|
|
const auto num_str = utils::string::va("%i", info.entref.entnum);
|
|
|
|
const auto classnum_str = utils::string::va("%i", info.entref.classnum);
|
2021-12-22 16:25:41 -05:00
|
|
|
|
2022-01-04 11:57:09 -05:00
|
|
|
ImGui::Text("Entity number");
|
2021-12-22 16:25:41 -05:00
|
|
|
ImGui::SameLine();
|
2022-01-04 11:57:09 -05:00
|
|
|
|
2021-12-22 16:25:41 -05:00
|
|
|
if (ImGui::Button(num_str))
|
|
|
|
{
|
|
|
|
gui::copy_to_clipboard(num_str);
|
|
|
|
}
|
|
|
|
|
2022-01-04 11:57:09 -05:00
|
|
|
ImGui::Text("Entity class");
|
|
|
|
ImGui::SameLine();
|
|
|
|
|
|
|
|
if (ImGui::Button(classnum_str))
|
|
|
|
{
|
|
|
|
gui::copy_to_clipboard(classnum_str);
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto entity_code = utils::string::va("game:getentbyref(%i, %i)",
|
|
|
|
info.entref.entnum, info.entref.classnum);
|
|
|
|
|
2022-01-03 17:32:53 -05:00
|
|
|
if (ImGui::Button(entity_code))
|
|
|
|
{
|
|
|
|
gui::copy_to_clipboard(entity_code);
|
|
|
|
}
|
|
|
|
|
2021-12-20 03:59:57 -05:00
|
|
|
ImGui::Text("Commands");
|
2021-12-19 19:53:01 -05:00
|
|
|
|
2021-12-20 03:59:57 -05:00
|
|
|
if (ImGui::Button("Set field"))
|
|
|
|
{
|
|
|
|
set_field_window = true;
|
2022-01-04 11:57:09 -05:00
|
|
|
selected_entity = info.entref;
|
2021-12-19 19:53:01 -05:00
|
|
|
}
|
|
|
|
|
2021-12-20 03:59:57 -05:00
|
|
|
if (ImGui::Button("Teleport to"))
|
2021-12-19 19:53:01 -05:00
|
|
|
{
|
2022-01-04 11:57:09 -05:00
|
|
|
teleport_to(data, info.entref);
|
2021-12-20 03:59:57 -05:00
|
|
|
data.force_update = true;
|
2021-12-19 19:53:01 -05:00
|
|
|
}
|
|
|
|
|
2021-12-20 03:59:57 -05:00
|
|
|
if (ImGui::Button("Teleport to you"))
|
|
|
|
{
|
2022-01-04 11:57:09 -05:00
|
|
|
teleport_to_reverse(data, info.entref);
|
2021-12-20 03:59:57 -05:00
|
|
|
data.force_update = true;
|
|
|
|
}
|
2021-12-19 19:53:01 -05:00
|
|
|
|
2022-01-04 11:57:09 -05:00
|
|
|
if (info.entref.classnum == 0 && info.entref.entnum != 0 && ImGui::Button("Delete"))
|
2021-12-19 19:53:01 -05:00
|
|
|
{
|
2022-01-04 11:57:09 -05:00
|
|
|
delete_entity(data, info.entref);
|
2021-12-20 03:59:57 -05:00
|
|
|
data.force_update = true;
|
|
|
|
}
|
2021-12-19 19:53:01 -05:00
|
|
|
|
2021-12-20 03:59:57 -05:00
|
|
|
ImGui::Text("Fields");
|
2021-12-19 19:53:01 -05:00
|
|
|
|
2021-12-20 03:59:57 -05:00
|
|
|
for (const auto& field : info.fields)
|
|
|
|
{
|
|
|
|
if (field.second.empty())
|
2021-12-19 19:53:01 -05:00
|
|
|
{
|
2021-12-20 03:59:57 -05:00
|
|
|
continue;
|
2021-12-19 19:53:01 -05:00
|
|
|
}
|
|
|
|
|
2021-12-20 03:59:57 -05:00
|
|
|
if (ImGui::Button(field.first.data()))
|
2021-12-19 19:53:01 -05:00
|
|
|
{
|
2021-12-21 09:21:50 -05:00
|
|
|
gui::copy_to_clipboard(field.first);
|
2021-12-19 19:53:01 -05:00
|
|
|
}
|
|
|
|
|
2021-12-20 03:59:57 -05:00
|
|
|
ImGui::SameLine();
|
2021-12-19 19:53:01 -05:00
|
|
|
|
2021-12-20 03:59:57 -05:00
|
|
|
if (ImGui::Button(field.second.data()))
|
2021-12-19 19:53:01 -05:00
|
|
|
{
|
2021-12-21 09:21:50 -05:00
|
|
|
gui::copy_to_clipboard(field.second);
|
2021-12-20 03:59:57 -05:00
|
|
|
}
|
|
|
|
}
|
2021-12-19 19:53:01 -05:00
|
|
|
|
2021-12-20 03:59:57 -05:00
|
|
|
ImGui::TreePop();
|
|
|
|
}
|
|
|
|
}
|
2021-12-19 19:53:01 -05:00
|
|
|
|
2021-12-22 16:25:41 -05:00
|
|
|
ImGui::EndChild();
|
2021-12-20 03:59:57 -05:00
|
|
|
ImGui::End();
|
2021-12-22 16:25:41 -05:00
|
|
|
}
|
2021-12-19 19:53:01 -05:00
|
|
|
|
2021-12-22 16:25:41 -05:00
|
|
|
void show_selected_fields_window(data_t& data)
|
|
|
|
{
|
2021-12-20 03:59:57 -05:00
|
|
|
ImGui::SetNextWindowSizeConstraints(ImVec2(500, 500), ImVec2(1000, 1000));
|
2021-12-22 16:25:41 -05:00
|
|
|
ImGui::Begin("Selected fields", &selected_fields_window);
|
|
|
|
|
|
|
|
ImGui::InputText("field name", &field_filter);
|
|
|
|
ImGui::BeginChild("field list");
|
2021-12-19 19:53:01 -05:00
|
|
|
|
2021-12-20 03:59:57 -05:00
|
|
|
for (auto& field : data.selected_fields)
|
|
|
|
{
|
2021-12-22 16:25:41 -05:00
|
|
|
if (utils::string::find_lower(field.first, field_filter) &&
|
2021-12-21 09:21:50 -05:00
|
|
|
ImGui::Checkbox(field.first.data(), &field.second))
|
2021-12-20 03:59:57 -05:00
|
|
|
{
|
|
|
|
data.force_update = true;
|
2021-12-19 19:53:01 -05:00
|
|
|
}
|
2021-12-20 03:59:57 -05:00
|
|
|
}
|
2021-12-19 19:53:01 -05:00
|
|
|
|
2021-12-22 16:25:41 -05:00
|
|
|
ImGui::EndChild();
|
2021-12-20 03:59:57 -05:00
|
|
|
ImGui::End();
|
|
|
|
}
|
2021-12-19 19:53:01 -05:00
|
|
|
|
2021-12-20 03:59:57 -05:00
|
|
|
void on_frame()
|
|
|
|
{
|
2022-03-27 13:29:28 -04:00
|
|
|
static auto* enabled = &gui::enabled_menus["entity_list"];
|
|
|
|
if (!*enabled)
|
2021-12-20 03:59:57 -05:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2021-12-19 19:53:01 -05:00
|
|
|
|
2021-12-20 03:59:57 -05:00
|
|
|
data_.access([](data_t& data)
|
|
|
|
{
|
2021-12-22 16:25:41 -05:00
|
|
|
if (!game::CL_IsCgameInitialized())
|
2021-12-19 19:53:01 -05:00
|
|
|
{
|
2022-01-04 11:57:09 -05:00
|
|
|
selected_entity = {};
|
2021-12-20 03:59:57 -05:00
|
|
|
data.entity_info = {};
|
|
|
|
data.tasks = {};
|
2021-12-19 19:53:01 -05:00
|
|
|
}
|
|
|
|
|
2022-01-02 10:53:17 -05:00
|
|
|
|
2021-12-20 03:59:57 -05:00
|
|
|
show_entity_list_window(data);
|
2021-12-22 16:25:41 -05:00
|
|
|
|
|
|
|
if (selected_fields_window)
|
|
|
|
{
|
|
|
|
show_selected_fields_window(data);
|
|
|
|
}
|
|
|
|
|
2022-01-02 17:51:05 -05:00
|
|
|
if (set_field_window)
|
2021-12-20 03:59:57 -05:00
|
|
|
{
|
|
|
|
show_set_field_window(data);
|
|
|
|
}
|
2021-12-19 19:53:01 -05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class component final : public component_interface
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
void post_unpack() override
|
|
|
|
{
|
|
|
|
gui::on_frame(on_frame);
|
2022-01-02 10:53:17 -05:00
|
|
|
scheduler::loop([]()
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
update_entity_list();
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
}, scheduler::pipeline::server);
|
2021-12-19 19:53:01 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-03-27 13:29:28 -04:00
|
|
|
REGISTER_COMPONENT(gui::entity_list::component)
|