ITS BROKEN

This commit is contained in:
Jan Racek
2026-03-29 16:14:15 +02:00
parent 7c344e2e37
commit 9a82f9e3a1
10 changed files with 179 additions and 50 deletions

View File

@@ -170,10 +170,11 @@ int CollisionSystem::detectCollisions(const AABB& playerAABB, psyqo::Vec3& pushB
const FP zero(0);
pushBack = psyqo::Vec3{zero, zero, zero};
// Rebuild spatial grid with all colliders
// Rebuild spatial grid with active colliders only
m_grid.clear();
for (int i = 0; i < m_colliderCount; i++) {
if(scene.getGameObject(m_colliders[i].gameObjectIndex)->isActive()) {
auto* go = scene.getGameObject(m_colliders[i].gameObjectIndex);
if (go && go->isActive()) {
m_grid.insert(i, m_colliders[i].bounds);
}
}

View File

@@ -59,8 +59,8 @@ bool CutscenePlayer::play(const char* name, bool loop) {
track.initialValues[2] = (int16_t)track.target->position.z.value;
}
break;
case TrackType::ObjectRotationY:
// TODO: I added this. Then realized that it only stores rotation matrix. That sucks. To anyone who finds this Pull requests are open :P
case TrackType::ObjectRotation:
// Initial rotation angles: 0,0,0 (no way to extract Euler from matrix)
break;
case TrackType::ObjectActive:
if (track.target) {
@@ -289,7 +289,7 @@ void CutscenePlayer::applyTrack(CutsceneTrack& track) {
case TrackType::CameraRotation: {
if (!m_camera) return;
lerpAngles(track.keyframes, track.keyframeCount, track.initialValues, out);
lerpKeyframes(track.keyframes, track.keyframeCount, track.initialValues, out);
psyqo::Angle rx, ry, rz;
rx.value = (int32_t)out[0];
ry.value = (int32_t)out[1];
@@ -307,13 +307,18 @@ void CutscenePlayer::applyTrack(CutsceneTrack& track) {
break;
}
case TrackType::ObjectRotationY: {
case TrackType::ObjectRotation: {
if (!track.target) return;
lerpAngles(track.keyframes, track.keyframeCount, track.initialValues, out);
psyqo::Angle yAngle;
yAngle.value = (int32_t)out[1];
track.target->rotation = psyqo::SoftMath::generateRotationMatrix33(
yAngle, psyqo::SoftMath::Axis::Y, m_trig);
lerpKeyframes(track.keyframes, track.keyframeCount, track.initialValues, out);
psyqo::Angle rx, ry, rz;
rx.value = (int32_t)out[0];
ry.value = (int32_t)out[1];
rz.value = (int32_t)out[2];
auto matY = psyqo::SoftMath::generateRotationMatrix33(ry, psyqo::SoftMath::Axis::Y, m_trig);
auto matX = psyqo::SoftMath::generateRotationMatrix33(rx, psyqo::SoftMath::Axis::X, m_trig);
auto matZ = psyqo::SoftMath::generateRotationMatrix33(rz, psyqo::SoftMath::Axis::Z, m_trig);
auto temp = psyqo::SoftMath::multiplyMatrix33(matY, matX);
track.target->rotation = psyqo::SoftMath::multiplyMatrix33(temp, matZ);
break;
}

View File

@@ -24,7 +24,7 @@ enum class TrackType : uint8_t {
CameraPosition = 0,
CameraRotation = 1,
ObjectPosition = 2,
ObjectRotationY = 3,
ObjectRotation = 3,
ObjectActive = 4,
UICanvasVisible = 5,
UIElementVisible= 6,

View File

@@ -19,7 +19,7 @@ struct Interactable {
uint8_t interactButton;
// Configuration flags
uint8_t flags; // bit 0: isRepeatable, bit 1: showPrompt, bit 2: requireLineOfSight
uint8_t flags; // bit 0: isRepeatable, bit 1: showPrompt, bit 2: requireLineOfSight, bit 3: disabled
// Cooldown between interactions (in frames)
uint16_t cooldownFrames;
@@ -35,6 +35,11 @@ struct Interactable {
bool isRepeatable() const { return flags & 0x01; }
bool showPrompt() const { return flags & 0x02; }
bool requireLineOfSight() const { return flags & 0x04; }
bool isDisabled() const { return flags & 0x08; }
void setDisabled(bool disabled) {
if (disabled) flags |= 0x08;
else flags &= ~0x08;
}
// Check if ready to interact
bool canInteract() const {

View File

@@ -187,6 +187,12 @@ void psxsplash::Lua::Reset() {
}
void psxsplash::Lua::LoadLuaFile(const char* code, size_t len, int index) {
// Store bytecode reference for per-object re-execution in RegisterGameObject.
if (index < MAX_LUA_FILES) {
m_bytecodeRefs[index] = {code, len};
if (index >= m_bytecodeRefCount) m_bytecodeRefCount = index + 1;
}
auto L = m_state;
char filename[32];
snprintf(filename, sizeof(filename), "lua_asset:%d", index);
@@ -289,20 +295,43 @@ void psxsplash::Lua::RegisterGameObject(GameObject* go) {
// Initialize event mask for this object
uint32_t eventMask = EVENT_NONE;
if (go->luaFileIndex != -1) {
L.rawGetI(LUA_REGISTRYINDEX, m_luascriptsReference);
// (1) {} (2) script environments table
L.rawGetI(-1, go->luaFileIndex);
// (1) {} (2) script environments table (3) script environment table for this object
if (go->luaFileIndex != -1 && go->luaFileIndex < m_bytecodeRefCount) {
auto& ref = m_bytecodeRefs[go->luaFileIndex];
char filename[32];
snprintf(filename, sizeof(filename), "lua_asset:%d", go->luaFileIndex);
// Guard: if the script file failed to load (e.g. compilation error),
// the environment will be nil — skip event resolution.
if (!L.isTable(-1)) {
L.pop(2);
} else {
if (L.loadBuffer(ref.code, ref.len, filename) == LUA_OK) {
// (1) method_table (2) chunk_func
// Resolve each event and build the bitmask
// Only events that exist in the script get their bit set
// Create a per-object environment with __index = _G
// so this object's file-level locals are isolated.
L.newTable();
L.newTable();
L.pushGlobalTable();
L.setField(-2, "__index");
L.setMetatable(-2);
// (1) method_table (2) chunk_func (3) env
// Set env as the chunk's _ENV upvalue
L.copy(-1);
// (1) method_table (2) chunk_func (3) env (4) env_copy
lua_setupvalue(L.getState(), -3, 1);
// (1) method_table (2) chunk_func (3) env
// Move chunk to top for pcall
lua_insert(L.getState(), -2);
// (1) method_table (2) env (3) chunk_func
if (L.pcall(0, 0) == LUA_OK) {
// (1) method_table (2) env
// resolveGlobal expects: (1) method_table, (3) env
// Insert a placeholder at position 2 to push env to position 3
L.push(); // push nil
// (1) method_table (2) env (3) nil
lua_insert(L.getState(), 2);
// (1) method_table (2) nil (3) env
// Resolve each event - creates fresh function refs with isolated upvalues
if (onCreateMethodWrapper.resolveGlobal(L)) eventMask |= EVENT_ON_CREATE;
if (onCollideWithPlayerMethodWrapper.resolveGlobal(L)) eventMask |= EVENT_ON_COLLISION;
if (onInteractMethodWrapper.resolveGlobal(L)) eventMask |= EVENT_ON_INTERACT;
@@ -315,8 +344,14 @@ void psxsplash::Lua::RegisterGameObject(GameObject* go) {
if (onButtonPressMethodWrapper.resolveGlobal(L)) eventMask |= EVENT_ON_BUTTON_PRESS;
if (onButtonReleaseMethodWrapper.resolveGlobal(L)) eventMask |= EVENT_ON_BUTTON_RELEASE;
L.pop(2);
// (1) {}
L.pop(2); // pop nil and env
} else {
printf("Lua error: %s\n", L.toString(-1));
L.pop(2); // pop error msg and env
}
} else {
printf("Lua error: %s\n", L.toString(-1));
L.pop(); // pop error msg
}
}

View File

@@ -180,7 +180,15 @@ class Lua {
int m_luascriptsReference = LUA_NOREF;
int m_luaSceneScriptsReference = LUA_NOREF;
// Event mask now stored inline in GameObject::eventMask
// Bytecode references for per-object re-execution.
// Points into splashpack data which stays in memory for the scene lifetime.
static constexpr int MAX_LUA_FILES = 32;
struct BytecodeRef {
const char* code;
size_t len;
};
BytecodeRef m_bytecodeRefs[MAX_LUA_FILES];
int m_bytecodeRefCount = 0;
template <int methodId, typename methodName>
friend struct FunctionWrapper;

View File

@@ -298,6 +298,19 @@ void LuaAPI::RegisterAll(psyqo::Lua& L, SceneManager* scene, CutscenePlayer* cut
L.setGlobal("Controls");
// ========================================================================
// INTERACT API
// ========================================================================
L.newTable();
L.push(Interact_SetEnabled);
L.setField(-2, "SetEnabled");
L.push(Interact_IsEnabled);
L.setField(-2, "IsEnabled");
L.setGlobal("Interact");
// ========================================================================
// UI API
// ========================================================================
@@ -1563,6 +1576,49 @@ int LuaAPI::Controls_IsEnabled(lua_State* L) {
return 1;
}
// ============================================================================
// INTERACT API IMPLEMENTATION
// ============================================================================
int LuaAPI::Interact_SetEnabled(lua_State* L) {
psyqo::Lua lua(L);
if (!s_sceneManager || !lua.isTable(1) || !lua.isBoolean(2)) return 0;
lua.getField(1, "__cpp_ptr");
auto go = lua.toUserdata<psxsplash::GameObject>(-1);
lua.pop();
if (go && go->hasInteractable()) {
auto* inter = s_sceneManager->getInteractable(go->interactableIndex);
if (inter) {
inter->setDisabled(!lua.toBoolean(2));
}
}
return 0;
}
int LuaAPI::Interact_IsEnabled(lua_State* L) {
psyqo::Lua lua(L);
if (!s_sceneManager || !lua.isTable(1)) {
lua.push(false);
return 1;
}
lua.getField(1, "__cpp_ptr");
auto go = lua.toUserdata<psxsplash::GameObject>(-1);
lua.pop();
if (go && go->hasInteractable()) {
auto* inter = s_sceneManager->getInteractable(go->interactableIndex);
if (inter) {
lua.push(!inter->isDisabled());
return 1;
}
}
lua.push(false);
return 1;
}
// ============================================================================
// UI API IMPLEMENTATION
// ============================================================================

View File

@@ -277,6 +277,12 @@ private:
// Controls.IsEnabled() -> boolean
static int Controls_IsEnabled(lua_State* L);
// Interact.SetEnabled(entity, bool) - enable/disable interaction + prompt for an object
static int Interact_SetEnabled(lua_State* L);
// Interact.IsEnabled(entity) -> boolean
static int Interact_IsEnabled(lua_State* L);
// ========================================================================
// UI API - Canvas and element control
// ========================================================================

View File

@@ -314,8 +314,14 @@ void psxsplash::SceneManager::GameTick(psyqo::GPU &gpu) {
psyqo::FixedPoint<12> py = static_cast<psyqo::FixedPoint<12>>(m_playerPosition.y);
psyqo::FixedPoint<12> pz = static_cast<psyqo::FixedPoint<12>>(m_playerPosition.z);
psyqo::FixedPoint<12> h = static_cast<psyqo::FixedPoint<12>>(m_playerHeight);
playerAABB.min = psyqo::Vec3{px - r, py - h, pz - r};
playerAABB.max = psyqo::Vec3{px + r, py, pz + r};
// Y is inverted on PS1: negative = up, positive = down.
// m_playerPosition.y is the camera (head), feet are at py + h.
// Leave a small gap at the bottom so the floor geometry doesn't
// trigger constant collisions (floor contact is handled by nav).
psyqo::FixedPoint<12> bodyBottom;
bodyBottom.value = h.value * 3 / 4; // 75% of height below camera
playerAABB.min = psyqo::Vec3{px - r, py, pz - r};
playerAABB.max = psyqo::Vec3{px + r, py + bodyBottom, pz + r};
}
psyqo::Vec3 pushBack;
@@ -519,6 +525,7 @@ void psxsplash::SceneManager::updateInteractionSystem() {
for (auto* interactable : m_interactables) {
if (!interactable) continue;
if (interactable->isDisabled()) continue;
auto* go = getGameObject(interactable->gameObjectIndex);
if (!go || !go->isActive()) continue;

View File

@@ -86,6 +86,12 @@ class SceneManager {
void setControlsEnabled(bool enabled) { m_controlsEnabled = enabled; }
bool isControlsEnabled() const { return m_controlsEnabled; }
// Interactable access (for Lua API)
Interactable* getInteractable(uint16_t index) {
if (index < m_interactables.size()) return m_interactables[index];
return nullptr;
}
// Scene loading (for multi-scene support)
void requestSceneLoad(int sceneIndex);
int getCurrentSceneIndex() const { return m_currentSceneIndex; }