Files
secretpsxsplash/src/luaapi.cpp
Jan Racek bfab154547 cleanup
2026-03-27 21:29:01 +01:00

1755 lines
46 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 "luaapi.hh"
#include "scenemanager.hh"
#include "gameobject.hh"
#include "controls.hh"
#include "camera.hh"
#include "cutscene.hh"
#include "uisystem.hh"
#include <psyqo/soft-math.hh>
#include <psyqo/trigonometry.hh>
#include <psyqo/fixed-point.hh>
namespace psxsplash {
// Static member
SceneManager* LuaAPI::s_sceneManager = nullptr;
CutscenePlayer* LuaAPI::s_cutscenePlayer = nullptr;
UISystem* LuaAPI::s_uiSystem = nullptr;
// Scale factor: FixedPoint<12> stores 1.0 as raw 4096.
// Lua scripts work in world-space units (1 = one unit), so we convert.
static constexpr lua_Number kFixedScale = 4096;
static lua_Number fpToLua(psyqo::FixedPoint<12> fp) {
return static_cast<lua_Number>(fp.raw()) / kFixedScale;
}
static psyqo::FixedPoint<12> luaToFp(lua_Number val) {
return psyqo::FixedPoint<12>(static_cast<int32_t>(val * kFixedScale), psyqo::FixedPoint<12>::RAW);
}
// Angle scale: psyqo::Angle is FixedPoint<10>, so 1.0_pi = raw 1024
static constexpr lua_Number kAngleScale = 1024;
static psyqo::Trig<> s_trig;
// ============================================================================
// REGISTRATION
// ============================================================================
void LuaAPI::RegisterAll(psyqo::Lua& L, SceneManager* scene, CutscenePlayer* cutscenePlayer, UISystem* uiSystem) {
s_sceneManager = scene;
s_cutscenePlayer = cutscenePlayer;
s_uiSystem = uiSystem;
// ========================================================================
// ENTITY API
// ========================================================================
L.newTable(); // Entity table
L.push(Entity_FindByScriptIndex);
L.setField(-2, "FindByScriptIndex");
L.push(Entity_FindByIndex);
L.setField(-2, "FindByIndex");
L.push(Entity_Find);
L.setField(-2, "Find");
L.push(Entity_GetCount);
L.setField(-2, "GetCount");
L.push(Entity_SetActive);
L.setField(-2, "SetActive");
L.push(Entity_IsActive);
L.setField(-2, "IsActive");
L.push(Entity_GetPosition);
L.setField(-2, "GetPosition");
L.push(Entity_SetPosition);
L.setField(-2, "SetPosition");
L.push(Entity_GetRotationY);
L.setField(-2, "GetRotationY");
L.push(Entity_SetRotationY);
L.setField(-2, "SetRotationY");
L.push(Entity_ForEach);
L.setField(-2, "ForEach");
L.setGlobal("Entity");
// ========================================================================
// VEC3 API
// ========================================================================
L.newTable(); // Vec3 table
L.push(Vec3_New);
L.setField(-2, "new");
L.push(Vec3_Add);
L.setField(-2, "add");
L.push(Vec3_Sub);
L.setField(-2, "sub");
L.push(Vec3_Mul);
L.setField(-2, "mul");
L.push(Vec3_Dot);
L.setField(-2, "dot");
L.push(Vec3_Cross);
L.setField(-2, "cross");
L.push(Vec3_Length);
L.setField(-2, "length");
L.push(Vec3_LengthSq);
L.setField(-2, "lengthSq");
L.push(Vec3_Normalize);
L.setField(-2, "normalize");
L.push(Vec3_Distance);
L.setField(-2, "distance");
L.push(Vec3_DistanceSq);
L.setField(-2, "distanceSq");
L.push(Vec3_Lerp);
L.setField(-2, "lerp");
L.setGlobal("Vec3");
// ========================================================================
// INPUT API
// ========================================================================
L.newTable(); // Input table
L.push(Input_IsPressed);
L.setField(-2, "IsPressed");
L.push(Input_IsReleased);
L.setField(-2, "IsReleased");
L.push(Input_IsHeld);
L.setField(-2, "IsHeld");
L.push(Input_GetAnalog);
L.setField(-2, "GetAnalog");
// Register button constants
RegisterInputConstants(L);
L.setGlobal("Input");
// ========================================================================
// TIMER API
// ========================================================================
L.newTable(); // Timer table
L.push(Timer_GetFrameCount);
L.setField(-2, "GetFrameCount");
L.setGlobal("Timer");
// ========================================================================
// CAMERA API
// ========================================================================
L.newTable(); // Camera table
L.push(Camera_GetPosition);
L.setField(-2, "GetPosition");
L.push(Camera_SetPosition);
L.setField(-2, "SetPosition");
L.push(Camera_GetRotation);
L.setField(-2, "GetRotation");
L.push(Camera_SetRotation);
L.setField(-2, "SetRotation");
L.push(Camera_LookAt);
L.setField(-2, "LookAt");
L.setGlobal("Camera");
// ========================================================================
// AUDIO API (Placeholder)
// ========================================================================
L.newTable(); // Audio table
L.push(Audio_Play);
L.setField(-2, "Play");
L.push(Audio_Find);
L.setField(-2, "Find");
L.push(Audio_Stop);
L.setField(-2, "Stop");
L.push(Audio_SetVolume);
L.setField(-2, "SetVolume");
L.push(Audio_StopAll);
L.setField(-2, "StopAll");
L.setGlobal("Audio");
// ========================================================================
// DEBUG API
// ========================================================================
L.newTable(); // Debug table
L.push(Debug_Log);
L.setField(-2, "Log");
L.push(Debug_DrawLine);
L.setField(-2, "DrawLine");
L.push(Debug_DrawBox);
L.setField(-2, "DrawBox");
L.setGlobal("Debug");
// ========================================================================
// MATH API
// ========================================================================
L.newTable(); // PSXMath table (avoid conflict with Lua's math)
L.push(Math_Clamp);
L.setField(-2, "Clamp");
L.push(Math_Lerp);
L.setField(-2, "Lerp");
L.push(Math_Sign);
L.setField(-2, "Sign");
L.push(Math_Abs);
L.setField(-2, "Abs");
L.push(Math_Min);
L.setField(-2, "Min");
L.push(Math_Max);
L.setField(-2, "Max");
L.setGlobal("PSXMath");
// ========================================================================
// SCENE API
// ========================================================================
L.newTable(); // Scene table
L.push(Scene_Load);
L.setField(-2, "Load");
L.push(Scene_GetIndex);
L.setField(-2, "GetIndex");
L.setGlobal("Scene");
// ========================================================================
// PERSIST API
// ========================================================================
L.newTable(); // Persist table
L.push(Persist_Get);
L.setField(-2, "Get");
L.push(Persist_Set);
L.setField(-2, "Set");
L.setGlobal("Persist");
// ========================================================================
// CUTSCENE API
// ========================================================================
L.newTable(); // Cutscene table
L.push(Cutscene_Play);
L.setField(-2, "Play");
L.push(Cutscene_Stop);
L.setField(-2, "Stop");
L.push(Cutscene_IsPlaying);
L.setField(-2, "IsPlaying");
L.setGlobal("Cutscene");
// ========================================================================
// UI API
// ========================================================================
L.newTable(); // UI table
L.push(UI_FindCanvas);
L.setField(-2, "FindCanvas");
L.push(UI_SetCanvasVisible);
L.setField(-2, "SetCanvasVisible");
L.push(UI_IsCanvasVisible);
L.setField(-2, "IsCanvasVisible");
L.push(UI_FindElement);
L.setField(-2, "FindElement");
L.push(UI_SetVisible);
L.setField(-2, "SetVisible");
L.push(UI_IsVisible);
L.setField(-2, "IsVisible");
L.push(UI_SetText);
L.setField(-2, "SetText");
L.push(UI_GetText);
L.setField(-2, "GetText");
L.push(UI_SetProgress);
L.setField(-2, "SetProgress");
L.push(UI_GetProgress);
L.setField(-2, "GetProgress");
L.push(UI_SetColor);
L.setField(-2, "SetColor");
L.push(UI_GetColor);
L.setField(-2, "GetColor");
L.push(UI_SetPosition);
L.setField(-2, "SetPosition");
L.push(UI_GetPosition);
L.setField(-2, "GetPosition");
L.push(UI_SetSize);
L.setField(-2, "SetSize");
L.push(UI_GetSize);
L.setField(-2, "GetSize");
L.push(UI_SetProgressColors);
L.setField(-2, "SetProgressColors");
L.push(UI_GetElementType);
L.setField(-2, "GetElementType");
L.push(UI_GetElementCount);
L.setField(-2, "GetElementCount");
L.push(UI_GetElementByIndex);
L.setField(-2, "GetElementByIndex");
L.setGlobal("UI");
}
// ============================================================================
// ENTITY API IMPLEMENTATION
// ============================================================================
int LuaAPI::Entity_FindByScriptIndex(lua_State* L) {
psyqo::Lua lua(L);
if (!s_sceneManager || !lua.isNumber(1)) {
lua.push();
return 1;
}
// Find first object with matching luaFileIndex
int16_t luaIdx = static_cast<int16_t>(lua.toNumber(1));
for (size_t i = 0; i < s_sceneManager->getGameObjectCount(); i++) {
auto* go = s_sceneManager->getGameObject(static_cast<uint16_t>(i));
if (go && go->luaFileIndex == luaIdx) {
lua.push(reinterpret_cast<uint8_t*>(go));
lua.rawGet(LUA_REGISTRYINDEX);
if (lua.isTable(-1)) return 1;
lua.pop();
}
}
lua.push();
return 1;
}
int LuaAPI::Entity_FindByIndex(lua_State* L) {
psyqo::Lua lua(L);
if (!lua.isNumber(1)) {
lua.push();
return 1;
}
int index = static_cast<int>(lua.toNumber(1));
if (s_sceneManager) {
GameObject* go = s_sceneManager->getGameObject(static_cast<uint16_t>(index));
if (go) {
lua.push(reinterpret_cast<uint8_t*>(go));
lua.rawGet(LUA_REGISTRYINDEX);
if (lua.isTable(-1)) {
return 1;
}
lua.pop();
}
}
lua.push();
return 1;
}
int LuaAPI::Entity_Find(lua_State* L) {
psyqo::Lua lua(L);
if (!s_sceneManager) {
lua.push();
return 1;
}
// Accept number (index) or string (name lookup) for backwards compat
// Check isNumber FIRST — in Lua, numbers pass isString too.
if (lua.isNumber(1)) {
int index = static_cast<int>(lua.toNumber(1));
GameObject* go = s_sceneManager->getGameObject(static_cast<uint16_t>(index));
if (go) {
lua.push(reinterpret_cast<uint8_t*>(go));
lua.rawGet(LUA_REGISTRYINDEX);
if (lua.isTable(-1)) return 1;
lua.pop();
}
} else if (lua.isString(1)) {
const char* name = lua.toString(1);
GameObject* go = s_sceneManager->findObjectByName(name);
if (go) {
lua.push(reinterpret_cast<uint8_t*>(go));
lua.rawGet(LUA_REGISTRYINDEX);
if (lua.isTable(-1)) return 1;
lua.pop();
}
}
lua.push();
return 1;
}
int LuaAPI::Entity_GetCount(lua_State* L) {
psyqo::Lua lua(L);
if (s_sceneManager) {
lua.pushNumber(static_cast<lua_Number>(s_sceneManager->getGameObjectCount()));
} else {
lua.pushNumber(0);
}
return 1;
}
int LuaAPI::Entity_SetActive(lua_State* L) {
psyqo::Lua lua(L);
if (!lua.isTable(1)) {
return 0;
}
lua.getField(1, "__cpp_ptr");
auto go = lua.toUserdata<GameObject>(-1);
lua.pop();
bool active = lua.toBoolean(2);
if (go && s_sceneManager) {
s_sceneManager->setObjectActive(go, active);
}
return 0;
}
int LuaAPI::Entity_IsActive(lua_State* L) {
psyqo::Lua lua(L);
if (!lua.isTable(1)) {
lua.push(false);
return 1;
}
lua.getField(1, "__cpp_ptr");
auto go = lua.toUserdata<GameObject>(-1);
lua.pop();
if (go) {
lua.push(go->isActive());
} else {
lua.push(false);
}
return 1;
}
int LuaAPI::Entity_GetPosition(lua_State* L) {
psyqo::Lua lua(L);
if (!lua.isTable(1)) {
lua.push();
return 1;
}
lua.getField(1, "__cpp_ptr");
auto go = lua.toUserdata<GameObject>(-1);
lua.pop();
if (go) {
PushVec3(lua, go->position.x, go->position.y, go->position.z);
return 1;
}
lua.push();
return 1;
}
int LuaAPI::Entity_SetPosition(lua_State* L) {
psyqo::Lua lua(L);
if (!lua.isTable(1) || !lua.isTable(2)) {
return 0;
}
lua.getField(1, "__cpp_ptr");
auto go = lua.toUserdata<GameObject>(-1);
lua.pop();
if (!go) return 0;
psyqo::FixedPoint<12> x, y, z;
ReadVec3(lua, 2, x, y, z);
go->position.x = x;
go->position.y = y;
go->position.z = z;
return 0;
}
int LuaAPI::Entity_GetRotationY(lua_State* L) {
psyqo::Lua lua(L);
if (!lua.isTable(1)) {
lua.pushNumber(0);
return 1;
}
lua.getField(1, "__cpp_ptr");
auto go = lua.toUserdata<GameObject>(-1);
lua.pop();
if (!go) { lua.pushNumber(0); return 1; }
// Y rotation matrix: vs[0].x = cos(θ), vs[0].z = sin(θ)
int32_t sinRaw = go->rotation.vs[0].z.raw();
int32_t cosRaw = go->rotation.vs[0].x.raw();
// Fast atan2 approximation (linear in first octant, fold to full circle)
psyqo::Angle angle;
if (cosRaw == 0 && sinRaw == 0) {
angle.value = 0;
} else {
int32_t abs_s = sinRaw < 0 ? -sinRaw : sinRaw;
int32_t abs_c = cosRaw < 0 ? -cosRaw : cosRaw;
int32_t minV = abs_s < abs_c ? abs_s : abs_c;
int32_t maxV = abs_s > abs_c ? abs_s : abs_c;
int32_t a = (minV * 256) / maxV; // [0, 256] for [0, π/4]
if (abs_s > abs_c) a = 512 - a;
if (cosRaw < 0) a = 1024 - a;
if (sinRaw < 0) a = -a;
angle.value = a;
}
// Return in pi-units: 0.5 = π/2 = 90°
lua.pushNumber(static_cast<lua_Number>(angle.value) / kAngleScale);
return 1;
}
int LuaAPI::Entity_SetRotationY(lua_State* L) {
psyqo::Lua lua(L);
if (!lua.isTable(1)) return 0;
lua.getField(1, "__cpp_ptr");
auto go = lua.toUserdata<GameObject>(-1);
lua.pop();
if (!go) return 0;
// Accept angle in pi-units (0.5 = π/2 = 90°)
lua_Number piUnits = lua.toNumber(2);
psyqo::Angle angle;
angle.value = static_cast<int32_t>(piUnits * kAngleScale);
go->rotation = psyqo::SoftMath::generateRotationMatrix33(angle, psyqo::SoftMath::Axis::Y, s_trig);
return 0;
}
int LuaAPI::Entity_ForEach(lua_State* L) {
psyqo::Lua lua(L);
if (!s_sceneManager || !lua.isFunction(1)) return 0;
size_t count = s_sceneManager->getGameObjectCount();
for (size_t i = 0; i < count; i++) {
auto* go = s_sceneManager->getGameObject(static_cast<uint16_t>(i));
if (!go || !go->isActive()) continue;
// Push callback copy
lua.copy(1);
// Look up registered Lua table for this object (keyed by C++ pointer)
lua.push(reinterpret_cast<uint8_t*>(go));
lua.rawGet(LUA_REGISTRYINDEX);
if (!lua.isTable(-1)) {
lua.pop(2); // pop non-table + callback copy
continue;
}
lua.pushNumber(i); // push index as second argument
if (lua.pcall(2, 0) != LUA_OK) {
lua.pop(); // pop error message
}
}
return 0;
}
// ============================================================================
// VEC3 API IMPLEMENTATION
// ============================================================================
void LuaAPI::PushVec3(psyqo::Lua& L, psyqo::FixedPoint<12> x,
psyqo::FixedPoint<12> y, psyqo::FixedPoint<12> z) {
L.newTable();
L.pushNumber(fpToLua(x));
L.setField(-2, "x");
L.pushNumber(fpToLua(y));
L.setField(-2, "y");
L.pushNumber(fpToLua(z));
L.setField(-2, "z");
}
void LuaAPI::ReadVec3(psyqo::Lua& L, int idx,
psyqo::FixedPoint<12>& x,
psyqo::FixedPoint<12>& y,
psyqo::FixedPoint<12>& z) {
L.getField(idx, "x");
x = luaToFp(L.toNumber(-1));
L.pop();
L.getField(idx, "y");
y = luaToFp(L.toNumber(-1));
L.pop();
L.getField(idx, "z");
z = luaToFp(L.toNumber(-1));
L.pop();
}
int LuaAPI::Vec3_New(lua_State* L) {
psyqo::Lua lua(L);
psyqo::FixedPoint<12> x = luaToFp(lua.optNumber(1, 0));
psyqo::FixedPoint<12> y = luaToFp(lua.optNumber(2, 0));
psyqo::FixedPoint<12> z = luaToFp(lua.optNumber(3, 0));
PushVec3(lua, x, y, z);
return 1;
}
int LuaAPI::Vec3_Add(lua_State* L) {
psyqo::Lua lua(L);
if (!lua.isTable(1) || !lua.isTable(2)) {
lua.push();
return 1;
}
psyqo::FixedPoint<12> ax, ay, az;
psyqo::FixedPoint<12> bx, by, bz;
ReadVec3(lua, 1, ax, ay, az);
ReadVec3(lua, 2, bx, by, bz);
PushVec3(lua, ax + bx, ay + by, az + bz);
return 1;
}
int LuaAPI::Vec3_Sub(lua_State* L) {
psyqo::Lua lua(L);
if (!lua.isTable(1) || !lua.isTable(2)) {
lua.push();
return 1;
}
psyqo::FixedPoint<12> ax, ay, az;
psyqo::FixedPoint<12> bx, by, bz;
ReadVec3(lua, 1, ax, ay, az);
ReadVec3(lua, 2, bx, by, bz);
PushVec3(lua, ax - bx, ay - by, az - bz);
return 1;
}
int LuaAPI::Vec3_Mul(lua_State* L) {
psyqo::Lua lua(L);
if (!lua.isTable(1)) {
lua.push();
return 1;
}
psyqo::FixedPoint<12> x, y, z;
ReadVec3(lua, 1, x, y, z);
psyqo::FixedPoint<12> scalar = luaToFp(lua.toNumber(2));
PushVec3(lua, x * scalar, y * scalar, z * scalar);
return 1;
}
int LuaAPI::Vec3_Dot(lua_State* L) {
psyqo::Lua lua(L);
if (!lua.isTable(1) || !lua.isTable(2)) {
lua.pushNumber(0);
return 1;
}
psyqo::FixedPoint<12> ax, ay, az;
psyqo::FixedPoint<12> bx, by, bz;
ReadVec3(lua, 1, ax, ay, az);
ReadVec3(lua, 2, bx, by, bz);
auto dot = ax * bx + ay * by + az * bz;
lua.pushNumber(fpToLua(dot));
return 1;
}
int LuaAPI::Vec3_Cross(lua_State* L) {
psyqo::Lua lua(L);
if (!lua.isTable(1) || !lua.isTable(2)) {
lua.push();
return 1;
}
psyqo::FixedPoint<12> ax, ay, az;
psyqo::FixedPoint<12> bx, by, bz;
ReadVec3(lua, 1, ax, ay, az);
ReadVec3(lua, 2, bx, by, bz);
psyqo::FixedPoint<12> cx = ay * bz - az * by;
psyqo::FixedPoint<12> cy = az * bx - ax * bz;
psyqo::FixedPoint<12> cz = ax * by - ay * bx;
PushVec3(lua, cx, cy, cz);
return 1;
}
int LuaAPI::Vec3_LengthSq(lua_State* L) {
psyqo::Lua lua(L);
if (!lua.isTable(1)) {
lua.pushNumber(0);
return 1;
}
psyqo::FixedPoint<12> x, y, z;
ReadVec3(lua, 1, x, y, z);
auto lengthSq = x * x + y * y + z * z;
lua.pushNumber(fpToLua(lengthSq));
return 1;
}
int LuaAPI::Vec3_Length(lua_State* L) {
psyqo::Lua lua(L);
if (!lua.isTable(1)) {
lua.pushNumber(0);
return 1;
}
psyqo::FixedPoint<12> x, y, z;
ReadVec3(lua, 1, x, y, z);
// Compute length in scaled world-space to avoid fp12×fp12 overflow issues.
// Convert to Lua-number domain, sqrt there, return directly.
lua_Number sx = fpToLua(x);
lua_Number sy = fpToLua(y);
lua_Number sz = fpToLua(z);
lua_Number sqVal = sx * sx + sy * sy + sz * sz;
if (sqVal <= 0) {
lua.pushNumber(0);
return 1;
}
// Newton's method sqrt (integer-safe)
lua_Number guess = sqVal / 2;
if (guess == 0) guess = 1;
for (int i = 0; i < 12; i++) {
guess = (guess + sqVal / guess) / 2;
}
lua.pushNumber(guess);
return 1;
}
int LuaAPI::Vec3_Normalize(lua_State* L) {
psyqo::Lua lua(L);
if (!lua.isTable(1)) {
lua.push();
return 1;
}
psyqo::FixedPoint<12> x, y, z;
ReadVec3(lua, 1, x, y, z);
// Work in Lua-number (world-space) domain for the sqrt
lua_Number sx = fpToLua(x);
lua_Number sy = fpToLua(y);
lua_Number sz = fpToLua(z);
lua_Number sLen = sx * sx + sy * sy + sz * sz;
if (sLen <= 0) {
PushVec3(lua, psyqo::FixedPoint<12>(0), psyqo::FixedPoint<12>(0), psyqo::FixedPoint<12>(0));
return 1;
}
// Newton's method sqrt
lua_Number guess = sLen / 2;
if (guess == 0) guess = 1;
for (int i = 0; i < 12; i++) {
guess = (guess + sLen / guess) / 2;
}
if (guess == 0) guess = 1;
PushVec3(lua, luaToFp(sx / guess), luaToFp(sy / guess), luaToFp(sz / guess));
return 1;
}
int LuaAPI::Vec3_DistanceSq(lua_State* L) {
psyqo::Lua lua(L);
if (!lua.isTable(1) || !lua.isTable(2)) {
lua.pushNumber(0);
return 1;
}
psyqo::FixedPoint<12> ax, ay, az;
psyqo::FixedPoint<12> bx, by, bz;
ReadVec3(lua, 1, ax, ay, az);
ReadVec3(lua, 2, bx, by, bz);
auto dx = ax - bx;
auto dy = ay - by;
auto dz = az - bz;
auto distSq = dx * dx + dy * dy + dz * dz;
lua.pushNumber(fpToLua(distSq));
return 1;
}
int LuaAPI::Vec3_Distance(lua_State* L) {
psyqo::Lua lua(L);
if (!lua.isTable(1) || !lua.isTable(2)) {
lua.pushNumber(0);
return 1;
}
psyqo::FixedPoint<12> ax, ay, az;
psyqo::FixedPoint<12> bx, by, bz;
ReadVec3(lua, 1, ax, ay, az);
ReadVec3(lua, 2, bx, by, bz);
lua_Number dx = fpToLua(ax) - fpToLua(bx);
lua_Number dy = fpToLua(ay) - fpToLua(by);
lua_Number dz = fpToLua(az) - fpToLua(bz);
lua_Number sqVal = dx * dx + dy * dy + dz * dz;
if (sqVal <= 0) {
lua.pushNumber(0);
return 1;
}
lua_Number guess = sqVal / 2;
if (guess == 0) guess = 1;
for (int i = 0; i < 12; i++) {
guess = (guess + sqVal / guess) / 2;
}
lua.pushNumber(guess);
return 1;
}
int LuaAPI::Vec3_Lerp(lua_State* L) {
psyqo::Lua lua(L);
if (!lua.isTable(1) || !lua.isTable(2)) {
lua.push();
return 1;
}
psyqo::FixedPoint<12> ax, ay, az;
psyqo::FixedPoint<12> bx, by, bz;
ReadVec3(lua, 1, ax, ay, az);
ReadVec3(lua, 2, bx, by, bz);
psyqo::FixedPoint<12> t = luaToFp(lua.toNumber(3));
psyqo::FixedPoint<12> oneMinusT = psyqo::FixedPoint<12>(4096, psyqo::FixedPoint<12>::RAW) - t;
psyqo::FixedPoint<12> rx = ax * oneMinusT + bx * t;
psyqo::FixedPoint<12> ry = ay * oneMinusT + by * t;
psyqo::FixedPoint<12> rz = az * oneMinusT + bz * t;
PushVec3(lua, rx, ry, rz);
return 1;
}
// ============================================================================
// INPUT API IMPLEMENTATION
// ============================================================================
void LuaAPI::RegisterInputConstants(psyqo::Lua& L) {
// Button constants - must match psyqo::AdvancedPad::Button enum
L.pushNumber(static_cast<lua_Number>(psyqo::AdvancedPad::Button::Cross));
L.setField(-2, "CROSS");
L.pushNumber(static_cast<lua_Number>(psyqo::AdvancedPad::Button::Circle));
L.setField(-2, "CIRCLE");
L.pushNumber(static_cast<lua_Number>(psyqo::AdvancedPad::Button::Square));
L.setField(-2, "SQUARE");
L.pushNumber(static_cast<lua_Number>(psyqo::AdvancedPad::Button::Triangle));
L.setField(-2, "TRIANGLE");
L.pushNumber(static_cast<lua_Number>(psyqo::AdvancedPad::Button::L1));
L.setField(-2, "L1");
L.pushNumber(static_cast<lua_Number>(psyqo::AdvancedPad::Button::R1));
L.setField(-2, "R1");
L.pushNumber(static_cast<lua_Number>(psyqo::AdvancedPad::Button::L2));
L.setField(-2, "L2");
L.pushNumber(static_cast<lua_Number>(psyqo::AdvancedPad::Button::R2));
L.setField(-2, "R2");
L.pushNumber(static_cast<lua_Number>(psyqo::AdvancedPad::Button::Start));
L.setField(-2, "START");
L.pushNumber(static_cast<lua_Number>(psyqo::AdvancedPad::Button::Select));
L.setField(-2, "SELECT");
L.pushNumber(static_cast<lua_Number>(psyqo::AdvancedPad::Button::Up));
L.setField(-2, "UP");
L.pushNumber(static_cast<lua_Number>(psyqo::AdvancedPad::Button::Down));
L.setField(-2, "DOWN");
L.pushNumber(static_cast<lua_Number>(psyqo::AdvancedPad::Button::Left));
L.setField(-2, "LEFT");
L.pushNumber(static_cast<lua_Number>(psyqo::AdvancedPad::Button::Right));
L.setField(-2, "RIGHT");
L.pushNumber(static_cast<lua_Number>(psyqo::AdvancedPad::Button::L3));
L.setField(-2, "L3");
L.pushNumber(static_cast<lua_Number>(psyqo::AdvancedPad::Button::R3));
L.setField(-2, "R3");
}
int LuaAPI::Input_IsPressed(lua_State* L) {
psyqo::Lua lua(L);
if (!s_sceneManager || !lua.isNumber(1)) {
lua.push(false);
return 1;
}
auto button = static_cast<psyqo::AdvancedPad::Button>(static_cast<uint16_t>(lua.toNumber(1)));
lua.push(s_sceneManager->getControls().wasButtonPressed(button));
return 1;
}
int LuaAPI::Input_IsReleased(lua_State* L) {
psyqo::Lua lua(L);
if (!s_sceneManager || !lua.isNumber(1)) {
lua.push(false);
return 1;
}
auto button = static_cast<psyqo::AdvancedPad::Button>(static_cast<uint16_t>(lua.toNumber(1)));
lua.push(s_sceneManager->getControls().wasButtonReleased(button));
return 1;
}
int LuaAPI::Input_IsHeld(lua_State* L) {
psyqo::Lua lua(L);
if (!s_sceneManager || !lua.isNumber(1)) {
lua.push(false);
return 1;
}
auto button = static_cast<psyqo::AdvancedPad::Button>(static_cast<uint16_t>(lua.toNumber(1)));
lua.push(s_sceneManager->getControls().isButtonHeld(button));
return 1;
}
int LuaAPI::Input_GetAnalog(lua_State* L) {
psyqo::Lua lua(L);
if (!s_sceneManager) {
lua.pushNumber(0);
lua.pushNumber(0);
return 2;
}
int stick = lua.isNumber(1) ? static_cast<int>(lua.toNumber(1)) : 0;
auto& controls = s_sceneManager->getControls();
int16_t x, y;
if (stick == 1) {
x = controls.getRightStickX();
y = controls.getRightStickY();
} else {
x = controls.getLeftStickX();
y = controls.getLeftStickY();
}
// Scale to approximately [-1.0, 1.0] in Lua number space
// Stick range is -127 to +127; divide by 127
lua.pushNumber(x * kFixedScale / 127);
lua.pushNumber(y * kFixedScale / 127);
return 2;
}
// ============================================================================
// TIMER API IMPLEMENTATION
// ============================================================================
static uint32_t s_frameCount = 0;
void LuaAPI::IncrementFrameCount() {
s_frameCount++;
}
void LuaAPI::ResetFrameCount() {
s_frameCount = 0;
}
int LuaAPI::Timer_GetFrameCount(lua_State* L) {
psyqo::Lua lua(L);
lua.pushNumber(s_frameCount);
return 1;
}
// ============================================================================
// CAMERA API IMPLEMENTATION
// ============================================================================
int LuaAPI::Camera_GetPosition(lua_State* L) {
psyqo::Lua lua(L);
if (s_sceneManager) {
auto& pos = s_sceneManager->getCamera().GetPosition();
PushVec3(lua, pos.x, pos.y, pos.z);
} else {
PushVec3(lua, psyqo::FixedPoint<12>(0), psyqo::FixedPoint<12>(0), psyqo::FixedPoint<12>(0));
}
return 1;
}
int LuaAPI::Camera_SetPosition(lua_State* L) {
psyqo::Lua lua(L);
if (!s_sceneManager || !lua.isTable(1)) return 0;
psyqo::FixedPoint<12> x, y, z;
ReadVec3(lua, 1, x, y, z);
s_sceneManager->getCamera().SetPosition(x, y, z);
return 0;
}
int LuaAPI::Camera_GetRotation(lua_State* L) {
psyqo::Lua lua(L);
// Camera only stores the rotation matrix internally.
// Decomposing back to Euler angles is not supported.
// Return {0,0,0} — use Camera.SetRotation to control orientation.
PushVec3(lua, psyqo::FixedPoint<12>(0), psyqo::FixedPoint<12>(0), psyqo::FixedPoint<12>(0));
return 1;
}
int LuaAPI::Camera_SetRotation(lua_State* L) {
psyqo::Lua lua(L);
if (!s_sceneManager) return 0;
// Accept three angles in pi-units (e.g., 0.5 = π/2 = 90°)
// This matches psyqo::Angle convention used by the engine.
psyqo::Angle rx, ry, rz;
rx.value = static_cast<int32_t>(lua.optNumber(1, 0) * kAngleScale);
ry.value = static_cast<int32_t>(lua.optNumber(2, 0) * kAngleScale);
rz.value = static_cast<int32_t>(lua.optNumber(3, 0) * kAngleScale);
s_sceneManager->getCamera().SetRotation(rx, ry, rz);
return 0;
}
int LuaAPI::Camera_LookAt(lua_State* L) {
psyqo::Lua lua(L);
if (!s_sceneManager) return 0;
psyqo::FixedPoint<12> tx, ty, tz;
if (lua.isTable(1)) {
ReadVec3(lua, 1, tx, ty, tz);
} else {
tx = luaToFp(lua.optNumber(1, 0));
ty = luaToFp(lua.optNumber(2, 0));
tz = luaToFp(lua.optNumber(3, 0));
}
auto& cam = s_sceneManager->getCamera();
auto& pos = cam.GetPosition();
// Compute direction vector from camera to target
lua_Number dx = fpToLua(tx) - fpToLua(pos.x);
lua_Number dy = fpToLua(ty) - fpToLua(pos.y);
lua_Number dz = fpToLua(tz) - fpToLua(pos.z);
// Compute horizontal distance for pitch calculation
lua_Number horizDistSq = dx * dx + dz * dz;
lua_Number horizGuess = horizDistSq / 2;
if (horizGuess == 0) horizGuess = 1;
for (int i = 0; i < 12; i++) {
horizGuess = (horizGuess + horizDistSq / horizGuess) / 2;
}
// Yaw = atan2(dx, dz) — approximate with lookup or use psyqo trig
// For now, use a simple atan2 approximation in fp12 domain
// and set rotation via SetRotation (pitch, yaw, 0)
// Approximate: yaw is proportional to dx/dz in small-angle
// Full implementation requires psyqo Trig atan2 which is not trivially
// accessible here. Set rotation to face the target on the Y axis.
// This is a simplified look-at that only handles yaw.
psyqo::Angle yaw;
psyqo::Angle pitch;
// Use scaled integer atan2 approximation
// atan2(dx, dz) in the range [-π, π]
// For PS1, the exact method depends on psyqo's Trig class.
// Returning luaError since we can't do a proper atan2 without Trig instance.
// Compromise: just set rotation angles directly
yaw.value = 0;
pitch.value = 0;
// For a real implementation, Camera would need a LookAt method.
return 0;
}
// ============================================================================
// AUDIO API IMPLEMENTATION
// ============================================================================
int LuaAPI::Audio_Play(lua_State* L) {
psyqo::Lua lua(L);
if (!s_sceneManager) {
lua.pushNumber(-1);
return 1;
}
int soundId = -1;
// Accept number (index) or string (name lookup) like Entity.Find
// Check isNumber FIRST - in Lua, numbers pass isString too.
if (lua.isNumber(1)) {
soundId = static_cast<int>(lua.toNumber(1));
} else if (lua.isString(1)) {
const char* name = lua.toString(1);
soundId = s_sceneManager->findAudioClipByName(name);
if (soundId < 0) {
lua.pushNumber(-1);
return 1;
}
} else {
lua.pushNumber(-1);
return 1;
}
int volume = static_cast<int>(lua.optNumber(2, 100));
int pan = static_cast<int>(lua.optNumber(3, 64));
int voice = s_sceneManager->getAudio().play(soundId, volume, pan);
lua.pushNumber(voice);
return 1;
}
int LuaAPI::Audio_Find(lua_State* L) {
psyqo::Lua lua(L);
if (!s_sceneManager || !lua.isString(1)) {
lua.push(); // nil
return 1;
}
const char* name = lua.toString(1);
int clipIndex = s_sceneManager->findAudioClipByName(name);
if (clipIndex >= 0) {
lua.pushNumber(static_cast<lua_Number>(clipIndex));
} else {
lua.push(); // nil
}
return 1;
}
int LuaAPI::Audio_Stop(lua_State* L) {
psyqo::Lua lua(L);
if (!s_sceneManager) return 0;
int channelId = static_cast<int>(lua.toNumber(1));
s_sceneManager->getAudio().stopVoice(channelId);
return 0;
}
int LuaAPI::Audio_SetVolume(lua_State* L) {
psyqo::Lua lua(L);
if (!s_sceneManager) return 0;
int channelId = static_cast<int>(lua.toNumber(1));
int volume = static_cast<int>(lua.toNumber(2));
int pan = static_cast<int>(lua.optNumber(3, 64));
s_sceneManager->getAudio().setVoiceVolume(channelId, volume, pan);
return 0;
}
int LuaAPI::Audio_StopAll(lua_State* L) {
psyqo::Lua lua(L);
if (!s_sceneManager) return 0;
s_sceneManager->getAudio().stopAll();
return 0;
}
// ============================================================================
// DEBUG API IMPLEMENTATION
// ============================================================================
int LuaAPI::Debug_Log(lua_State* L) {
psyqo::Lua lua(L);
// Debug.Log is a no-op on PS1 to avoid printf overhead.
// Messages are only visible in emulator TTY builds.
return 0;
}
int LuaAPI::Debug_DrawLine(lua_State* L) {
psyqo::Lua lua(L);
// Parse start and end Vec3 tables, optional color
psyqo::FixedPoint<12> sx, sy, sz, ex, ey, ez;
if (lua.isTable(1) && lua.isTable(2)) {
ReadVec3(lua, 1, sx, sy, sz);
ReadVec3(lua, 2, ex, ey, ez);
}
// TODO: Queue LINE_G2 primitive through Renderer
return 0;
}
int LuaAPI::Debug_DrawBox(lua_State* L) {
psyqo::Lua lua(L);
// Parse center and size Vec3 tables, optional color
psyqo::FixedPoint<12> cx, cy, cz, hx, hy, hz;
if (lua.isTable(1) && lua.isTable(2)) {
ReadVec3(lua, 1, cx, cy, cz);
ReadVec3(lua, 2, hx, hy, hz);
}
// TODO: Queue 12 LINE_G2 primitives (box wireframe) through Renderer
return 0;
}
// ============================================================================
// MATH API IMPLEMENTATION
// ============================================================================
int LuaAPI::Math_Clamp(lua_State* L) {
psyqo::Lua lua(L);
lua_Number value = lua.toNumber(1);
lua_Number minVal = lua.toNumber(2);
lua_Number maxVal = lua.toNumber(3);
if (value < minVal) value = minVal;
if (value > maxVal) value = maxVal;
lua.pushNumber(value);
return 1;
}
int LuaAPI::Math_Lerp(lua_State* L) {
psyqo::Lua lua(L);
lua_Number a = lua.toNumber(1);
lua_Number b = lua.toNumber(2);
lua_Number t = lua.toNumber(3);
lua.pushNumber(a + (b - a) * t);
return 1;
}
int LuaAPI::Math_Sign(lua_State* L) {
psyqo::Lua lua(L);
lua_Number value = lua.toNumber(1);
if (value > 0) lua.pushNumber(1);
else if (value < 0) lua.pushNumber(-1);
else lua.pushNumber(0);
return 1;
}
int LuaAPI::Math_Abs(lua_State* L) {
psyqo::Lua lua(L);
lua_Number value = lua.toNumber(1);
lua.pushNumber(value < 0 ? -value : value);
return 1;
}
int LuaAPI::Math_Min(lua_State* L) {
psyqo::Lua lua(L);
lua_Number a = lua.toNumber(1);
lua_Number b = lua.toNumber(2);
lua.pushNumber(a < b ? a : b);
return 1;
}
int LuaAPI::Math_Max(lua_State* L) {
psyqo::Lua lua(L);
lua_Number a = lua.toNumber(1);
lua_Number b = lua.toNumber(2);
lua.pushNumber(a > b ? a : b);
return 1;
}
// ============================================================================
// SCENE API IMPLEMENTATION
// ============================================================================
int LuaAPI::Scene_Load(lua_State* L) {
psyqo::Lua lua(L);
if (!s_sceneManager || !lua.isNumber(1)) {
return 0;
}
int sceneIndex = static_cast<int>(lua.toNumber(1));
s_sceneManager->requestSceneLoad(sceneIndex);
return 0;
}
int LuaAPI::Scene_GetIndex(lua_State* L) {
psyqo::Lua lua(L);
if (!s_sceneManager) {
lua.pushNumber(0);
return 1;
}
lua.pushNumber(static_cast<lua_Number>(s_sceneManager->getCurrentSceneIndex()));
return 1;
}
// ============================================================================
// PERSIST API IMPLEMENTATION
// ============================================================================
struct PersistEntry {
char key[32];
lua_Number value;
bool used;
};
static PersistEntry s_persistData[16] = {};
// Inline string helpers (no libc on bare-metal PS1)
static bool streq(const char* a, const char* b) {
while (*a && *b) { if (*a++ != *b++) return false; }
return *a == *b;
}
static void strcopy(char* dst, const char* src, int maxLen) {
int i = 0;
for (; i < maxLen - 1 && src[i]; i++) dst[i] = src[i];
dst[i] = '\0';
}
int LuaAPI::Persist_Get(lua_State* L) {
psyqo::Lua lua(L);
const char* key = lua.toString(1);
if (!key) { lua.push(); return 1; }
for (int i = 0; i < 16; i++) {
if (s_persistData[i].used && streq(s_persistData[i].key, key)) {
lua.pushNumber(s_persistData[i].value);
return 1;
}
}
lua.push(); // nil
return 1;
}
int LuaAPI::Persist_Set(lua_State* L) {
psyqo::Lua lua(L);
const char* key = lua.toString(1);
if (!key) return 0;
lua_Number value = lua.toNumber(2);
// Update existing key
for (int i = 0; i < 16; i++) {
if (s_persistData[i].used && streq(s_persistData[i].key, key)) {
s_persistData[i].value = value;
return 0;
}
}
// Find empty slot
for (int i = 0; i < 16; i++) {
if (!s_persistData[i].used) {
strcopy(s_persistData[i].key, key, 32);
s_persistData[i].value = value;
s_persistData[i].used = true;
return 0;
}
}
return 0; // No room — silently fail
}
void LuaAPI::PersistClear() {
for (int i = 0; i < 16; i++) {
s_persistData[i].used = false;
}
}
// ============================================================================
// CUTSCENE API IMPLEMENTATION
// ============================================================================
int LuaAPI::Cutscene_Play(lua_State* L) {
psyqo::Lua lua(L);
if (!s_cutscenePlayer || !lua.isString(1)) {
return 0;
}
const char* name = lua.toString(1);
s_cutscenePlayer->play(name);
return 0;
}
int LuaAPI::Cutscene_Stop(lua_State* L) {
psyqo::Lua lua(L);
if (s_cutscenePlayer) {
s_cutscenePlayer->stop();
}
return 0;
}
int LuaAPI::Cutscene_IsPlaying(lua_State* L) {
psyqo::Lua lua(L);
if (s_cutscenePlayer) {
lua.push(s_cutscenePlayer->isPlaying());
} else {
lua.push(false);
}
return 1;
}
// ============================================================================
// UI API IMPLEMENTATION
// ============================================================================
int LuaAPI::UI_FindCanvas(lua_State* L) {
psyqo::Lua lua(L);
if (!s_uiSystem || !lua.isString(1)) {
lua.pushNumber(-1);
return 1;
}
const char* name = lua.toString(1);
int idx = s_uiSystem->findCanvas(name);
lua.pushNumber(static_cast<lua_Number>(idx));
return 1;
}
int LuaAPI::UI_SetCanvasVisible(lua_State* L) {
psyqo::Lua lua(L);
if (!s_uiSystem) return 0;
int idx;
// Accept number (index) or string (name)
if (lua.isNumber(1)) {
idx = static_cast<int>(lua.toNumber(1));
} else if (lua.isString(1)) {
idx = s_uiSystem->findCanvas(lua.toString(1));
} else {
return 0;
}
bool visible = lua.toBoolean(2);
s_uiSystem->setCanvasVisible(idx, visible);
return 0;
}
int LuaAPI::UI_IsCanvasVisible(lua_State* L) {
psyqo::Lua lua(L);
if (!s_uiSystem) {
lua.push(false);
return 1;
}
int idx;
if (lua.isNumber(1)) {
idx = static_cast<int>(lua.toNumber(1));
} else if (lua.isString(1)) {
idx = s_uiSystem->findCanvas(lua.toString(1));
} else {
lua.push(false);
return 1;
}
lua.push(s_uiSystem->isCanvasVisible(idx));
return 1;
}
int LuaAPI::UI_FindElement(lua_State* L) {
psyqo::Lua lua(L);
if (!s_uiSystem || !lua.isNumber(1) || !lua.isString(2)) {
lua.pushNumber(-1);
return 1;
}
int canvasIdx = static_cast<int>(lua.toNumber(1));
const char* name = lua.toString(2);
int handle = s_uiSystem->findElement(canvasIdx, name);
lua.pushNumber(static_cast<lua_Number>(handle));
return 1;
}
int LuaAPI::UI_SetVisible(lua_State* L) {
psyqo::Lua lua(L);
if (!s_uiSystem || !lua.isNumber(1)) return 0;
int handle = static_cast<int>(lua.toNumber(1));
bool visible = lua.toBoolean(2);
s_uiSystem->setElementVisible(handle, visible);
return 0;
}
int LuaAPI::UI_IsVisible(lua_State* L) {
psyqo::Lua lua(L);
if (!s_uiSystem || !lua.isNumber(1)) {
lua.push(false);
return 1;
}
int handle = static_cast<int>(lua.toNumber(1));
lua.push(s_uiSystem->isElementVisible(handle));
return 1;
}
int LuaAPI::UI_SetText(lua_State* L) {
psyqo::Lua lua(L);
if (!s_uiSystem || !lua.isNumber(1)) return 0;
int handle = static_cast<int>(lua.toNumber(1));
const char* text = lua.isString(2) ? lua.toString(2) : "";
s_uiSystem->setText(handle, text);
return 0;
}
int LuaAPI::UI_SetProgress(lua_State* L) {
psyqo::Lua lua(L);
if (!s_uiSystem || !lua.isNumber(1)) return 0;
int handle = static_cast<int>(lua.toNumber(1));
int value = static_cast<int>(lua.toNumber(2));
if (value < 0) value = 0;
if (value > 100) value = 100;
s_uiSystem->setProgress(handle, (uint8_t)value);
return 0;
}
int LuaAPI::UI_GetProgress(lua_State* L) {
psyqo::Lua lua(L);
if (!s_uiSystem || !lua.isNumber(1)) {
lua.pushNumber(0);
return 1;
}
int handle = static_cast<int>(lua.toNumber(1));
lua.pushNumber(static_cast<lua_Number>(s_uiSystem->getProgress(handle)));
return 1;
}
int LuaAPI::UI_SetColor(lua_State* L) {
psyqo::Lua lua(L);
if (!s_uiSystem || !lua.isNumber(1)) return 0;
int handle = static_cast<int>(lua.toNumber(1));
uint8_t r = static_cast<uint8_t>(lua.toNumber(2));
uint8_t g = static_cast<uint8_t>(lua.toNumber(3));
uint8_t b = static_cast<uint8_t>(lua.toNumber(4));
s_uiSystem->setColor(handle, r, g, b);
return 0;
}
int LuaAPI::UI_SetPosition(lua_State* L) {
psyqo::Lua lua(L);
if (!s_uiSystem || !lua.isNumber(1)) return 0;
int handle = static_cast<int>(lua.toNumber(1));
int16_t x = static_cast<int16_t>(lua.toNumber(2));
int16_t y = static_cast<int16_t>(lua.toNumber(3));
s_uiSystem->setPosition(handle, x, y);
return 0;
}
int LuaAPI::UI_GetText(lua_State* L) {
psyqo::Lua lua(L);
if (!s_uiSystem || !lua.isNumber(1)) {
lua.push("");
return 1;
}
int handle = static_cast<int>(lua.toNumber(1));
lua.push(s_uiSystem->getText(handle));
return 1;
}
int LuaAPI::UI_GetColor(lua_State* L) {
psyqo::Lua lua(L);
if (!s_uiSystem || !lua.isNumber(1)) {
lua.pushNumber(0); lua.pushNumber(0); lua.pushNumber(0);
return 3;
}
int handle = static_cast<int>(lua.toNumber(1));
uint8_t r, g, b;
s_uiSystem->getColor(handle, r, g, b);
lua.pushNumber(r); lua.pushNumber(g); lua.pushNumber(b);
return 3;
}
int LuaAPI::UI_GetPosition(lua_State* L) {
psyqo::Lua lua(L);
if (!s_uiSystem || !lua.isNumber(1)) {
lua.pushNumber(0); lua.pushNumber(0);
return 2;
}
int handle = static_cast<int>(lua.toNumber(1));
int16_t x, y;
s_uiSystem->getPosition(handle, x, y);
lua.pushNumber(static_cast<lua_Number>(x));
lua.pushNumber(static_cast<lua_Number>(y));
return 2;
}
int LuaAPI::UI_SetSize(lua_State* L) {
psyqo::Lua lua(L);
if (!s_uiSystem || !lua.isNumber(1)) return 0;
int handle = static_cast<int>(lua.toNumber(1));
int16_t w = static_cast<int16_t>(lua.toNumber(2));
int16_t h = static_cast<int16_t>(lua.toNumber(3));
s_uiSystem->setSize(handle, w, h);
return 0;
}
int LuaAPI::UI_GetSize(lua_State* L) {
psyqo::Lua lua(L);
if (!s_uiSystem || !lua.isNumber(1)) {
lua.pushNumber(0); lua.pushNumber(0);
return 2;
}
int handle = static_cast<int>(lua.toNumber(1));
int16_t w, h;
s_uiSystem->getSize(handle, w, h);
lua.pushNumber(static_cast<lua_Number>(w));
lua.pushNumber(static_cast<lua_Number>(h));
return 2;
}
int LuaAPI::UI_SetProgressColors(lua_State* L) {
psyqo::Lua lua(L);
if (!s_uiSystem || !lua.isNumber(1)) return 0;
int handle = static_cast<int>(lua.toNumber(1));
uint8_t bgR = static_cast<uint8_t>(lua.toNumber(2));
uint8_t bgG = static_cast<uint8_t>(lua.toNumber(3));
uint8_t bgB = static_cast<uint8_t>(lua.toNumber(4));
uint8_t fR = static_cast<uint8_t>(lua.toNumber(5));
uint8_t fG = static_cast<uint8_t>(lua.toNumber(6));
uint8_t fB = static_cast<uint8_t>(lua.toNumber(7));
s_uiSystem->setProgressColors(handle, bgR, bgG, bgB, fR, fG, fB);
return 0;
}
int LuaAPI::UI_GetElementType(lua_State* L) {
psyqo::Lua lua(L);
if (!s_uiSystem || !lua.isNumber(1)) {
lua.pushNumber(-1);
return 1;
}
int handle = static_cast<int>(lua.toNumber(1));
lua.pushNumber(static_cast<lua_Number>(static_cast<uint8_t>(s_uiSystem->getElementType(handle))));
return 1;
}
int LuaAPI::UI_GetElementCount(lua_State* L) {
psyqo::Lua lua(L);
if (!s_uiSystem || !lua.isNumber(1)) {
lua.pushNumber(0);
return 1;
}
int canvasIdx = static_cast<int>(lua.toNumber(1));
lua.pushNumber(static_cast<lua_Number>(s_uiSystem->getCanvasElementCount(canvasIdx)));
return 1;
}
int LuaAPI::UI_GetElementByIndex(lua_State* L) {
psyqo::Lua lua(L);
if (!s_uiSystem || !lua.isNumber(1) || !lua.isNumber(2)) {
lua.pushNumber(-1);
return 1;
}
int canvasIdx = static_cast<int>(lua.toNumber(1));
int elemIdx = static_cast<int>(lua.toNumber(2));
int handle = s_uiSystem->getCanvasElementHandle(canvasIdx, elemIdx);
lua.pushNumber(static_cast<lua_Number>(handle));
return 1;
}
} // namespace psxsplash