From a63ce4f2545c405096f6cd88e04e69602d0bc530 Mon Sep 17 00:00:00 2001 From: Federico Cecchetto Date: Thu, 30 Jun 2022 21:24:49 +0200 Subject: [PATCH] Mapents & scripting changes + some fixes --- src/client/component/gui_asset_list.cpp | 11 +- src/client/component/mapents.cpp | 65 +++-- src/client/game/scripting/animation.cpp | 2 +- src/client/game/scripting/animation.hpp | 2 +- src/client/game/scripting/execution.cpp | 25 +- src/client/game/scripting/function_tables.cpp | 251 +++++++++++++++++- src/client/game/scripting/lua/context.cpp | 5 + .../game/scripting/lua/value_conversion.cpp | 10 +- 8 files changed, 337 insertions(+), 34 deletions(-) diff --git a/src/client/component/gui_asset_list.cpp b/src/client/component/gui_asset_list.cpp index a5ece325..d94241cb 100644 --- a/src/client/component/gui_asset_list.cpp +++ b/src/client/component/gui_asset_list.cpp @@ -63,15 +63,22 @@ namespace asset_list ImGui::InputText("asset name", &assets_name_filter[type]); ImGui::BeginChild("assets list"); - fastfiles::enum_assets(type, [type](const game::XAssetHeader header) + size_t asset_num{}; + fastfiles::enum_assets(type, [type, &asset_num](const game::XAssetHeader header) { const auto asset = game::XAsset{type, header}; - const auto* const asset_name = game::DB_GetXAssetName(&asset); + auto asset_name = game::DB_GetXAssetName(&asset); + if (asset_name[0] == '\0') + { + asset_name = utils::string::va("__%i", asset_num); + } if (utils::string::strstr_lower(asset_name, assets_name_filter[type].data()) && ImGui::Button(asset_name)) { gui::copy_to_clipboard(asset_name); } + + asset_num++; }, true); ImGui::EndChild(); diff --git a/src/client/component/mapents.cpp b/src/client/component/mapents.cpp index 3a4a009d..b868de64 100644 --- a/src/client/component/mapents.cpp +++ b/src/client/component/mapents.cpp @@ -9,6 +9,7 @@ #include "scheduler.hpp" #include "mapents.hpp" #include "command.hpp" +#include "game/scripting/functions.hpp" #include #include @@ -22,7 +23,7 @@ namespace mapents game::dvar_t* addon_mapname = nullptr; utils::memory::allocator allocator; - std::unordered_map keys = + std::unordered_map keys = { {"code_classname", 172}, {"classname", 170}, @@ -50,7 +51,7 @@ namespace mapents {"skycolor", 34255}, {"suncolor", 1049}, {"sundirection", 1050}, - + {"modelscale", 23881}, {"export", 13703}, {"script_flag", 31190}, @@ -72,8 +73,15 @@ namespace mapents {"script_parameters", 31388}, {"script_combatmode", 31102}, {"script_ammo_clip", 31034}, + {"script_moveoverride", 31299}, + {"script_forcegoal", 31212}, + {"script_ammo_max", 31036}, }; + std::unordered_map custom_fields; + + unsigned int token_id_start = 0x16000; + // zonetool/iw4/addonmapents.cpp class asset_reader { @@ -202,29 +210,27 @@ namespace mapents empty = false; - auto key_id = 0; - if (key[0] != '"') + auto key_id = std::atoi(key.data()); + if (key_id != 0) { - key_id = std::atoi(key.data()); + out_buffer.append(utils::string::va("%i \"%s\"\n", key_id, value.data())); + continue; } - else if (key.size() >= 3 && key[key.size() - 1] == '"') - { - const auto key_ = key.substr(1, key.size() - 2); - if (keys.find(key_) == keys.end()) - { - console::warn("[addon_map_ents parser] Key '%s' not found, on line %i", key_.data(), line_index); - continue; - } - key_id = keys[key_];// - } - else + if (key.size() < 3 || (!key.starts_with("\"") || !key.ends_with("\""))) { console::warn("[addon_map_ents parser] Bad key '%s' on line %i", key.data(), line_index); continue; } - out_buffer.append(utils::string::va("%i \"%s\"\n", key_id, value.data())); + const auto key_ = key.substr(1, key.size() - 2); + if (keys.find(key_) == keys.end()) + { + console::warn("[addon_map_ents parser] Key '%s' not found, on line %i", key_.data(), line_index); + continue; + } + + out_buffer.append(utils::string::va("%i \"%s\"\n", keys[key_], value.data())); } return out_buffer; @@ -257,6 +263,7 @@ namespace mapents void try_parse_mapents(const std::string& path, const std::string& data, game::AddonMapEnts* mapents) { const auto parsed = parse_mapents(data); + utils::io::write_file("parsed_mapents.txt", parsed, false); mapents->entityString = allocator.duplicate_string(parsed.data()); mapents->numEntityChars = static_cast(parsed.size()) + 1; @@ -316,6 +323,26 @@ namespace mapents game::Com_Error(game::ERR_DROP, "CM_TriggerModelBounds: you are probably missing a mapents.triggers file"); } } + + void add_field(const std::string& name, game::scriptType_e type) + { + const auto id = token_id_start++; + custom_fields[id] = type; + keys[name] = id; + scripting::token_map[name] = id; + } + + utils::hook::detour scr_find_field_hook; + unsigned int scr_find_field_stub(unsigned int name, game::scriptType_e* type) + { + if (custom_fields.find(name) != custom_fields.end()) + { + *type = custom_fields[name]; + return name; + } + + return scr_find_field_hook.invoke(name, type); + } } void clear_dvars() @@ -333,6 +360,8 @@ namespace mapents public: void post_unpack() override { + scr_find_field_hook.create(0x1405C5240, scr_find_field_stub); + scheduler::once([]() { addon_mapname = dvars::register_string("addon_mapname", "", 0, ""); @@ -365,6 +394,8 @@ namespace mapents utils::hook::call(0x14058BDD3, db_find_xasset_header_stub); utils::hook::call(0x14058BD6B, should_load_addon_mapents); utils::hook::call(0x1406B3384, cm_trigger_model_bounds_stub); + + add_field("script_specialops", game::SCRIPT_INTEGER); } }; } diff --git a/src/client/game/scripting/animation.cpp b/src/client/game/scripting/animation.cpp index c13eed0b..82dd64af 100644 --- a/src/client/game/scripting/animation.cpp +++ b/src/client/game/scripting/animation.cpp @@ -5,7 +5,7 @@ namespace scripting { - animation::animation(unsigned int value) + animation::animation(uint64_t value) : value_(value) { } diff --git a/src/client/game/scripting/animation.hpp b/src/client/game/scripting/animation.hpp index a8ebc65c..338b46a7 100644 --- a/src/client/game/scripting/animation.hpp +++ b/src/client/game/scripting/animation.hpp @@ -7,7 +7,7 @@ namespace scripting class animation final { public: - animation(unsigned int value); + animation(uint64_t value); uint64_t get_value() const; private: diff --git a/src/client/game/scripting/execution.cpp b/src/client/game/scripting/execution.cpp index 969efc02..824bb671 100644 --- a/src/client/game/scripting/execution.cpp +++ b/src/client/game/scripting/execution.cpp @@ -39,6 +39,21 @@ namespace scripting return script_value(game::scr_VmPub->top[1 - game::scr_VmPub->outparamcount]); } + + bool is_entity_variable(const game::scr_entref_t& entref, const unsigned int id) + { + const auto type = game::scr_VarGlob->objectVariableValue[id].w.type; + if (entref.classnum == 0) + { + return type == game::SCRIPT_ENTITY; + } + else if (entref.classnum > 0) + { + return true; + } + + return false; + } } void push_value(const script_value& value) @@ -149,8 +164,9 @@ namespace scripting { const auto entref = entity.get_entity_reference(); const int id = get_field_id(entref.classnum, field); + const auto ent_id = entity.get_entity_id(); - if (id != -1) + if (id != -1 && is_entity_variable(entref, ent_id)) { stack_isolation _; push_value(value); @@ -165,7 +181,7 @@ namespace scripting } else { - set_object_variable(entity.get_entity_id(), field, value); + set_object_variable(ent_id, field, value); } } @@ -173,8 +189,9 @@ namespace scripting { const auto entref = entity.get_entity_reference(); const auto id = get_field_id(entref.classnum, field); + const auto ent_id = entity.get_entity_id(); - if (id != -1) + if (id != -1 && is_entity_variable(entref, ent_id)) { stack_isolation _; @@ -192,7 +209,7 @@ namespace scripting return value; } - return get_object_variable(entity.get_entity_id(), field); + return get_object_variable(ent_id, field); } unsigned int make_array() diff --git a/src/client/game/scripting/function_tables.cpp b/src/client/game/scripting/function_tables.cpp index 72a8f7ab..1c171769 100644 --- a/src/client/game/scripting/function_tables.cpp +++ b/src/client/game/scripting/function_tables.cpp @@ -411,9 +411,9 @@ namespace scripting {"objective_delete", 0x1E4}, // 0x50FA30 {"objective_state", 0x1E5}, // 0x50EFD0 {"objective_icon", 0x1E6}, // 0x50F010 - {"objective_position", 0x1E7}, // 0x50F110 - {"objective_current", 0x1E8}, // 0x50F1B0 - {"_func_1e9", 0x1E9}, // similar to objective_current_nomessage + {"_func_1e7", 0x1E7}, // 0x50F110 + {"objective_position", 0x1E8}, // 0x50F1B0 + {"objective_current", 0x1E9}, // similar to objective_current_nomessage {"weaponinventorytype", 0x1EA}, // 0x4EE760 {"weaponstartammo", 0x1EB}, // 0x4EE920 {"weaponmaxammo", 0x1EC}, // 0x4EEB40 @@ -1778,10 +1778,253 @@ namespace scripting std::unordered_map token_map = { + {"maps/_utility", 42407}, + {"common_scripts/utility", 42237}, + {"maps/_load", 42323}, + {"maps/_compass", 42272}, + {"maps/_spawner", 42372}, + {"init", 521}, {"main", 616}, {"player", 794}, {"default_start", 10126}, - {"maps/_utility", 42407}, + {"setupminimap", 33575}, + {"set_player_viewhand_model", 32417}, + + // built-in entity fields + {"code_classname", 172}, + {"classname", 170}, + {"model", 669}, + {"count", 216}, + {"health", 486}, + {"dmg", 293}, + {"maxhealth", 626}, + {"anglelerprate", 64}, + {"activator", 19}, + {"slidevelocity", 974}, + {"disableplayeradsloscheck", 291}, + {"accuracy", 10}, + {"lookforward", 604}, + {"lookright", 605}, + {"lookup", 607}, + {"fovcosine", 411}, + {"fovcosinebusy", 412}, + {"fovcosinez", 413}, + {"upaimlimit", 1252}, + {"downaimlimit", 307}, + {"rightaimlimit", 894}, + {"leftaimlimit", 590}, + {"maxsightdistsqrd", 628}, + {"sightlatency", 967}, + {"defaultsightlatency", 968}, + {"ignoreclosefoliage", 508}, + {"interval", 525}, + {"teammovewaittime", 1199}, + {"damagetaken", 257}, + {"damagedir", 252}, + {"damageyaw", 259}, + {"damagelocation", 253}, + {"damageweapon", 258}, + {"damagemod", 254}, + {"proneok", 841}, + {"walkdistfacingmotion", 1299}, + {"walkdist", 1298}, + {"desiredangle", 278}, + {"pacifist", 744}, + {"pacifistwait", 745}, + {"footstepdetectdist", 398}, + {"footstepdetectdistwalk", 400}, + {"footstepdetectdistsprint", 399}, + {"reactiontargetpos", 859}, + {"newenemyreactiondistsq", 686}, + {"ignoreexplosionevents", 509}, + {"ignoresuppression", 513}, + {"suppressionwait", 1060}, + {"suppressionduration", 1056}, + {"suppressionstarttime", 1058}, + {"suppressionmeter", 1057}, + {"ignoreplayersuppression", 514}, + {"name", 680}, + {"weapon", 1302}, + {"dontavoidplayer", 304}, + {"grenadeawareness", 465}, + {"grenade", 458}, + {"grenadeweapon", 470}, + {"grenadeammo", 464}, + {"grenadetargetpos", 467}, + {"grenadetargetvalid", 468}, + {"grenadetossvel", 469}, + {"favoriteenemy", 377}, + {"highlyawareradius", 495}, + {"minpaindamage", 642}, + {"allowpain", 52}, + {"allowdeath", 49}, + {"delayeddeath", 274}, + {"diequietly", 287}, + {"forceragdollimmediate", 405}, + {"providecoveringfire", 842}, + {"doingambush", 302}, + {"combatmode", 199}, + {"alertlevel", 38}, + {"alertlevelint", 39}, + {"useable", 1257}, + {"ignoretriggers", 515}, + {"pushable", 846}, + {"script_pushable", 926}, + {"dropweapon", 309}, + {"drawoncompass", 308}, + {"groundtype", 474}, + {"anim_pose", 68}, + {"goalradius", 452}, + {"goalheight", 450}, + {"goalpos", 451}, + {"nodeoffsetpos", 705}, + {"ignoreforfixednodesafecheck", 510}, + {"fixednode", 381}, + {"fixednodesaferadius", 382}, + {"pathgoalpos", 762}, + {"pathrandompercent", 764}, + {"usechokepoints", 1258}, + {"stopanimdistsq", 1044}, + {"lastenemysightpos", 584}, + {"pathenemylookahead", 761}, + {"pathenemyfightdist", 760}, + {"meleeattackdist", 633}, + {"movemode", 675}, + {"script_move_distance_override", 31298}, + {"usecombatscriptatcover", 1259}, + {"safetochangescript", 906}, + {"keepclaimednode", 561}, + {"keepclaimednodeifvalid", 562}, + {"keepnodeduringscriptedanim", 563}, + {"dodangerreact", 295}, + {"dangerreactduration", 260}, + {"nododgemove", 706}, + {"noteammove", 707}, + {"leanamount", 587}, + {"pitchamount", 788}, + {"turnrate", 1230}, + {"turnanimactive", 1229}, + {"badplaceawareness", 107}, + {"damageshield", 256}, + {"nogrenadereturnthrow", 709}, + {"noattackeraccuracymod", 698}, + {"frontshieldanglecos", 426}, + {"lookaheaddir", 601}, + {"lookaheaddist", 602}, + {"lookaheadhitsstairs", 603}, + {"velocity", 1283}, + {"prevanimdelta", 821}, + {"exposedduration", 356}, + {"requestarrivalnotify", 875}, + {"scriptedarrivalent", 938}, + {"goingtoruntopos", 455}, + {"engagemindist", 334}, + {"engageminfalloffdist", 335}, + {"engagemaxdist", 332}, + {"engagemaxfalloffdist", 333}, + {"usingcovermoveup", 42987}, + {"finalaccuracy", 378}, + {"facemotion", 373}, + {"gunblockedbywall", 475}, + {"relativedir", 866}, + {"lockorientation", 597}, + {"maxfaceenemydist", 625}, + {"stairsstate", 1012}, + {"script", 912}, + {"prevscript", 823}, + {"headicon", 483}, + {"headiconteam", 484}, + {"coversearchinterval", 219}, + {"threatupdateinterval", 37018}, + {"canclimbladders", 150}, + {"swimmer", 1063}, + {"space", 986}, + {"doghandler", 301}, + {"sharpturnlookaheaddist", 961}, + {"postsharpturnlookaheaddist", 813}, + {"sharpturntooclosetodestdist", 963}, + {"usepathsmoothingvalues", 1262}, + {"pathlookaheaddist", 763}, + {"maxturnspeed", 629}, + {"sharpturn", 960}, + {"disablesightandthreatupdate", 54743}, + {"team", 1194}, + {"threatbias", 1204}, + {"threatbiasgroup", 1205}, + {"node", 700}, + {"prevnode", 822}, + {"enemy", 322}, + {"syncedmeleetarget", 1065}, + {"lastattacker", 583}, + {"lastpusher", 42997}, + {"ignoreme", 511}, + {"ignoreall", 507}, + {"maxvisibledist", 630}, + {"surprisedbymedistsq", 1062}, + {"attackeraccuracy", 86}, + {"ignorerandombulletdamage", 512}, + {"dodamagetoall", 294}, + {"turretinvulnerability", 1240}, + {"useorcaavoidance", 1261}, + {"reciprocality", 863}, + {"avoidanceboundshalfsize", 94}, + {"onlygoodnearestnodes", 735}, + {"playername", 803}, + {"deathinvulnerabletime", 266}, + {"criticalbulletdamagedist", 222}, + {"attackercount", 87}, + {"damagemultiplier", 255}, + {"laststand", 586}, + {"motiontrackerenabled", 672}, + {"veh_speed", 1276}, + {"veh_pathspeed", 1273}, + {"veh_transmission", 1279}, + {"veh_pathdir", 1272}, + {"veh_pathtype", 1274}, + {"veh_topspeed", 1278}, + {"veh_brake", 1266}, + {"veh_throttle", 1277}, + {"x", 1331}, + {"y", 1339}, + {"z", 1342}, + {"fontscale", 393}, + {"font", 392}, + {"alignx", 44}, + {"aligny", 45}, + {"horzalign", 499}, + {"vertalign", 1284}, + {"color", 196}, + {"alpha", 55}, + {"label", 578}, + {"sort", 983}, + {"foreground", 408}, + {"lowresbackground", 612}, + {"hidewhendead", 491}, + {"hidewheninmenu", 493}, + {"glowcolor", 445}, + {"glowalpha", 444}, + {"positioninworld", 812}, + {"relativeoffset", 867}, + {"enablehudlighting", 315}, + {"enableinputprogressicon", 42996}, + {"rotation", 902}, + {"targetname", 1193}, + {"target", 1191}, + {"animscript", 71}, + {"script_linkname", 920}, + {"script_noteworthy", 922}, + {"origin", 740}, + {"angles", 65}, + {"minusedistsq", 643}, + {"parentname", 749}, + {"spawnflags", 989}, + {"type", 1244}, + {"owner", 743}, + {"radius", 851}, + {"customangles", 9555}, + {"speed", 997}, + {"lookahead", 600}, + {"script_vehicle_anim", 40318}, }; } diff --git a/src/client/game/scripting/lua/context.cpp b/src/client/game/scripting/lua/context.cpp index c636128a..e690d71e 100644 --- a/src/client/game/scripting/lua/context.cpp +++ b/src/client/game/scripting/lua/context.cpp @@ -326,6 +326,11 @@ namespace scripting::lua return keys; }; + array_type["getentity"] = [](const array& array, const sol::this_state s) + { + return array.get_raw(); + }; + auto entity_type = state.new_usertype("entity"); for (const auto& func : method_map) diff --git a/src/client/game/scripting/lua/value_conversion.cpp b/src/client/game/scripting/lua/value_conversion.cpp index c7acc791..489daa18 100644 --- a/src/client/game/scripting/lua/value_conversion.cpp +++ b/src/client/game/scripting/lua/value_conversion.cpp @@ -176,6 +176,11 @@ namespace scripting::lua auto table = sol::table::create(state); auto metatable = sol::table::create(state); + table["getentity"] = [parent_id]() + { + return entity(parent_id); + }; + metatable[sol::meta_function::new_index] = [parent_id](const sol::table t, const sol::this_state s, const sol::lua_value& field, const sol::lua_value& value) { @@ -287,11 +292,6 @@ namespace scripting::lua return {state, value.as()}; } - if (value.is>()) - { - return entity_to_struct(state, value.get_raw().u.uintValue); - } - if (value.is()) { return {state, value.as()};