Testing cutscenes
This commit is contained in:
@@ -0,0 +1,34 @@
|
|||||||
|
var PHASE_LEFT = 0;
|
||||||
|
var PHASE_RIGHT = 1;
|
||||||
|
|
||||||
|
var SPEED = 3.0;
|
||||||
|
var DURATION = 2.0;
|
||||||
|
|
||||||
|
function MoveCubeCutscene(params) {
|
||||||
|
Cutscene.call(this);
|
||||||
|
this.cube = params.cube;
|
||||||
|
this.phase = PHASE_LEFT;
|
||||||
|
this.timer = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
MoveCubeCutscene.prototype = Object.create(Cutscene.prototype);
|
||||||
|
MoveCubeCutscene.prototype.constructor = MoveCubeCutscene;
|
||||||
|
|
||||||
|
MoveCubeCutscene.prototype.update = function() {
|
||||||
|
this.timer += TIME.delta;
|
||||||
|
|
||||||
|
if(this.phase === PHASE_LEFT) {
|
||||||
|
this.cube.position.position.x -= SPEED * TIME.delta;
|
||||||
|
if(this.timer >= DURATION) {
|
||||||
|
this.phase = PHASE_RIGHT;
|
||||||
|
this.timer = 0.0;
|
||||||
|
}
|
||||||
|
} else if(this.phase === PHASE_RIGHT) {
|
||||||
|
this.cube.position.position.x += SPEED * TIME.delta;
|
||||||
|
if(this.timer >= DURATION) {
|
||||||
|
Cutscene.finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module = MoveCubeCutscene;
|
||||||
+11
-1
@@ -1,4 +1,5 @@
|
|||||||
var CubeEntity = include('entities/CubeEntity.js');
|
var CubeEntity = include('entities/CubeEntity.js');
|
||||||
|
var MoveCubeCutscene = include('cutscenes/MoveCubeCutscene.js');
|
||||||
|
|
||||||
function CubeScene() {
|
function CubeScene() {
|
||||||
this.cam = new Entity();
|
this.cam = new Entity();
|
||||||
@@ -13,17 +14,26 @@ function CubeScene() {
|
|||||||
this.spriteEnt = new Entity();
|
this.spriteEnt = new Entity();
|
||||||
this.spriteEnt.add(POSITION);
|
this.spriteEnt.add(POSITION);
|
||||||
this.spriteEnt.position.position = new Vec3(16, 16, 0);
|
this.spriteEnt.position.position = new Vec3(16, 16, 0);
|
||||||
// this.spriteEnt.sprite.setTexture('ui/minogram.png');
|
|
||||||
|
this.inputEnabled = false;
|
||||||
|
|
||||||
|
var scene = this;
|
||||||
|
Cutscene.play(new MoveCubeCutscene({ cube: this.cube })).then(function() {
|
||||||
|
scene.inputEnabled = true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
CubeScene.prototype = Object.create(Scene.prototype);
|
CubeScene.prototype = Object.create(Scene.prototype);
|
||||||
CubeScene.prototype.constructor = CubeScene;
|
CubeScene.prototype.constructor = CubeScene;
|
||||||
|
|
||||||
CubeScene.prototype.update = function() {
|
CubeScene.prototype.update = function() {
|
||||||
|
if(this.inputEnabled) {
|
||||||
this.cube.update();
|
this.cube.update();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
CubeScene.prototype.dispose = function() {
|
CubeScene.prototype.dispose = function() {
|
||||||
|
Cutscene.stop();
|
||||||
this.cam.dispose();
|
this.cam.dispose();
|
||||||
this.cube.dispose();
|
this.cube.dispose();
|
||||||
this.spriteEnt.dispose();
|
this.spriteEnt.dispose();
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ target_sources(${DUSK_BINARY_TARGET_NAME}
|
|||||||
# Subdirs
|
# Subdirs
|
||||||
add_subdirectory(assert)
|
add_subdirectory(assert)
|
||||||
add_subdirectory(asset)
|
add_subdirectory(asset)
|
||||||
|
add_subdirectory(cutscene)
|
||||||
add_subdirectory(item)
|
add_subdirectory(item)
|
||||||
add_subdirectory(story)
|
add_subdirectory(story)
|
||||||
add_subdirectory(console)
|
add_subdirectory(console)
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
// Copyright (c) 2026 Dominic Masters
|
||||||
|
//
|
||||||
|
// This software is released under the MIT License.
|
||||||
|
// https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "keyframe.h"
|
||||||
|
#include "error/error.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
keyframe_t keyframes[ANIMATION_KEYFRAME_COUNT_MAX];
|
||||||
|
uint8_t keyframeCount;
|
||||||
|
float_t time;
|
||||||
|
float_t duration;
|
||||||
|
bool_t loop;
|
||||||
|
bool_t complete;
|
||||||
|
} animation_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeroes an animation struct.
|
||||||
|
*
|
||||||
|
* @param anim The animation to initialize.
|
||||||
|
*/
|
||||||
|
void animationInit(animation_t *anim);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends a keyframe. Keyframes must be added in ascending time order.
|
||||||
|
*
|
||||||
|
* @param anim The animation to modify.
|
||||||
|
* @param time Time in seconds for this keyframe.
|
||||||
|
* @param value Value at this keyframe.
|
||||||
|
* @param easing Easing applied to the segment after this keyframe.
|
||||||
|
*/
|
||||||
|
void animationAddKeyframe(
|
||||||
|
animation_t *anim,
|
||||||
|
float_t time,
|
||||||
|
float_t value,
|
||||||
|
easingtype_t easing
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Advances the animation by delta seconds and returns the current value.
|
||||||
|
*
|
||||||
|
* @param anim The animation to update.
|
||||||
|
* @param delta Seconds elapsed since last update.
|
||||||
|
* @return Interpolated value at the new time.
|
||||||
|
*/
|
||||||
|
float_t animationUpdate(animation_t *anim, float_t delta);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the interpolated value at the current time without advancing.
|
||||||
|
*
|
||||||
|
* @param anim The animation to read.
|
||||||
|
* @return Interpolated value.
|
||||||
|
*/
|
||||||
|
float_t animationGetValue(const animation_t *anim);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the animation to the beginning.
|
||||||
|
*
|
||||||
|
* @param anim The animation to reset.
|
||||||
|
*/
|
||||||
|
void animationReset(animation_t *anim);
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
// Copyright (c) 2026 Dominic Masters
|
||||||
|
//
|
||||||
|
// This software is released under the MIT License.
|
||||||
|
// https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
#include "easing.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#define EASING_PI 3.14159265358979323846f
|
||||||
|
#define EASING_C1 1.70158f
|
||||||
|
#define EASING_C2 (EASING_C1 * 1.525f)
|
||||||
|
#define EASING_C3 (EASING_C1 + 1.0f)
|
||||||
|
|
||||||
|
float_t easingLinear(float_t t) {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
float_t easingInSine(float_t t) {
|
||||||
|
return 1.0f - cosf(t * EASING_PI * 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
float_t easingOutSine(float_t t) {
|
||||||
|
return sinf(t * EASING_PI * 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
float_t easingInOutSine(float_t t) {
|
||||||
|
return -(cosf(EASING_PI * t) - 1.0f) * 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float_t easingInQuad(float_t t) {
|
||||||
|
return t * t;
|
||||||
|
}
|
||||||
|
|
||||||
|
float_t easingOutQuad(float_t t) {
|
||||||
|
float_t u = 1.0f - t;
|
||||||
|
return 1.0f - u * u;
|
||||||
|
}
|
||||||
|
|
||||||
|
float_t easingInOutQuad(float_t t) {
|
||||||
|
if(t < 0.5f) return 2.0f * t * t;
|
||||||
|
float_t u = -2.0f * t + 2.0f;
|
||||||
|
return 1.0f - u * u * 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float_t easingInCubic(float_t t) {
|
||||||
|
return t * t * t;
|
||||||
|
}
|
||||||
|
|
||||||
|
float_t easingOutCubic(float_t t) {
|
||||||
|
float_t u = 1.0f - t;
|
||||||
|
return 1.0f - u * u * u;
|
||||||
|
}
|
||||||
|
|
||||||
|
float_t easingInOutCubic(float_t t) {
|
||||||
|
if(t < 0.5f) return 4.0f * t * t * t;
|
||||||
|
float_t u = -2.0f * t + 2.0f;
|
||||||
|
return 1.0f - u * u * u * 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float_t easingInQuart(float_t t) {
|
||||||
|
return t * t * t * t;
|
||||||
|
}
|
||||||
|
|
||||||
|
float_t easingOutQuart(float_t t) {
|
||||||
|
float_t u = 1.0f - t;
|
||||||
|
return 1.0f - u * u * u * u;
|
||||||
|
}
|
||||||
|
|
||||||
|
float_t easingInOutQuart(float_t t) {
|
||||||
|
if(t < 0.5f) return 8.0f * t * t * t * t;
|
||||||
|
float_t u = -2.0f * t + 2.0f;
|
||||||
|
return 1.0f - u * u * u * u * 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float_t easingInBack(float_t t) {
|
||||||
|
return EASING_C3 * t * t * t - EASING_C1 * t * t;
|
||||||
|
}
|
||||||
|
|
||||||
|
float_t easingOutBack(float_t t) {
|
||||||
|
float_t u = t - 1.0f;
|
||||||
|
return 1.0f + EASING_C3 * u * u * u + EASING_C1 * u * u;
|
||||||
|
}
|
||||||
|
|
||||||
|
float_t easingInOutBack(float_t t) {
|
||||||
|
if(t < 0.5f) {
|
||||||
|
float_t u = 2.0f * t;
|
||||||
|
return u * u * ((EASING_C2 + 1.0f) * u - EASING_C2) * 0.5f;
|
||||||
|
}
|
||||||
|
float_t u = 2.0f * t - 2.0f;
|
||||||
|
return (u * u * ((EASING_C2 + 1.0f) * u + EASING_C2) + 2.0f) * 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
const easingfn_t EASING_FUNCTIONS[EASING_COUNT] = {
|
||||||
|
easingLinear,
|
||||||
|
easingInSine, easingOutSine, easingInOutSine,
|
||||||
|
easingInQuad, easingOutQuad, easingInOutQuad,
|
||||||
|
easingInCubic, easingOutCubic, easingInOutCubic,
|
||||||
|
easingInQuart, easingOutQuart, easingInOutQuart,
|
||||||
|
easingInBack, easingOutBack, easingInOutBack,
|
||||||
|
};
|
||||||
|
|
||||||
|
float_t easingApply(easingtype_t type, float_t t) {
|
||||||
|
assertTrue(type < EASING_COUNT, "Invalid easing type");
|
||||||
|
return EASING_FUNCTIONS[type](t);
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
// Copyright (c) 2026 Dominic Masters
|
||||||
|
//
|
||||||
|
// This software is released under the MIT License.
|
||||||
|
// https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "dusk.h"
|
||||||
|
|
||||||
|
#define EASING_LINEAR 0
|
||||||
|
#define EASING_IN_SINE 1
|
||||||
|
#define EASING_OUT_SINE 2
|
||||||
|
#define EASING_IN_OUT_SINE 3
|
||||||
|
#define EASING_IN_QUAD 4
|
||||||
|
#define EASING_OUT_QUAD 5
|
||||||
|
#define EASING_IN_OUT_QUAD 6
|
||||||
|
#define EASING_IN_CUBIC 7
|
||||||
|
#define EASING_OUT_CUBIC 8
|
||||||
|
#define EASING_IN_OUT_CUBIC 9
|
||||||
|
#define EASING_IN_QUART 10
|
||||||
|
#define EASING_OUT_QUART 11
|
||||||
|
#define EASING_IN_OUT_QUART 12
|
||||||
|
#define EASING_IN_BACK 13
|
||||||
|
#define EASING_OUT_BACK 14
|
||||||
|
#define EASING_IN_OUT_BACK 15
|
||||||
|
#define EASING_COUNT 16
|
||||||
|
|
||||||
|
typedef uint8_t easingtype_t;
|
||||||
|
typedef float_t (*easingfn_t)(float_t t);
|
||||||
|
|
||||||
|
extern const easingfn_t EASING_FUNCTIONS[EASING_COUNT];
|
||||||
|
|
||||||
|
float_t easingApply(easingtype_t type, float_t t);
|
||||||
|
|
||||||
|
float_t easingLinear(float_t t);
|
||||||
|
float_t easingInSine(float_t t);
|
||||||
|
float_t easingOutSine(float_t t);
|
||||||
|
float_t easingInOutSine(float_t t);
|
||||||
|
float_t easingInQuad(float_t t);
|
||||||
|
float_t easingOutQuad(float_t t);
|
||||||
|
float_t easingInOutQuad(float_t t);
|
||||||
|
float_t easingInCubic(float_t t);
|
||||||
|
float_t easingOutCubic(float_t t);
|
||||||
|
float_t easingInOutCubic(float_t t);
|
||||||
|
float_t easingInQuart(float_t t);
|
||||||
|
float_t easingOutQuart(float_t t);
|
||||||
|
float_t easingInOutQuart(float_t t);
|
||||||
|
float_t easingInBack(float_t t);
|
||||||
|
float_t easingOutBack(float_t t);
|
||||||
|
float_t easingInOutBack(float_t t);
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// Copyright (c) 2026 Dominic Masters
|
||||||
|
//
|
||||||
|
// This software is released under the MIT License.
|
||||||
|
// https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "easing.h"
|
||||||
|
|
||||||
|
#define ANIMATION_KEYFRAME_COUNT_MAX 16
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
float_t time;
|
||||||
|
float_t value;
|
||||||
|
easingtype_t easing;
|
||||||
|
} keyframe_t;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2026 Dominic Masters
|
||||||
|
#
|
||||||
|
# This software is released under the MIT License.
|
||||||
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||||
|
PUBLIC
|
||||||
|
cutscene.c
|
||||||
|
)
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
// Copyright (c) 2026 Dominic Masters
|
||||||
|
//
|
||||||
|
// This software is released under the MIT License.
|
||||||
|
// https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
#include "cutscene.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
#include "util/memory.h"
|
||||||
|
#include "script/module/modulebase.h"
|
||||||
|
#include "script/module/cutscene/modulecutscene.h"
|
||||||
|
#include "console/console.h"
|
||||||
|
|
||||||
|
cutscene_t CUTSCENE;
|
||||||
|
|
||||||
|
static errorret_t cutsceneCallMethod(const char_t *method) {
|
||||||
|
if(CUTSCENE.scriptRef == CUTSCENE_SCRIPT_REF_NONE) errorOk();
|
||||||
|
|
||||||
|
jerry_value_t key = jerry_string_sz(method);
|
||||||
|
jerry_value_t fn = jerry_object_get(CUTSCENE.scriptRef, key);
|
||||||
|
jerry_value_free(key);
|
||||||
|
|
||||||
|
if(!jerry_value_is_function(fn)) {
|
||||||
|
jerry_value_free(fn);
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
jerry_value_t result = jerry_call(fn, CUTSCENE.scriptRef, NULL, 0);
|
||||||
|
jerry_value_free(fn);
|
||||||
|
|
||||||
|
if(jerry_value_is_exception(result)) {
|
||||||
|
cutsceneevent_t *event = &CUTSCENE.events[CUTSCENE.eventCurrent];
|
||||||
|
char_t errMsg[512];
|
||||||
|
moduleBaseExceptionMessage(result, errMsg, sizeof(errMsg));
|
||||||
|
jerry_value_free(result);
|
||||||
|
errorThrow(
|
||||||
|
"Cutscene event '%s' %s failed: %s",
|
||||||
|
event->script.script, method, errMsg
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
jerry_value_free(result);
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
static errorret_t cutsceneEventStart(void) {
|
||||||
|
cutsceneevent_t *event = &CUTSCENE.events[CUTSCENE.eventCurrent];
|
||||||
|
|
||||||
|
if(event->type == CUTSCENE_EVENT_TYPE_NATIVE) {
|
||||||
|
if(event->native.onStart) errorChain(event->native.onStart());
|
||||||
|
} else if(event->type == CUTSCENE_EVENT_TYPE_SCRIPT) {
|
||||||
|
jerry_value_t eventClass = CUTSCENE_SCRIPT_REF_NONE;
|
||||||
|
errorChain(scriptManagerExecFile(event->script.script, &eventClass));
|
||||||
|
|
||||||
|
if(!jerry_value_is_function(eventClass)) {
|
||||||
|
if(eventClass != CUTSCENE_SCRIPT_REF_NONE) jerry_value_free(eventClass);
|
||||||
|
errorThrow(
|
||||||
|
"Cutscene event '%s' must export a constructor function",
|
||||||
|
event->script.script
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
jerry_value_t eventObj = jerry_construct(eventClass, NULL, 0);
|
||||||
|
jerry_value_free(eventClass);
|
||||||
|
|
||||||
|
if(jerry_value_is_exception(eventObj)) {
|
||||||
|
char_t errMsg[512];
|
||||||
|
moduleBaseExceptionMessage(eventObj, errMsg, sizeof(errMsg));
|
||||||
|
jerry_value_free(eventObj);
|
||||||
|
errorThrow(
|
||||||
|
"Cutscene event '%s' constructor threw: %s",
|
||||||
|
event->script.script, errMsg
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
CUTSCENE.scriptRef = eventObj;
|
||||||
|
errorChain(cutsceneCallMethod("onStart"));
|
||||||
|
}
|
||||||
|
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
static errorret_t cutsceneEventEnd(void) {
|
||||||
|
cutsceneevent_t *event = &CUTSCENE.events[CUTSCENE.eventCurrent];
|
||||||
|
|
||||||
|
if(event->type == CUTSCENE_EVENT_TYPE_NATIVE) {
|
||||||
|
if(event->native.onEnd) errorChain(event->native.onEnd());
|
||||||
|
} else if(event->type == CUTSCENE_EVENT_TYPE_SCRIPT) {
|
||||||
|
errorret_t err = cutsceneCallMethod("onEnd");
|
||||||
|
if(CUTSCENE.scriptRef != CUTSCENE_SCRIPT_REF_NONE) {
|
||||||
|
jerry_value_free(CUTSCENE.scriptRef);
|
||||||
|
CUTSCENE.scriptRef = CUTSCENE_SCRIPT_REF_NONE;
|
||||||
|
}
|
||||||
|
errorChain(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t cutsceneInit(void) {
|
||||||
|
memoryZero(&CUTSCENE, sizeof(cutscene_t));
|
||||||
|
CUTSCENE.scriptRef = CUTSCENE_SCRIPT_REF_NONE;
|
||||||
|
CUTSCENE.activeRef = CUTSCENE_SCRIPT_REF_NONE;
|
||||||
|
consolePrint("Cutscene init");
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t cutsceneUpdate(void) {
|
||||||
|
errorChain(moduleCutsceneUpdate());
|
||||||
|
|
||||||
|
if(!CUTSCENE.active) errorOk();
|
||||||
|
|
||||||
|
cutsceneevent_t *event = &CUTSCENE.events[CUTSCENE.eventCurrent];
|
||||||
|
|
||||||
|
if(event->type == CUTSCENE_EVENT_TYPE_NATIVE) {
|
||||||
|
if(event->native.onUpdate) errorChain(event->native.onUpdate());
|
||||||
|
} else if(event->type == CUTSCENE_EVENT_TYPE_SCRIPT) {
|
||||||
|
errorChain(cutsceneCallMethod("onUpdate"));
|
||||||
|
}
|
||||||
|
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t cutscenePlay(
|
||||||
|
const cutsceneevent_t *events,
|
||||||
|
const uint8_t eventCount
|
||||||
|
) {
|
||||||
|
assertNotNull(events, "Events cannot be null");
|
||||||
|
assertTrue(eventCount > 0, "Event count must be greater than zero");
|
||||||
|
assertTrue(
|
||||||
|
eventCount <= CUTSCENE_EVENT_COUNT_MAX,
|
||||||
|
"Event count exceeds CUTSCENE_EVENT_COUNT_MAX"
|
||||||
|
);
|
||||||
|
|
||||||
|
if(CUTSCENE.active) {
|
||||||
|
errorChain(cutsceneStop());
|
||||||
|
}
|
||||||
|
|
||||||
|
memoryCopy(CUTSCENE.events, events, sizeof(cutsceneevent_t) * eventCount);
|
||||||
|
CUTSCENE.eventCount = eventCount;
|
||||||
|
CUTSCENE.eventCurrent = 0;
|
||||||
|
CUTSCENE.active = true;
|
||||||
|
|
||||||
|
errorChain(cutsceneEventStart());
|
||||||
|
consolePrint("Cutscene play");
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t cutsceneAdvance(void) {
|
||||||
|
if(!CUTSCENE.active) errorOk();
|
||||||
|
|
||||||
|
errorChain(cutsceneEventEnd());
|
||||||
|
CUTSCENE.eventCurrent++;
|
||||||
|
|
||||||
|
if(CUTSCENE.eventCurrent >= CUTSCENE.eventCount) {
|
||||||
|
CUTSCENE.active = false;
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorChain(cutsceneEventStart());
|
||||||
|
consolePrint("Cutscene advance");
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t cutsceneStop(void) {
|
||||||
|
if(!CUTSCENE.active) errorOk();
|
||||||
|
|
||||||
|
errorChain(cutsceneEventEnd());
|
||||||
|
CUTSCENE.active = false;
|
||||||
|
consolePrint("Cutscene stop");
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t cutsceneDispose(void) {
|
||||||
|
errorChain(cutsceneStop());
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool_t cutsceneIsActive(void) {
|
||||||
|
return CUTSCENE.active;
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
// Copyright (c) 2026 Dominic Masters
|
||||||
|
//
|
||||||
|
// This software is released under the MIT License.
|
||||||
|
// https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "script/scriptmanager.h"
|
||||||
|
#include "error/error.h"
|
||||||
|
|
||||||
|
#define CUTSCENE_EVENT_COUNT_MAX 16
|
||||||
|
#define CUTSCENE_SCRIPT_REF_NONE ((jerry_value_t)0)
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CUTSCENE_EVENT_TYPE_NULL,
|
||||||
|
CUTSCENE_EVENT_TYPE_NATIVE,
|
||||||
|
CUTSCENE_EVENT_TYPE_SCRIPT,
|
||||||
|
CUTSCENE_EVENT_TYPE_COUNT
|
||||||
|
} cutsceneeventtype_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
cutsceneeventtype_t type;
|
||||||
|
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
errorret_t (*onStart)(void);
|
||||||
|
errorret_t (*onEnd)(void);
|
||||||
|
errorret_t (*onUpdate)(void);
|
||||||
|
} native;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
const char_t *script;
|
||||||
|
} script;
|
||||||
|
};
|
||||||
|
} cutsceneevent_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
cutsceneevent_t events[CUTSCENE_EVENT_COUNT_MAX];
|
||||||
|
uint8_t eventCount;
|
||||||
|
uint8_t eventCurrent;
|
||||||
|
bool_t active;
|
||||||
|
jerry_value_t scriptRef;
|
||||||
|
jerry_value_t activeRef;
|
||||||
|
} cutscene_t;
|
||||||
|
|
||||||
|
extern cutscene_t CUTSCENE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the cutscene manager.
|
||||||
|
*
|
||||||
|
* @return Any error state that happened.
|
||||||
|
*/
|
||||||
|
errorret_t cutsceneInit(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ticks the active cutscene event, calling its onUpdate callback.
|
||||||
|
* Does nothing when no cutscene is playing.
|
||||||
|
*
|
||||||
|
* @return Any error state that happened.
|
||||||
|
*/
|
||||||
|
errorret_t cutsceneUpdate(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies the given event array and begins playing from the first
|
||||||
|
* event. If a cutscene is already playing it is stopped first.
|
||||||
|
*
|
||||||
|
* @param events Array of events to copy.
|
||||||
|
* @param eventCount Number of events. Must be > 0 and
|
||||||
|
* <= CUTSCENE_EVENT_COUNT_MAX.
|
||||||
|
* @return Any error state that happened.
|
||||||
|
*/
|
||||||
|
errorret_t cutscenePlay(
|
||||||
|
const cutsceneevent_t *events,
|
||||||
|
const uint8_t eventCount
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ends the current event and starts the next one.
|
||||||
|
* Marks the cutscene as inactive after the last event ends.
|
||||||
|
* Does nothing when no cutscene is playing.
|
||||||
|
*
|
||||||
|
* @return Any error state that happened.
|
||||||
|
*/
|
||||||
|
errorret_t cutsceneAdvance(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ends the current event and stops the cutscene immediately.
|
||||||
|
* Does nothing when no cutscene is playing.
|
||||||
|
*
|
||||||
|
* @return Any error state that happened.
|
||||||
|
*/
|
||||||
|
errorret_t cutsceneStop(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disposes of the cutscene manager, stopping any active cutscene.
|
||||||
|
*
|
||||||
|
* @return Any error state that happened.
|
||||||
|
*/
|
||||||
|
errorret_t cutsceneDispose(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether a cutscene is currently playing.
|
||||||
|
*
|
||||||
|
* @return true if a cutscene is active.
|
||||||
|
*/
|
||||||
|
bool_t cutsceneIsActive(void);
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "locale/localemanager.h"
|
#include "locale/localemanager.h"
|
||||||
#include "display/display.h"
|
#include "display/display.h"
|
||||||
#include "scene/scene.h"
|
#include "scene/scene.h"
|
||||||
|
#include "cutscene/cutscene.h"
|
||||||
#include "asset/asset.h"
|
#include "asset/asset.h"
|
||||||
#include "ui/ui.h"
|
#include "ui/ui.h"
|
||||||
#include "script/scriptmanager.h"
|
#include "script/scriptmanager.h"
|
||||||
@@ -52,6 +53,7 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
|
|||||||
errorChain(scriptManagerInit());
|
errorChain(scriptManagerInit());
|
||||||
errorChain(displayInit());
|
errorChain(displayInit());
|
||||||
errorChain(uiInit());
|
errorChain(uiInit());
|
||||||
|
errorChain(cutsceneInit());
|
||||||
errorChain(sceneInit());
|
errorChain(sceneInit());
|
||||||
entityManagerInit();
|
entityManagerInit();
|
||||||
backpackInit();
|
backpackInit();
|
||||||
@@ -79,6 +81,7 @@ errorret_t engineUpdate(void) {
|
|||||||
|
|
||||||
// Scene update occurs last because only after rendering would we want to do
|
// Scene update occurs last because only after rendering would we want to do
|
||||||
// scene switching, refer to sceneSet() for information.
|
// scene switching, refer to sceneSet() for information.
|
||||||
|
errorChain(cutsceneUpdate());
|
||||||
errorChain(sceneUpdate());
|
errorChain(sceneUpdate());
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
@@ -89,6 +92,7 @@ void engineExit(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
errorret_t engineDispose(void) {
|
errorret_t engineDispose(void) {
|
||||||
|
cutsceneDispose();
|
||||||
sceneDispose();
|
sceneDispose();
|
||||||
errorChain(networkDispose());
|
errorChain(networkDispose());
|
||||||
entityManagerDispose();
|
entityManagerDispose();
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include "util/string.h"
|
#include "util/string.h"
|
||||||
#include "script/scriptmanager.h"
|
#include "script/scriptmanager.h"
|
||||||
#include "script/module/scene/modulescene.h"
|
#include "script/module/scene/modulescene.h"
|
||||||
|
#include "script/module/cutscene/modulecutscene.h"
|
||||||
#include "ui/ui.h"
|
#include "ui/ui.h"
|
||||||
|
|
||||||
scene_t SCENE;
|
scene_t SCENE;
|
||||||
@@ -175,6 +176,7 @@ errorret_t sceneSetImmediate(const char_t *scene) {
|
|||||||
SCENE.sceneActive = false;
|
SCENE.sceneActive = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
moduleCutsceneReset();
|
||||||
moduleSceneReset();
|
moduleSceneReset();
|
||||||
|
|
||||||
stringCopy(
|
stringCopy(
|
||||||
|
|||||||
@@ -0,0 +1,204 @@
|
|||||||
|
// Copyright (c) 2026 Dominic Masters
|
||||||
|
//
|
||||||
|
// This software is released under the MIT License.
|
||||||
|
// https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "script/module/modulebase.h"
|
||||||
|
#include "script/scriptproto.h"
|
||||||
|
#include "cutscene/cutscene.h"
|
||||||
|
#include "error/error.h"
|
||||||
|
|
||||||
|
static scriptproto_t MODULE_CUTSCENE_PROTO;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disposes the active JS cutscene without firing the then callback.
|
||||||
|
* Safe to call when no cutscene is active.
|
||||||
|
*/
|
||||||
|
static void moduleCutsceneReset(void) {
|
||||||
|
if(CUTSCENE.activeRef == CUTSCENE_SCRIPT_REF_NONE) return;
|
||||||
|
|
||||||
|
jerry_value_t key = jerry_string_sz("dispose");
|
||||||
|
jerry_value_t fn = jerry_object_get(CUTSCENE.activeRef, key);
|
||||||
|
jerry_value_free(key);
|
||||||
|
|
||||||
|
if(jerry_value_is_function(fn)) {
|
||||||
|
jerry_value_t result = jerry_call(fn, CUTSCENE.activeRef, NULL, 0);
|
||||||
|
jerry_value_free(fn);
|
||||||
|
jerry_value_free(result);
|
||||||
|
} else {
|
||||||
|
jerry_value_free(fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
jerry_value_free(CUTSCENE.activeRef);
|
||||||
|
CUTSCENE.activeRef = CUTSCENE_SCRIPT_REF_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ticks the active JS cutscene. Called from cutsceneUpdate().
|
||||||
|
*
|
||||||
|
* @return Any error state that happened.
|
||||||
|
*/
|
||||||
|
static errorret_t moduleCutsceneUpdate(void) {
|
||||||
|
if(CUTSCENE.activeRef == CUTSCENE_SCRIPT_REF_NONE) errorOk();
|
||||||
|
|
||||||
|
jerry_value_t key = jerry_string_sz("update");
|
||||||
|
jerry_value_t fn = jerry_object_get(CUTSCENE.activeRef, key);
|
||||||
|
jerry_value_free(key);
|
||||||
|
|
||||||
|
if(!jerry_value_is_function(fn)) {
|
||||||
|
jerry_value_free(fn);
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
jerry_value_t result = jerry_call(fn, CUTSCENE.activeRef, NULL, 0);
|
||||||
|
jerry_value_free(fn);
|
||||||
|
|
||||||
|
if(jerry_value_is_exception(result)) {
|
||||||
|
char_t errMsg[512];
|
||||||
|
moduleBaseExceptionMessage(result, errMsg, sizeof(errMsg));
|
||||||
|
jerry_value_free(result);
|
||||||
|
errorThrow("Cutscene update failed: %s", errMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
jerry_value_free(result);
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleCutsceneDefaultConstructor) {
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleCutsceneDefaultUpdate) {
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleCutsceneDefaultDispose) {
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cutscene.then(callback)
|
||||||
|
*
|
||||||
|
* Sets the completion callback fired when Cutscene.finish() is called.
|
||||||
|
* Only one callback may be set; calling then() again replaces it.
|
||||||
|
* Returns the instance for optional chaining.
|
||||||
|
*/
|
||||||
|
moduleBaseFunction(moduleCutsceneThen) {
|
||||||
|
if(argc < 1 || !jerry_value_is_function(args[0])) {
|
||||||
|
return moduleBaseThrow("then() expects a function argument");
|
||||||
|
}
|
||||||
|
|
||||||
|
jerry_value_t key = jerry_string_sz("_onFinished");
|
||||||
|
jerry_object_set(callInfo->this_value, key, args[0]);
|
||||||
|
jerry_value_free(key);
|
||||||
|
|
||||||
|
return jerry_value_copy(callInfo->this_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cutscene.play(instance)
|
||||||
|
*
|
||||||
|
* Registers a cutscene instance with the engine and begins calling its
|
||||||
|
* update() each tick. Stops any currently active cutscene first.
|
||||||
|
* Returns the instance to allow chaining .then() directly.
|
||||||
|
*/
|
||||||
|
moduleBaseFunction(moduleCutscenePlay) {
|
||||||
|
if(argc < 1 || !jerry_value_is_object(args[0])) {
|
||||||
|
return moduleBaseThrow("play() expects a cutscene instance");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(CUTSCENE.activeRef != CUTSCENE_SCRIPT_REF_NONE) {
|
||||||
|
moduleCutsceneReset();
|
||||||
|
}
|
||||||
|
|
||||||
|
CUTSCENE.activeRef = jerry_value_copy(args[0]);
|
||||||
|
return jerry_value_copy(args[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cutscene.finish()
|
||||||
|
*
|
||||||
|
* Marks the active cutscene as complete: calls dispose(), clears the
|
||||||
|
* active ref, then fires the then() callback if one was set.
|
||||||
|
* Intended to be called from within the cutscene's own update().
|
||||||
|
*/
|
||||||
|
moduleBaseFunction(moduleCutsceneFinish) {
|
||||||
|
if(CUTSCENE.activeRef == CUTSCENE_SCRIPT_REF_NONE) {
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
jerry_value_t cbKey = jerry_string_sz("_onFinished");
|
||||||
|
jerry_value_t callback = jerry_object_get(CUTSCENE.activeRef, cbKey);
|
||||||
|
jerry_value_free(cbKey);
|
||||||
|
|
||||||
|
jerry_value_t dispKey = jerry_string_sz("dispose");
|
||||||
|
jerry_value_t dispFn = jerry_object_get(CUTSCENE.activeRef, dispKey);
|
||||||
|
jerry_value_free(dispKey);
|
||||||
|
|
||||||
|
if(jerry_value_is_function(dispFn)) {
|
||||||
|
jerry_value_t r = jerry_call(dispFn, CUTSCENE.activeRef, NULL, 0);
|
||||||
|
jerry_value_free(dispFn);
|
||||||
|
if(jerry_value_is_exception(r)) {
|
||||||
|
jerry_value_free(callback);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
jerry_value_free(r);
|
||||||
|
} else {
|
||||||
|
jerry_value_free(dispFn);
|
||||||
|
}
|
||||||
|
|
||||||
|
jerry_value_free(CUTSCENE.activeRef);
|
||||||
|
CUTSCENE.activeRef = CUTSCENE_SCRIPT_REF_NONE;
|
||||||
|
|
||||||
|
if(jerry_value_is_function(callback)) {
|
||||||
|
jerry_value_t r = jerry_call(callback, jerry_undefined(), NULL, 0);
|
||||||
|
jerry_value_free(callback);
|
||||||
|
if(jerry_value_is_exception(r)) return r;
|
||||||
|
jerry_value_free(r);
|
||||||
|
} else {
|
||||||
|
jerry_value_free(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cutscene.stop()
|
||||||
|
*
|
||||||
|
* Immediately disposes the active cutscene without firing the then
|
||||||
|
* callback. No-op when no cutscene is active.
|
||||||
|
*/
|
||||||
|
moduleBaseFunction(moduleCutsceneStop) {
|
||||||
|
moduleCutsceneReset();
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void moduleCutscene(void) {
|
||||||
|
scriptProtoInit(
|
||||||
|
&MODULE_CUTSCENE_PROTO,
|
||||||
|
"Cutscene",
|
||||||
|
sizeof(uint8_t),
|
||||||
|
moduleCutsceneDefaultConstructor
|
||||||
|
);
|
||||||
|
|
||||||
|
scriptProtoDefineFunc(
|
||||||
|
&MODULE_CUTSCENE_PROTO, "update", moduleCutsceneDefaultUpdate
|
||||||
|
);
|
||||||
|
scriptProtoDefineFunc(
|
||||||
|
&MODULE_CUTSCENE_PROTO, "dispose", moduleCutsceneDefaultDispose
|
||||||
|
);
|
||||||
|
scriptProtoDefineFunc(
|
||||||
|
&MODULE_CUTSCENE_PROTO, "then", moduleCutsceneThen
|
||||||
|
);
|
||||||
|
|
||||||
|
scriptProtoDefineStaticFunc(
|
||||||
|
&MODULE_CUTSCENE_PROTO, "play", moduleCutscenePlay
|
||||||
|
);
|
||||||
|
scriptProtoDefineStaticFunc(
|
||||||
|
&MODULE_CUTSCENE_PROTO, "finish", moduleCutsceneFinish
|
||||||
|
);
|
||||||
|
scriptProtoDefineStaticFunc(
|
||||||
|
&MODULE_CUTSCENE_PROTO, "stop", moduleCutsceneStop
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
#include "script/module/display/modulespritebatch.h"
|
#include "script/module/display/modulespritebatch.h"
|
||||||
#include "script/module/display/moduletext.h"
|
#include "script/module/display/moduletext.h"
|
||||||
#include "script/module/scene/modulescene.h"
|
#include "script/module/scene/modulescene.h"
|
||||||
|
#include "script/module/cutscene/modulecutscene.h"
|
||||||
#include "script/module/console/moduleconsole.h"
|
#include "script/module/console/moduleconsole.h"
|
||||||
#include "script/module/engine/moduleengine.h"
|
#include "script/module/engine/moduleengine.h"
|
||||||
#include "script/module/item/moduleitem.h"
|
#include "script/module/item/moduleitem.h"
|
||||||
@@ -36,6 +37,7 @@ static void moduleRegister(void) {
|
|||||||
moduleSpriteBatch();
|
moduleSpriteBatch();
|
||||||
moduleText();
|
moduleText();
|
||||||
moduleScene();
|
moduleScene();
|
||||||
|
moduleCutscene();
|
||||||
moduleConsole();
|
moduleConsole();
|
||||||
moduleEngine();
|
moduleEngine();
|
||||||
moduleItem();
|
moduleItem();
|
||||||
|
|||||||
Reference in New Issue
Block a user