diff --git a/src/core/scripting/LuaUtils.hpp b/src/core/scripting/LuaUtils.hpp index 9332136..2a43e8c 100644 --- a/src/core/scripting/LuaUtils.hpp +++ b/src/core/scripting/LuaUtils.hpp @@ -8,6 +8,7 @@ namespace YimMenu::Lua class Metatable { inline static int m_Index; + inline static std::vector m_Subclasses; public: static inline void Register(lua_State* state) @@ -19,6 +20,17 @@ namespace YimMenu::Lua { return m_Index; } + + static inline const std::vector& GetSubclasses() + { + return m_Subclasses; + } + + template + static inline void AddSubclass() + { + m_Subclasses.push_back(Metatable::Get()); + } }; // Tries to get the userdata or errors out @@ -34,12 +46,34 @@ namespace YimMenu::Lua lua_rawgeti(state, LUA_REGISTRYINDEX, Metatable::Get()); auto equals = lua_rawequal(state, -1, -2); - lua_pop(state, 2); + lua_pop(state, 1); - if (!equals) - luaL_argerror(state, idx, "the metatable for this userdata at this index is incorrect"); + if (equals) + { + lua_pop(state, 1); + return *reinterpret_cast(data); + } - return *reinterpret_cast(data); + const auto& subclasses = Metatable::GetSubclasses(); + if (!equals && subclasses.size()) + { + for (const auto subclass : subclasses) + { + lua_rawgeti(state, LUA_REGISTRYINDEX, subclass); + auto equals = lua_rawequal(state, -1, -2); + lua_pop(state, 1); + + if (equals) + { + lua_pop(state, 1); + return *reinterpret_cast(data); + } + } + } + + lua_pop(state, 1); + luaL_argerror(state, idx, "the metatable for this userdata at this index is incorrect"); + std::unreachable(); } inline void SetFunction(lua_State* state, lua_CFunction func, const char* name) diff --git a/src/core/scripting/libraries/Vector3.cpp b/src/core/scripting/libraries/Vector3.cpp index 2b00306..bcb9c14 100644 --- a/src/core/scripting/libraries/Vector3.cpp +++ b/src/core/scripting/libraries/Vector3.cpp @@ -21,13 +21,72 @@ namespace YimMenu::Lua return 1; } + // returns three values + static int GetCoords(lua_State* state) + { + lua_pushnumber(state, GetObject(state, 1).x); + lua_pushnumber(state, GetObject(state, 1).y); + lua_pushnumber(state, GetObject(state, 1).z); + return 3; + } + + static int GetX(lua_State* state) + { + lua_pushnumber(state, GetObject(state, 1).x); + return 1; + } + + static int GetY(lua_State* state) + { + lua_pushnumber(state, GetObject(state, 1).y); + return 1; + } + + static int GetZ(lua_State* state) + { + lua_pushnumber(state, GetObject(state, 1).z); + return 1; + } + + static int SetX(lua_State* state) + { + GetObject(state, 1).x = luaL_checknumber(state, 2); + return 1; + } + + static int SetY(lua_State* state) + { + GetObject(state, 1).y = luaL_checknumber(state, 2); + return 1; + } + + static int SetZ(lua_State* state) + { + GetObject(state, 1).z = luaL_checknumber(state, 2); + return 1; + } + + static int GetDistance(lua_State* state) + { + lua_pushnumber(state, GetObject(state, 1).GetDistance(GetObject(state, 2))); + return 1; + } + virtual void Register(lua_State* state) override { luaL_newmetatable(state, "Vector3"); { lua_newtable(state); { - // TODO? + SetFunction(state, GetCoords, "get_coords"); + SetFunction(state, GetX, "get_x"); + SetFunction(state, GetY, "get_y"); + SetFunction(state, GetZ, "get_z"); + SetFunction(state, SetX, "set_x"); + SetFunction(state, SetY, "set_y"); + SetFunction(state, SetZ, "set_z"); + SetFunction(state, SetZ, "set_z"); + SetFunction(state, GetDistance, "get_distance"); } lua_setfield(state, -2, "__index"); // prototype } diff --git a/src/game/gta/Vehicle.cpp b/src/game/gta/Vehicle.cpp index ab4d190..b02358c 100644 --- a/src/game/gta/Vehicle.cpp +++ b/src/game/gta/Vehicle.cpp @@ -115,6 +115,13 @@ namespace YimMenu VEHICLE::SET_VEHICLE_TYRES_CAN_BURST(veh, false); } + std::string Vehicle::GetPlateText() + { + ENTITY_ASSERT_VALID(); + + return VEHICLE::GET_VEHICLE_NUMBER_PLATE_TEXT(GetHandle()); + } + void Vehicle::SetPlateText(std::string_view text) { ENTITY_ASSERT_VALID(); diff --git a/src/game/gta/Vehicle.hpp b/src/game/gta/Vehicle.hpp index b962480..382ece8 100644 --- a/src/game/gta/Vehicle.hpp +++ b/src/game/gta/Vehicle.hpp @@ -21,6 +21,7 @@ namespace YimMenu // mods void Upgrade(); + std::string GetPlateText(); void SetPlateText(std::string_view text); std::map GetOwnedMods(); diff --git a/src/game/gta/invoker/GenerateNatives.py b/src/game/gta/invoker/GenerateNatives.py index 91814bc..9b90314 100644 --- a/src/game/gta/invoker/GenerateNatives.py +++ b/src/game/gta/invoker/GenerateNatives.py @@ -218,7 +218,7 @@ def create_lua_namespace(name: str, natives: list[NativeFunc]): def create_lua_defs(): with open("../../scripting/libraries/NativeDefs.cpp", "w+") as file: - string = "#pragma once\n#include \"NativeDefs.hpp\"\n\nconst char* g_LuaNativeDefs[] = \n{\n" + string = "#include \"NativeDefs.hpp\"\n\nconst char* g_LuaNativeDefs[] = \n{\n" for ns, natives_list in natives.items(): string += create_lua_namespace(ns, natives_list) string += f"}};\n\nint g_NumLuaNativeDefs = {len(natives.items())};" diff --git a/src/game/scripting/libraries/Vehicle.cpp b/src/game/scripting/libraries/Vehicle.cpp new file mode 100644 index 0000000..098ee37 --- /dev/null +++ b/src/game/scripting/libraries/Vehicle.cpp @@ -0,0 +1,144 @@ +#include "core/scripting/LuaLibrary.hpp" +#include "core/scripting/LuaScript.hpp" +#include "core/scripting/LuaUtils.hpp" +#include "core/scripting/LatentFunction.hpp" +#include "game/gta/Vehicle.hpp" +#include "Entity.hpp" + +namespace YimMenu::Lua +{ + // TODO: we need to automate this + class Vehicle : LuaLibrary + { + public: + using LuaLibrary::LuaLibrary; + + static int New(lua_State* state) + { + CreateObject(state, luaL_checkinteger(state, 1)); + return 1; + } + + static int Create(lua_State* state) + { + CopyObject(state, YimMenu::Vehicle::Create(GetHashArgument(state, 1), GetObject(state, 2), lua_gettop(state) >= 3 ? luaL_checknumber(state, 3) : 0.0f)); + return 1; + } + + static int Fix(lua_State* state) + { + GetObject(state, 1).Fix(); + return 0; + } + + static int GetGear(lua_State* state) + { + lua_pushinteger(state, GetObject(state, 1).GetGear()); + return 1; + } + + static int GetRevRatio(lua_State* state) + { + lua_pushnumber(state, GetObject(state, 1).GetRevRatio()); + return 1; + } + + static int GetSpeed(lua_State* state) + { + lua_pushnumber(state, GetObject(state, 1).GetSpeed()); + return 1; + } + + static int Upgrade(lua_State* state) + { + GetObject(state, 1).Upgrade(); + return 0; + } + + static int GetPlateText(lua_State* state) + { + auto text = GetObject(state, 1).GetPlateText(); + lua_pushlstring(state, text.c_str(), text.size()); + return 1; + } + + static int SetPlateText(lua_State* state) + { + GetObject(state, 1).SetPlateText(CheckStringSafe(state, 2)); + return 0; + } + + // GetOwnedMods + + static int IsSeatFree(lua_State* state) + { + lua_pushboolean(state, GetObject(state, 1).IsSeatFree(luaL_checkinteger(state, 2))); + return 1; + } + + static int SupportsBoost(lua_State* state) + { + lua_pushboolean(state, GetObject(state, 1).SupportsBoost()); + return 1; + } + + static int IsBoostActive(lua_State* state) + { + lua_pushboolean(state, GetObject(state, 1).IsBoostActive()); + return 1; + } + + static int SetBoostCharge(lua_State* state) + { + GetObject(state, 1).SetBoostCharge(lua_gettop(state) >= 2 ? luaL_checkinteger(state, 3) : 100); + return 0; + } + + static int LowerStance(lua_State* state) + { + GetObject(state, 1).LowerStance(CheckBooleanSafe(state, 2)); + return 0; + } + + static int GetFullName(lua_State* state) + { + auto name = GetObject(state, 1).GetFullName(); + lua_pushlstring(state, name.c_str(), name.size()); + return 1; + } + + virtual void Register(lua_State* state) override + { + luaL_newmetatable(state, "Vehicle"); + { + lua_newtable(state); + { + RegisterEntityMethods(state); // re-registering the entity methods under the vehicle table is more efficient than subclassing + SetFunction(state, Fix, "fix"); + SetFunction(state, GetGear, "get_gear"); + SetFunction(state, GetRevRatio, "get_rev_ratio"); + SetFunction(state, GetSpeed, "get_speed"); + SetFunction(state, Upgrade, "upgrade"); + SetFunction(state, GetPlateText, "get_plate_text"); + SetFunction(state, SetPlateText, "set_plate_text"); + SetFunction(state, IsSeatFree, "is_seat_free"); + SetFunction(state, SupportsBoost, "supports_boost"); + SetFunction(state, IsBoostActive, "is_boost_active"); + SetFunction(state, SetBoostCharge, "set_boost_charge"); + SetFunction(state, LowerStance, "lower_stance"); + SetFunction(state, GetFullName, "get_full_name"); + } + lua_setfield(state, -2, "__index"); // prototype + } + Metatable::Register(state); + Metatable::AddSubclass(); + + lua_newtable(state); + SetConstructor(state); + SetFunction(state, LatentFunction, "create"); + lua_setglobal(state, "Vehicle"); + } + }; + + Vehicle _Vehicle; +}