animz
This commit is contained in:
2
Makefile
2
Makefile
@@ -20,6 +20,8 @@ src/profiler.cpp \
|
|||||||
src/collision.cpp \
|
src/collision.cpp \
|
||||||
src/bvh.cpp \
|
src/bvh.cpp \
|
||||||
src/cutscene.cpp \
|
src/cutscene.cpp \
|
||||||
|
src/interpolation.cpp \
|
||||||
|
src/animation.cpp \
|
||||||
src/uisystem.cpp \
|
src/uisystem.cpp \
|
||||||
src/loadingscreen.cpp \
|
src/loadingscreen.cpp \
|
||||||
src/memoverlay.cpp \
|
src/memoverlay.cpp \
|
||||||
|
|||||||
297
src/animation.cpp
Normal file
297
src/animation.cpp
Normal file
@@ -0,0 +1,297 @@
|
|||||||
|
#include "animation.hh"
|
||||||
|
#include "interpolation.hh"
|
||||||
|
|
||||||
|
#include <psyqo/fixed-point.hh>
|
||||||
|
#include <psyqo/soft-math.hh>
|
||||||
|
#include "streq.hh"
|
||||||
|
#include "uisystem.hh"
|
||||||
|
|
||||||
|
namespace psxsplash {
|
||||||
|
|
||||||
|
void AnimationPlayer::init(Animation* animations, int count, UISystem* uiSystem) {
|
||||||
|
m_animations = animations;
|
||||||
|
m_animCount = count;
|
||||||
|
m_uiSystem = uiSystem;
|
||||||
|
for (int i = 0; i < MAX_SIMULTANEOUS_ANIMS; i++) {
|
||||||
|
m_slots[i].anim = nullptr;
|
||||||
|
m_slots[i].onCompleteRef = LUA_NOREF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Animation* AnimationPlayer::findByName(const char* name) const {
|
||||||
|
if (!name || !m_animations) return nullptr;
|
||||||
|
for (int i = 0; i < m_animCount; i++) {
|
||||||
|
if (m_animations[i].name && streq(m_animations[i].name, name))
|
||||||
|
return &m_animations[i];
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AnimationPlayer::play(const char* name, bool loop) {
|
||||||
|
Animation* anim = findByName(name);
|
||||||
|
if (!anim) return false;
|
||||||
|
|
||||||
|
// Find a free slot
|
||||||
|
int freeSlot = -1;
|
||||||
|
for (int i = 0; i < MAX_SIMULTANEOUS_ANIMS; i++) {
|
||||||
|
if (!m_slots[i].anim) {
|
||||||
|
freeSlot = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (freeSlot < 0) return false;
|
||||||
|
|
||||||
|
ActiveSlot& slot = m_slots[freeSlot];
|
||||||
|
slot.anim = anim;
|
||||||
|
slot.frame = 0;
|
||||||
|
slot.loop = loop;
|
||||||
|
// onCompleteRef is set separately via setOnCompleteRef before play()
|
||||||
|
|
||||||
|
captureInitialValues(anim);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationPlayer::stop(const char* name) {
|
||||||
|
if (!name) return;
|
||||||
|
for (int i = 0; i < MAX_SIMULTANEOUS_ANIMS; i++) {
|
||||||
|
if (m_slots[i].anim && m_slots[i].anim->name && streq(m_slots[i].anim->name, name)) {
|
||||||
|
fireSlotComplete(m_slots[i]);
|
||||||
|
m_slots[i].anim = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationPlayer::stopAll() {
|
||||||
|
for (int i = 0; i < MAX_SIMULTANEOUS_ANIMS; i++) {
|
||||||
|
if (m_slots[i].anim) {
|
||||||
|
fireSlotComplete(m_slots[i]);
|
||||||
|
m_slots[i].anim = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AnimationPlayer::isPlaying(const char* name) const {
|
||||||
|
if (!name) return false;
|
||||||
|
for (int i = 0; i < MAX_SIMULTANEOUS_ANIMS; i++) {
|
||||||
|
if (m_slots[i].anim && m_slots[i].anim->name && streq(m_slots[i].anim->name, name))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationPlayer::setOnCompleteRef(const char* name, int ref) {
|
||||||
|
// Find the most recently started slot for this animation (highest index with frame 0)
|
||||||
|
// Fallback: find first slot with this name
|
||||||
|
for (int i = MAX_SIMULTANEOUS_ANIMS - 1; i >= 0; i--) {
|
||||||
|
if (m_slots[i].anim && m_slots[i].anim->name && streq(m_slots[i].anim->name, name)) {
|
||||||
|
m_slots[i].onCompleteRef = ref;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationPlayer::tick() {
|
||||||
|
for (int i = 0; i < MAX_SIMULTANEOUS_ANIMS; i++) {
|
||||||
|
ActiveSlot& slot = m_slots[i];
|
||||||
|
if (!slot.anim) continue;
|
||||||
|
|
||||||
|
// Apply all tracks
|
||||||
|
for (uint8_t ti = 0; ti < slot.anim->trackCount; ti++) {
|
||||||
|
applyTrack(slot.anim->tracks[ti], slot.frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
slot.frame++;
|
||||||
|
if (slot.frame > slot.anim->totalFrames) {
|
||||||
|
if (slot.loop) {
|
||||||
|
slot.frame = 0;
|
||||||
|
} else {
|
||||||
|
Animation* finished = slot.anim;
|
||||||
|
slot.anim = nullptr;
|
||||||
|
fireSlotComplete(slot);
|
||||||
|
(void)finished;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationPlayer::captureInitialValues(Animation* anim) {
|
||||||
|
for (uint8_t ti = 0; ti < anim->trackCount; ti++) {
|
||||||
|
CutsceneTrack& track = anim->tracks[ti];
|
||||||
|
track.initialValues[0] = track.initialValues[1] = track.initialValues[2] = 0;
|
||||||
|
switch (track.trackType) {
|
||||||
|
case TrackType::ObjectPosition:
|
||||||
|
if (track.target) {
|
||||||
|
track.initialValues[0] = (int16_t)track.target->position.x.value;
|
||||||
|
track.initialValues[1] = (int16_t)track.target->position.y.value;
|
||||||
|
track.initialValues[2] = (int16_t)track.target->position.z.value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TrackType::ObjectRotation:
|
||||||
|
break;
|
||||||
|
case TrackType::ObjectActive:
|
||||||
|
if (track.target) {
|
||||||
|
track.initialValues[0] = track.target->isActive() ? 1 : 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TrackType::UICanvasVisible:
|
||||||
|
if (m_uiSystem) {
|
||||||
|
track.initialValues[0] = m_uiSystem->isCanvasVisible(track.uiHandle) ? 1 : 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TrackType::UIElementVisible:
|
||||||
|
if (m_uiSystem) {
|
||||||
|
track.initialValues[0] = m_uiSystem->isElementVisible(track.uiHandle) ? 1 : 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TrackType::UIProgress:
|
||||||
|
if (m_uiSystem) {
|
||||||
|
track.initialValues[0] = m_uiSystem->getProgress(track.uiHandle);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TrackType::UIPosition:
|
||||||
|
if (m_uiSystem) {
|
||||||
|
int16_t px, py;
|
||||||
|
m_uiSystem->getPosition(track.uiHandle, px, py);
|
||||||
|
track.initialValues[0] = px;
|
||||||
|
track.initialValues[1] = py;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TrackType::UIColor:
|
||||||
|
if (m_uiSystem) {
|
||||||
|
uint8_t cr, cg, cb;
|
||||||
|
m_uiSystem->getColor(track.uiHandle, cr, cg, cb);
|
||||||
|
track.initialValues[0] = cr;
|
||||||
|
track.initialValues[1] = cg;
|
||||||
|
track.initialValues[2] = cb;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationPlayer::applyTrack(CutsceneTrack& track, uint16_t frame) {
|
||||||
|
if (track.keyframeCount == 0 || !track.keyframes) return;
|
||||||
|
|
||||||
|
int16_t out[3];
|
||||||
|
|
||||||
|
switch (track.trackType) {
|
||||||
|
case TrackType::ObjectPosition: {
|
||||||
|
if (!track.target) return;
|
||||||
|
psxsplash::lerpKeyframes(track.keyframes, track.keyframeCount, frame, track.initialValues, out);
|
||||||
|
track.target->position.x.value = (int32_t)out[0];
|
||||||
|
track.target->position.y.value = (int32_t)out[1];
|
||||||
|
track.target->position.z.value = (int32_t)out[2];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case TrackType::ObjectRotation: {
|
||||||
|
if (!track.target) return;
|
||||||
|
psxsplash::lerpKeyframes(track.keyframes, track.keyframeCount, frame, 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
case TrackType::ObjectActive: {
|
||||||
|
if (!track.target) return;
|
||||||
|
CutsceneKeyframe* kf = track.keyframes;
|
||||||
|
uint8_t count = track.keyframeCount;
|
||||||
|
int16_t activeVal = (count > 0 && frame < kf[0].getFrame())
|
||||||
|
? track.initialValues[0]
|
||||||
|
: kf[0].values[0];
|
||||||
|
for (uint8_t i = 0; i < count; i++) {
|
||||||
|
if (kf[i].getFrame() <= frame) {
|
||||||
|
activeVal = kf[i].values[0];
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
track.target->setActive(activeVal != 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case TrackType::UICanvasVisible: {
|
||||||
|
if (!m_uiSystem) return;
|
||||||
|
CutsceneKeyframe* kf = track.keyframes;
|
||||||
|
uint8_t count = track.keyframeCount;
|
||||||
|
int16_t val = (count > 0 && frame < kf[0].getFrame())
|
||||||
|
? track.initialValues[0] : kf[0].values[0];
|
||||||
|
for (uint8_t i = 0; i < count; i++) {
|
||||||
|
if (kf[i].getFrame() <= frame) val = kf[i].values[0];
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
m_uiSystem->setCanvasVisible(track.uiHandle, val != 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case TrackType::UIElementVisible: {
|
||||||
|
if (!m_uiSystem) return;
|
||||||
|
CutsceneKeyframe* kf = track.keyframes;
|
||||||
|
uint8_t count = track.keyframeCount;
|
||||||
|
int16_t val = (count > 0 && frame < kf[0].getFrame())
|
||||||
|
? track.initialValues[0] : kf[0].values[0];
|
||||||
|
for (uint8_t i = 0; i < count; i++) {
|
||||||
|
if (kf[i].getFrame() <= frame) val = kf[i].values[0];
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
m_uiSystem->setElementVisible(track.uiHandle, val != 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case TrackType::UIProgress: {
|
||||||
|
if (!m_uiSystem) return;
|
||||||
|
psxsplash::lerpKeyframes(track.keyframes, track.keyframeCount, frame, track.initialValues, out);
|
||||||
|
int16_t v = out[0];
|
||||||
|
if (v < 0) v = 0;
|
||||||
|
if (v > 100) v = 100;
|
||||||
|
m_uiSystem->setProgress(track.uiHandle, (uint8_t)v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case TrackType::UIPosition: {
|
||||||
|
if (!m_uiSystem) return;
|
||||||
|
psxsplash::lerpKeyframes(track.keyframes, track.keyframeCount, frame, track.initialValues, out);
|
||||||
|
m_uiSystem->setPosition(track.uiHandle, out[0], out[1]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case TrackType::UIColor: {
|
||||||
|
if (!m_uiSystem) return;
|
||||||
|
psxsplash::lerpKeyframes(track.keyframes, track.keyframeCount, frame, track.initialValues, out);
|
||||||
|
uint8_t cr = (out[0] < 0) ? 0 : ((out[0] > 255) ? 255 : (uint8_t)out[0]);
|
||||||
|
uint8_t cg = (out[1] < 0) ? 0 : ((out[1] > 255) ? 255 : (uint8_t)out[1]);
|
||||||
|
uint8_t cb = (out[2] < 0) ? 0 : ((out[2] > 255) ? 255 : (uint8_t)out[2]);
|
||||||
|
m_uiSystem->setColor(track.uiHandle, cr, cg, cb);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationPlayer::fireSlotComplete(ActiveSlot& slot) {
|
||||||
|
if (slot.onCompleteRef == LUA_NOREF || !m_luaState) return;
|
||||||
|
psyqo::Lua L(m_luaState);
|
||||||
|
L.rawGetI(LUA_REGISTRYINDEX, slot.onCompleteRef);
|
||||||
|
if (L.isFunction(-1)) {
|
||||||
|
if (L.pcall(0, 0) != LUA_OK) {
|
||||||
|
L.pop();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
L.pop();
|
||||||
|
}
|
||||||
|
luaL_unref(m_luaState, LUA_REGISTRYINDEX, slot.onCompleteRef);
|
||||||
|
slot.onCompleteRef = LUA_NOREF;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace psxsplash
|
||||||
71
src/animation.hh
Normal file
71
src/animation.hh
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <psyqo/trigonometry.hh>
|
||||||
|
|
||||||
|
#include "cutscene.hh"
|
||||||
|
#include <psyqo-lua/lua.hh>
|
||||||
|
|
||||||
|
namespace psxsplash {
|
||||||
|
|
||||||
|
class UISystem;
|
||||||
|
|
||||||
|
static constexpr int MAX_ANIMATIONS = 16;
|
||||||
|
static constexpr int MAX_ANIM_TRACKS = 8;
|
||||||
|
static constexpr int MAX_SIMULTANEOUS_ANIMS = 8;
|
||||||
|
|
||||||
|
struct Animation {
|
||||||
|
const char* name;
|
||||||
|
uint16_t totalFrames;
|
||||||
|
uint8_t trackCount;
|
||||||
|
uint8_t pad;
|
||||||
|
CutsceneTrack tracks[MAX_ANIM_TRACKS];
|
||||||
|
};
|
||||||
|
|
||||||
|
class AnimationPlayer {
|
||||||
|
public:
|
||||||
|
void init(Animation* animations, int count, UISystem* uiSystem = nullptr);
|
||||||
|
|
||||||
|
/// Play animation by name. Returns false if not found or no free slots.
|
||||||
|
bool play(const char* name, bool loop = false);
|
||||||
|
|
||||||
|
/// Stop all instances of animation by name.
|
||||||
|
void stop(const char* name);
|
||||||
|
|
||||||
|
/// Stop all running animations.
|
||||||
|
void stopAll();
|
||||||
|
|
||||||
|
/// True if any instance of the named animation is playing.
|
||||||
|
bool isPlaying(const char* name) const;
|
||||||
|
|
||||||
|
/// Set a Lua callback for the next play() call.
|
||||||
|
void setOnCompleteRef(const char* name, int ref);
|
||||||
|
|
||||||
|
/// Set the lua_State for callbacks.
|
||||||
|
void setLuaState(lua_State* L) { m_luaState = L; }
|
||||||
|
|
||||||
|
/// Advance all active animations one frame.
|
||||||
|
void tick();
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct ActiveSlot {
|
||||||
|
Animation* anim = nullptr;
|
||||||
|
uint16_t frame = 0;
|
||||||
|
bool loop = false;
|
||||||
|
int onCompleteRef = LUA_NOREF;
|
||||||
|
};
|
||||||
|
|
||||||
|
Animation* m_animations = nullptr;
|
||||||
|
int m_animCount = 0;
|
||||||
|
ActiveSlot m_slots[MAX_SIMULTANEOUS_ANIMS];
|
||||||
|
UISystem* m_uiSystem = nullptr;
|
||||||
|
lua_State* m_luaState = nullptr;
|
||||||
|
psyqo::Trig<> m_trig;
|
||||||
|
|
||||||
|
Animation* findByName(const char* name) const;
|
||||||
|
void applyTrack(CutsceneTrack& track, uint16_t frame);
|
||||||
|
void captureInitialValues(Animation* anim);
|
||||||
|
void fireSlotComplete(ActiveSlot& slot);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace psxsplash
|
||||||
133
src/cutscene.cpp
133
src/cutscene.cpp
@@ -1,4 +1,5 @@
|
|||||||
#include "cutscene.hh"
|
#include "cutscene.hh"
|
||||||
|
#include "interpolation.hh"
|
||||||
|
|
||||||
#include <psyqo/fixed-point.hh>
|
#include <psyqo/fixed-point.hh>
|
||||||
#include <psyqo/soft-math.hh>
|
#include <psyqo/soft-math.hh>
|
||||||
@@ -114,6 +115,16 @@ void CutscenePlayer::stop() {
|
|||||||
fireOnComplete();
|
fireOnComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CutscenePlayer::hasCameraTracks() const {
|
||||||
|
if (!m_active) return false;
|
||||||
|
for (uint8_t i = 0; i < m_active->trackCount; i++) {
|
||||||
|
auto t = m_active->tracks[i].trackType;
|
||||||
|
if (t == TrackType::CameraPosition || t == TrackType::CameraRotation)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void CutscenePlayer::tick() {
|
void CutscenePlayer::tick() {
|
||||||
if (!m_active) return;
|
if (!m_active) return;
|
||||||
|
|
||||||
@@ -162,114 +173,6 @@ void CutscenePlayer::fireOnComplete() {
|
|||||||
m_onCompleteRef = LUA_NOREF;
|
m_onCompleteRef = LUA_NOREF;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t applyCurve(int32_t t, InterpMode mode) {
|
|
||||||
switch (mode) {
|
|
||||||
default:
|
|
||||||
case InterpMode::Linear:
|
|
||||||
return t;
|
|
||||||
case InterpMode::Step:
|
|
||||||
return 0;
|
|
||||||
case InterpMode::EaseIn:
|
|
||||||
return (int32_t)((int64_t)t * t >> 12);
|
|
||||||
case InterpMode::EaseOut:
|
|
||||||
return (int32_t)(((int64_t)t * (8192 - t)) >> 12);
|
|
||||||
case InterpMode::EaseInOut: {
|
|
||||||
int64_t t2 = (int64_t)t * t;
|
|
||||||
int64_t t3 = t2 * t;
|
|
||||||
return (int32_t)((3 * t2 - 2 * (t3 >> 12)) >> 12);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool findKfPair(CutsceneKeyframe* kf, uint8_t count, uint16_t frame,
|
|
||||||
uint8_t& a, uint8_t& b, int32_t& t, int16_t out[3]) {
|
|
||||||
if (count == 0) {
|
|
||||||
out[0] = out[1] = out[2] = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (frame <= kf[0].getFrame() || count == 1) {
|
|
||||||
out[0] = kf[0].values[0];
|
|
||||||
out[1] = kf[0].values[1];
|
|
||||||
out[2] = kf[0].values[2];
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (frame >= kf[count - 1].getFrame()) {
|
|
||||||
out[0] = kf[count - 1].values[0];
|
|
||||||
out[1] = kf[count - 1].values[1];
|
|
||||||
out[2] = kf[count - 1].values[2];
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
b = 1;
|
|
||||||
while (b < count && kf[b].getFrame() <= frame) b++;
|
|
||||||
a = b - 1;
|
|
||||||
uint16_t span = kf[b].getFrame() - kf[a].getFrame();
|
|
||||||
if (span == 0) {
|
|
||||||
out[0] = kf[a].values[0];
|
|
||||||
out[1] = kf[a].values[1];
|
|
||||||
out[2] = kf[a].values[2];
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
uint32_t num = (uint32_t)(frame - kf[a].getFrame()) << 12;
|
|
||||||
int32_t rawT = (int32_t)(num / span);
|
|
||||||
t = applyCurve(rawT, kf[b].getInterp());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CutscenePlayer::lerpKeyframes(CutsceneKeyframe* kf, uint8_t count, const int16_t initial[3], int16_t out[3]) {
|
|
||||||
uint8_t a, b;
|
|
||||||
int32_t t;
|
|
||||||
if (!findKfPair(kf, count, m_frame, a, b, t, out)) {
|
|
||||||
if (count > 0 && kf[0].getFrame() > 0 && m_frame < kf[0].getFrame()) {
|
|
||||||
uint16_t span = kf[0].getFrame();
|
|
||||||
uint32_t num = (uint32_t)m_frame << 12;
|
|
||||||
int32_t rawT = (int32_t)(num / span);
|
|
||||||
int32_t ct = applyCurve(rawT, kf[0].getInterp());
|
|
||||||
for (int i = 0; i < 3; i++) {
|
|
||||||
int32_t delta = (int32_t)kf[0].values[i] - (int32_t)initial[i];
|
|
||||||
out[i] = (int16_t)((int32_t)initial[i] + ((delta * ct) >> 12));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++) {
|
|
||||||
int32_t delta = (int32_t)kf[b].values[i] - (int32_t)kf[a].values[i];
|
|
||||||
out[i] = (int16_t)((int32_t)kf[a].values[i] + ((delta * t) >> 12));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr int32_t ANGLE_FULL_CIRCLE = 2048;
|
|
||||||
static constexpr int32_t ANGLE_HALF_CIRCLE = 1024;
|
|
||||||
|
|
||||||
void CutscenePlayer::lerpAngles(CutsceneKeyframe* kf, uint8_t count, const int16_t initial[3], int16_t out[3]) {
|
|
||||||
uint8_t a, b;
|
|
||||||
int32_t t;
|
|
||||||
if (!findKfPair(kf, count, m_frame, a, b, t, out)) {
|
|
||||||
if (count > 0 && kf[0].getFrame() > 0 && m_frame < kf[0].getFrame()) {
|
|
||||||
uint16_t span = kf[0].getFrame();
|
|
||||||
uint32_t num = (uint32_t)m_frame << 12;
|
|
||||||
int32_t rawT = (int32_t)(num / span);
|
|
||||||
int32_t ct = applyCurve(rawT, kf[0].getInterp());
|
|
||||||
for (int i = 0; i < 3; i++) {
|
|
||||||
int32_t from = (int32_t)initial[i];
|
|
||||||
int32_t to = (int32_t)kf[0].values[i];
|
|
||||||
int32_t delta = to - from;
|
|
||||||
delta = ((delta + ANGLE_HALF_CIRCLE) % ANGLE_FULL_CIRCLE + ANGLE_FULL_CIRCLE) % ANGLE_FULL_CIRCLE - ANGLE_HALF_CIRCLE;
|
|
||||||
out[i] = (int16_t)(from + ((delta * ct) >> 12));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++) {
|
|
||||||
int32_t from = (int32_t)kf[a].values[i];
|
|
||||||
int32_t to = (int32_t)kf[b].values[i];
|
|
||||||
int32_t delta = to - from;
|
|
||||||
delta = ((delta + ANGLE_HALF_CIRCLE) % ANGLE_FULL_CIRCLE + ANGLE_FULL_CIRCLE) % ANGLE_FULL_CIRCLE - ANGLE_HALF_CIRCLE;
|
|
||||||
out[i] = (int16_t)(from + ((delta * t) >> 12));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CutscenePlayer::applyTrack(CutsceneTrack& track) {
|
void CutscenePlayer::applyTrack(CutsceneTrack& track) {
|
||||||
if (track.keyframeCount == 0 || !track.keyframes) return;
|
if (track.keyframeCount == 0 || !track.keyframes) return;
|
||||||
|
|
||||||
@@ -278,7 +181,7 @@ void CutscenePlayer::applyTrack(CutsceneTrack& track) {
|
|||||||
switch (track.trackType) {
|
switch (track.trackType) {
|
||||||
case TrackType::CameraPosition: {
|
case TrackType::CameraPosition: {
|
||||||
if (!m_camera) return;
|
if (!m_camera) return;
|
||||||
lerpKeyframes(track.keyframes, track.keyframeCount, track.initialValues, out);
|
psxsplash::lerpKeyframes(track.keyframes, track.keyframeCount, m_frame, track.initialValues, out);
|
||||||
psyqo::FixedPoint<12> x, y, z;
|
psyqo::FixedPoint<12> x, y, z;
|
||||||
x.value = (int32_t)out[0];
|
x.value = (int32_t)out[0];
|
||||||
y.value = (int32_t)out[1];
|
y.value = (int32_t)out[1];
|
||||||
@@ -289,7 +192,7 @@ void CutscenePlayer::applyTrack(CutsceneTrack& track) {
|
|||||||
|
|
||||||
case TrackType::CameraRotation: {
|
case TrackType::CameraRotation: {
|
||||||
if (!m_camera) return;
|
if (!m_camera) return;
|
||||||
lerpKeyframes(track.keyframes, track.keyframeCount, track.initialValues, out);
|
psxsplash::lerpKeyframes(track.keyframes, track.keyframeCount, m_frame, 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];
|
||||||
@@ -300,7 +203,7 @@ void CutscenePlayer::applyTrack(CutsceneTrack& track) {
|
|||||||
|
|
||||||
case TrackType::ObjectPosition: {
|
case TrackType::ObjectPosition: {
|
||||||
if (!track.target) return;
|
if (!track.target) return;
|
||||||
lerpKeyframes(track.keyframes, track.keyframeCount, track.initialValues, out);
|
psxsplash::lerpKeyframes(track.keyframes, track.keyframeCount, m_frame, track.initialValues, out);
|
||||||
track.target->position.x.value = (int32_t)out[0];
|
track.target->position.x.value = (int32_t)out[0];
|
||||||
track.target->position.y.value = (int32_t)out[1];
|
track.target->position.y.value = (int32_t)out[1];
|
||||||
track.target->position.z.value = (int32_t)out[2];
|
track.target->position.z.value = (int32_t)out[2];
|
||||||
@@ -309,7 +212,7 @@ void CutscenePlayer::applyTrack(CutsceneTrack& track) {
|
|||||||
|
|
||||||
case TrackType::ObjectRotation: {
|
case TrackType::ObjectRotation: {
|
||||||
if (!track.target) return;
|
if (!track.target) return;
|
||||||
lerpKeyframes(track.keyframes, track.keyframeCount, track.initialValues, out);
|
psxsplash::lerpKeyframes(track.keyframes, track.keyframeCount, m_frame, 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];
|
||||||
@@ -372,7 +275,7 @@ void CutscenePlayer::applyTrack(CutsceneTrack& track) {
|
|||||||
|
|
||||||
case TrackType::UIProgress: {
|
case TrackType::UIProgress: {
|
||||||
if (!m_uiSystem) return;
|
if (!m_uiSystem) return;
|
||||||
lerpKeyframes(track.keyframes, track.keyframeCount, track.initialValues, out);
|
psxsplash::lerpKeyframes(track.keyframes, track.keyframeCount, m_frame, track.initialValues, out);
|
||||||
int16_t v = out[0];
|
int16_t v = out[0];
|
||||||
if (v < 0) v = 0;
|
if (v < 0) v = 0;
|
||||||
if (v > 100) v = 100;
|
if (v > 100) v = 100;
|
||||||
@@ -382,14 +285,14 @@ void CutscenePlayer::applyTrack(CutsceneTrack& track) {
|
|||||||
|
|
||||||
case TrackType::UIPosition: {
|
case TrackType::UIPosition: {
|
||||||
if (!m_uiSystem) return;
|
if (!m_uiSystem) return;
|
||||||
lerpKeyframes(track.keyframes, track.keyframeCount, track.initialValues, out);
|
psxsplash::lerpKeyframes(track.keyframes, track.keyframeCount, m_frame, track.initialValues, out);
|
||||||
m_uiSystem->setPosition(track.uiHandle, out[0], out[1]);
|
m_uiSystem->setPosition(track.uiHandle, out[0], out[1]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TrackType::UIColor: {
|
case TrackType::UIColor: {
|
||||||
if (!m_uiSystem) return;
|
if (!m_uiSystem) return;
|
||||||
lerpKeyframes(track.keyframes, track.keyframeCount, track.initialValues, out);
|
psxsplash::lerpKeyframes(track.keyframes, track.keyframeCount, m_frame, track.initialValues, out);
|
||||||
uint8_t cr = (out[0] < 0) ? 0 : ((out[0] > 255) ? 255 : (uint8_t)out[0]);
|
uint8_t cr = (out[0] < 0) ? 0 : ((out[0] > 255) ? 255 : (uint8_t)out[0]);
|
||||||
uint8_t cg = (out[1] < 0) ? 0 : ((out[1] > 255) ? 255 : (uint8_t)out[1]);
|
uint8_t cg = (out[1] < 0) ? 0 : ((out[1] > 255) ? 255 : (uint8_t)out[1]);
|
||||||
uint8_t cb = (out[2] < 0) ? 0 : ((out[2] > 255) ? 255 : (uint8_t)out[2]);
|
uint8_t cb = (out[2] < 0) ? 0 : ((out[2] > 255) ? 255 : (uint8_t)out[2]);
|
||||||
|
|||||||
@@ -97,6 +97,10 @@ public:
|
|||||||
/// True if a cutscene is currently active.
|
/// True if a cutscene is currently active.
|
||||||
bool isPlaying() const { return m_active != nullptr; }
|
bool isPlaying() const { return m_active != nullptr; }
|
||||||
|
|
||||||
|
/// True if the active cutscene has camera tracks (position or rotation).
|
||||||
|
/// Use this to decide whether to suppress player camera follow.
|
||||||
|
bool hasCameraTracks() const;
|
||||||
|
|
||||||
/// Set a Lua registry reference to call when the cutscene finishes.
|
/// Set a Lua registry reference to call when the cutscene finishes.
|
||||||
/// Pass LUA_NOREF to clear. The callback is called ONCE when the
|
/// Pass LUA_NOREF to clear. The callback is called ONCE when the
|
||||||
/// cutscene ends (not on each loop iteration - only when it truly stops).
|
/// cutscene ends (not on each loop iteration - only when it truly stops).
|
||||||
@@ -125,8 +129,6 @@ private:
|
|||||||
psyqo::Trig<> m_trig;
|
psyqo::Trig<> m_trig;
|
||||||
|
|
||||||
void applyTrack(CutsceneTrack& track);
|
void applyTrack(CutsceneTrack& track);
|
||||||
void lerpKeyframes(CutsceneKeyframe* kf, uint8_t count, const int16_t initial[3], int16_t out[3]);
|
|
||||||
void lerpAngles(CutsceneKeyframe* kf, uint8_t count, const int16_t initial[3], int16_t out[3]);
|
|
||||||
void fireOnComplete();
|
void fireOnComplete();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,140 +1,141 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// Pre-compiled PS1 Lua bytecode for GAMEOBJECT_SCRIPT
|
// Pre-compiled PS1 Lua bytecode for GAMEOBJECT_SCRIPT
|
||||||
// Generated by luac_psx - do not edit manually
|
// Generated by luac_psx (32-bit, LUA_NUMBER=long) - do not edit manually
|
||||||
// 1581 bytes
|
// 943 bytes
|
||||||
|
//
|
||||||
|
// Original Lua source:
|
||||||
|
// return function(metatable)
|
||||||
|
// local get_position = metatable.get_position
|
||||||
|
// local set_position = metatable.set_position
|
||||||
|
// local get_active = metatable.get_active
|
||||||
|
// local set_active = metatable.set_active
|
||||||
|
// local get_rotation = metatable.get_rotation
|
||||||
|
// local set_rotation = metatable.set_rotation
|
||||||
|
// local get_rotationY = metatable.get_rotationY
|
||||||
|
// local set_rotationY = metatable.set_rotationY
|
||||||
|
//
|
||||||
|
// metatable.get_position = nil
|
||||||
|
// metatable.set_position = nil
|
||||||
|
// metatable.get_active = nil
|
||||||
|
// metatable.set_active = nil
|
||||||
|
// metatable.get_rotation = nil
|
||||||
|
// metatable.set_rotation = nil
|
||||||
|
// metatable.get_rotationY = nil
|
||||||
|
// metatable.set_rotationY = nil
|
||||||
|
//
|
||||||
|
// function metatable.__index(self, key)
|
||||||
|
// local raw = rawget(self, key)
|
||||||
|
// if raw ~= nil then return raw end
|
||||||
|
// if key == "position" then
|
||||||
|
// return get_position(self.__cpp_ptr)
|
||||||
|
// elseif key == "active" then
|
||||||
|
// return get_active(self.__cpp_ptr)
|
||||||
|
// elseif key == "rotation" then
|
||||||
|
// return get_rotation(self.__cpp_ptr)
|
||||||
|
// elseif key == "rotationY" then
|
||||||
|
// return get_rotationY(self.__cpp_ptr)
|
||||||
|
// end
|
||||||
|
// return nil
|
||||||
|
// end
|
||||||
|
//
|
||||||
|
// function metatable.__newindex(self, key, value)
|
||||||
|
// if key == "position" then
|
||||||
|
// set_position(self.__cpp_ptr, value)
|
||||||
|
// return
|
||||||
|
// elseif key == "active" then
|
||||||
|
// set_active(self.__cpp_ptr, value)
|
||||||
|
// return
|
||||||
|
// elseif key == "rotation" then
|
||||||
|
// set_rotation(self.__cpp_ptr, value)
|
||||||
|
// return
|
||||||
|
// elseif key == "rotationY" then
|
||||||
|
// set_rotationY(self.__cpp_ptr, value)
|
||||||
|
// return
|
||||||
|
// end
|
||||||
|
// rawset(self, key, value)
|
||||||
|
// end
|
||||||
|
// end
|
||||||
|
|
||||||
static const unsigned char GAMEOBJECT_BYTECODE[] = {
|
static const unsigned char GAMEOBJECT_BYTECODE[] = {
|
||||||
0x1b, 0x4c, 0x75, 0x61, 0x52, 0x00, 0x01, 0x04, 0x04, 0x04, 0x04, 0x01,
|
0x1b, 0x4c, 0x75, 0x61, 0x52, 0x00, 0x01, 0x04, 0x04, 0x04, 0x04, 0x01,
|
||||||
0x19, 0x93, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x19, 0x93, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00,
|
||||||
0x00, 0x1f, 0x00, 0x00, 0x01, 0x1f, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x1f, 0x00, 0x00, 0x01, 0x1f, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00,
|
0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00,
|
||||||
0x00, 0x01, 0x00, 0x08, 0x11, 0x00, 0x00, 0x00, 0x47, 0x00, 0x40, 0x00,
|
0x00, 0x01, 0x00, 0x0a, 0x15, 0x00, 0x00, 0x00, 0x47, 0x00, 0x40, 0x00,
|
||||||
0x87, 0x40, 0x40, 0x00, 0xc7, 0x80, 0x40, 0x00, 0x07, 0xc1, 0x40, 0x00,
|
0x87, 0x40, 0x40, 0x00, 0xc7, 0x80, 0x40, 0x00, 0x07, 0xc1, 0x40, 0x00,
|
||||||
0x47, 0x01, 0x41, 0x00, 0x87, 0x41, 0x41, 0x00, 0x0a, 0x80, 0x41, 0x80,
|
0x47, 0x01, 0x41, 0x00, 0x87, 0x41, 0x41, 0x00, 0xc7, 0x81, 0x41, 0x00,
|
||||||
0x0a, 0x80, 0xc1, 0x80, 0x0a, 0x80, 0x41, 0x81, 0x0a, 0x80, 0xc1, 0x81,
|
0x07, 0xc2, 0x41, 0x00, 0x0a, 0x00, 0x42, 0x80, 0x0a, 0x00, 0xc2, 0x80,
|
||||||
0x0a, 0x80, 0x41, 0x82, 0x0a, 0x80, 0xc1, 0x82, 0xe5, 0x01, 0x00, 0x00,
|
0x0a, 0x00, 0x42, 0x81, 0x0a, 0x00, 0xc2, 0x81, 0x0a, 0x00, 0x42, 0x82,
|
||||||
0x0a, 0xc0, 0x81, 0x83, 0xe5, 0x41, 0x00, 0x00, 0x0a, 0xc0, 0x01, 0x84,
|
0x0a, 0x00, 0xc2, 0x82, 0x0a, 0x00, 0x42, 0x83, 0x0a, 0x00, 0xc2, 0x83,
|
||||||
0x1f, 0x00, 0x80, 0x00, 0x09, 0x00, 0x00, 0x00, 0x04, 0x0d, 0x00, 0x00,
|
0x65, 0x02, 0x00, 0x00, 0x0a, 0x40, 0x82, 0x84, 0x65, 0x42, 0x00, 0x00,
|
||||||
0x00, 0x67, 0x65, 0x74, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f,
|
0x0a, 0x40, 0x02, 0x85, 0x1f, 0x00, 0x80, 0x00, 0x0b, 0x00, 0x00, 0x00,
|
||||||
0x6e, 0x00, 0x04, 0x0d, 0x00, 0x00, 0x00, 0x73, 0x65, 0x74, 0x5f, 0x70,
|
0x04, 0x0d, 0x00, 0x00, 0x00, 0x67, 0x65, 0x74, 0x5f, 0x70, 0x6f, 0x73,
|
||||||
0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x04, 0x0b, 0x00, 0x00,
|
0x69, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x04, 0x0d, 0x00, 0x00, 0x00, 0x73,
|
||||||
0x00, 0x67, 0x65, 0x74, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00,
|
|
||||||
0x04, 0x0b, 0x00, 0x00, 0x00, 0x73, 0x65, 0x74, 0x5f, 0x61, 0x63, 0x74,
|
|
||||||
0x69, 0x76, 0x65, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00, 0x67, 0x65, 0x74,
|
|
||||||
0x5f, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x59, 0x00, 0x04,
|
|
||||||
0x0e, 0x00, 0x00, 0x00, 0x73, 0x65, 0x74, 0x5f, 0x72, 0x6f, 0x74, 0x61,
|
|
||||||
0x74, 0x69, 0x6f, 0x6e, 0x59, 0x00, 0x00, 0x04, 0x08, 0x00, 0x00, 0x00,
|
|
||||||
0x5f, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, 0x04, 0x0b, 0x00, 0x00,
|
|
||||||
0x00, 0x5f, 0x5f, 0x6e, 0x65, 0x77, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00,
|
|
||||||
0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00,
|
|
||||||
0x02, 0x00, 0x05, 0x1e, 0x00, 0x00, 0x00, 0x86, 0x00, 0x40, 0x00, 0xc0,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x9d, 0x80, 0x80, 0x01, 0x58,
|
|
||||||
0x40, 0x40, 0x01, 0x17, 0x00, 0x00, 0x80, 0x9f, 0x00, 0x00, 0x01, 0x18,
|
|
||||||
0x80, 0xc0, 0x00, 0x17, 0x00, 0x01, 0x80, 0xc5, 0x00, 0x80, 0x00, 0x07,
|
|
||||||
0xc1, 0x40, 0x00, 0xde, 0x00, 0x00, 0x01, 0xdf, 0x00, 0x00, 0x00, 0x17,
|
|
||||||
0x00, 0x03, 0x80, 0x18, 0x00, 0xc1, 0x00, 0x17, 0x00, 0x01, 0x80, 0xc5,
|
|
||||||
0x00, 0x00, 0x01, 0x07, 0xc1, 0x40, 0x00, 0xde, 0x00, 0x00, 0x01, 0xdf,
|
|
||||||
0x00, 0x00, 0x00, 0x17, 0x40, 0x01, 0x80, 0x18, 0x40, 0xc1, 0x00, 0x17,
|
|
||||||
0xc0, 0x00, 0x80, 0xc5, 0x00, 0x80, 0x01, 0x07, 0xc1, 0x40, 0x00, 0xde,
|
|
||||||
0x00, 0x00, 0x01, 0xdf, 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0xdf,
|
|
||||||
0x00, 0x00, 0x01, 0x1f, 0x00, 0x80, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04,
|
|
||||||
0x07, 0x00, 0x00, 0x00, 0x72, 0x61, 0x77, 0x67, 0x65, 0x74, 0x00, 0x00,
|
|
||||||
0x04, 0x09, 0x00, 0x00, 0x00, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f,
|
|
||||||
0x6e, 0x00, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x63, 0x70, 0x70,
|
|
||||||
0x5f, 0x70, 0x74, 0x72, 0x00, 0x04, 0x07, 0x00, 0x00, 0x00, 0x61, 0x63,
|
|
||||||
0x74, 0x69, 0x76, 0x65, 0x00, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x72, 0x6f,
|
|
||||||
0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x03, 0x01, 0x05,
|
|
||||||
0x0f, 0x00, 0x00, 0x00, 0x67, 0x61, 0x6d, 0x65, 0x6f, 0x62, 0x6a, 0x65,
|
|
||||||
0x63, 0x74, 0x2e, 0x6c, 0x75, 0x61, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x11,
|
|
||||||
0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x11,
|
|
||||||
0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x12,
|
|
||||||
0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x14,
|
|
||||||
0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x14,
|
|
||||||
0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x15,
|
|
||||||
0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x16,
|
|
||||||
0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17,
|
|
||||||
0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18,
|
|
||||||
0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1a,
|
|
||||||
0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x03,
|
|
||||||
0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x73, 0x65, 0x6c, 0x66, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
|
||||||
0x6b, 0x65, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
|
|
||||||
0x04, 0x00, 0x00, 0x00, 0x72, 0x61, 0x77, 0x00, 0x04, 0x00, 0x00, 0x00,
|
|
||||||
0x1e, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
|
|
||||||
0x5f, 0x45, 0x4e, 0x56, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x67, 0x65, 0x74,
|
|
||||||
0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x0b, 0x00,
|
|
||||||
0x00, 0x00, 0x67, 0x65, 0x74, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65,
|
|
||||||
0x00, 0x0e, 0x00, 0x00, 0x00, 0x67, 0x65, 0x74, 0x5f, 0x72, 0x6f, 0x74,
|
|
||||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x59, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x29,
|
|
||||||
0x00, 0x00, 0x00, 0x03, 0x00, 0x07, 0x1d, 0x00, 0x00, 0x00, 0x18, 0x00,
|
|
||||||
0xc0, 0x00, 0x17, 0x40, 0x01, 0x80, 0xc5, 0x00, 0x00, 0x00, 0x07, 0x41,
|
|
||||||
0x40, 0x00, 0x40, 0x01, 0x00, 0x01, 0xdd, 0x40, 0x80, 0x01, 0x1f, 0x00,
|
|
||||||
0x80, 0x00, 0x17, 0x80, 0x03, 0x80, 0x18, 0x80, 0xc0, 0x00, 0x17, 0x40,
|
|
||||||
0x01, 0x80, 0xc5, 0x00, 0x80, 0x00, 0x07, 0x41, 0x40, 0x00, 0x40, 0x01,
|
|
||||||
0x00, 0x01, 0xdd, 0x40, 0x80, 0x01, 0x1f, 0x00, 0x80, 0x00, 0x17, 0x80,
|
|
||||||
0x01, 0x80, 0x18, 0xc0, 0xc0, 0x00, 0x17, 0x00, 0x01, 0x80, 0xc5, 0x00,
|
|
||||||
0x00, 0x01, 0x07, 0x41, 0x40, 0x00, 0x40, 0x01, 0x00, 0x01, 0xdd, 0x40,
|
|
||||||
0x80, 0x01, 0x1f, 0x00, 0x80, 0x00, 0xc6, 0x00, 0xc1, 0x01, 0x00, 0x01,
|
|
||||||
0x00, 0x00, 0x40, 0x01, 0x80, 0x00, 0x80, 0x01, 0x00, 0x01, 0xdd, 0x40,
|
|
||||||
0x00, 0x02, 0x1f, 0x00, 0x80, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x09,
|
|
||||||
0x00, 0x00, 0x00, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x00,
|
|
||||||
0x04, 0x0a, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x63, 0x70, 0x70, 0x5f, 0x70,
|
|
||||||
0x74, 0x72, 0x00, 0x04, 0x07, 0x00, 0x00, 0x00, 0x61, 0x63, 0x74, 0x69,
|
|
||||||
0x76, 0x65, 0x00, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x72, 0x6f, 0x74, 0x61,
|
|
||||||
0x74, 0x69, 0x6f, 0x6e, 0x59, 0x00, 0x04, 0x07, 0x00, 0x00, 0x00, 0x72,
|
|
||||||
0x61, 0x77, 0x73, 0x65, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
|
|
||||||
0x00, 0x00, 0x01, 0x02, 0x01, 0x04, 0x01, 0x06, 0x00, 0x00, 0x0f, 0x00,
|
|
||||||
0x00, 0x00, 0x67, 0x61, 0x6d, 0x65, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74,
|
|
||||||
0x2e, 0x6c, 0x75, 0x61, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00,
|
|
||||||
0x00, 0x1e, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00,
|
|
||||||
0x00, 0x1f, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
|
|
||||||
0x00, 0x20, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00,
|
|
||||||
0x00, 0x22, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00,
|
|
||||||
0x00, 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00,
|
|
||||||
0x00, 0x24, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00,
|
|
||||||
0x00, 0x25, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00,
|
|
||||||
0x00, 0x26, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00,
|
|
||||||
0x00, 0x28, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00,
|
|
||||||
0x00, 0x29, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
|
|
||||||
0x00, 0x73, 0x65, 0x6c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00,
|
|
||||||
0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6b, 0x65, 0x79, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x76, 0x61,
|
|
||||||
0x6c, 0x75, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00,
|
|
||||||
0x04, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x73, 0x65, 0x74, 0x5f,
|
|
||||||
0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x0b, 0x00, 0x00,
|
|
||||||
0x00, 0x73, 0x65, 0x74, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00,
|
|
||||||
0x0e, 0x00, 0x00, 0x00, 0x73, 0x65, 0x74, 0x5f, 0x72, 0x6f, 0x74, 0x61,
|
|
||||||
0x74, 0x69, 0x6f, 0x6e, 0x59, 0x00, 0x05, 0x00, 0x00, 0x00, 0x5f, 0x45,
|
|
||||||
0x4e, 0x56, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00,
|
|
||||||
0x00, 0x67, 0x61, 0x6d, 0x65, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e,
|
|
||||||
0x6c, 0x75, 0x61, 0x00, 0x11, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
|
||||||
0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
|
|
||||||
0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
|
|
||||||
0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
|
|
||||||
0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00,
|
|
||||||
0x10, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00,
|
|
||||||
0x2a, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
|
|
||||||
0x6d, 0x65, 0x74, 0x61, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x67, 0x65,
|
|
||||||
0x74, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x01,
|
|
||||||
0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x73,
|
|
||||||
0x65, 0x74, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x00,
|
0x65, 0x74, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x00,
|
||||||
0x02, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
|
0x04, 0x0b, 0x00, 0x00, 0x00, 0x67, 0x65, 0x74, 0x5f, 0x61, 0x63, 0x74,
|
||||||
0x67, 0x65, 0x74, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x03,
|
0x69, 0x76, 0x65, 0x00, 0x04, 0x0b, 0x00, 0x00, 0x00, 0x73, 0x65, 0x74,
|
||||||
0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x73,
|
0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x04, 0x0d, 0x00, 0x00,
|
||||||
0x65, 0x74, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x04, 0x00,
|
0x00, 0x67, 0x65, 0x74, 0x5f, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f,
|
||||||
0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x67, 0x65,
|
0x6e, 0x00, 0x04, 0x0d, 0x00, 0x00, 0x00, 0x73, 0x65, 0x74, 0x5f, 0x72,
|
||||||
0x74, 0x5f, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x59, 0x00,
|
0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x04, 0x0e, 0x00, 0x00,
|
||||||
0x05, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
|
0x00, 0x67, 0x65, 0x74, 0x5f, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f,
|
||||||
0x73, 0x65, 0x74, 0x5f, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
0x6e, 0x59, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00, 0x73, 0x65, 0x74, 0x5f,
|
||||||
0x59, 0x00, 0x06, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x00,
|
0x72, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x59, 0x00, 0x00, 0x04,
|
||||||
0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x5f, 0x45, 0x4e, 0x56, 0x00, 0x01,
|
0x08, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x01, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x67, 0x61, 0x6d,
|
0x04, 0x0b, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x6e, 0x65, 0x77, 0x69, 0x6e,
|
||||||
0x65, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x6c, 0x75, 0x61, 0x00,
|
0x64, 0x65, 0x78, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
|
||||||
0x03, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00,
|
0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x05, 0x25, 0x00, 0x00, 0x00, 0x86,
|
||||||
0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
0x00, 0x40, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x9d,
|
||||||
0x05, 0x00, 0x00, 0x00, 0x5f, 0x45, 0x4e, 0x56, 0x00,
|
0x80, 0x80, 0x01, 0x58, 0x40, 0x40, 0x01, 0x17, 0x00, 0x00, 0x80, 0x9f,
|
||||||
|
0x00, 0x00, 0x01, 0x18, 0x80, 0xc0, 0x00, 0x17, 0x00, 0x01, 0x80, 0xc5,
|
||||||
|
0x00, 0x80, 0x00, 0x07, 0xc1, 0x40, 0x00, 0xde, 0x00, 0x00, 0x01, 0xdf,
|
||||||
|
0x00, 0x00, 0x00, 0x17, 0xc0, 0x04, 0x80, 0x18, 0x00, 0xc1, 0x00, 0x17,
|
||||||
|
0x00, 0x01, 0x80, 0xc5, 0x00, 0x00, 0x01, 0x07, 0xc1, 0x40, 0x00, 0xde,
|
||||||
|
0x00, 0x00, 0x01, 0xdf, 0x00, 0x00, 0x00, 0x17, 0x00, 0x03, 0x80, 0x18,
|
||||||
|
0x40, 0xc1, 0x00, 0x17, 0x00, 0x01, 0x80, 0xc5, 0x00, 0x80, 0x01, 0x07,
|
||||||
|
0xc1, 0x40, 0x00, 0xde, 0x00, 0x00, 0x01, 0xdf, 0x00, 0x00, 0x00, 0x17,
|
||||||
|
0x40, 0x01, 0x80, 0x18, 0x80, 0xc1, 0x00, 0x17, 0xc0, 0x00, 0x80, 0xc5,
|
||||||
|
0x00, 0x00, 0x02, 0x07, 0xc1, 0x40, 0x00, 0xde, 0x00, 0x00, 0x01, 0xdf,
|
||||||
|
0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0xdf, 0x00, 0x00, 0x01, 0x1f,
|
||||||
|
0x00, 0x80, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x07, 0x00, 0x00, 0x00,
|
||||||
|
0x72, 0x61, 0x77, 0x67, 0x65, 0x74, 0x00, 0x00, 0x04, 0x09, 0x00, 0x00,
|
||||||
|
0x00, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x04, 0x0a,
|
||||||
|
0x00, 0x00, 0x00, 0x5f, 0x5f, 0x63, 0x70, 0x70, 0x5f, 0x70, 0x74, 0x72,
|
||||||
|
0x00, 0x04, 0x07, 0x00, 0x00, 0x00, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65,
|
||||||
|
0x00, 0x04, 0x09, 0x00, 0x00, 0x00, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x69,
|
||||||
|
0x6f, 0x6e, 0x00, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x72, 0x6f, 0x74, 0x61,
|
||||||
|
0x74, 0x69, 0x6f, 0x6e, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x03, 0x01, 0x05, 0x01, 0x07,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00,
|
||||||
|
0x03, 0x00, 0x07, 0x25, 0x00, 0x00, 0x00, 0x18, 0x00, 0xc0, 0x00, 0x17,
|
||||||
|
0x40, 0x01, 0x80, 0xc5, 0x00, 0x00, 0x00, 0x07, 0x41, 0x40, 0x00, 0x40,
|
||||||
|
0x01, 0x00, 0x01, 0xdd, 0x40, 0x80, 0x01, 0x1f, 0x00, 0x80, 0x00, 0x17,
|
||||||
|
0x80, 0x05, 0x80, 0x18, 0x80, 0xc0, 0x00, 0x17, 0x40, 0x01, 0x80, 0xc5,
|
||||||
|
0x00, 0x80, 0x00, 0x07, 0x41, 0x40, 0x00, 0x40, 0x01, 0x00, 0x01, 0xdd,
|
||||||
|
0x40, 0x80, 0x01, 0x1f, 0x00, 0x80, 0x00, 0x17, 0x80, 0x03, 0x80, 0x18,
|
||||||
|
0xc0, 0xc0, 0x00, 0x17, 0x40, 0x01, 0x80, 0xc5, 0x00, 0x00, 0x01, 0x07,
|
||||||
|
0x41, 0x40, 0x00, 0x40, 0x01, 0x00, 0x01, 0xdd, 0x40, 0x80, 0x01, 0x1f,
|
||||||
|
0x00, 0x80, 0x00, 0x17, 0x80, 0x01, 0x80, 0x18, 0x00, 0xc1, 0x00, 0x17,
|
||||||
|
0x00, 0x01, 0x80, 0xc5, 0x00, 0x80, 0x01, 0x07, 0x41, 0x40, 0x00, 0x40,
|
||||||
|
0x01, 0x00, 0x01, 0xdd, 0x40, 0x80, 0x01, 0x1f, 0x00, 0x80, 0x00, 0xc6,
|
||||||
|
0x40, 0x41, 0x02, 0x00, 0x01, 0x00, 0x00, 0x40, 0x01, 0x80, 0x00, 0x80,
|
||||||
|
0x01, 0x00, 0x01, 0xdd, 0x40, 0x00, 0x02, 0x1f, 0x00, 0x80, 0x00, 0x06,
|
||||||
|
0x00, 0x00, 0x00, 0x04, 0x09, 0x00, 0x00, 0x00, 0x70, 0x6f, 0x73, 0x69,
|
||||||
|
0x74, 0x69, 0x6f, 0x6e, 0x00, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x5f, 0x5f,
|
||||||
|
0x63, 0x70, 0x70, 0x5f, 0x70, 0x74, 0x72, 0x00, 0x04, 0x07, 0x00, 0x00,
|
||||||
|
0x00, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x04, 0x09, 0x00, 0x00,
|
||||||
|
0x00, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x04, 0x0a,
|
||||||
|
0x00, 0x00, 0x00, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x59,
|
||||||
|
0x00, 0x04, 0x07, 0x00, 0x00, 0x00, 0x72, 0x61, 0x77, 0x73, 0x65, 0x74,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x02, 0x01,
|
||||||
|
0x04, 0x01, 0x06, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
|
||||||
|
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
115
src/interpolation.cpp
Normal file
115
src/interpolation.cpp
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
#include "interpolation.hh"
|
||||||
|
|
||||||
|
namespace psxsplash {
|
||||||
|
|
||||||
|
int32_t applyCurve(int32_t t, InterpMode mode) {
|
||||||
|
switch (mode) {
|
||||||
|
default:
|
||||||
|
case InterpMode::Linear:
|
||||||
|
return t;
|
||||||
|
case InterpMode::Step:
|
||||||
|
return 0;
|
||||||
|
case InterpMode::EaseIn:
|
||||||
|
return (int32_t)((int64_t)t * t >> 12);
|
||||||
|
case InterpMode::EaseOut:
|
||||||
|
return (int32_t)(((int64_t)t * (8192 - t)) >> 12);
|
||||||
|
case InterpMode::EaseInOut: {
|
||||||
|
int64_t t2 = (int64_t)t * t;
|
||||||
|
int64_t t3 = t2 * t;
|
||||||
|
return (int32_t)((3 * t2 - 2 * (t3 >> 12)) >> 12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool findKfPair(CutsceneKeyframe* kf, uint8_t count, uint16_t frame,
|
||||||
|
uint8_t& a, uint8_t& b, int32_t& t, int16_t out[3]) {
|
||||||
|
if (count == 0) {
|
||||||
|
out[0] = out[1] = out[2] = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (frame <= kf[0].getFrame() || count == 1) {
|
||||||
|
out[0] = kf[0].values[0];
|
||||||
|
out[1] = kf[0].values[1];
|
||||||
|
out[2] = kf[0].values[2];
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (frame >= kf[count - 1].getFrame()) {
|
||||||
|
out[0] = kf[count - 1].values[0];
|
||||||
|
out[1] = kf[count - 1].values[1];
|
||||||
|
out[2] = kf[count - 1].values[2];
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
b = 1;
|
||||||
|
while (b < count && kf[b].getFrame() <= frame) b++;
|
||||||
|
a = b - 1;
|
||||||
|
uint16_t span = kf[b].getFrame() - kf[a].getFrame();
|
||||||
|
if (span == 0) {
|
||||||
|
out[0] = kf[a].values[0];
|
||||||
|
out[1] = kf[a].values[1];
|
||||||
|
out[2] = kf[a].values[2];
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint32_t num = (uint32_t)(frame - kf[a].getFrame()) << 12;
|
||||||
|
int32_t rawT = (int32_t)(num / span);
|
||||||
|
t = applyCurve(rawT, kf[b].getInterp());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void lerpKeyframes(CutsceneKeyframe* kf, uint8_t count, uint16_t frame,
|
||||||
|
const int16_t initial[3], int16_t out[3]) {
|
||||||
|
uint8_t a, b;
|
||||||
|
int32_t t;
|
||||||
|
if (!findKfPair(kf, count, frame, a, b, t, out)) {
|
||||||
|
if (count > 0 && kf[0].getFrame() > 0 && frame < kf[0].getFrame()) {
|
||||||
|
uint16_t span = kf[0].getFrame();
|
||||||
|
uint32_t num = (uint32_t)frame << 12;
|
||||||
|
int32_t rawT = (int32_t)(num / span);
|
||||||
|
int32_t ct = applyCurve(rawT, kf[0].getInterp());
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
int32_t delta = (int32_t)kf[0].values[i] - (int32_t)initial[i];
|
||||||
|
out[i] = (int16_t)((int32_t)initial[i] + ((delta * ct) >> 12));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
int32_t delta = (int32_t)kf[b].values[i] - (int32_t)kf[a].values[i];
|
||||||
|
out[i] = (int16_t)((int32_t)kf[a].values[i] + ((delta * t) >> 12));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr int32_t ANGLE_FULL_CIRCLE = 2048;
|
||||||
|
static constexpr int32_t ANGLE_HALF_CIRCLE = 1024;
|
||||||
|
|
||||||
|
void lerpAngles(CutsceneKeyframe* kf, uint8_t count, uint16_t frame,
|
||||||
|
const int16_t initial[3], int16_t out[3]) {
|
||||||
|
uint8_t a, b;
|
||||||
|
int32_t t;
|
||||||
|
if (!findKfPair(kf, count, frame, a, b, t, out)) {
|
||||||
|
if (count > 0 && kf[0].getFrame() > 0 && frame < kf[0].getFrame()) {
|
||||||
|
uint16_t span = kf[0].getFrame();
|
||||||
|
uint32_t num = (uint32_t)frame << 12;
|
||||||
|
int32_t rawT = (int32_t)(num / span);
|
||||||
|
int32_t ct = applyCurve(rawT, kf[0].getInterp());
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
int32_t from = (int32_t)initial[i];
|
||||||
|
int32_t to = (int32_t)kf[0].values[i];
|
||||||
|
int32_t delta = to - from;
|
||||||
|
delta = ((delta + ANGLE_HALF_CIRCLE) % ANGLE_FULL_CIRCLE + ANGLE_FULL_CIRCLE) % ANGLE_FULL_CIRCLE - ANGLE_HALF_CIRCLE;
|
||||||
|
out[i] = (int16_t)(from + ((delta * ct) >> 12));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
int32_t from = (int32_t)kf[a].values[i];
|
||||||
|
int32_t to = (int32_t)kf[b].values[i];
|
||||||
|
int32_t delta = to - from;
|
||||||
|
delta = ((delta + ANGLE_HALF_CIRCLE) % ANGLE_FULL_CIRCLE + ANGLE_FULL_CIRCLE) % ANGLE_FULL_CIRCLE - ANGLE_HALF_CIRCLE;
|
||||||
|
out[i] = (int16_t)(from + ((delta * t) >> 12));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace psxsplash
|
||||||
19
src/interpolation.hh
Normal file
19
src/interpolation.hh
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "cutscene.hh"
|
||||||
|
|
||||||
|
namespace psxsplash {
|
||||||
|
|
||||||
|
int32_t applyCurve(int32_t t, InterpMode mode);
|
||||||
|
|
||||||
|
bool findKfPair(CutsceneKeyframe* kf, uint8_t count, uint16_t frame,
|
||||||
|
uint8_t& a, uint8_t& b, int32_t& t, int16_t out[3]);
|
||||||
|
|
||||||
|
void lerpKeyframes(CutsceneKeyframe* kf, uint8_t count, uint16_t frame,
|
||||||
|
const int16_t initial[3], int16_t out[3]);
|
||||||
|
|
||||||
|
void lerpAngles(CutsceneKeyframe* kf, uint8_t count, uint16_t frame,
|
||||||
|
const int16_t initial[3], int16_t out[3]);
|
||||||
|
|
||||||
|
} // namespace psxsplash
|
||||||
156
src/lua.cpp
156
src/lua.cpp
@@ -26,18 +26,24 @@ extern "C" void *lua_oom_realloc(void *ptr, size_t size) {
|
|||||||
|
|
||||||
// Lua helpers
|
// Lua helpers
|
||||||
|
|
||||||
static constexpr lua_Number kFixedScale = 4096;
|
static constexpr int32_t kFixedScale = 4096;
|
||||||
|
|
||||||
|
// Accept FixedPoint object or plain number from Lua
|
||||||
|
static psyqo::FixedPoint<12> readFP(psyqo::Lua& L, int idx) {
|
||||||
|
if (L.isFixedPoint(idx)) return L.toFixedPoint(idx);
|
||||||
|
return psyqo::FixedPoint<12>(static_cast<int32_t>(L.toNumber(idx) * kFixedScale), psyqo::FixedPoint<12>::RAW);
|
||||||
|
}
|
||||||
|
|
||||||
static int gameobjectGetPosition(psyqo::Lua L) {
|
static int gameobjectGetPosition(psyqo::Lua L) {
|
||||||
|
|
||||||
auto go = L.toUserdata<psxsplash::GameObject>(1);
|
auto go = L.toUserdata<psxsplash::GameObject>(1);
|
||||||
|
|
||||||
L.newTable();
|
L.newTable();
|
||||||
L.pushNumber(static_cast<lua_Number>(go->position.x.raw()) / kFixedScale);
|
L.push(go->position.x);
|
||||||
L.setField(2, "x");
|
L.setField(2, "x");
|
||||||
L.pushNumber(static_cast<lua_Number>(go->position.y.raw()) / kFixedScale);
|
L.push(go->position.y);
|
||||||
L.setField(2, "y");
|
L.setField(2, "y");
|
||||||
L.pushNumber(static_cast<lua_Number>(go->position.z.raw()) / kFixedScale);
|
L.push(go->position.z);
|
||||||
L.setField(2, "z");
|
L.setField(2, "z");
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
@@ -49,15 +55,15 @@ static int gameobjectSetPosition(psyqo::Lua L) {
|
|||||||
auto go = L.toUserdata<psxsplash::GameObject>(1);
|
auto go = L.toUserdata<psxsplash::GameObject>(1);
|
||||||
|
|
||||||
L.getField(2, "x");
|
L.getField(2, "x");
|
||||||
go->position.x = psyqo::FixedPoint<>(static_cast<int32_t>(L.toNumber(3) * kFixedScale), psyqo::FixedPoint<>::RAW);
|
go->position.x = readFP(L, 3);
|
||||||
L.pop();
|
L.pop();
|
||||||
|
|
||||||
L.getField(2, "y");
|
L.getField(2, "y");
|
||||||
go->position.y = psyqo::FixedPoint<>(static_cast<int32_t>(L.toNumber(3) * kFixedScale), psyqo::FixedPoint<>::RAW);
|
go->position.y = readFP(L, 3);
|
||||||
L.pop();
|
L.pop();
|
||||||
L.getField(2, "z");
|
|
||||||
|
|
||||||
go->position.z = psyqo::FixedPoint<>(static_cast<int32_t>(L.toNumber(3) * kFixedScale), psyqo::FixedPoint<>::RAW);
|
L.getField(2, "z");
|
||||||
|
go->position.z = readFP(L, 3);
|
||||||
L.pop();
|
L.pop();
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -76,10 +82,8 @@ static int gameobjectSetActive(psyqo::Lua L) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr lua_Number kAngleScale = 1024;
|
|
||||||
static psyqo::Trig<> s_trig;
|
static psyqo::Trig<> s_trig;
|
||||||
|
|
||||||
|
|
||||||
static psyqo::Angle fastAtan2(int32_t sinVal, int32_t cosVal) {
|
static psyqo::Angle fastAtan2(int32_t sinVal, int32_t cosVal) {
|
||||||
psyqo::Angle result;
|
psyqo::Angle result;
|
||||||
if (cosVal == 0 && sinVal == 0) { result.value = 0; return result; }
|
if (cosVal == 0 && sinVal == 0) { result.value = 0; return result; }
|
||||||
@@ -99,20 +103,85 @@ static psyqo::Angle fastAtan2(int32_t sinVal, int32_t cosVal) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int gameobjectGetRotation(psyqo::Lua L) {
|
||||||
|
auto go = L.toUserdata<psxsplash::GameObject>(1);
|
||||||
|
// Decompose Y-axis rotation from the matrix (vs[0].x = cos, vs[0].z = sin)
|
||||||
|
// For full XYZ, we extract approximate Euler angles from the rotation matrix.
|
||||||
|
// Row 0: [cos(Y)cos(Z), -cos(Y)sin(Z), sin(Y)]
|
||||||
|
// This is a simplified extraction assuming common rotation order Y*X*Z.
|
||||||
|
int32_t sinY = go->rotation.vs[0].z.raw();
|
||||||
|
int32_t cosY = go->rotation.vs[0].x.raw();
|
||||||
|
int32_t sinX = -go->rotation.vs[1].z.raw();
|
||||||
|
int32_t cosX = go->rotation.vs[2].z.raw();
|
||||||
|
int32_t sinZ = -go->rotation.vs[0].y.raw();
|
||||||
|
int32_t cosZ = go->rotation.vs[0].x.raw();
|
||||||
|
|
||||||
|
auto toFP12 = [](psyqo::Angle a) -> psyqo::FixedPoint<12> {
|
||||||
|
psyqo::FixedPoint<12> fp;
|
||||||
|
fp.value = a.value << 2;
|
||||||
|
return fp;
|
||||||
|
};
|
||||||
|
|
||||||
|
L.newTable();
|
||||||
|
L.push(toFP12(fastAtan2(sinX, cosX)));
|
||||||
|
L.setField(2, "x");
|
||||||
|
L.push(toFP12(fastAtan2(sinY, cosY)));
|
||||||
|
L.setField(2, "y");
|
||||||
|
L.push(toFP12(fastAtan2(sinZ, cosZ)));
|
||||||
|
L.setField(2, "z");
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gameobjectSetRotation(psyqo::Lua L) {
|
||||||
|
auto go = L.toUserdata<psxsplash::GameObject>(1);
|
||||||
|
|
||||||
|
L.getField(2, "x");
|
||||||
|
psyqo::FixedPoint<12> fpX = readFP(L, 3);
|
||||||
|
L.pop();
|
||||||
|
|
||||||
|
L.getField(2, "y");
|
||||||
|
psyqo::FixedPoint<12> fpY = readFP(L, 3);
|
||||||
|
L.pop();
|
||||||
|
|
||||||
|
L.getField(2, "z");
|
||||||
|
psyqo::FixedPoint<12> fpZ = readFP(L, 3);
|
||||||
|
L.pop();
|
||||||
|
|
||||||
|
// Convert FixedPoint<12> to Angle (FixedPoint<10>)
|
||||||
|
psyqo::Angle rx, ry, rz;
|
||||||
|
rx.value = fpX.value >> 2;
|
||||||
|
ry.value = fpY.value >> 2;
|
||||||
|
rz.value = fpZ.value >> 2;
|
||||||
|
|
||||||
|
// Compose Y * X * Z rotation matrix
|
||||||
|
auto matY = psyqo::SoftMath::generateRotationMatrix33(ry, psyqo::SoftMath::Axis::Y, s_trig);
|
||||||
|
auto matX = psyqo::SoftMath::generateRotationMatrix33(rx, psyqo::SoftMath::Axis::X, s_trig);
|
||||||
|
auto matZ = psyqo::SoftMath::generateRotationMatrix33(rz, psyqo::SoftMath::Axis::Z, s_trig);
|
||||||
|
auto temp = psyqo::SoftMath::multiplyMatrix33(matY, matX);
|
||||||
|
go->rotation = psyqo::SoftMath::multiplyMatrix33(temp, matZ);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int gameobjectGetRotationY(psyqo::Lua L) {
|
static int gameobjectGetRotationY(psyqo::Lua L) {
|
||||||
auto go = L.toUserdata<psxsplash::GameObject>(1);
|
auto go = L.toUserdata<psxsplash::GameObject>(1);
|
||||||
int32_t sinRaw = go->rotation.vs[0].z.raw();
|
int32_t sinRaw = go->rotation.vs[0].z.raw();
|
||||||
int32_t cosRaw = go->rotation.vs[0].x.raw();
|
int32_t cosRaw = go->rotation.vs[0].x.raw();
|
||||||
psyqo::Angle angle = fastAtan2(sinRaw, cosRaw);
|
psyqo::Angle angle = fastAtan2(sinRaw, cosRaw);
|
||||||
L.pushNumber(static_cast<lua_Number>(angle.value) / kAngleScale);
|
// Angle is FixedPoint<10> (pi-units). Convert to FixedPoint<12> for Lua.
|
||||||
|
psyqo::FixedPoint<12> fp12;
|
||||||
|
fp12.value = angle.value << 2;
|
||||||
|
L.push(fp12);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gameobjectSetRotationY(psyqo::Lua L) {
|
static int gameobjectSetRotationY(psyqo::Lua L) {
|
||||||
auto go = L.toUserdata<psxsplash::GameObject>(1);
|
auto go = L.toUserdata<psxsplash::GameObject>(1);
|
||||||
lua_Number piUnits = L.toNumber(2);
|
// Accept FixedPoint<12> from Lua, convert to Angle (FixedPoint<10>)
|
||||||
|
psyqo::FixedPoint<12> fp12 = readFP(L, 2);
|
||||||
psyqo::Angle angle;
|
psyqo::Angle angle;
|
||||||
angle.value = static_cast<int32_t>(piUnits * kAngleScale);
|
angle.value = fp12.value >> 2;
|
||||||
go->rotation = psyqo::SoftMath::generateRotationMatrix33(angle, psyqo::SoftMath::Axis::Y, s_trig);
|
go->rotation = psyqo::SoftMath::generateRotationMatrix33(angle, psyqo::SoftMath::Axis::Y, s_trig);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -137,6 +206,12 @@ void psxsplash::Lua::Init() {
|
|||||||
L.push(gameobjectSetActive);
|
L.push(gameobjectSetActive);
|
||||||
L.setField(-2, "set_active");
|
L.setField(-2, "set_active");
|
||||||
|
|
||||||
|
L.push(gameobjectGetRotation);
|
||||||
|
L.setField(-2, "get_rotation");
|
||||||
|
|
||||||
|
L.push(gameobjectSetRotation);
|
||||||
|
L.setField(-2, "set_rotation");
|
||||||
|
|
||||||
L.push(gameobjectGetRotationY);
|
L.push(gameobjectGetRotationY);
|
||||||
L.setField(-2, "get_rotationY");
|
L.setField(-2, "get_rotationY");
|
||||||
|
|
||||||
@@ -168,6 +243,49 @@ void psxsplash::Lua::Init() {
|
|||||||
|
|
||||||
L.newTable();
|
L.newTable();
|
||||||
m_luascriptsReference = L.ref();
|
m_luascriptsReference = L.ref();
|
||||||
|
|
||||||
|
// Add __concat to the FixedPoint metatable so FixedPoint values work with ..
|
||||||
|
// psyqo-lua doesn't provide this, but scripts need it for Debug.Log etc.
|
||||||
|
L.getField(LUA_REGISTRYINDEX, "psyqo.FixedPoint");
|
||||||
|
if (L.isTable(-1)) {
|
||||||
|
L.push([](psyqo::Lua L) -> int {
|
||||||
|
// Convert both operands to strings and concatenate
|
||||||
|
char buf[64];
|
||||||
|
int len = 0;
|
||||||
|
|
||||||
|
for (int i = 1; i <= 2; i++) {
|
||||||
|
if (L.isFixedPoint(i)) {
|
||||||
|
auto fp = L.toFixedPoint(i);
|
||||||
|
int32_t raw = fp.raw();
|
||||||
|
int integer = raw >> 12;
|
||||||
|
unsigned fraction = (raw < 0 ? -raw : raw) & 0xfff;
|
||||||
|
if (fraction == 0) {
|
||||||
|
len += snprintf(buf + len, sizeof(buf) - len, "%d", integer);
|
||||||
|
} else {
|
||||||
|
unsigned decimal = (fraction * 1000) >> 12;
|
||||||
|
if (raw < 0 && integer == 0)
|
||||||
|
len += snprintf(buf + len, sizeof(buf) - len, "-%d.%03u", integer, decimal);
|
||||||
|
else
|
||||||
|
len += snprintf(buf + len, sizeof(buf) - len, "%d.%03u", integer, decimal);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const char* s = L.toString(i);
|
||||||
|
if (s) {
|
||||||
|
int slen = 0;
|
||||||
|
while (s[slen]) slen++;
|
||||||
|
if (len + slen < (int)sizeof(buf)) {
|
||||||
|
for (int j = 0; j < slen; j++) buf[len++] = s[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf[len] = '\0';
|
||||||
|
L.push(buf, len);
|
||||||
|
return 1;
|
||||||
|
});
|
||||||
|
L.setField(-2, "__concat");
|
||||||
|
}
|
||||||
|
L.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void psxsplash::Lua::Shutdown() {
|
void psxsplash::Lua::Shutdown() {
|
||||||
@@ -360,11 +478,15 @@ void psxsplash::Lua::RegisterGameObject(GameObject* go) {
|
|||||||
|
|
||||||
L.pop();
|
L.pop();
|
||||||
// empty stack
|
// empty stack
|
||||||
|
// Note: onCreate is NOT fired here. Call FireAllOnCreate() after all objects
|
||||||
|
// are registered so that Entity.Find works across all objects in onCreate.
|
||||||
|
}
|
||||||
|
|
||||||
|
void psxsplash::Lua::FireAllOnCreate(GameObject** objects, size_t count) {
|
||||||
// Fire onCreate event if this object handles it
|
for (size_t i = 0; i < count; i++) {
|
||||||
if (eventMask & EVENT_ON_CREATE) {
|
if (objects[i] && (objects[i]->eventMask & EVENT_ON_CREATE)) {
|
||||||
onCreateMethodWrapper.callMethod(*this, go);
|
onCreateMethodWrapper.callMethod(*this, objects[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ class Lua {
|
|||||||
void LoadLuaFile(const char* code, size_t len, int index);
|
void LoadLuaFile(const char* code, size_t len, int index);
|
||||||
void RegisterSceneScripts(int index);
|
void RegisterSceneScripts(int index);
|
||||||
void RegisterGameObject(GameObject* go);
|
void RegisterGameObject(GameObject* go);
|
||||||
|
void FireAllOnCreate(GameObject** objects, size_t count);
|
||||||
void RelocateGameObjects(GameObject** objects, size_t count, intptr_t delta);
|
void RelocateGameObjects(GameObject** objects, size_t count, intptr_t delta);
|
||||||
|
|
||||||
// Get the underlying psyqo::Lua state for API registration
|
// Get the underlying psyqo::Lua state for API registration
|
||||||
|
|||||||
252
src/luaapi.cpp
252
src/luaapi.cpp
@@ -4,6 +4,7 @@
|
|||||||
#include "controls.hh"
|
#include "controls.hh"
|
||||||
#include "camera.hh"
|
#include "camera.hh"
|
||||||
#include "cutscene.hh"
|
#include "cutscene.hh"
|
||||||
|
#include "animation.hh"
|
||||||
#include "uisystem.hh"
|
#include "uisystem.hh"
|
||||||
|
|
||||||
#include <psyqo/soft-math.hh>
|
#include <psyqo/soft-math.hh>
|
||||||
@@ -16,18 +17,20 @@ namespace psxsplash {
|
|||||||
// Static member
|
// Static member
|
||||||
SceneManager* LuaAPI::s_sceneManager = nullptr;
|
SceneManager* LuaAPI::s_sceneManager = nullptr;
|
||||||
CutscenePlayer* LuaAPI::s_cutscenePlayer = nullptr;
|
CutscenePlayer* LuaAPI::s_cutscenePlayer = nullptr;
|
||||||
|
AnimationPlayer* LuaAPI::s_animationPlayer = nullptr;
|
||||||
UISystem* LuaAPI::s_uiSystem = nullptr;
|
UISystem* LuaAPI::s_uiSystem = nullptr;
|
||||||
|
|
||||||
// Scale factor: FixedPoint<12> stores 1.0 as raw 4096.
|
// Scale factor: FixedPoint<12> stores 1.0 as raw 4096.
|
||||||
// Lua scripts work in world-space units (1 = one unit), so we convert.
|
// Lua scripts work in world-space units (1 = one unit), so we convert.
|
||||||
static constexpr lua_Number kFixedScale = 4096;
|
static constexpr lua_Number kFixedScale = 4096;
|
||||||
|
|
||||||
static lua_Number fpToLua(psyqo::FixedPoint<12> fp) {
|
// Read a FixedPoint<12> from the stack, accepting either a FixedPoint object
|
||||||
return static_cast<lua_Number>(fp.raw()) / kFixedScale;
|
// or a plain integer (which gets scaled by 4096 to become fp12).
|
||||||
|
static psyqo::FixedPoint<12> readFP(psyqo::Lua& L, int idx) {
|
||||||
|
if (L.isFixedPoint(idx)) {
|
||||||
|
return L.toFixedPoint(idx);
|
||||||
}
|
}
|
||||||
|
return psyqo::FixedPoint<12>(static_cast<int32_t>(L.toNumber(idx) * kFixedScale), psyqo::FixedPoint<12>::RAW);
|
||||||
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
|
// Angle scale: psyqo::Angle is FixedPoint<10>, so 1.0_pi = raw 1024
|
||||||
@@ -38,9 +41,10 @@ static psyqo::Trig<> s_trig;
|
|||||||
// REGISTRATION
|
// REGISTRATION
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
void LuaAPI::RegisterAll(psyqo::Lua& L, SceneManager* scene, CutscenePlayer* cutscenePlayer, UISystem* uiSystem) {
|
void LuaAPI::RegisterAll(psyqo::Lua& L, SceneManager* scene, CutscenePlayer* cutscenePlayer, AnimationPlayer* animationPlayer, UISystem* uiSystem) {
|
||||||
s_sceneManager = scene;
|
s_sceneManager = scene;
|
||||||
s_cutscenePlayer = cutscenePlayer;
|
s_cutscenePlayer = cutscenePlayer;
|
||||||
|
s_animationPlayer = animationPlayer;
|
||||||
s_uiSystem = uiSystem;
|
s_uiSystem = uiSystem;
|
||||||
|
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
@@ -285,6 +289,22 @@ void LuaAPI::RegisterAll(psyqo::Lua& L, SceneManager* scene, CutscenePlayer* cut
|
|||||||
|
|
||||||
L.setGlobal("Cutscene");
|
L.setGlobal("Cutscene");
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// ANIMATION API
|
||||||
|
// ========================================================================
|
||||||
|
L.newTable();
|
||||||
|
|
||||||
|
L.push(Animation_Play);
|
||||||
|
L.setField(-2, "Play");
|
||||||
|
|
||||||
|
L.push(Animation_Stop);
|
||||||
|
L.setField(-2, "Stop");
|
||||||
|
|
||||||
|
L.push(Animation_IsPlaying);
|
||||||
|
L.setField(-2, "IsPlaying");
|
||||||
|
|
||||||
|
L.setGlobal("Animation");
|
||||||
|
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
// CONTROLS API
|
// CONTROLS API
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
@@ -598,8 +618,10 @@ int LuaAPI::Entity_GetRotationY(lua_State* L) {
|
|||||||
angle.value = a;
|
angle.value = a;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return in pi-units: 0.5 = π/2 = 90°
|
// Return as FixedPoint<12> (Angle is FixedPoint<10>, shift left 2 for fp12)
|
||||||
lua.pushNumber(static_cast<lua_Number>(angle.value) / kAngleScale);
|
psyqo::FixedPoint<12> fp12;
|
||||||
|
fp12.value = angle.value << 2;
|
||||||
|
lua.push(fp12);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -614,10 +636,10 @@ int LuaAPI::Entity_SetRotationY(lua_State* L) {
|
|||||||
|
|
||||||
if (!go) return 0;
|
if (!go) return 0;
|
||||||
|
|
||||||
// Accept angle in pi-units (0.5 = π/2 = 90°)
|
// Accept FixedPoint or number, convert to Angle (FixedPoint<10>)
|
||||||
lua_Number piUnits = lua.toNumber(2);
|
psyqo::FixedPoint<12> fp12 = readFP(lua, 2);
|
||||||
psyqo::Angle angle;
|
psyqo::Angle angle;
|
||||||
angle.value = static_cast<int32_t>(piUnits * kAngleScale);
|
angle.value = fp12.value >> 2;
|
||||||
go->rotation = psyqo::SoftMath::generateRotationMatrix33(angle, psyqo::SoftMath::Axis::Y, s_trig);
|
go->rotation = psyqo::SoftMath::generateRotationMatrix33(angle, psyqo::SoftMath::Axis::Y, s_trig);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -657,11 +679,11 @@ int LuaAPI::Entity_ForEach(lua_State* L) {
|
|||||||
void LuaAPI::PushVec3(psyqo::Lua& L, psyqo::FixedPoint<12> x,
|
void LuaAPI::PushVec3(psyqo::Lua& L, psyqo::FixedPoint<12> x,
|
||||||
psyqo::FixedPoint<12> y, psyqo::FixedPoint<12> z) {
|
psyqo::FixedPoint<12> y, psyqo::FixedPoint<12> z) {
|
||||||
L.newTable();
|
L.newTable();
|
||||||
L.pushNumber(fpToLua(x));
|
L.push(x);
|
||||||
L.setField(-2, "x");
|
L.setField(-2, "x");
|
||||||
L.pushNumber(fpToLua(y));
|
L.push(y);
|
||||||
L.setField(-2, "y");
|
L.setField(-2, "y");
|
||||||
L.pushNumber(fpToLua(z));
|
L.push(z);
|
||||||
L.setField(-2, "z");
|
L.setField(-2, "z");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -670,24 +692,24 @@ void LuaAPI::ReadVec3(psyqo::Lua& L, int idx,
|
|||||||
psyqo::FixedPoint<12>& y,
|
psyqo::FixedPoint<12>& y,
|
||||||
psyqo::FixedPoint<12>& z) {
|
psyqo::FixedPoint<12>& z) {
|
||||||
L.getField(idx, "x");
|
L.getField(idx, "x");
|
||||||
x = luaToFp(L.toNumber(-1));
|
x = readFP(L, -1);
|
||||||
L.pop();
|
L.pop();
|
||||||
|
|
||||||
L.getField(idx, "y");
|
L.getField(idx, "y");
|
||||||
y = luaToFp(L.toNumber(-1));
|
y = readFP(L, -1);
|
||||||
L.pop();
|
L.pop();
|
||||||
|
|
||||||
L.getField(idx, "z");
|
L.getField(idx, "z");
|
||||||
z = luaToFp(L.toNumber(-1));
|
z = readFP(L, -1);
|
||||||
L.pop();
|
L.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
int LuaAPI::Vec3_New(lua_State* L) {
|
int LuaAPI::Vec3_New(lua_State* L) {
|
||||||
psyqo::Lua lua(L);
|
psyqo::Lua lua(L);
|
||||||
|
|
||||||
psyqo::FixedPoint<12> x = luaToFp(lua.optNumber(1, 0));
|
psyqo::FixedPoint<12> x = lua.isNoneOrNil(1) ? psyqo::FixedPoint<12>() : readFP(lua, 1);
|
||||||
psyqo::FixedPoint<12> y = luaToFp(lua.optNumber(2, 0));
|
psyqo::FixedPoint<12> y = lua.isNoneOrNil(2) ? psyqo::FixedPoint<12>() : readFP(lua, 2);
|
||||||
psyqo::FixedPoint<12> z = luaToFp(lua.optNumber(3, 0));
|
psyqo::FixedPoint<12> z = lua.isNoneOrNil(3) ? psyqo::FixedPoint<12>() : readFP(lua, 3);
|
||||||
|
|
||||||
PushVec3(lua, x, y, z);
|
PushVec3(lua, x, y, z);
|
||||||
return 1;
|
return 1;
|
||||||
@@ -740,7 +762,7 @@ int LuaAPI::Vec3_Mul(lua_State* L) {
|
|||||||
psyqo::FixedPoint<12> x, y, z;
|
psyqo::FixedPoint<12> x, y, z;
|
||||||
ReadVec3(lua, 1, x, y, z);
|
ReadVec3(lua, 1, x, y, z);
|
||||||
|
|
||||||
psyqo::FixedPoint<12> scalar = luaToFp(lua.toNumber(2));
|
psyqo::FixedPoint<12> scalar = readFP(lua, 2);
|
||||||
|
|
||||||
PushVec3(lua, x * scalar, y * scalar, z * scalar);
|
PushVec3(lua, x * scalar, y * scalar, z * scalar);
|
||||||
return 1;
|
return 1;
|
||||||
@@ -761,7 +783,7 @@ int LuaAPI::Vec3_Dot(lua_State* L) {
|
|||||||
ReadVec3(lua, 2, bx, by, bz);
|
ReadVec3(lua, 2, bx, by, bz);
|
||||||
|
|
||||||
auto dot = ax * bx + ay * by + az * bz;
|
auto dot = ax * bx + ay * by + az * bz;
|
||||||
lua.pushNumber(fpToLua(dot));
|
lua.push(dot);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -799,7 +821,7 @@ int LuaAPI::Vec3_LengthSq(lua_State* L) {
|
|||||||
ReadVec3(lua, 1, x, y, z);
|
ReadVec3(lua, 1, x, y, z);
|
||||||
|
|
||||||
auto lengthSq = x * x + y * y + z * z;
|
auto lengthSq = x * x + y * y + z * z;
|
||||||
lua.pushNumber(fpToLua(lengthSq));
|
lua.push(lengthSq);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -814,26 +836,32 @@ int LuaAPI::Vec3_Length(lua_State* L) {
|
|||||||
psyqo::FixedPoint<12> x, y, z;
|
psyqo::FixedPoint<12> x, y, z;
|
||||||
ReadVec3(lua, 1, x, y, z);
|
ReadVec3(lua, 1, x, y, z);
|
||||||
|
|
||||||
// Compute length in scaled world-space to avoid fp12×fp12 overflow issues.
|
// lengthSq in fp12: (x*x + y*y + z*z) is fp24 (two fp12 multiplied).
|
||||||
// Convert to Lua-number domain, sqrt there, return directly.
|
// We need sqrt(lengthSq) as fp12.
|
||||||
lua_Number sx = fpToLua(x);
|
// lengthSq raw = sum of (raw*raw >> 12) values = fp12 result
|
||||||
lua_Number sy = fpToLua(y);
|
auto lengthSq = x * x + y * y + z * z;
|
||||||
lua_Number sz = fpToLua(z);
|
int32_t lsRaw = lengthSq.raw();
|
||||||
lua_Number sqVal = sx * sx + sy * sy + sz * sz;
|
|
||||||
|
|
||||||
if (sqVal <= 0) {
|
if (lsRaw <= 0) {
|
||||||
lua.pushNumber(0);
|
lua.push(psyqo::FixedPoint<12>());
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Newton's method sqrt (integer-safe)
|
// Integer sqrt of (lsRaw << 12) to get result in fp12
|
||||||
lua_Number guess = sqVal / 2;
|
// sqrt(fp12_value) = sqrt(raw/4096) = sqrt(raw)/64
|
||||||
if (guess == 0) guess = 1;
|
// So: result_raw = isqrt(raw * 4096) = isqrt(raw << 12)
|
||||||
for (int i = 0; i < 12; i++) {
|
// isqrt(lsRaw) gives integer sqrt. Multiply by 64 (sqrt(4096)) to get fp12.
|
||||||
guess = (guess + sqVal / guess) / 2;
|
// Newton's method in 32-bit: isqrt(n)
|
||||||
|
uint32_t n = (uint32_t)lsRaw;
|
||||||
|
uint32_t guess = n;
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
if (guess == 0) break;
|
||||||
|
guess = (guess + n / guess) / 2;
|
||||||
}
|
}
|
||||||
|
// guess = isqrt(lsRaw). lsRaw is in fp12, so sqrt needs * sqrt(4096) = 64
|
||||||
lua.pushNumber(guess);
|
psyqo::FixedPoint<12> result;
|
||||||
|
result.value = (int32_t)(guess * 64);
|
||||||
|
lua.push(result);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -848,26 +876,31 @@ int LuaAPI::Vec3_Normalize(lua_State* L) {
|
|||||||
psyqo::FixedPoint<12> x, y, z;
|
psyqo::FixedPoint<12> x, y, z;
|
||||||
ReadVec3(lua, 1, x, y, z);
|
ReadVec3(lua, 1, x, y, z);
|
||||||
|
|
||||||
// Work in Lua-number (world-space) domain for the sqrt
|
auto lengthSq = x * x + y * y + z * z;
|
||||||
lua_Number sx = fpToLua(x);
|
int32_t lsRaw = lengthSq.raw();
|
||||||
lua_Number sy = fpToLua(y);
|
|
||||||
lua_Number sz = fpToLua(z);
|
|
||||||
lua_Number sLen = sx * sx + sy * sy + sz * sz;
|
|
||||||
|
|
||||||
if (sLen <= 0) {
|
if (lsRaw <= 0) {
|
||||||
PushVec3(lua, psyqo::FixedPoint<12>(0), psyqo::FixedPoint<12>(0), psyqo::FixedPoint<12>(0));
|
PushVec3(lua, psyqo::FixedPoint<12>(), psyqo::FixedPoint<12>(), psyqo::FixedPoint<12>());
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Newton's method sqrt
|
// isqrt(lsRaw) * 64 = length in fp12
|
||||||
lua_Number guess = sLen / 2;
|
uint32_t n = (uint32_t)lsRaw;
|
||||||
if (guess == 0) guess = 1;
|
uint32_t guess = n;
|
||||||
for (int i = 0; i < 12; i++) {
|
for (int i = 0; i < 16; i++) {
|
||||||
guess = (guess + sLen / guess) / 2;
|
if (guess == 0) break;
|
||||||
|
guess = (guess + n / guess) / 2;
|
||||||
}
|
}
|
||||||
if (guess == 0) guess = 1;
|
int32_t len = (int32_t)(guess * 64);
|
||||||
|
if (len == 0) len = 1;
|
||||||
|
|
||||||
PushVec3(lua, luaToFp(sx / guess), luaToFp(sy / guess), luaToFp(sz / guess));
|
// Divide each component by length: component / length in fp12
|
||||||
|
// (x.raw * 4096) / len using 32-bit math (safe since raw values fit int16 range)
|
||||||
|
psyqo::FixedPoint<12> nx, ny, nz;
|
||||||
|
nx.value = (x.raw() * 4096) / len;
|
||||||
|
ny.value = (y.raw() * 4096) / len;
|
||||||
|
nz.value = (z.raw() * 4096) / len;
|
||||||
|
PushVec3(lua, nx, ny, nz);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -890,7 +923,7 @@ int LuaAPI::Vec3_DistanceSq(lua_State* L) {
|
|||||||
auto dz = az - bz;
|
auto dz = az - bz;
|
||||||
|
|
||||||
auto distSq = dx * dx + dy * dy + dz * dz;
|
auto distSq = dx * dx + dy * dy + dz * dz;
|
||||||
lua.pushNumber(fpToLua(distSq));
|
lua.push(distSq);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -908,24 +941,28 @@ int LuaAPI::Vec3_Distance(lua_State* L) {
|
|||||||
ReadVec3(lua, 1, ax, ay, az);
|
ReadVec3(lua, 1, ax, ay, az);
|
||||||
ReadVec3(lua, 2, bx, by, bz);
|
ReadVec3(lua, 2, bx, by, bz);
|
||||||
|
|
||||||
lua_Number dx = fpToLua(ax) - fpToLua(bx);
|
auto dx = ax - bx;
|
||||||
lua_Number dy = fpToLua(ay) - fpToLua(by);
|
auto dy = ay - by;
|
||||||
lua_Number dz = fpToLua(az) - fpToLua(bz);
|
auto dz = az - bz;
|
||||||
|
|
||||||
lua_Number sqVal = dx * dx + dy * dy + dz * dz;
|
auto distSq = dx * dx + dy * dy + dz * dz;
|
||||||
|
int32_t dsRaw = distSq.raw();
|
||||||
|
|
||||||
if (sqVal <= 0) {
|
if (dsRaw <= 0) {
|
||||||
lua.pushNumber(0);
|
lua.push(psyqo::FixedPoint<12>());
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
lua_Number guess = sqVal / 2;
|
uint32_t n = (uint32_t)dsRaw;
|
||||||
if (guess == 0) guess = 1;
|
uint32_t guess = n;
|
||||||
for (int i = 0; i < 12; i++) {
|
for (int i = 0; i < 16; i++) {
|
||||||
guess = (guess + sqVal / guess) / 2;
|
if (guess == 0) break;
|
||||||
|
guess = (guess + n / guess) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
lua.pushNumber(guess);
|
psyqo::FixedPoint<12> result;
|
||||||
|
result.value = (int32_t)(guess * 64);
|
||||||
|
lua.push(result);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -943,7 +980,7 @@ int LuaAPI::Vec3_Lerp(lua_State* L) {
|
|||||||
ReadVec3(lua, 1, ax, ay, az);
|
ReadVec3(lua, 1, ax, ay, az);
|
||||||
ReadVec3(lua, 2, bx, by, bz);
|
ReadVec3(lua, 2, bx, by, bz);
|
||||||
|
|
||||||
psyqo::FixedPoint<12> t = luaToFp(lua.toNumber(3));
|
psyqo::FixedPoint<12> t = readFP(lua, 3);
|
||||||
psyqo::FixedPoint<12> oneMinusT = psyqo::FixedPoint<12>(4096, psyqo::FixedPoint<12>::RAW) - t;
|
psyqo::FixedPoint<12> oneMinusT = psyqo::FixedPoint<12>(4096, psyqo::FixedPoint<12>::RAW) - t;
|
||||||
|
|
||||||
psyqo::FixedPoint<12> rx = ax * oneMinusT + bx * t;
|
psyqo::FixedPoint<12> rx = ax * oneMinusT + bx * t;
|
||||||
@@ -1158,25 +1195,27 @@ int LuaAPI::Camera_LookAt(lua_State* L) {
|
|||||||
if (lua.isTable(1)) {
|
if (lua.isTable(1)) {
|
||||||
ReadVec3(lua, 1, tx, ty, tz);
|
ReadVec3(lua, 1, tx, ty, tz);
|
||||||
} else {
|
} else {
|
||||||
tx = luaToFp(lua.optNumber(1, 0));
|
tx = lua.isNoneOrNil(1) ? psyqo::FixedPoint<12>() : readFP(lua, 1);
|
||||||
ty = luaToFp(lua.optNumber(2, 0));
|
ty = lua.isNoneOrNil(2) ? psyqo::FixedPoint<12>() : readFP(lua, 2);
|
||||||
tz = luaToFp(lua.optNumber(3, 0));
|
tz = lua.isNoneOrNil(3) ? psyqo::FixedPoint<12>() : readFP(lua, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& cam = s_sceneManager->getCamera();
|
auto& cam = s_sceneManager->getCamera();
|
||||||
auto& pos = cam.GetPosition();
|
auto& pos = cam.GetPosition();
|
||||||
|
|
||||||
// Compute direction vector from camera to target
|
// Compute direction vector from camera to target
|
||||||
lua_Number dx = fpToLua(tx) - fpToLua(pos.x);
|
auto dx = tx - pos.x;
|
||||||
lua_Number dy = fpToLua(ty) - fpToLua(pos.y);
|
auto dy = ty - pos.y;
|
||||||
lua_Number dz = fpToLua(tz) - fpToLua(pos.z);
|
auto dz = tz - pos.z;
|
||||||
|
|
||||||
// Compute horizontal distance for pitch calculation
|
// Compute horizontal distance for pitch calculation
|
||||||
lua_Number horizDistSq = dx * dx + dz * dz;
|
auto horizDistSq = dx * dx + dz * dz;
|
||||||
lua_Number horizGuess = horizDistSq / 2;
|
int32_t hdsRaw = horizDistSq.raw();
|
||||||
if (horizGuess == 0) horizGuess = 1;
|
uint32_t hn = (uint32_t)(hdsRaw > 0 ? hdsRaw : 1);
|
||||||
for (int i = 0; i < 12; i++) {
|
uint32_t horizGuess = hn;
|
||||||
horizGuess = (horizGuess + horizDistSq / horizGuess) / 2;
|
for (int i = 0; i < 16; i++) {
|
||||||
|
if (horizGuess == 0) break;
|
||||||
|
horizGuess = (horizGuess + hn / horizGuess) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Yaw = atan2(dx, dz) — approximate with lookup or use psyqo trig
|
// Yaw = atan2(dx, dz) — approximate with lookup or use psyqo trig
|
||||||
@@ -1554,6 +1593,67 @@ int LuaAPI::Cutscene_IsPlaying(lua_State* L) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// ANIMATION API IMPLEMENTATION
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
int LuaAPI::Animation_Play(lua_State* L) {
|
||||||
|
psyqo::Lua lua(L);
|
||||||
|
|
||||||
|
if (!s_animationPlayer || !lua.isString(1)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* name = lua.toString(1);
|
||||||
|
bool loop = false;
|
||||||
|
int onCompleteRef = LUA_NOREF;
|
||||||
|
|
||||||
|
if (lua.isTable(2)) {
|
||||||
|
lua.getField(2, "loop");
|
||||||
|
if (lua.isBoolean(-1)) loop = lua.toBoolean(-1);
|
||||||
|
lua.pop();
|
||||||
|
|
||||||
|
lua.getField(2, "onComplete");
|
||||||
|
if (lua.isFunction(-1)) {
|
||||||
|
onCompleteRef = lua.ref(); // pops and stores in registry
|
||||||
|
} else {
|
||||||
|
lua.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s_animationPlayer->setLuaState(L);
|
||||||
|
s_animationPlayer->play(name, loop);
|
||||||
|
|
||||||
|
if (onCompleteRef != LUA_NOREF) {
|
||||||
|
s_animationPlayer->setOnCompleteRef(name, onCompleteRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int LuaAPI::Animation_Stop(lua_State* L) {
|
||||||
|
psyqo::Lua lua(L);
|
||||||
|
if (!s_animationPlayer) return 0;
|
||||||
|
|
||||||
|
if (lua.isString(1)) {
|
||||||
|
s_animationPlayer->stop(lua.toString(1));
|
||||||
|
} else {
|
||||||
|
s_animationPlayer->stopAll();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int LuaAPI::Animation_IsPlaying(lua_State* L) {
|
||||||
|
psyqo::Lua lua(L);
|
||||||
|
|
||||||
|
if (s_animationPlayer && lua.isString(1)) {
|
||||||
|
lua.push(s_animationPlayer->isPlaying(lua.toString(1)));
|
||||||
|
} else {
|
||||||
|
lua.push(false);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// CONTROLS API IMPLEMENTATION
|
// CONTROLS API IMPLEMENTATION
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ namespace psxsplash {
|
|||||||
|
|
||||||
class SceneManager; // Forward declaration
|
class SceneManager; // Forward declaration
|
||||||
class CutscenePlayer; // Forward declaration
|
class CutscenePlayer; // Forward declaration
|
||||||
|
class AnimationPlayer; // Forward declaration
|
||||||
class UISystem; // Forward declaration
|
class UISystem; // Forward declaration
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -25,7 +26,7 @@ class UISystem; // Forward declaration
|
|||||||
class LuaAPI {
|
class LuaAPI {
|
||||||
public:
|
public:
|
||||||
// Initialize all API modules
|
// Initialize all API modules
|
||||||
static void RegisterAll(psyqo::Lua& L, SceneManager* scene, CutscenePlayer* cutscenePlayer = nullptr, UISystem* uiSystem = nullptr);
|
static void RegisterAll(psyqo::Lua& L, SceneManager* scene, CutscenePlayer* cutscenePlayer = nullptr, AnimationPlayer* animationPlayer = nullptr, UISystem* uiSystem = nullptr);
|
||||||
|
|
||||||
// Called once per frame to advance the Lua frame counter
|
// Called once per frame to advance the Lua frame counter
|
||||||
static void IncrementFrameCount();
|
static void IncrementFrameCount();
|
||||||
@@ -40,6 +41,9 @@ private:
|
|||||||
// Cutscene player pointer (set during RegisterAll)
|
// Cutscene player pointer (set during RegisterAll)
|
||||||
static CutscenePlayer* s_cutscenePlayer;
|
static CutscenePlayer* s_cutscenePlayer;
|
||||||
|
|
||||||
|
// Animation player pointer (set during RegisterAll)
|
||||||
|
static AnimationPlayer* s_animationPlayer;
|
||||||
|
|
||||||
// UI system pointer (set during RegisterAll)
|
// UI system pointer (set during RegisterAll)
|
||||||
static UISystem* s_uiSystem;
|
static UISystem* s_uiSystem;
|
||||||
|
|
||||||
@@ -271,6 +275,19 @@ private:
|
|||||||
// Cutscene.IsPlaying() -> boolean
|
// Cutscene.IsPlaying() -> boolean
|
||||||
static int Cutscene_IsPlaying(lua_State* L);
|
static int Cutscene_IsPlaying(lua_State* L);
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// ANIMATION API - Multi-instance animation playback
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
// Animation.Play(name) or Animation.Play(name, {loop=bool, onComplete=fn})
|
||||||
|
static int Animation_Play(lua_State* L);
|
||||||
|
|
||||||
|
// Animation.Stop(name) -> nil
|
||||||
|
static int Animation_Stop(lua_State* L);
|
||||||
|
|
||||||
|
// Animation.IsPlaying(name) -> boolean
|
||||||
|
static int Animation_IsPlaying(lua_State* L);
|
||||||
|
|
||||||
// Controls.SetEnabled(bool) - enable/disable all player input
|
// Controls.SetEnabled(bool) - enable/disable all player input
|
||||||
static int Controls_SetEnabled(lua_State* L);
|
static int Controls_SetEnabled(lua_State* L);
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ void psxsplash::SceneManager::InitializeScene(uint8_t* splashpackData, LoadingSc
|
|||||||
m_audio.init();
|
m_audio.init();
|
||||||
|
|
||||||
// Register the Lua API
|
// Register the Lua API
|
||||||
LuaAPI::RegisterAll(L.getState(), this, &m_cutscenePlayer, &m_uiSystem);
|
LuaAPI::RegisterAll(L.getState(), this, &m_cutscenePlayer, &m_animationPlayer, &m_uiSystem);
|
||||||
|
|
||||||
#ifdef PSXSPLASH_PROFILER
|
#ifdef PSXSPLASH_PROFILER
|
||||||
debug::Profiler::getInstance().initialize();
|
debug::Profiler::getInstance().initialize();
|
||||||
@@ -113,6 +113,19 @@ void psxsplash::SceneManager::InitializeScene(uint8_t* splashpackData, LoadingSc
|
|||||||
&m_uiSystem
|
&m_uiSystem
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Copy animation data into scene manager storage
|
||||||
|
m_animationCount = sceneSetup.animationCount;
|
||||||
|
for (int i = 0; i < m_animationCount; i++) {
|
||||||
|
m_animations[i] = sceneSetup.loadedAnimations[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize animation player
|
||||||
|
m_animationPlayer.init(
|
||||||
|
m_animationCount > 0 ? m_animations : nullptr,
|
||||||
|
m_animationCount,
|
||||||
|
&m_uiSystem
|
||||||
|
);
|
||||||
|
|
||||||
// Initialize UI system (v13+)
|
// Initialize UI system (v13+)
|
||||||
if (sceneSetup.uiCanvasCount > 0 && sceneSetup.uiTableOffset != 0 && s_font != nullptr) {
|
if (sceneSetup.uiCanvasCount > 0 && sceneSetup.uiTableOffset != 0 && s_font != nullptr) {
|
||||||
m_uiSystem.init(*s_font);
|
m_uiSystem.init(*s_font);
|
||||||
@@ -157,6 +170,35 @@ void psxsplash::SceneManager::InitializeScene(uint8_t* splashpackData, LoadingSc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolve UI track handles for animation tracks (same logic)
|
||||||
|
for (int ai = 0; ai < m_animationCount; ai++) {
|
||||||
|
for (uint8_t ti = 0; ti < m_animations[ai].trackCount; ti++) {
|
||||||
|
auto& track = m_animations[ai].tracks[ti];
|
||||||
|
bool isUI = static_cast<uint8_t>(track.trackType) >= 5;
|
||||||
|
if (!isUI || track.target == nullptr) continue;
|
||||||
|
|
||||||
|
const char* nameStr = reinterpret_cast<const char*>(track.target);
|
||||||
|
track.target = nullptr;
|
||||||
|
|
||||||
|
if (track.trackType == TrackType::UICanvasVisible) {
|
||||||
|
track.uiHandle = static_cast<int16_t>(m_uiSystem.findCanvas(nameStr));
|
||||||
|
} else {
|
||||||
|
const char* sep = nameStr;
|
||||||
|
while (*sep && *sep != '/') sep++;
|
||||||
|
if (*sep == '/') {
|
||||||
|
char* mutableSep = const_cast<char*>(sep);
|
||||||
|
*mutableSep = '\0';
|
||||||
|
int canvasIdx = m_uiSystem.findCanvas(nameStr);
|
||||||
|
*mutableSep = '/';
|
||||||
|
if (canvasIdx >= 0) {
|
||||||
|
track.uiHandle = static_cast<int16_t>(
|
||||||
|
m_uiSystem.findElement(canvasIdx, sep + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Renderer::GetInstance().SetUISystem(nullptr);
|
Renderer::GetInstance().SetUISystem(nullptr);
|
||||||
}
|
}
|
||||||
@@ -244,6 +286,14 @@ void psxsplash::SceneManager::InitializeScene(uint8_t* splashpackData, LoadingSc
|
|||||||
L.RegisterGameObject(object);
|
L.RegisterGameObject(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fire all onCreate events AFTER all objects are registered,
|
||||||
|
// so Entity.Find works across all objects in onCreate handlers.
|
||||||
|
if (!m_gameObjects.empty()) {
|
||||||
|
L.FireAllOnCreate(
|
||||||
|
reinterpret_cast<GameObject**>(m_gameObjects.data()),
|
||||||
|
m_gameObjects.size());
|
||||||
|
}
|
||||||
|
|
||||||
m_controls.forceAnalogMode();
|
m_controls.forceAnalogMode();
|
||||||
m_controls.Init();
|
m_controls.Init();
|
||||||
Renderer::GetInstance().SetCamera(m_currentCamera);
|
Renderer::GetInstance().SetCamera(m_currentCamera);
|
||||||
@@ -262,6 +312,7 @@ void psxsplash::SceneManager::GameTick(psyqo::GPU &gpu) {
|
|||||||
LuaAPI::IncrementFrameCount();
|
LuaAPI::IncrementFrameCount();
|
||||||
|
|
||||||
m_cutscenePlayer.tick();
|
m_cutscenePlayer.tick();
|
||||||
|
m_animationPlayer.tick();
|
||||||
|
|
||||||
{
|
{
|
||||||
uint32_t now = gpu.now();
|
uint32_t now = gpu.now();
|
||||||
@@ -279,7 +330,7 @@ void psxsplash::SceneManager::GameTick(psyqo::GPU &gpu) {
|
|||||||
|
|
||||||
int camRoom = -1;
|
int camRoom = -1;
|
||||||
if (m_navRegions.isLoaded()) {
|
if (m_navRegions.isLoaded()) {
|
||||||
if (m_cutscenePlayer.isPlaying()) {
|
if (m_cutscenePlayer.isPlaying() && m_cutscenePlayer.hasCameraTracks()) {
|
||||||
auto& camPos = m_currentCamera.GetPosition();
|
auto& camPos = m_currentCamera.GetPosition();
|
||||||
uint16_t camRegion = m_navRegions.findRegion(camPos.x.value, camPos.z.value);
|
uint16_t camRegion = m_navRegions.findRegion(camPos.x.value, camPos.z.value);
|
||||||
if (camRegion != NAV_NO_REGION) {
|
if (camRegion != NAV_NO_REGION) {
|
||||||
@@ -478,7 +529,7 @@ void psxsplash::SceneManager::GameTick(psyqo::GPU &gpu) {
|
|||||||
// (no nav regions / no PSXPlayer), the camera is driven entirely
|
// (no nav regions / no PSXPlayer), the camera is driven entirely
|
||||||
// by cutscenes and Lua. After a cutscene ends in free mode, the
|
// by cutscenes and Lua. After a cutscene ends in free mode, the
|
||||||
// camera stays at the last cutscene position.
|
// camera stays at the last cutscene position.
|
||||||
if (m_cameraFollowsPlayer && !m_cutscenePlayer.isPlaying()) {
|
if (m_cameraFollowsPlayer && !(m_cutscenePlayer.isPlaying() && m_cutscenePlayer.hasCameraTracks())) {
|
||||||
m_currentCamera.SetPosition(static_cast<psyqo::FixedPoint<12>>(m_playerPosition.x),
|
m_currentCamera.SetPosition(static_cast<psyqo::FixedPoint<12>>(m_playerPosition.x),
|
||||||
static_cast<psyqo::FixedPoint<12>>(m_playerPosition.y),
|
static_cast<psyqo::FixedPoint<12>>(m_playerPosition.y),
|
||||||
static_cast<psyqo::FixedPoint<12>>(m_playerPosition.z));
|
static_cast<psyqo::FixedPoint<12>>(m_playerPosition.z));
|
||||||
@@ -647,7 +698,6 @@ void psxsplash::SceneManager::processEnableDisableEvents() {
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
void psxsplash::SceneManager::requestSceneLoad(int sceneIndex) {
|
void psxsplash::SceneManager::requestSceneLoad(int sceneIndex) {
|
||||||
if (sceneIndex == m_currentSceneIndex) return;
|
|
||||||
m_pendingSceneIndex = sceneIndex;
|
m_pendingSceneIndex = sceneIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -763,6 +813,8 @@ void psxsplash::SceneManager::clearScene() {
|
|||||||
m_cutsceneCount = 0;
|
m_cutsceneCount = 0;
|
||||||
s_activePromptCanvas = -1; // Reset prompt tracking
|
s_activePromptCanvas = -1; // Reset prompt tracking
|
||||||
m_cutscenePlayer.init(nullptr, 0, nullptr, nullptr); // Reset cutscene player
|
m_cutscenePlayer.init(nullptr, 0, nullptr, nullptr); // Reset cutscene player
|
||||||
|
m_animationCount = 0;
|
||||||
|
m_animationPlayer.init(nullptr, 0); // Reset animation player
|
||||||
// BVH and NavRegions will be overwritten by next load
|
// BVH and NavRegions will be overwritten by next load
|
||||||
|
|
||||||
// Reset UI system (disconnect from renderer before splashpack data disappears)
|
// Reset UI system (disconnect from renderer before splashpack data disappears)
|
||||||
@@ -825,6 +877,16 @@ void psxsplash::SceneManager::shrinkBuffer() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int ai = 0; ai < m_animationCount; ai++) {
|
||||||
|
auto& an = m_animations[ai];
|
||||||
|
an.name = reloc(an.name);
|
||||||
|
for (uint8_t ti = 0; ti < an.trackCount; ti++) {
|
||||||
|
auto& track = an.tracks[ti];
|
||||||
|
track.keyframes = reloc(track.keyframes);
|
||||||
|
if (track.target) track.target = reloc(track.target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m_uiSystem.relocate(delta);
|
m_uiSystem.relocate(delta);
|
||||||
|
|
||||||
if (!m_gameObjects.empty()) {
|
if (!m_gameObjects.empty()) {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#include "luaapi.hh"
|
#include "luaapi.hh"
|
||||||
#include "fileloader.hh"
|
#include "fileloader.hh"
|
||||||
#include "cutscene.hh"
|
#include "cutscene.hh"
|
||||||
|
#include "animation.hh"
|
||||||
#include "uisystem.hh"
|
#include "uisystem.hh"
|
||||||
#ifdef PSXSPLASH_MEMOVERLAY
|
#ifdef PSXSPLASH_MEMOVERLAY
|
||||||
#include "memoverlay.hh"
|
#include "memoverlay.hh"
|
||||||
@@ -147,6 +148,10 @@ class SceneManager {
|
|||||||
int m_cutsceneCount = 0;
|
int m_cutsceneCount = 0;
|
||||||
CutscenePlayer m_cutscenePlayer;
|
CutscenePlayer m_cutscenePlayer;
|
||||||
|
|
||||||
|
Animation m_animations[MAX_ANIMATIONS];
|
||||||
|
int m_animationCount = 0;
|
||||||
|
AnimationPlayer m_animationPlayer;
|
||||||
|
|
||||||
UISystem m_uiSystem;
|
UISystem m_uiSystem;
|
||||||
#ifdef PSXSPLASH_MEMOVERLAY
|
#ifdef PSXSPLASH_MEMOVERLAY
|
||||||
MemOverlay m_memOverlay;
|
MemOverlay m_memOverlay;
|
||||||
|
|||||||
@@ -64,8 +64,11 @@ struct SPLASHPACKFileHeader {
|
|||||||
uint8_t uiPad5;
|
uint8_t uiPad5;
|
||||||
uint32_t uiTableOffset;
|
uint32_t uiTableOffset;
|
||||||
uint32_t pixelDataOffset;
|
uint32_t pixelDataOffset;
|
||||||
|
uint16_t animationCount;
|
||||||
|
uint16_t animPad;
|
||||||
|
uint32_t animationTableOffset;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(SPLASHPACKFileHeader) == 104, "SPLASHPACKFileHeader must be 104 bytes");
|
static_assert(sizeof(SPLASHPACKFileHeader) == 112, "SPLASHPACKFileHeader must be 112 bytes");
|
||||||
|
|
||||||
struct SPLASHPACKTextureAtlas {
|
struct SPLASHPACKTextureAtlas {
|
||||||
uint32_t polygonsOffset;
|
uint32_t polygonsOffset;
|
||||||
@@ -85,7 +88,7 @@ void SplashPackLoader::LoadSplashpack(uint8_t *data, SplashpackSceneSetup &setup
|
|||||||
psyqo::Kernel::assert(data != nullptr, "Splashpack loading data pointer is null");
|
psyqo::Kernel::assert(data != nullptr, "Splashpack loading data pointer is null");
|
||||||
psxsplash::SPLASHPACKFileHeader *header = reinterpret_cast<psxsplash::SPLASHPACKFileHeader *>(data);
|
psxsplash::SPLASHPACKFileHeader *header = reinterpret_cast<psxsplash::SPLASHPACKFileHeader *>(data);
|
||||||
psyqo::Kernel::assert(__builtin_memcmp(header->magic, "SP", 2) == 0, "Splashpack has incorrect magic");
|
psyqo::Kernel::assert(__builtin_memcmp(header->magic, "SP", 2) == 0, "Splashpack has incorrect magic");
|
||||||
psyqo::Kernel::assert(header->version >= 16, "Splashpack version too old (need v16+): re-export from SplashEdit");
|
psyqo::Kernel::assert(header->version >= 17, "Splashpack version too old (need v17+): re-export from SplashEdit");
|
||||||
|
|
||||||
setup.playerStartPosition = header->playerStartPos;
|
setup.playerStartPosition = header->playerStartPos;
|
||||||
setup.playerStartRotation = header->playerStartRot;
|
setup.playerStartRotation = header->playerStartRot;
|
||||||
@@ -364,6 +367,84 @@ void SplashPackLoader::LoadSplashpack(uint8_t *data, SplashpackSceneSetup &setup
|
|||||||
setup.uiTableOffset = header->uiTableOffset;
|
setup.uiTableOffset = header->uiTableOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Animation loading (v17+)
|
||||||
|
if (header->animationCount > 0 && header->animationTableOffset != 0) {
|
||||||
|
setup.animationCount = 0;
|
||||||
|
uint8_t* tablePtr = data + header->animationTableOffset;
|
||||||
|
int anCount = header->animationCount;
|
||||||
|
if (anCount > MAX_ANIMATIONS) anCount = MAX_ANIMATIONS;
|
||||||
|
|
||||||
|
for (int ai = 0; ai < anCount; ai++) {
|
||||||
|
// SPLASHPACKAnimationEntry: 12 bytes (same layout as cutscene entry)
|
||||||
|
uint32_t dataOffset = *reinterpret_cast<uint32_t*>(tablePtr); tablePtr += 4;
|
||||||
|
uint8_t nameLen = *tablePtr++;
|
||||||
|
tablePtr += 3; // pad
|
||||||
|
uint32_t nameOffset = *reinterpret_cast<uint32_t*>(tablePtr); tablePtr += 4;
|
||||||
|
|
||||||
|
Animation& an = setup.loadedAnimations[ai];
|
||||||
|
an.name = (nameLen > 0 && nameOffset != 0)
|
||||||
|
? reinterpret_cast<const char*>(data + nameOffset)
|
||||||
|
: nullptr;
|
||||||
|
|
||||||
|
// SPLASHPACKAnimation: 8 bytes (no audio)
|
||||||
|
uint8_t* anPtr = data + dataOffset;
|
||||||
|
an.totalFrames = *reinterpret_cast<uint16_t*>(anPtr); anPtr += 2;
|
||||||
|
an.trackCount = *anPtr++;
|
||||||
|
an.pad = *anPtr++;
|
||||||
|
uint32_t tracksOff = *reinterpret_cast<uint32_t*>(anPtr); anPtr += 4;
|
||||||
|
|
||||||
|
if (an.trackCount > MAX_ANIM_TRACKS) an.trackCount = MAX_ANIM_TRACKS;
|
||||||
|
|
||||||
|
// Parse tracks (same format as cutscene tracks)
|
||||||
|
uint8_t* trackPtr = data + tracksOff;
|
||||||
|
for (uint8_t ti = 0; ti < an.trackCount; ti++) {
|
||||||
|
CutsceneTrack& track = an.tracks[ti];
|
||||||
|
|
||||||
|
track.trackType = static_cast<TrackType>(*trackPtr++);
|
||||||
|
track.keyframeCount = *trackPtr++;
|
||||||
|
uint8_t objNameLen = *trackPtr++;
|
||||||
|
trackPtr++; // pad
|
||||||
|
uint32_t objNameOff = *reinterpret_cast<uint32_t*>(trackPtr); trackPtr += 4;
|
||||||
|
uint32_t kfOff = *reinterpret_cast<uint32_t*>(trackPtr); trackPtr += 4;
|
||||||
|
|
||||||
|
track.keyframes = (track.keyframeCount > 0 && kfOff != 0)
|
||||||
|
? reinterpret_cast<CutsceneKeyframe*>(data + kfOff)
|
||||||
|
: nullptr;
|
||||||
|
|
||||||
|
track.target = nullptr;
|
||||||
|
track.uiHandle = -1;
|
||||||
|
if (objNameLen > 0 && objNameOff != 0) {
|
||||||
|
const char* objName = reinterpret_cast<const char*>(data + objNameOff);
|
||||||
|
bool isUITrack = static_cast<uint8_t>(track.trackType) >= 5;
|
||||||
|
if (isUITrack) {
|
||||||
|
track.target = reinterpret_cast<GameObject*>(const_cast<char*>(objName));
|
||||||
|
} else {
|
||||||
|
for (size_t oi = 0; oi < setup.objectNames.size(); oi++) {
|
||||||
|
if (setup.objectNames[oi] &&
|
||||||
|
streq(setup.objectNames[oi], objName)) {
|
||||||
|
track.target = setup.objects[oi];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zero unused track slots
|
||||||
|
for (uint8_t ti = an.trackCount; ti < MAX_ANIM_TRACKS; ti++) {
|
||||||
|
an.tracks[ti].keyframeCount = 0;
|
||||||
|
an.tracks[ti].keyframes = nullptr;
|
||||||
|
an.tracks[ti].target = nullptr;
|
||||||
|
an.tracks[ti].uiHandle = -1;
|
||||||
|
an.tracks[ti].initialValues[0] = 0;
|
||||||
|
an.tracks[ti].initialValues[1] = 0;
|
||||||
|
an.tracks[ti].initialValues[2] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
setup.animationCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setup.liveDataSize = header->pixelDataOffset;
|
setup.liveDataSize = header->pixelDataOffset;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "audiomanager.hh"
|
#include "audiomanager.hh"
|
||||||
#include "interactable.hh"
|
#include "interactable.hh"
|
||||||
#include "cutscene.hh"
|
#include "cutscene.hh"
|
||||||
|
#include "animation.hh"
|
||||||
#include "uisystem.hh"
|
#include "uisystem.hh"
|
||||||
|
|
||||||
namespace psxsplash {
|
namespace psxsplash {
|
||||||
@@ -94,6 +95,9 @@ struct SplashpackSceneSetup {
|
|||||||
Cutscene loadedCutscenes[MAX_CUTSCENES];
|
Cutscene loadedCutscenes[MAX_CUTSCENES];
|
||||||
int cutsceneCount = 0;
|
int cutsceneCount = 0;
|
||||||
|
|
||||||
|
Animation loadedAnimations[MAX_ANIMATIONS];
|
||||||
|
int animationCount = 0;
|
||||||
|
|
||||||
uint16_t uiCanvasCount = 0;
|
uint16_t uiCanvasCount = 0;
|
||||||
uint8_t uiFontCount = 0;
|
uint8_t uiFontCount = 0;
|
||||||
uint32_t uiTableOffset = 0;
|
uint32_t uiTableOffset = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user