ITS BROKEN
This commit is contained in:
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
63
src/lua.cpp
63
src/lua.cpp
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
10
src/lua.h
10
src/lua.h
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|||||||
@@ -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
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
|||||||
Reference in New Issue
Block a user