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); const FP zero(0);
pushBack = psyqo::Vec3{zero, zero, zero}; pushBack = psyqo::Vec3{zero, zero, zero};
// Rebuild spatial grid with all colliders // Rebuild spatial grid with active colliders only
m_grid.clear(); m_grid.clear();
for (int i = 0; i < m_colliderCount; i++) { 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); 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; track.initialValues[2] = (int16_t)track.target->position.z.value;
} }
break; break;
case TrackType::ObjectRotationY: case TrackType::ObjectRotation:
// TODO: I added this. Then realized that it only stores rotation matrix. That sucks. To anyone who finds this Pull requests are open :P // Initial rotation angles: 0,0,0 (no way to extract Euler from matrix)
break; break;
case TrackType::ObjectActive: case TrackType::ObjectActive:
if (track.target) { if (track.target) {
@@ -289,7 +289,7 @@ void CutscenePlayer::applyTrack(CutsceneTrack& track) {
case TrackType::CameraRotation: { case TrackType::CameraRotation: {
if (!m_camera) return; 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; psyqo::Angle rx, ry, rz;
rx.value = (int32_t)out[0]; rx.value = (int32_t)out[0];
ry.value = (int32_t)out[1]; ry.value = (int32_t)out[1];
@@ -307,13 +307,18 @@ void CutscenePlayer::applyTrack(CutsceneTrack& track) {
break; break;
} }
case TrackType::ObjectRotationY: { case TrackType::ObjectRotation: {
if (!track.target) return; if (!track.target) return;
lerpAngles(track.keyframes, track.keyframeCount, track.initialValues, out); lerpKeyframes(track.keyframes, track.keyframeCount, track.initialValues, out);
psyqo::Angle yAngle; psyqo::Angle rx, ry, rz;
yAngle.value = (int32_t)out[1]; rx.value = (int32_t)out[0];
track.target->rotation = psyqo::SoftMath::generateRotationMatrix33( ry.value = (int32_t)out[1];
yAngle, psyqo::SoftMath::Axis::Y, m_trig); 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; break;
} }

View File

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

View File

@@ -19,7 +19,7 @@ struct Interactable {
uint8_t interactButton; uint8_t interactButton;
// Configuration flags // 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) // Cooldown between interactions (in frames)
uint16_t cooldownFrames; uint16_t cooldownFrames;
@@ -35,6 +35,11 @@ struct Interactable {
bool isRepeatable() const { return flags & 0x01; } bool isRepeatable() const { return flags & 0x01; }
bool showPrompt() const { return flags & 0x02; } bool showPrompt() const { return flags & 0x02; }
bool requireLineOfSight() const { return flags & 0x04; } 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 // Check if ready to interact
bool canInteract() const { 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) { 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; auto L = m_state;
char filename[32]; char filename[32];
snprintf(filename, sizeof(filename), "lua_asset:%d", index); snprintf(filename, sizeof(filename), "lua_asset:%d", index);
@@ -289,20 +295,43 @@ void psxsplash::Lua::RegisterGameObject(GameObject* go) {
// Initialize event mask for this object // Initialize event mask for this object
uint32_t eventMask = EVENT_NONE; uint32_t eventMask = EVENT_NONE;
if (go->luaFileIndex != -1) { if (go->luaFileIndex != -1 && go->luaFileIndex < m_bytecodeRefCount) {
L.rawGetI(LUA_REGISTRYINDEX, m_luascriptsReference); auto& ref = m_bytecodeRefs[go->luaFileIndex];
// (1) {} (2) script environments table char filename[32];
L.rawGetI(-1, go->luaFileIndex); snprintf(filename, sizeof(filename), "lua_asset:%d", go->luaFileIndex);
// (1) {} (2) script environments table (3) script environment table for this object
// Guard: if the script file failed to load (e.g. compilation error), if (L.loadBuffer(ref.code, ref.len, filename) == LUA_OK) {
// the environment will be nil — skip event resolution. // (1) method_table (2) chunk_func
if (!L.isTable(-1)) {
L.pop(2);
} else {
// Resolve each event and build the bitmask // Create a per-object environment with __index = _G
// Only events that exist in the script get their bit set // 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 (onCreateMethodWrapper.resolveGlobal(L)) eventMask |= EVENT_ON_CREATE;
if (onCollideWithPlayerMethodWrapper.resolveGlobal(L)) eventMask |= EVENT_ON_COLLISION; if (onCollideWithPlayerMethodWrapper.resolveGlobal(L)) eventMask |= EVENT_ON_COLLISION;
if (onInteractMethodWrapper.resolveGlobal(L)) eventMask |= EVENT_ON_INTERACT; 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 (onButtonPressMethodWrapper.resolveGlobal(L)) eventMask |= EVENT_ON_BUTTON_PRESS;
if (onButtonReleaseMethodWrapper.resolveGlobal(L)) eventMask |= EVENT_ON_BUTTON_RELEASE; if (onButtonReleaseMethodWrapper.resolveGlobal(L)) eventMask |= EVENT_ON_BUTTON_RELEASE;
L.pop(2); L.pop(2); // pop nil and env
// (1) {} } 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_luascriptsReference = LUA_NOREF;
int m_luaSceneScriptsReference = 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> template <int methodId, typename methodName>
friend struct FunctionWrapper; friend struct FunctionWrapper;

View File

@@ -298,6 +298,19 @@ void LuaAPI::RegisterAll(psyqo::Lua& L, SceneManager* scene, CutscenePlayer* cut
L.setGlobal("Controls"); 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 // UI API
// ======================================================================== // ========================================================================
@@ -1563,6 +1576,49 @@ int LuaAPI::Controls_IsEnabled(lua_State* L) {
return 1; 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 // UI API IMPLEMENTATION
// ============================================================================ // ============================================================================

View File

@@ -277,6 +277,12 @@ private:
// Controls.IsEnabled() -> boolean // Controls.IsEnabled() -> boolean
static int Controls_IsEnabled(lua_State* L); 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 // 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> py = static_cast<psyqo::FixedPoint<12>>(m_playerPosition.y);
psyqo::FixedPoint<12> pz = static_cast<psyqo::FixedPoint<12>>(m_playerPosition.z); psyqo::FixedPoint<12> pz = static_cast<psyqo::FixedPoint<12>>(m_playerPosition.z);
psyqo::FixedPoint<12> h = static_cast<psyqo::FixedPoint<12>>(m_playerHeight); psyqo::FixedPoint<12> h = static_cast<psyqo::FixedPoint<12>>(m_playerHeight);
playerAABB.min = psyqo::Vec3{px - r, py - h, pz - r}; // Y is inverted on PS1: negative = up, positive = down.
playerAABB.max = psyqo::Vec3{px + r, py, pz + r}; // 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; psyqo::Vec3 pushBack;
@@ -519,6 +525,7 @@ void psxsplash::SceneManager::updateInteractionSystem() {
for (auto* interactable : m_interactables) { for (auto* interactable : m_interactables) {
if (!interactable) continue; if (!interactable) continue;
if (interactable->isDisabled()) continue;
auto* go = getGameObject(interactable->gameObjectIndex); auto* go = getGameObject(interactable->gameObjectIndex);
if (!go || !go->isActive()) continue; if (!go || !go->isActive()) continue;

View File

@@ -86,6 +86,12 @@ class SceneManager {
void setControlsEnabled(bool enabled) { m_controlsEnabled = enabled; } void setControlsEnabled(bool enabled) { m_controlsEnabled = enabled; }
bool isControlsEnabled() const { return m_controlsEnabled; } 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) // Scene loading (for multi-scene support)
void requestSceneLoad(int sceneIndex); void requestSceneLoad(int sceneIndex);
int getCurrentSceneIndex() const { return m_currentSceneIndex; } int getCurrentSceneIndex() const { return m_currentSceneIndex; }