Create-Engine/src/src/Components/ScriptComponent.cpp
OusmBlueNinja b36e6dfc6a Fixed Bugs
2025-05-09 20:27:33 -05:00

1220 lines
31 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "ScriptComponent.h"
#include "../core/utils/Logging.h"
#include "../core/utils/ExceptionHandler.h"
#include "../utils/GameObjectsList.h"
#include "../Entitys/Object.h"
#include "../Components/SpriteComponent.h"
#include "../Components/CameraComponent.h"
#include "../Components/LightComponent.h"
#include "../Components/TextComponent.h"
#include "../Components/TilemapComponent.h"
#include "../Components/AnimationComponent.h"
#include "../Components/AudioPlayerComponent.h"
#include "../core/utils/Profiler.h"
#include "../core/utils/utils.h"
#include "../core/utils/keycode.h"
#include "../core/utils/input.h"
#include "../core/types/vec2.h"
#include "../core/types/vec3.h"
#include "../core/scripts/LuaGlobalBridge.h"
#include "../core/scripts/ScriptCore.h"
void PushVector3(lua_State* L, float x, float y, float z);
static int Lua_SetGlobal(lua_State *L)
{
const char *key = luaL_checkstring(L, 1);
switch (lua_type(L, 2))
{
case LUA_TNUMBER:
LuaGlobalBridge::Set(key, lua_tonumber(L, 2));
break;
case LUA_TBOOLEAN:
LuaGlobalBridge::Set(key, (bool)lua_toboolean(L, 2));
break;
case LUA_TSTRING:
LuaGlobalBridge::Set(key, std::string(lua_tostring(L, 2)));
break;
default:
luaL_error(L, "[Engine.SetGlobal] Unsupported value type");
}
return 0;
}
static int Lua_GetGlobal(lua_State *L)
{
const char *key = luaL_checkstring(L, 1);
auto value = LuaGlobalBridge::Get(key);
if (!value.has_value())
{
lua_pushnil(L);
return 1;
}
std::visit([L](auto &&val)
{
using T = std::decay_t<decltype(val)>;
if constexpr (std::is_same_v<T, double>)
lua_pushnumber(L, val);
else if constexpr (std::is_same_v<T, bool>)
lua_pushboolean(L, val);
else if constexpr (std::is_same_v<T, std::string>)
lua_pushstring(L, val.c_str()); }, *value);
return 1;
}
static bool luaDebugEnabled = false;
static bool old_state = false;
#define SET_KC(name) \
lua_pushinteger(L, static_cast<int>(Keycode::name)); \
lua_setfield(L, -2, #name);
struct LuaObjectWrapper
{
Object *obj;
};
#define LUA_OBJECT_MT "LuaObjectMeta"
struct LuaVector2
{
float x, y;
};
struct LuaVector3
{
float x, y, z;
};
#define LUA_VECTOR3_MT "LuaVector3Meta"
#define LUA_VECTOR2_MT "LuaVector2Meta"
struct LuaAnimationWrapper
{
AnimationComponent *comp;
};
struct LuaLightWrapper
{
LightComponent *comp;
};
struct LuaSoundWrapper
{
AudioPlayerComponent *comp;
};
#define LUA_SOUND_MT "LuaSoundMeta"
#define LUA_LIGHT_MT "LuaLightMeta"
#define LUA_ANIMATION_MT "LuaAnimationMeta"
static core::types::Vec3 GetLuaVector3(lua_State *L, int index);
ScriptComponent::ScriptComponent(Object *owner) : Component(owner), L(nullptr) {}
ScriptComponent::~ScriptComponent()
{
if (L)
{
ScriptCore::UnregisterState(L);
lua_close(L);
}
}
void ScriptComponent::SetScriptPath(const std::string &path)
{
scriptPath = path;
ReloadScript();
}
const std::string &ScriptComponent::GetScriptPath() const { return scriptPath; }
void ScriptComponent::Hook(lua_State *L, lua_Debug *ar)
{
if (!g_engineConfig.settings.profile_deep)
return;
// Quickly fetch component from Lua extraspace
ScriptComponent *self = *reinterpret_cast<ScriptComponent **>(lua_getextraspace(L));
if (!self || (ar->event != LUA_HOOKCALL && ar->event != LUA_HOOKRET))
return;
// Only fetch debug info if needed
if (!lua_getinfo(L, "nS", ar))
return;
// Only build label if entering a function
if (ar->event == LUA_HOOKCALL)
{
const char *name = ar->name;
std::string label;
if (name && *name)
{
label = name;
}
else if (ar->short_src && *ar->short_src)
{
label = GetFilenameFromPath(ar->short_src);
}
else
{
label = "unknown";
}
self->luaCallStack.emplace_back(label);
profiler.BeginDeepSection(label);
}
else if (ar->event == LUA_HOOKRET && !self->luaCallStack.empty())
{
profiler.EndDeepSection();
self->luaCallStack.pop_back();
}
}
static int Lua_LogInfo(lua_State *L)
{
PROFILE_DEEP_SCOPE("Lua_LogInfo");
Logger::LogInfo("[Lua] %s", lua_tostring(L, 1));
return 0;
}
static int Lua_LogError(lua_State *L)
{
PROFILE_DEEP_SCOPE("Lua_LogError");
Logger::LogError("[Lua] %s", lua_tostring(L, 1));
return 0;
}
static int Lua_LogDebug(lua_State *L)
{
PROFILE_DEEP_SCOPE("Lua_LogDebug");
Logger::LogDebug("[Lua] %s", lua_tostring(L, 1));
return 0;
}
static int Lua_DebugLua(lua_State *L)
{
PROFILE_DEEP_SCOPE("Lua_DebugLua");
luaDebugEnabled = lua_toboolean(L, 1);
if (old_state != luaDebugEnabled)
Logger::LogInfo("[Lua] DebugLua(%s)", luaDebugEnabled ? "true" : "false");
old_state = luaDebugEnabled;
return 0;
}
static Component *GetComponentByName(Object *obj, const std::string &type)
{
PROFILE_DEEP_SCOPE("GetComponentByName");
using GetterFn = std::function<Component *(Object *)>;
static const std::unordered_map<std::string, GetterFn> table = {
{"SpriteComponent", [](Object *o)
{ return o->GetComponent<SpriteComponent>().get(); }},
{"CameraComponent", [](Object *o)
{ return o->GetComponent<CameraComponent>().get(); }},
{"LightComponent", [](Object *o)
{ return o->GetComponent<LightComponent>().get(); }},
{"TilemapComponent", [](Object *o)
{ return o->GetComponent<TilemapComponent>().get(); }},
{"TextComponent", [](Object *o)
{ return o->GetComponent<TextComponent>().get(); }},
{"ScriptComponent", [](Object *o)
{ return o->GetComponent<ScriptComponent>().get(); }},
{"AnimationComponent", [](Object *o)
{ return o->GetComponent<AnimationComponent>().get(); }},
{"AudioPlayerComponent", [](Object *o)
{ return o->GetComponent<AudioPlayerComponent>().get(); }},
};
auto it = table.find(type);
if (it != table.end())
return it->second(obj);
return nullptr;
}
static int Lua_GetMousePos(lua_State *L)
{
PROFILE_DEEP_SCOPE("Engine::GetMousePos");
core::types::Vec2 m = Input::GetMousePosition();
LuaVector2 *vec = (LuaVector2 *)lua_newuserdata(L, sizeof(LuaVector2));
vec->x = m.x;
vec->y = m.y;
luaL_getmetatable(L, LUA_VECTOR2_MT);
lua_setmetatable(L, -2);
return 1;
}
static int Lua_Object_GetComponent(lua_State *L)
{
PROFILE_DEEP_SCOPE("Object::GetComponent");
auto *wrapper = (LuaObjectWrapper *)luaL_checkudata(L, 1, LUA_OBJECT_MT);
const char *type = luaL_checkstring(L, 2);
if (strcmp(type, "AnimationComponent") == 0)
{
auto compPtr = wrapper->obj->GetComponent<AnimationComponent>();
if (compPtr)
{
LuaAnimationWrapper *aw = (LuaAnimationWrapper *)lua_newuserdata(L, sizeof(LuaAnimationWrapper));
aw->comp = compPtr.get();
luaL_getmetatable(L, LUA_ANIMATION_MT);
lua_setmetatable(L, -2);
return 1;
}
lua_pushnil(L);
return 1;
}
if (strcmp(type, "LightComponent") == 0)
{
auto compPtr = wrapper->obj->GetComponent<LightComponent>();
if (compPtr)
{
LuaLightWrapper *lw = (LuaLightWrapper *)lua_newuserdata(L, sizeof(LuaLightWrapper));
lw->comp = compPtr.get();
luaL_getmetatable(L, LUA_LIGHT_MT);
lua_setmetatable(L, -2);
return 1;
}
lua_pushnil(L);
return 1;
}
if (strcmp(type, "SoundComponent") == 0)
{
auto compPtr = wrapper->obj->GetComponent<AudioPlayerComponent>();
if (compPtr)
{
LuaSoundWrapper *sw = (LuaSoundWrapper *)lua_newuserdata(L, sizeof(LuaSoundWrapper));
sw->comp = compPtr.get();
luaL_getmetatable(L, LUA_SOUND_MT);
lua_setmetatable(L, -2);
return 1;
}
lua_pushnil(L);
return 1;
}
Component *comp = GetComponentByName(wrapper->obj, type);
lua_pushlightuserdata(L, comp ? comp : nullptr);
return 1;
}
static AudioPlayerComponent *CheckSound(lua_State *L)
{
return ((LuaSoundWrapper *)luaL_checkudata(L, 1, LUA_SOUND_MT))->comp;
}
static int Lua_Sound_Play(lua_State *L)
{
CheckSound(L)->Play();
return 0;
}
static int Lua_Sound_Stop(lua_State *L)
{
CheckSound(L)->Stop();
return 0;
}
static int Lua_Sound_Pause(lua_State *L)
{
CheckSound(L)->Pause();
return 0;
}
static int Lua_Sound_SetVolume(lua_State *L)
{
float vol = static_cast<float>(luaL_checknumber(L, 2));
CheckSound(L)->SetVolume(vol);
return 0;
}
static int Lua_Sound_Index(lua_State *L)
{
luaL_getmetatable(L, LUA_SOUND_MT);
lua_pushvalue(L, 2);
lua_rawget(L, -2);
return 1;
}
static int Lua_Object_GetPosition(lua_State *L)
{
PROFILE_DEEP_SCOPE("Object::GetPosition");
auto *wrapper = (LuaObjectWrapper *)luaL_checkudata(L, 1, LUA_OBJECT_MT);
glm::vec2 pos = wrapper->obj->GetLocalPosition();
LuaVector2 *vec = (LuaVector2 *)lua_newuserdata(L, sizeof(LuaVector2));
vec->x = pos.x;
vec->y = pos.y;
luaL_getmetatable(L, LUA_VECTOR2_MT);
lua_setmetatable(L, -2);
return 1;
}
static int Lua_Object_SetPosition(lua_State *L)
{
PROFILE_DEEP_SCOPE("Object::SetPosition");
auto *wrapper = (LuaObjectWrapper *)luaL_checkudata(L, 1, LUA_OBJECT_MT);
auto *vec = (LuaVector2 *)luaL_checkudata(L, 2, LUA_VECTOR2_MT);
wrapper->obj->SetLocalPosition({vec->x, vec->y});
return 0;
}
static int Lua_Object_Index(lua_State *L)
{
PROFILE_DEEP_SCOPE("Object::__index");
const char *key = luaL_checkstring(L, 2);
lua_getfield(L, lua_upvalueindex(1), key);
return 1;
}
void RegisterObjectType(lua_State *L)
{
PROFILE_DEEP_SCOPE("RegisterObjectType");
luaL_newmetatable(L, LUA_OBJECT_MT);
lua_newtable(L);
lua_pushcfunction(L, Lua_Object_GetComponent);
lua_setfield(L, -2, "GetComponent");
lua_pushcfunction(L, Lua_Object_GetPosition);
lua_setfield(L, -2, "GetPosition");
lua_pushcfunction(L, Lua_Object_SetPosition);
lua_setfield(L, -2, "SetPosition");
lua_pushcclosure(L, Lua_Object_Index, 1);
lua_setfield(L, -2, "__index");
lua_pop(L, 1);
}
static void PushObject(lua_State *L, Object *obj)
{
PROFILE_DEEP_SCOPE("PushObject");
auto *wrapper = (LuaObjectWrapper *)lua_newuserdata(L, sizeof(LuaObjectWrapper));
wrapper->obj = obj;
luaL_getmetatable(L, LUA_OBJECT_MT);
lua_setmetatable(L, -2);
}
static int Lua_GetObjectByTag(lua_State *L)
{
PROFILE_DEEP_SCOPE("GetObjectByTag");
const char *name = luaL_checkstring(L, 1);
for (const auto &obj : objects)
{
auto found = FindByTagRecursive(obj, name);
if (found)
{
PushObject(L, found.get());
return 1;
}
}
lua_pushnil(L);
return 1;
}
static LightComponent *CheckLight(lua_State *L)
{
return ((LuaLightWrapper *)luaL_checkudata(L, 1, LUA_LIGHT_MT))->comp;
}
static int Lua_Light_GetColor(lua_State *L)
{
auto *light = CheckLight(L);
glm::vec3 c = light->GetColor();
LuaVector3 *vec = (LuaVector3 *)lua_newuserdata(L, sizeof(LuaVector3));
vec->x = c.x;
vec->y = c.y;
vec->z = c.z;
luaL_getmetatable(L, LUA_VECTOR3_MT);
lua_setmetatable(L, -2);
return 1;
}
static int Lua_Light_SetColor(lua_State *L)
{
auto *light = CheckLight(L);
core::types::Vec3 color = GetLuaVector3(L, 2);
light->SetColor(color);
return 0;
}
static int Lua_Light_GetIntensity(lua_State *L)
{
lua_pushnumber(L, CheckLight(L)->GetIntensity());
return 1;
}
static int Lua_Light_SetIntensity(lua_State *L)
{
CheckLight(L)->SetIntensity(static_cast<float>(luaL_checknumber(L, 2)));
return 0;
}
static int Lua_Light_GetRadius(lua_State *L)
{
lua_pushnumber(L, CheckLight(L)->GetRadius());
return 1;
}
static int Lua_Light_SetRadius(lua_State *L)
{
CheckLight(L)->SetRadius(static_cast<float>(luaL_checknumber(L, 2)));
return 0;
}
static int Lua_Light_GetFalloff(lua_State *L)
{
lua_pushnumber(L, CheckLight(L)->GetFalloff());
return 1;
}
static int Lua_Light_SetFalloff(lua_State *L)
{
CheckLight(L)->SetFalloff(static_cast<float>(luaL_checknumber(L, 2)));
return 0;
}
static int Lua_Light_GetType(lua_State *L)
{
lua_pushinteger(L, CheckLight(L)->GetType());
return 1;
}
static int Lua_Light_SetType(lua_State *L)
{
CheckLight(L)->SetType(luaL_checkinteger(L, 2));
return 0;
}
static int Lua_Light_Index(lua_State *L)
{
luaL_getmetatable(L, LUA_LIGHT_MT);
lua_pushvalue(L, 2);
lua_rawget(L, -2);
return 1;
}
void RegisterSoundType(lua_State *L)
{
luaL_newmetatable(L, LUA_SOUND_MT);
lua_pushcfunction(L, Lua_Sound_Play);
lua_setfield(L, -2, "Play");
lua_pushcfunction(L, Lua_Sound_Stop);
lua_setfield(L, -2, "Stop");
lua_pushcfunction(L, Lua_Sound_Pause);
lua_setfield(L, -2, "Pause");
lua_pushcfunction(L, Lua_Sound_SetVolume);
lua_setfield(L, -2, "SetVolume");
lua_pushcfunction(L, Lua_Sound_Index);
lua_setfield(L, -2, "__index");
lua_pop(L, 1);
}
void RegisterLightType(lua_State *L)
{
luaL_newmetatable(L, LUA_LIGHT_MT);
lua_pushcfunction(L, Lua_Light_GetColor);
lua_setfield(L, -2, "GetColor");
lua_pushcfunction(L, Lua_Light_SetColor);
lua_setfield(L, -2, "SetColor");
lua_pushcfunction(L, Lua_Light_GetIntensity);
lua_setfield(L, -2, "GetIntensity");
lua_pushcfunction(L, Lua_Light_SetIntensity);
lua_setfield(L, -2, "SetIntensity");
lua_pushcfunction(L, Lua_Light_GetRadius);
lua_setfield(L, -2, "GetRadius");
lua_pushcfunction(L, Lua_Light_SetRadius);
lua_setfield(L, -2, "SetRadius");
lua_pushcfunction(L, Lua_Light_GetFalloff);
lua_setfield(L, -2, "GetFalloff");
lua_pushcfunction(L, Lua_Light_SetFalloff);
lua_setfield(L, -2, "SetFalloff");
lua_pushcfunction(L, Lua_Light_GetType);
lua_setfield(L, -2, "GetType");
lua_pushcfunction(L, Lua_Light_SetType);
lua_setfield(L, -2, "SetType");
lua_pushcfunction(L, Lua_Light_Index);
lua_setfield(L, -2, "__index");
lua_pop(L, 1);
}
// --- AnimationComponent Lua bindings ---
static AnimationComponent *CheckAnimationComponent(lua_State *L, int idx)
{
void *ptr = lua_touserdata(L, idx);
return static_cast<AnimationComponent *>(ptr);
}
static int Lua_Animation_GetCurrentFrame(lua_State *L)
{
PROFILE_DEEP_SCOPE("Animation::GetCurrentFrame");
AnimationComponent *anim = CheckAnimationComponent(L, 1);
lua_pushinteger(L, anim ? anim->GetCurrentFrame() : -1);
return 1;
}
static int Lua_Animation_SetCurrentFrame(lua_State *L)
{
PROFILE_DEEP_SCOPE("Animation::SetCurrentFrame");
AnimationComponent *anim = CheckAnimationComponent(L, 1);
int frame = luaL_checkinteger(L, 2);
if (anim)
anim->SetFrame(frame);
return 0;
}
static int Lua_Animation_IsPlaying(lua_State *L)
{
PROFILE_DEEP_SCOPE("Animation::IsPlaying");
AnimationComponent *anim = CheckAnimationComponent(L, 1);
lua_pushboolean(L, anim ? anim->IsPlaying() : false);
return 1;
}
static int Lua_Animation_SetPlaying(lua_State *L)
{
PROFILE_DEEP_SCOPE("Animation::SetPlaying");
AnimationComponent *anim = CheckAnimationComponent(L, 1);
bool play = lua_toboolean(L, 2);
if (anim)
anim->SetPlaying(play);
return 0;
}
static int Lua_Animation_GetStartFrame(lua_State *L)
{
PROFILE_DEEP_SCOPE("Animation::GetStartFrame");
AnimationComponent *anim = CheckAnimationComponent(L, 1);
lua_pushinteger(L, anim ? anim->GetStartFrame() : 0);
return 1;
}
static int Lua_Animation_SetStartFrame(lua_State *L)
{
PROFILE_DEEP_SCOPE("Animation::SetStartFrame");
AnimationComponent *anim = CheckAnimationComponent(L, 1);
int start = luaL_checkinteger(L, 2);
if (anim)
anim->SetStartFrame(start);
return 0;
}
static int Lua_Animation_GetEndFrame(lua_State *L)
{
PROFILE_DEEP_SCOPE("Animation::GetEndFrame");
AnimationComponent *anim = CheckAnimationComponent(L, 1);
lua_pushinteger(L, anim ? anim->GetEndFrame() : 0);
return 1;
}
static int Lua_Animation_SetEndFrame(lua_State *L)
{
PROFILE_DEEP_SCOPE("Animation::SetEndFrame");
AnimationComponent *anim = CheckAnimationComponent(L, 1);
int end = luaL_checkinteger(L, 2);
if (anim)
anim->SetEndFrame(end);
return 0;
}
static int Lua_Animation_Index(lua_State *L)
{
// stack: [1]=userdata, [2]=key
luaL_getmetatable(L, LUA_ANIMATION_MT);
lua_pushvalue(L, 2);
lua_rawget(L, -2);
return 1;
}
static int l_ShortNumberFormat(lua_State *L)
{
double num = 0.0;
if (lua_isnumber(L, 1))
num = lua_tonumber(L, 1);
else if (lua_isstring(L, 1))
num = std::strtod(lua_tostring(L, 1), nullptr);
else
return luaL_error(L, "Expected number or string as first argument");
int precision = 0;
if (lua_gettop(L) >= 2 && lua_isnumber(L, 2))
{
precision = (int)lua_tointeger(L, 2);
if (precision < 0)
precision = 0;
if (precision > 9)
precision = 9;
}
bool negative = false;
if (num < 0)
{
negative = true;
num = -num;
}
static const char *suffixes[] = {"", "k", "M", "B", "T", "Q", "E", "Z", "Y"};
int suffixIndex = 0;
while (num >= 1000.0 && suffixIndex < 8)
{
num /= 1000.0;
++suffixIndex;
}
char fmt[16];
std::snprintf(fmt, sizeof(fmt), "%%s%%.%df%%s", precision);
char out[64];
std::snprintf(out, sizeof(out), fmt, negative ? "-" : "", num, suffixes[suffixIndex]);
lua_pushstring(L, out);
return 1;
}
static int l_HSVtoRGB(lua_State *L)
{
float h = luaL_checknumber(L, 1);
float s = luaL_checknumber(L, 2);
float v = luaL_checknumber(L, 3);
float c = v * s;
float x = c * (1 - std::fabs(fmod(h / 60.0f, 2) - 1));
float m = v - c;
float r = 0, g = 0, b = 0;
if (h < 60) { r = c; g = x; }
else if (h < 120) { r = x; g = c; }
else if (h < 180) { g = c; b = x; }
else if (h < 240) { g = x; b = c; }
else if (h < 300) { r = x; b = c; }
else { r = c; b = x; }
PushVector3(L, r + m, g + m, b + m);
return 1;
}
static int l_RGBtoHSV(lua_State *L)
{
float r = luaL_checknumber(L, 1);
float g = luaL_checknumber(L, 2);
float b = luaL_checknumber(L, 3);
float max = std::fmax(r, std::fmax(g, b));
float min = std::fmin(r, std::fmin(g, b));
float d = max - min;
float h = 0.0f;
if (d != 0)
{
if (max == r) h = fmod((g - b) / d, 6.0f);
else if (max == g) h = ((b - r) / d) + 2.0f;
else h = ((r - g) / d) + 4.0f;
h *= 60.0f;
if (h < 0) h += 360.0f;
}
float s = (max == 0) ? 0 : d / max;
float v = max;
PushVector3(L, h, s, v);
return 1;
}
void RegisterUtils(lua_State *L)
{
lua_newtable(L);
lua_pushcfunction(L, l_ShortNumberFormat);
lua_setfield(L, -2, "ShortNumberFormat");
lua_pushcfunction(L, l_HSVtoRGB);
lua_setfield(L, -2, "HSVtoRGB");
lua_pushcfunction(L, l_RGBtoHSV);
lua_setfield(L, -2, "RGBtoHSV");
lua_setfield(L, -2, "Utils"); // Engine.Utils
}
static int keycode_loader(lua_State *L)
{
lua_newtable(L);
// Printable
SET_KC(Space)
SET_KC(Apostrophe)
SET_KC(Comma)
SET_KC(Minus)
SET_KC(Period)
SET_KC(Slash)
SET_KC(Key0)
SET_KC(Key1)
SET_KC(Key2)
SET_KC(Key3)
SET_KC(Key4)
SET_KC(Key5)
SET_KC(Key6)
SET_KC(Key7)
SET_KC(Key8)
SET_KC(Key9)
SET_KC(Semicolon)
SET_KC(Equal)
SET_KC(A)
SET_KC(B)
SET_KC(C)
SET_KC(D)
SET_KC(E)
SET_KC(F)
SET_KC(G)
SET_KC(H)
SET_KC(I)
SET_KC(J)
SET_KC(K)
SET_KC(L)
SET_KC(M)
SET_KC(N)
SET_KC(O)
SET_KC(P)
SET_KC(Q)
SET_KC(R)
SET_KC(S)
SET_KC(T)
SET_KC(U)
SET_KC(V)
SET_KC(W)
SET_KC(X)
SET_KC(Y)
SET_KC(Z)
SET_KC(LeftBracket)
SET_KC(Backslash)
SET_KC(RightBracket)
SET_KC(GraveAccent)
SET_KC(World1)
SET_KC(World2)
// Function & Navigation
SET_KC(Escape)
SET_KC(Enter)
SET_KC(Tab)
SET_KC(Backspace)
SET_KC(Insert)
SET_KC(Delete)
SET_KC(Right)
SET_KC(Left)
SET_KC(Down)
SET_KC(Up)
SET_KC(PageUp)
SET_KC(PageDown)
SET_KC(Home)
SET_KC(End)
SET_KC(CapsLock)
SET_KC(ScrollLock)
SET_KC(NumLock)
SET_KC(PrintScreen)
SET_KC(Pause)
SET_KC(F1)
SET_KC(F2)
SET_KC(F3)
SET_KC(F4)
SET_KC(F5)
SET_KC(F6)
SET_KC(F7)
SET_KC(F8)
SET_KC(F9)
SET_KC(F10)
SET_KC(F11)
SET_KC(F12)
SET_KC(F13)
SET_KC(F14)
SET_KC(F15)
SET_KC(F16)
SET_KC(F17)
SET_KC(F18)
SET_KC(F19)
SET_KC(F20)
SET_KC(F21)
SET_KC(F22)
SET_KC(F23)
SET_KC(F24)
SET_KC(F25)
// Keypad
SET_KC(KP0)
SET_KC(KP1)
SET_KC(KP2)
SET_KC(KP3)
SET_KC(KP4)
SET_KC(KP5)
SET_KC(KP6)
SET_KC(KP7)
SET_KC(KP8)
SET_KC(KP9)
SET_KC(KPDecimal)
SET_KC(KPDivide)
SET_KC(KPMultiply)
SET_KC(KPSubtract)
SET_KC(KPAdd)
SET_KC(KPEnter)
SET_KC(KPEqual)
// Modifiers
SET_KC(LeftShift)
SET_KC(LeftControl)
SET_KC(LeftAlt)
SET_KC(LeftSuper)
SET_KC(RightShift)
SET_KC(RightControl)
SET_KC(RightAlt)
SET_KC(RightSuper)
SET_KC(Menu)
#undef SET_KC
lua_setglobal(L, "Keycode");
return 1;
}
static int Lua_KeyDown(lua_State *L)
{
// match your other deepprofile scopes
PROFILE_DEEP_SCOPE("Engine::KeyDown");
// grab the integer keycode from Lua
lua_Integer code = luaL_checkinteger(L, 1);
// cast into your stronglytyped enum and query
bool down = Input::IsKeyDown(static_cast<Keycode>(code));
// return a Lua boolean
lua_pushboolean(L, down);
return 1;
}
static core::types::Vec3 GetLuaVector3(lua_State *L, int index)
{
if (luaL_testudata(L, index, LUA_VECTOR3_MT))
{
auto *vec = static_cast<LuaVector3 *>(lua_touserdata(L, index));
return core::types::Vec3(vec->x, vec->y, vec->z);
}
else if (lua_istable(L, index))
{
core::types::Vec3 result;
static const char *keys1[3] = {"x", "y", "z"};
static const char *keys2[3] = {"r", "g", "b"};
const char **keys = keys1;
lua_pushstring(L, "x");
lua_rawget(L, index);
if (lua_isnil(L, -1))
{
keys = keys2;
lua_pop(L, 1);
}
for (int i = 0; i < 3; ++i)
{
lua_pushstring(L, keys[i]);
lua_rawget(L, index);
result[i] = static_cast<float>(lua_tonumber(L, -1));
lua_pop(L, 1);
}
return result;
}
luaL_error(L, "Expected Vector3 userdata or table with x/y/z or r/g/b");
return core::types::Vec3(0, 0, 0);
}
void PushVector3(lua_State* L, float x, float y, float z)
{
LuaVector3* vec = (LuaVector3*)lua_newuserdata(L, sizeof(LuaVector3));
vec->x = x;
vec->y = y;
vec->z = z;
luaL_setmetatable(L, LUA_VECTOR3_MT);
}
static int Lua_Vector3_New(lua_State *L)
{
PROFILE_DEEP_SCOPE("Vector3()");
LuaVector3 *vec = (LuaVector3 *)lua_newuserdata(L, sizeof(LuaVector3));
int nargs = lua_gettop(L);
vec->x = nargs >= 1 ? static_cast<float>(lua_tonumber(L, 1)) : 0.0f;
vec->y = nargs >= 2 ? static_cast<float>(lua_tonumber(L, 2)) : 0.0f;
vec->z = nargs >= 3 ? static_cast<float>(lua_tonumber(L, 3)) : 0.0f;
luaL_setmetatable(L, LUA_VECTOR3_MT);
return 1;
}
static int Lua_Vector3_Index(lua_State *L)
{
auto *vec = (LuaVector3 *)luaL_checkudata(L, 1, LUA_VECTOR3_MT);
const char *key = lua_tostring(L, 2);
if (!strcmp(key, "x"))
lua_pushnumber(L, vec->x);
else if (!strcmp(key, "y"))
lua_pushnumber(L, vec->y);
else if (!strcmp(key, "z"))
lua_pushnumber(L, vec->z);
else
lua_pushnil(L);
return 1;
}
static int Lua_Vector3_NewIndex(lua_State *L)
{
auto *vec = (LuaVector3 *)luaL_checkudata(L, 1, LUA_VECTOR3_MT);
const char *key = lua_tostring(L, 2);
float value = static_cast<float>(luaL_checknumber(L, 3));
if (!strcmp(key, "x"))
vec->x = value;
else if (!strcmp(key, "y"))
vec->y = value;
else if (!strcmp(key, "z"))
vec->z = value;
return 0;
}
void RegisterVector3Type(lua_State *L)
{
luaL_newmetatable(L, LUA_VECTOR3_MT);
lua_pushcfunction(L, Lua_Vector3_Index);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, Lua_Vector3_NewIndex);
lua_setfield(L, -2, "__newindex");
lua_pop(L, 1);
lua_pushcfunction(L, Lua_Vector3_New);
lua_setglobal(L, "Vector3");
}
static int Lua_Vector2_New(lua_State *L)
{
PROFILE_DEEP_SCOPE("Vector2()");
LuaVector2 *vec = static_cast<LuaVector2 *>(lua_newuserdata(L, sizeof(LuaVector2)));
int nargs = lua_gettop(L);
vec->x = nargs >= 1 ? static_cast<float>(lua_tonumber(L, 1)) : 0.0f;
vec->y = nargs >= 2 ? static_cast<float>(lua_tonumber(L, 2)) : 0.0f;
luaL_setmetatable(L, LUA_VECTOR2_MT);
return 1;
}
static int Lua_Vector2_Index(lua_State *L)
{
PROFILE_DEEP_SCOPE("Vector2::index");
auto *vec = static_cast<LuaVector2 *>(luaL_checkudata(L, 1, LUA_VECTOR2_MT));
const char *key = lua_tostring(L, 2); // safer and faster than luaL_checkstring for profiling edge cases
// Fast string comparison
if (key[0] == 'x' && key[1] == '\0')
{
lua_pushnumber(L, vec->x);
}
else if (key[0] == 'y' && key[1] == '\0')
{
lua_pushnumber(L, vec->y);
}
else
{
lua_pushnil(L);
}
return 1;
}
static int Lua_Vector2_NewIndex(lua_State *L)
{
PROFILE_DEEP_SCOPE("Vector2::newindex");
auto *vec = static_cast<LuaVector2 *>(luaL_checkudata(L, 1, LUA_VECTOR2_MT));
const char *key = lua_tostring(L, 2);
float value = static_cast<float>(luaL_checknumber(L, 3));
if (key[0] == 'x' && key[1] == '\0')
{
vec->x = value;
}
else if (key[0] == 'y' && key[1] == '\0')
{
vec->y = value;
}
return 0;
}
void RegisterVector2Type(lua_State *L)
{
PROFILE_DEEP_SCOPE("RegisterVector2Type");
luaL_newmetatable(L, LUA_VECTOR2_MT);
lua_pushcfunction(L, Lua_Vector2_Index);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, Lua_Vector2_NewIndex);
lua_setfield(L, -2, "__newindex");
lua_pop(L, 1);
lua_pushcfunction(L, Lua_Vector2_New);
lua_setglobal(L, "Vector2");
}
// Register the AnimationComponent metatable
void RegisterAnimationType(lua_State *L)
{
PROFILE_DEEP_SCOPE("RegisterAnimationType");
luaL_newmetatable(L, LUA_ANIMATION_MT);
// methods
lua_pushcfunction(L, Lua_Animation_GetCurrentFrame);
lua_setfield(L, -2, "GetCurrentFrame");
lua_pushcfunction(L, Lua_Animation_SetCurrentFrame);
lua_setfield(L, -2, "SetCurrentFrame");
lua_pushcfunction(L, Lua_Animation_IsPlaying);
lua_setfield(L, -2, "IsPlaying");
lua_pushcfunction(L, Lua_Animation_SetPlaying);
lua_setfield(L, -2, "SetPlaying");
lua_pushcfunction(L, Lua_Animation_GetStartFrame);
lua_setfield(L, -2, "GetStartFrame");
lua_pushcfunction(L, Lua_Animation_SetStartFrame);
lua_setfield(L, -2, "SetStartFrame");
lua_pushcfunction(L, Lua_Animation_GetEndFrame);
lua_setfield(L, -2, "GetEndFrame");
lua_pushcfunction(L, Lua_Animation_SetEndFrame);
lua_setfield(L, -2, "SetEndFrame");
// __index
lua_pushcfunction(L, Lua_Animation_Index);
lua_setfield(L, -2, "__index");
lua_pop(L, 1);
}
void ScriptComponent::RegisterEngineBindings()
{
PROFILE_DEEP_SCOPE("RegisterEngineBindings");
lua_newtable(L);
lua_pushcfunction(L, Lua_LogInfo);
lua_setfield(L, -2, "LogInfo");
lua_pushcfunction(L, Lua_LogInfo);
lua_setfield(L, -2, "Log");
lua_pushcfunction(L, Lua_LogError);
lua_setfield(L, -2, "LogError");
lua_pushcfunction(L, Lua_LogDebug);
lua_setfield(L, -2, "LogDebug");
lua_pushcfunction(L, Lua_GetObjectByTag);
lua_setfield(L, -2, "GetObjectByTag");
lua_pushcfunction(L, Lua_DebugLua);
lua_setfield(L, -2, "DebugLua");
lua_pushcfunction(L, Lua_KeyDown);
lua_setfield(L, -2, "KeyDown");
lua_pushcfunction(L, Lua_GetMousePos);
lua_setfield(L, -2, "GetMousePos");
lua_pushcfunction(L, Lua_SetGlobal);
lua_setfield(L, -2, "SetGlobal");
lua_pushcfunction(L, Lua_GetGlobal);
lua_setfield(L, -2, "GetGlobal");
RegisterUtils(L); // called here so its under Engine table
lua_setglobal(L, "Engine");
}
void ScriptComponent::ReloadScript()
{
PROFILE_DEEP_SCOPE("ScriptComponent::ReloadScript");
if (scriptPath.empty())
return;
if (L)
{
ScriptCore::UnregisterState(L);
lua_close(L);
}
L = luaL_newstate();
*(ScriptComponent **)lua_getextraspace(L) = this;
luaL_openlibs(L);
lua_sethook(L, Hook, LUA_MASKCALL | LUA_MASKRET, 0);
ScriptCore::RegisterState(L, scriptPath, GetFilenameNoExtension(scriptPath), this);
RegisterObjectType(L);
RegisterVector2Type(L);
RegisterVector3Type(L);
RegisterAnimationType(L);
RegisterEngineBindings();
RegisterLightType(L);
RegisterSoundType(L);
keycode_loader(L);
Logger::LogVerbose("[Lua] Loading Script from file.");
if (luaL_dofile(L, scriptPath.c_str()))
{
Logger::LogError("[Lua] %s", lua_tostring(L, -1));
RecoverableError("Failed to load Lua script: " + scriptPath, Create::Exceptions::ComponentLoad).Handle();
return;
}
}
void ScriptComponent::OnUpdate(float dt)
{
PROFILE_DEEP_SCOPE("ScriptComponent::OnUpdate");
if (!L)
return;
lua_getglobal(L, "OnUpdate");
if (lua_isfunction(L, -1))
{
lua_pushnumber(L, dt);
if (lua_pcall(L, 1, 0, 0) != LUA_OK)
{
Logger::LogError("[Lua] %s", lua_tostring(L, -1));
RecoverableError("OnUpdate failed in: " + scriptPath, Create::Exceptions::ComponentLoad).Handle();
}
}
else
lua_pop(L, 1);
}
void ScriptComponent::Save(YAML::Emitter &out) const
{
out << YAML::BeginMap;
out << YAML::Key << "type" << YAML::Value << "ScriptComponent";
out << YAML::Key << "scriptPath" << YAML::Value << scriptPath;
out << YAML::EndMap;
}
void ScriptComponent::Load(const YAML::Node &node)
{
try
{
if (!node["scriptPath"])
{
RecoverableError("Missing 'scriptPath' in ScriptComponent", Create::Exceptions::MissingField).Handle();
return;
}
scriptPath = node["scriptPath"].as<std::string>();
ReloadScript();
}
catch (const YAML::Exception &e)
{
RecoverableError("YAML error in ScriptComponent::Load: " + std::string(e.what()), Create::Exceptions::ComponentLoad).Handle();
}
}