scripting improvements
This commit is contained in:
@@ -7,30 +7,23 @@ function MoveCubeCutscene(params) {
|
||||
|
||||
var startX = this.cube.position.position.x;
|
||||
|
||||
// this.animLeft = new Animation([
|
||||
// { time: 0.0, value: startX, easing: Easing.outQuad },
|
||||
// { time: DURATION, value: startX - SPEED * DURATION }
|
||||
// ]);
|
||||
this.anim = new Animation([
|
||||
[
|
||||
{ time: 0.0, value: startX, easing: Easing.outQuad },
|
||||
{ time: DURATION, value: startX - SPEED * DURATION, easing: Easing.inOutQuad },
|
||||
{ time: DURATION * 2, value: startX }
|
||||
]
|
||||
]);
|
||||
|
||||
// this.animRight = new Animation([
|
||||
// { time: 0.0, value: startX - SPEED * DURATION, easing: Easing.inOutQuad },
|
||||
// { time: DURATION, value: startX }
|
||||
// ]);
|
||||
this.anim.onComplete = function() { Cutscene.finish(); };
|
||||
}
|
||||
|
||||
MoveCubeCutscene.prototype = Object.create(Cutscene.prototype);
|
||||
MoveCubeCutscene.prototype.constructor = MoveCubeCutscene;
|
||||
|
||||
MoveCubeCutscene.prototype.update = function() {
|
||||
Cutscene.finish();
|
||||
// if(!this.animLeft.complete) {
|
||||
// this.cube.position.position.x = this.animLeft.update(TIME.delta);
|
||||
// } else {
|
||||
// this.cube.position.position.x = this.animRight.update(TIME.delta);
|
||||
// if(this.animRight.complete) {
|
||||
// Cutscene.finish();
|
||||
// }
|
||||
// }
|
||||
this.anim.update(TIME.delta);
|
||||
this.cube.position.position.x = this.anim.properties[0].value;
|
||||
};
|
||||
|
||||
module = MoveCubeCutscene;
|
||||
|
||||
@@ -55,6 +55,7 @@ target_sources(${DUSK_BINARY_TARGET_NAME}
|
||||
|
||||
# Subdirs
|
||||
add_subdirectory(animation)
|
||||
add_subdirectory(event)
|
||||
add_subdirectory(assert)
|
||||
add_subdirectory(asset)
|
||||
add_subdirectory(cutscene)
|
||||
|
||||
@@ -10,42 +10,56 @@
|
||||
|
||||
void animationInit(animation_t *anim) {
|
||||
memoryZero(anim, sizeof(animation_t));
|
||||
eventInit(&anim->onStart);
|
||||
eventInit(&anim->onComplete);
|
||||
}
|
||||
|
||||
animationproperty_t *animationAddProperty(animation_t *anim) {
|
||||
animationproperty_t *animationAddProperty(animation_t *anim, float_t *target) {
|
||||
assertTrue(
|
||||
anim->propertyCount < ANIMATION_PROPERTY_COUNT_MAX,
|
||||
"Property count exceeds ANIMATION_PROPERTY_COUNT_MAX"
|
||||
);
|
||||
animationproperty_t *prop = &anim->properties[anim->propertyCount++];
|
||||
prop->keyframeCount = 0;
|
||||
prop->target = target;
|
||||
return prop;
|
||||
}
|
||||
|
||||
void animationUpdate(animation_t *anim, float_t delta) {
|
||||
assertNotNull(anim, "Animation cannot be null");
|
||||
|
||||
// Anim done?
|
||||
if(
|
||||
(anim->flags & ANIMATION_FLAG_FINISHED) &&
|
||||
!(anim->flags & ANIMATION_FLAG_LOOP_ENABLED)
|
||||
) return;
|
||||
|
||||
// Start
|
||||
bool_t wasStarted = (anim->flags & ANIMATION_FLAG_STARTED) != 0;
|
||||
anim->flags |= ANIMATION_FLAG_STARTED;
|
||||
anim->time += delta;
|
||||
|
||||
// Tick
|
||||
float_t duration = animationGetDuration(anim);
|
||||
bool_t justFinished = false;
|
||||
if(anim->time >= duration) {
|
||||
// Done/Loop
|
||||
if(anim->flags & ANIMATION_FLAG_LOOP_ENABLED) {
|
||||
anim->time = mathModFloat(anim->time, duration);
|
||||
} else {
|
||||
anim->time = duration;
|
||||
anim->flags |= ANIMATION_FLAG_FINISHED;
|
||||
if(!(anim->flags & ANIMATION_FLAG_FINISHED)) {
|
||||
anim->flags |= ANIMATION_FLAG_FINISHED;
|
||||
justFinished = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(uint8_t i = 0; i < anim->propertyCount; i++) {
|
||||
animationproperty_t *prop = &anim->properties[i];
|
||||
if(prop->target != NULL) {
|
||||
*prop->target = animationPropertyGetValue(prop, anim->time);
|
||||
}
|
||||
}
|
||||
|
||||
if(!wasStarted) eventInvoke(&anim->onStart, anim);
|
||||
if(justFinished) eventInvoke(&anim->onComplete, anim);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#pragma once
|
||||
#include "animationproperty.h"
|
||||
#include "event/event.h"
|
||||
|
||||
#define ANIMATION_PROPERTY_COUNT_MAX 8
|
||||
|
||||
@@ -17,6 +18,8 @@ typedef struct {
|
||||
uint8_t propertyCount;
|
||||
float_t time;
|
||||
uint8_t flags;
|
||||
event_t onStart;
|
||||
event_t onComplete;
|
||||
} animation_t;
|
||||
|
||||
/**
|
||||
@@ -31,9 +34,10 @@ void animationInit(animation_t *anim);
|
||||
* for the lifetime of the animation.
|
||||
*
|
||||
* @param anim The animation to add the property to.
|
||||
* @param target Pointer to the float to write interpolated values into.
|
||||
* @return A pointer to the new property.
|
||||
*/
|
||||
animationproperty_t *animationAddProperty(animation_t *anim);
|
||||
animationproperty_t *animationAddProperty(animation_t *anim, float_t *target);
|
||||
|
||||
/**
|
||||
* Advances the animation by delta seconds and writes interpolated values to
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
typedef struct {
|
||||
keyframe_t keyframes[ANIMATION_PROPERTY_KEYFRAME_COUNT_MAX];
|
||||
uint8_t keyframeCount;
|
||||
float_t *target;
|
||||
} animationproperty_t;
|
||||
|
||||
/**
|
||||
|
||||
@@ -66,40 +66,54 @@ errorret_t spriteBatchPushv(
|
||||
const float_t *sMaxX = maxX + offset;
|
||||
const float_t *sMinY = minY + offset;
|
||||
const float_t *sMaxY = maxY + offset;
|
||||
const float_t *sZ = z + offset;
|
||||
const float_t *sU0 = u0 + offset;
|
||||
const float_t *sU1 = u1 + offset;
|
||||
const float_t *sV0 = v0 + offset;
|
||||
const float_t *sV1 = v1 + offset;
|
||||
const float_t *sZ = z + offset;
|
||||
const float_t *sU0 = u0 + offset;
|
||||
const float_t *sU1 = u1 + offset;
|
||||
const float_t *sV0 = v0 + offset;
|
||||
const float_t *sV1 = v1 + offset;
|
||||
|
||||
#define COPY_FIELD(vi, field, srcArr) \
|
||||
memoryCopyInterleaved(&start[vi].field, dstStride, srcArr, fSz, fSz, toPush)
|
||||
#define memshVertCopy(vi, field, srcArr) \
|
||||
memoryCopyInterleaved( \
|
||||
&start[vi].field, dstStride, srcArr, fSz, fSz, toPush \
|
||||
)
|
||||
|
||||
COPY_FIELD(0, pos[0], sMinX); COPY_FIELD(0, pos[1], sMinY);
|
||||
COPY_FIELD(0, pos[2], sZ); COPY_FIELD(0, uv[0], sU0);
|
||||
COPY_FIELD(0, uv[1], sV0);
|
||||
memshVertCopy(0, pos[0], sMinX);
|
||||
memshVertCopy(0, pos[1], sMinY);
|
||||
memshVertCopy(0, pos[2], sZ);
|
||||
memshVertCopy(0, uv[0], sU0);
|
||||
memshVertCopy(0, uv[1], sV0);
|
||||
|
||||
COPY_FIELD(1, pos[0], sMaxX); COPY_FIELD(1, pos[1], sMinY);
|
||||
COPY_FIELD(1, pos[2], sZ); COPY_FIELD(1, uv[0], sU1);
|
||||
COPY_FIELD(1, uv[1], sV0);
|
||||
memshVertCopy(1, pos[0], sMaxX);
|
||||
memshVertCopy(1, pos[1], sMinY);
|
||||
memshVertCopy(1, pos[2], sZ);
|
||||
memshVertCopy(1, uv[0], sU1);
|
||||
memshVertCopy(1, uv[1], sV0);
|
||||
|
||||
COPY_FIELD(2, pos[0], sMaxX); COPY_FIELD(2, pos[1], sMaxY);
|
||||
COPY_FIELD(2, pos[2], sZ); COPY_FIELD(2, uv[0], sU1);
|
||||
COPY_FIELD(2, uv[1], sV1);
|
||||
memshVertCopy(2, pos[0], sMaxX);
|
||||
memshVertCopy(2, pos[1], sMaxY);
|
||||
memshVertCopy(2, pos[2], sZ);
|
||||
memshVertCopy(2, uv[0], sU1);
|
||||
memshVertCopy(2, uv[1], sV1);
|
||||
|
||||
COPY_FIELD(3, pos[0], sMinX); COPY_FIELD(3, pos[1], sMinY);
|
||||
COPY_FIELD(3, pos[2], sZ); COPY_FIELD(3, uv[0], sU0);
|
||||
COPY_FIELD(3, uv[1], sV0);
|
||||
memshVertCopy(3, pos[0], sMinX);
|
||||
memshVertCopy(3, pos[1], sMinY);
|
||||
memshVertCopy(3, pos[2], sZ);
|
||||
memshVertCopy(3, uv[0], sU0);
|
||||
memshVertCopy(3, uv[1], sV0);
|
||||
|
||||
COPY_FIELD(4, pos[0], sMaxX); COPY_FIELD(4, pos[1], sMaxY);
|
||||
COPY_FIELD(4, pos[2], sZ); COPY_FIELD(4, uv[0], sU1);
|
||||
COPY_FIELD(4, uv[1], sV1);
|
||||
memshVertCopy(4, pos[0], sMaxX);
|
||||
memshVertCopy(4, pos[1], sMaxY);
|
||||
memshVertCopy(4, pos[2], sZ);
|
||||
memshVertCopy(4, uv[0], sU1);
|
||||
memshVertCopy(4, uv[1], sV1);
|
||||
|
||||
COPY_FIELD(5, pos[0], sMinX); COPY_FIELD(5, pos[1], sMaxY);
|
||||
COPY_FIELD(5, pos[2], sZ); COPY_FIELD(5, uv[0], sU0);
|
||||
COPY_FIELD(5, uv[1], sV1);
|
||||
memshVertCopy(5, pos[0], sMinX);
|
||||
memshVertCopy(5, pos[1], sMaxY);
|
||||
memshVertCopy(5, pos[2], sZ);
|
||||
memshVertCopy(5, uv[0], sU0);
|
||||
memshVertCopy(5, uv[1], sV1);
|
||||
|
||||
#undef COPY_FIELD
|
||||
#undef memshVertCopy
|
||||
|
||||
#if MESH_ENABLE_COLOR
|
||||
for(uint8_t vi = 0; vi < QUAD_VERTEX_COUNT; vi++) {
|
||||
|
||||
@@ -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
|
||||
event.c
|
||||
)
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "event.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
|
||||
void eventInit(event_t *event) {
|
||||
assertNotNull(event, "event must not be NULL");
|
||||
memoryZero(event, sizeof(event_t));
|
||||
}
|
||||
|
||||
void eventSubscribe(event_t *event, eventcallback_t callback, void *user) {
|
||||
assertNotNull(event, "event must not be NULL");
|
||||
assertNotNull((void *)callback, "callback must not be NULL");
|
||||
|
||||
for(uint8_t i = 0; i < event->count; i++) {
|
||||
if(event->callbacks[i] == callback && event->users[i] == user) return;
|
||||
}
|
||||
|
||||
assertTrue(
|
||||
event->count < EVENT_SUBSCRIBER_COUNT_MAX,
|
||||
"EVENT_SUBSCRIBER_COUNT_MAX exceeded"
|
||||
);
|
||||
|
||||
event->callbacks[event->count] = callback;
|
||||
event->users[event->count] = user;
|
||||
event->count++;
|
||||
}
|
||||
|
||||
void eventUnsubscribe(event_t *event, eventcallback_t callback, void *user) {
|
||||
assertNotNull(event, "event must not be NULL");
|
||||
assertNotNull((void *)callback, "callback must not be NULL");
|
||||
|
||||
for(uint8_t i = 0; i < event->count; i++) {
|
||||
if(event->callbacks[i] != callback || event->users[i] != user) continue;
|
||||
|
||||
uint8_t last = event->count - 1;
|
||||
if(i != last) {
|
||||
event->callbacks[i] = event->callbacks[last];
|
||||
event->users[i] = event->users[last];
|
||||
}
|
||||
event->callbacks[last] = NULL;
|
||||
event->users[last] = NULL;
|
||||
event->count--;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void eventInvoke(const event_t *event, void *params) {
|
||||
assertNotNull(event, "event must not be NULL");
|
||||
for(uint8_t i = 0; i < event->count; i++) {
|
||||
event->callbacks[i](params, event->users[i]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "dusk.h"
|
||||
|
||||
#define EVENT_SUBSCRIBER_COUNT_MAX 8
|
||||
|
||||
typedef void (*eventcallback_t)(void *params, void *user);
|
||||
|
||||
typedef struct {
|
||||
eventcallback_t callbacks[EVENT_SUBSCRIBER_COUNT_MAX];
|
||||
void *users[EVENT_SUBSCRIBER_COUNT_MAX];
|
||||
uint8_t count;
|
||||
} event_t;
|
||||
|
||||
/**
|
||||
* Initializes an event, clearing all subscribers.
|
||||
*
|
||||
* @param event The event to initialize.
|
||||
*/
|
||||
void eventInit(event_t *event);
|
||||
|
||||
/**
|
||||
* Subscribes a callback to an event. The callback is invoked with params and
|
||||
* the provided user pointer each time the event fires. The same (callback,
|
||||
* user) pair may only be subscribed once.
|
||||
*
|
||||
* @param event The event to subscribe to.
|
||||
* @param callback The function to call when the event fires.
|
||||
* @param user Arbitrary pointer forwarded to the callback unchanged.
|
||||
*/
|
||||
void eventSubscribe(event_t *event, eventcallback_t callback, void *user);
|
||||
|
||||
/**
|
||||
* Removes a previously subscribed (callback, user) pair. Does nothing if the
|
||||
* pair is not currently subscribed.
|
||||
*
|
||||
* @param event The event to unsubscribe from.
|
||||
* @param callback The callback that was passed to eventSubscribe.
|
||||
* @param user The user pointer that was passed to eventSubscribe.
|
||||
*/
|
||||
void eventUnsubscribe(event_t *event, eventcallback_t callback, void *user);
|
||||
|
||||
/**
|
||||
* Invokes all subscribed callbacks, passing params and each subscriber's user
|
||||
* pointer.
|
||||
*
|
||||
* @param event The event to invoke.
|
||||
* @param params Arbitrary pointer forwarded to every callback unchanged.
|
||||
*/
|
||||
void eventInvoke(const event_t *event, void *params);
|
||||
+11
-1
@@ -11,6 +11,7 @@
|
||||
#include "util/string.h"
|
||||
#include "util/math.h"
|
||||
#include "time/time.h"
|
||||
#include "event/event.h"
|
||||
|
||||
input_t INPUT;
|
||||
|
||||
@@ -21,6 +22,8 @@ errorret_t inputInit(void) {
|
||||
INPUT.actions[i].action = (inputaction_t)i;
|
||||
INPUT.actions[i].lastValue = 0.0f;
|
||||
INPUT.actions[i].currentValue = 0.0f;
|
||||
eventInit(&INPUT.actions[i].onPressed);
|
||||
eventInit(&INPUT.actions[i].onReleased);
|
||||
}
|
||||
|
||||
#ifdef inputInitPlatform
|
||||
@@ -83,10 +86,17 @@ void inputUpdate(void) {
|
||||
cur++;
|
||||
}
|
||||
|
||||
// Do we need to fire off events?
|
||||
#ifdef DUSK_TIME_DYNAMIC
|
||||
if(TIME.dynamicUpdate) return;
|
||||
#endif
|
||||
|
||||
for(uint8_t i = INPUT_ACTION_NULL + 1; i < INPUT_ACTION_COUNT; i++) {
|
||||
inputactiondata_t *act = &INPUT.actions[i];
|
||||
bool_t isDown = act->currentValue > 0.0f;
|
||||
bool_t wasDown = act->lastValue > 0.0f;
|
||||
if(isDown && !wasDown) eventInvoke(&act->onPressed, act);
|
||||
if(!isDown && wasDown) eventInvoke(&act->onReleased, act);
|
||||
}
|
||||
}
|
||||
|
||||
float_t inputGetCurrentValue(const inputaction_t action) {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#pragma once
|
||||
#include "time/time.h"
|
||||
#include "input/inputactiondefs.h"
|
||||
#include "event/event.h"
|
||||
|
||||
typedef struct {
|
||||
inputaction_t action;
|
||||
@@ -18,6 +19,9 @@ typedef struct {
|
||||
float_t lastDynamicValue;
|
||||
float_t currentDynamicValue;
|
||||
#endif
|
||||
|
||||
event_t onPressed;
|
||||
event_t onReleased;
|
||||
} inputactiondata_t;
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,307 +9,396 @@
|
||||
#include "animation/animation.h"
|
||||
#include "util/memory.h"
|
||||
|
||||
// static scriptproto_t MODULE_ANIMATION_PROTO;
|
||||
static scriptproto_t MODULE_ANIMATION_PROP_PROTO;
|
||||
static scriptproto_t MODULE_ANIMATION_PROTO;
|
||||
|
||||
// // JS animation wraps a single-property animation; value is the managed target.
|
||||
// typedef struct {
|
||||
// animation_t anim;
|
||||
// float_t value;
|
||||
// } jsAnimation_t;
|
||||
typedef struct {
|
||||
float_t value;
|
||||
} jsAnimationProperty_t;
|
||||
|
||||
// static inline jsAnimation_t *moduleAnimationGetWrapper(
|
||||
// const jerry_call_info_t *callInfo
|
||||
// ) {
|
||||
// return (jsAnimation_t *)scriptProtoGetValue(
|
||||
// &MODULE_ANIMATION_PROTO, callInfo->this_value
|
||||
// );
|
||||
// }
|
||||
typedef struct {
|
||||
animation_t anim;
|
||||
jerry_value_t onStartCallback;
|
||||
jerry_value_t onCompleteCallback;
|
||||
} jsAnimation_t;
|
||||
|
||||
// // Returns the animation_t pointer (first field of jsAnimation_t — same address).
|
||||
// static inline animation_t *moduleAnimationGet(
|
||||
// const jerry_call_info_t *callInfo
|
||||
// ) {
|
||||
// return (animation_t *)moduleAnimationGetWrapper(callInfo);
|
||||
// }
|
||||
static inline jsAnimation_t *moduleAnimationGetWrapper(
|
||||
const jerry_call_info_t *callInfo
|
||||
) {
|
||||
return (jsAnimation_t *)scriptProtoGetValue(
|
||||
&MODULE_ANIMATION_PROTO, callInfo->this_value
|
||||
);
|
||||
}
|
||||
|
||||
// // Extracts an easingtype_t from a JS value. Accepts either a plain number
|
||||
// // or an Easing.xxx function (which has a .type numeric property).
|
||||
// static easingtype_t moduleAnimationReadEasing(
|
||||
// const jerry_value_t val
|
||||
// ) {
|
||||
// if(jerry_value_is_number(val)) {
|
||||
// return (easingtype_t)(uint32_t)jerry_value_as_number(val);
|
||||
// }
|
||||
// if(jerry_value_is_object(val) || jerry_value_is_function(val)) {
|
||||
// jerry_value_t key = jerry_string_sz("type");
|
||||
// jerry_value_t typeVal = jerry_object_get(val, key);
|
||||
// jerry_value_free(key);
|
||||
// easingtype_t result = EASING_LINEAR;
|
||||
// if(jerry_value_is_number(typeVal)) {
|
||||
// result = (easingtype_t)(uint32_t)jerry_value_as_number(typeVal);
|
||||
// }
|
||||
// jerry_value_free(typeVal);
|
||||
// return result;
|
||||
// }
|
||||
// return EASING_LINEAR;
|
||||
// }
|
||||
static inline animation_t *moduleAnimationGet(
|
||||
const jerry_call_info_t *callInfo
|
||||
) {
|
||||
return (animation_t *)moduleAnimationGetWrapper(callInfo);
|
||||
}
|
||||
|
||||
// // Fires onReach callbacks for any keyframes crossed between prevTime and
|
||||
// // newTime. Returns an exception value on error, or jerry_undefined().
|
||||
// static jerry_value_t moduleAnimationFireKeyframes(
|
||||
// const jerry_value_t thisVal,
|
||||
// float_t prevTime,
|
||||
// float_t newTime
|
||||
// ) {
|
||||
// jerry_value_t kfKey = jerry_string_sz("_keyframes");
|
||||
// jerry_value_t kfArr = jerry_object_get(thisVal, kfKey);
|
||||
// jerry_value_free(kfKey);
|
||||
static easingtype_t moduleAnimationReadEasing(const jerry_value_t val) {
|
||||
if(jerry_value_is_number(val)) {
|
||||
return (easingtype_t)(uint32_t)jerry_value_as_number(val);
|
||||
}
|
||||
if(jerry_value_is_object(val) || jerry_value_is_function(val)) {
|
||||
jerry_value_t key = jerry_string_sz("type");
|
||||
jerry_value_t typeVal = jerry_object_get(val, key);
|
||||
jerry_value_free(key);
|
||||
easingtype_t result = EASING_LINEAR;
|
||||
if(jerry_value_is_number(typeVal)) {
|
||||
result = (easingtype_t)(uint32_t)jerry_value_as_number(typeVal);
|
||||
}
|
||||
jerry_value_free(typeVal);
|
||||
return result;
|
||||
}
|
||||
return EASING_LINEAR;
|
||||
}
|
||||
|
||||
// if(!jerry_value_is_array(kfArr)) {
|
||||
// jerry_value_free(kfArr);
|
||||
// return jerry_undefined();
|
||||
// }
|
||||
static void jsAnimationPropertyFree(void *ptr, jerry_object_native_info_t *info) {
|
||||
(void)info;
|
||||
memoryFree(ptr);
|
||||
}
|
||||
|
||||
// jerry_length_t len = jerry_array_length(kfArr);
|
||||
// for(jerry_length_t i = 0; i < len; i++) {
|
||||
// jerry_value_t kfObj = jerry_object_get_index(kfArr, i);
|
||||
static void jsAnimationOnStartBridge(void *params, void *user) {
|
||||
(void)params;
|
||||
jsAnimation_t *janim = (jsAnimation_t *)user;
|
||||
if(!janim->onStartCallback) return;
|
||||
jerry_value_t ret = jerry_call(janim->onStartCallback, jerry_undefined(), NULL, 0);
|
||||
jerry_value_free(ret);
|
||||
}
|
||||
|
||||
// jerry_value_t tKey = jerry_string_sz("time");
|
||||
// jerry_value_t tVal = jerry_object_get(kfObj, tKey);
|
||||
// jerry_value_free(tKey);
|
||||
// float_t kfTime = (float_t)jerry_value_as_number(tVal);
|
||||
// jerry_value_free(tVal);
|
||||
static void jsAnimationOnCompleteBridge(void *params, void *user) {
|
||||
(void)params;
|
||||
jsAnimation_t *janim = (jsAnimation_t *)user;
|
||||
if(!janim->onCompleteCallback) return;
|
||||
jerry_value_t ret = jerry_call(janim->onCompleteCallback, jerry_undefined(), NULL, 0);
|
||||
jerry_value_free(ret);
|
||||
}
|
||||
|
||||
// if(prevTime < kfTime && newTime >= kfTime) {
|
||||
// jerry_value_t cbKey = jerry_string_sz("onReach");
|
||||
// jerry_value_t cb = jerry_object_get(kfObj, cbKey);
|
||||
// jerry_value_free(cbKey);
|
||||
static void jsAnimationFree(void *ptr, jerry_object_native_info_t *info) {
|
||||
(void)info;
|
||||
jsAnimation_t *janim = (jsAnimation_t *)ptr;
|
||||
if(janim->onStartCallback) {
|
||||
eventUnsubscribe(&janim->anim.onStart, jsAnimationOnStartBridge, janim);
|
||||
jerry_value_free(janim->onStartCallback);
|
||||
}
|
||||
if(janim->onCompleteCallback) {
|
||||
eventUnsubscribe(
|
||||
&janim->anim.onComplete, jsAnimationOnCompleteBridge, janim
|
||||
);
|
||||
jerry_value_free(janim->onCompleteCallback);
|
||||
}
|
||||
memoryFree(ptr);
|
||||
}
|
||||
|
||||
// if(jerry_value_is_function(cb)) {
|
||||
// jerry_value_t r = jerry_call(cb, thisVal, NULL, 0);
|
||||
// jerry_value_free(cb);
|
||||
// if(jerry_value_is_exception(r)) {
|
||||
// jerry_value_free(kfObj);
|
||||
// jerry_value_free(kfArr);
|
||||
// return r;
|
||||
// }
|
||||
// jerry_value_free(r);
|
||||
// } else {
|
||||
// jerry_value_free(cb);
|
||||
// }
|
||||
// }
|
||||
// Fires onReach callbacks for keyframes crossed between prevTime and newTime.
|
||||
// Reads _keyframes from propObj; calls each callback with propObj as this.
|
||||
static jerry_value_t moduleAnimationFireKeyframes(
|
||||
const jerry_value_t propObj,
|
||||
float_t prevTime,
|
||||
float_t newTime
|
||||
) {
|
||||
jerry_value_t kfKey = jerry_string_sz("_keyframes");
|
||||
jerry_value_t kfArr = jerry_object_get(propObj, kfKey);
|
||||
jerry_value_free(kfKey);
|
||||
|
||||
// jerry_value_free(kfObj);
|
||||
// }
|
||||
if(!jerry_value_is_array(kfArr)) {
|
||||
jerry_value_free(kfArr);
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
// jerry_value_free(kfArr);
|
||||
// return jerry_undefined();
|
||||
// }
|
||||
jerry_length_t len = jerry_array_length(kfArr);
|
||||
for(jerry_length_t i = 0; i < len; i++) {
|
||||
jerry_value_t kfObj = jerry_object_get_index(kfArr, i);
|
||||
|
||||
// // Fires onComplete once. Returns an exception on error or jerry_undefined().
|
||||
// static jerry_value_t moduleAnimationFireComplete(
|
||||
// const jerry_value_t thisVal
|
||||
// ) {
|
||||
// jerry_value_t firedKey = jerry_string_sz("_completeFired");
|
||||
// jerry_value_t firedVal = jerry_object_get(thisVal, firedKey);
|
||||
// bool_t alreadyFired = jerry_value_is_true(firedVal);
|
||||
// jerry_value_free(firedVal);
|
||||
jerry_value_t tKey = jerry_string_sz("time");
|
||||
jerry_value_t tVal = jerry_object_get(kfObj, tKey);
|
||||
jerry_value_free(tKey);
|
||||
float_t kfTime = (float_t)jerry_value_as_number(tVal);
|
||||
jerry_value_free(tVal);
|
||||
|
||||
// if(alreadyFired) {
|
||||
// jerry_value_free(firedKey);
|
||||
// return jerry_undefined();
|
||||
// }
|
||||
if(prevTime < kfTime && newTime >= kfTime) {
|
||||
jerry_value_t cbKey = jerry_string_sz("onReach");
|
||||
jerry_value_t cb = jerry_object_get(kfObj, cbKey);
|
||||
jerry_value_free(cbKey);
|
||||
|
||||
// jerry_value_t trueVal = jerry_boolean(true);
|
||||
// jerry_object_set(thisVal, firedKey, trueVal);
|
||||
// jerry_value_free(firedKey);
|
||||
// jerry_value_free(trueVal);
|
||||
if(jerry_value_is_function(cb)) {
|
||||
jerry_value_t r = jerry_call(cb, propObj, NULL, 0);
|
||||
jerry_value_free(cb);
|
||||
if(jerry_value_is_exception(r)) {
|
||||
jerry_value_free(kfObj);
|
||||
jerry_value_free(kfArr);
|
||||
return r;
|
||||
}
|
||||
jerry_value_free(r);
|
||||
} else {
|
||||
jerry_value_free(cb);
|
||||
}
|
||||
}
|
||||
|
||||
// jerry_value_t cbKey = jerry_string_sz("onComplete");
|
||||
// jerry_value_t cb = jerry_object_get(thisVal, cbKey);
|
||||
// jerry_value_free(cbKey);
|
||||
jerry_value_free(kfObj);
|
||||
}
|
||||
|
||||
// if(jerry_value_is_function(cb)) {
|
||||
// jerry_value_t r = jerry_call(cb, thisVal, NULL, 0);
|
||||
// jerry_value_free(cb);
|
||||
// if(jerry_value_is_exception(r)) return r;
|
||||
// jerry_value_free(r);
|
||||
// } else {
|
||||
// jerry_value_free(cb);
|
||||
// }
|
||||
jerry_value_free(kfArr);
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
// return jerry_undefined();
|
||||
// }
|
||||
moduleBaseFunction(moduleAnimationPropGetValue) {
|
||||
jsAnimationProperty_t *jsprop = (jsAnimationProperty_t *)scriptProtoGetValue(
|
||||
&MODULE_ANIMATION_PROP_PROTO, callInfo->this_value
|
||||
);
|
||||
if(!jsprop) return moduleBaseThrow("Invalid AnimationProperty instance");
|
||||
return jerry_number((double)jsprop->value);
|
||||
}
|
||||
|
||||
// moduleBaseFunction(moduleAnimationConstructor) {
|
||||
// jsAnimation_t *janim = (jsAnimation_t *)memoryAllocate(sizeof(jsAnimation_t));
|
||||
// animationInit(&janim->anim);
|
||||
// janim->value = 0.0f;
|
||||
moduleBaseFunction(moduleAnimationConstructor) {
|
||||
jsAnimation_t *janim = (jsAnimation_t *)memoryAllocate(sizeof(jsAnimation_t));
|
||||
animationInit(&janim->anim);
|
||||
janim->onStartCallback = 0;
|
||||
janim->onCompleteCallback = 0;
|
||||
|
||||
// if(argc > 0 && jerry_value_is_boolean(args[argc - 1])) {
|
||||
// if(jerry_value_is_true(args[argc - 1])) {
|
||||
// janim->anim.flags |= ANIMATION_FLAG_LOOP_ENABLED;
|
||||
// }
|
||||
// }
|
||||
if(argc > 0 && jerry_value_is_boolean(args[argc - 1])) {
|
||||
if(jerry_value_is_true(args[argc - 1])) {
|
||||
janim->anim.flags |= ANIMATION_FLAG_LOOP_ENABLED;
|
||||
}
|
||||
}
|
||||
|
||||
// animationproperty_t *prop = animationAddProperty(&janim->anim, &janim->value);
|
||||
jerry_object_set_native_ptr(callInfo->this_value, &MODULE_ANIMATION_PROTO.info, janim);
|
||||
|
||||
// if(argc > 0 && jerry_value_is_array(args[0])) {
|
||||
// jerry_length_t len = jerry_array_length(args[0]);
|
||||
// for(jerry_length_t i = 0; i < len; i++) {
|
||||
// jerry_value_t kf = jerry_object_get_index(args[0], i);
|
||||
jerry_value_t propsArr = jerry_array(0);
|
||||
|
||||
// jerry_value_t tKey = jerry_string_sz("time");
|
||||
// jerry_value_t vKey = jerry_string_sz("value");
|
||||
// jerry_value_t eKey = jerry_string_sz("easing");
|
||||
if(argc > 0 && jerry_value_is_array(args[0])) {
|
||||
jerry_length_t numProps = jerry_array_length(args[0]);
|
||||
for(jerry_length_t p = 0; p < numProps; p++) {
|
||||
jerry_value_t kfArr = jerry_object_get_index(args[0], p);
|
||||
if(!jerry_value_is_array(kfArr)) {
|
||||
jerry_value_free(kfArr);
|
||||
continue;
|
||||
}
|
||||
|
||||
// float_t t = (float_t)jerry_value_as_number(
|
||||
// jerry_object_get(kf, tKey)
|
||||
// );
|
||||
// float_t v = (float_t)jerry_value_as_number(
|
||||
// jerry_object_get(kf, vKey)
|
||||
// );
|
||||
// jerry_value_t eVal = jerry_object_get(kf, eKey);
|
||||
// easingtype_t e = moduleAnimationReadEasing(eVal);
|
||||
jsAnimationProperty_t *jsprop = (jsAnimationProperty_t *)memoryAllocate(
|
||||
sizeof(jsAnimationProperty_t)
|
||||
);
|
||||
jsprop->value = 0.0f;
|
||||
|
||||
// jerry_value_free(tKey);
|
||||
// jerry_value_free(vKey);
|
||||
// jerry_value_free(eKey);
|
||||
// jerry_value_free(eVal);
|
||||
// jerry_value_free(kf);
|
||||
animationproperty_t *prop = animationAddProperty(&janim->anim, &jsprop->value);
|
||||
|
||||
// animationPropertyAddKeyframe(&janim->anim, prop, t, v, e);
|
||||
// }
|
||||
jerry_length_t kfLen = jerry_array_length(kfArr);
|
||||
for(jerry_length_t i = 0; i < kfLen; i++) {
|
||||
jerry_value_t kf = jerry_object_get_index(kfArr, i);
|
||||
|
||||
// // Store the JS keyframes array for onReach callback detection.
|
||||
// jerry_value_t kfKey = jerry_string_sz("_keyframes");
|
||||
// jerry_object_set(callInfo->this_value, kfKey, args[0]);
|
||||
// jerry_value_free(kfKey);
|
||||
// }
|
||||
jerry_value_t tKey = jerry_string_sz("time");
|
||||
jerry_value_t tVal = jerry_object_get(kf, tKey);
|
||||
jerry_value_free(tKey);
|
||||
float_t t = (float_t)jerry_value_as_number(tVal);
|
||||
jerry_value_free(tVal);
|
||||
|
||||
// jerry_value_t firedKey = jerry_string_sz("_completeFired");
|
||||
// jerry_value_t falseVal = jerry_boolean(false);
|
||||
// jerry_object_set(callInfo->this_value, firedKey, falseVal);
|
||||
// jerry_value_free(firedKey);
|
||||
// jerry_value_free(falseVal);
|
||||
jerry_value_t vKey = jerry_string_sz("value");
|
||||
jerry_value_t vVal = jerry_object_get(kf, vKey);
|
||||
jerry_value_free(vKey);
|
||||
float_t v = (float_t)jerry_value_as_number(vVal);
|
||||
jerry_value_free(vVal);
|
||||
|
||||
// jerry_object_set_native_ptr(
|
||||
// callInfo->this_value, &MODULE_ANIMATION_PROTO.info, janim
|
||||
// );
|
||||
// return jerry_undefined();
|
||||
// }
|
||||
jerry_value_t eKey = jerry_string_sz("easing");
|
||||
jerry_value_t eVal = jerry_object_get(kf, eKey);
|
||||
jerry_value_free(eKey);
|
||||
easingtype_t e = moduleAnimationReadEasing(eVal);
|
||||
jerry_value_free(eVal);
|
||||
|
||||
// moduleBaseFunction(moduleAnimationUpdate) {
|
||||
// jsAnimation_t *janim = moduleAnimationGetWrapper(callInfo);
|
||||
// if(!janim) return moduleBaseThrow("Invalid Animation instance");
|
||||
// if(argc < 1 || !jerry_value_is_number(args[0])) {
|
||||
// return moduleBaseThrow("update() expects a number delta");
|
||||
// }
|
||||
jerry_value_free(kf);
|
||||
animationPropertyAddKeyframe(prop, t, v, e);
|
||||
}
|
||||
|
||||
// float_t prevTime = janim->anim.time;
|
||||
// float_t delta = (float_t)jerry_value_as_number(args[0]);
|
||||
// animationUpdate(&janim->anim, delta);
|
||||
// Create the JS AnimationProperty object manually so &jsprop->value stays
|
||||
// stable as the C animation target (scriptProtoCreateValue would memcpy).
|
||||
jerry_value_t propObj = jerry_object();
|
||||
jerry_object_set_native_ptr(propObj, &MODULE_ANIMATION_PROP_PROTO.info, jsprop);
|
||||
jerry_object_set_proto(propObj, MODULE_ANIMATION_PROP_PROTO.prototype);
|
||||
|
||||
// jerry_value_t kfResult = moduleAnimationFireKeyframes(
|
||||
// callInfo->this_value, prevTime, janim->anim.time
|
||||
// );
|
||||
// if(jerry_value_is_exception(kfResult)) return kfResult;
|
||||
// jerry_value_free(kfResult);
|
||||
jerry_value_t kfStoreKey = jerry_string_sz("_keyframes");
|
||||
jerry_object_set(propObj, kfStoreKey, kfArr);
|
||||
jerry_value_free(kfStoreKey);
|
||||
jerry_value_free(kfArr);
|
||||
|
||||
// if(animationIsFinished(&janim->anim)) {
|
||||
// jerry_value_t cResult = moduleAnimationFireComplete(
|
||||
// callInfo->this_value
|
||||
// );
|
||||
// if(jerry_value_is_exception(cResult)) return cResult;
|
||||
// jerry_value_free(cResult);
|
||||
// }
|
||||
jerry_object_set_index(propsArr, (uint32_t)p, propObj);
|
||||
jerry_value_free(propObj);
|
||||
}
|
||||
}
|
||||
|
||||
// return jerry_number((double)janim->value);
|
||||
// }
|
||||
jerry_value_t propsKey = jerry_string_sz("properties");
|
||||
jerry_object_set(callInfo->this_value, propsKey, propsArr);
|
||||
jerry_value_free(propsKey);
|
||||
jerry_value_free(propsArr);
|
||||
|
||||
// moduleBaseFunction(moduleAnimationGetValue) {
|
||||
// jsAnimation_t *janim = moduleAnimationGetWrapper(callInfo);
|
||||
// if(!janim) return moduleBaseThrow("Invalid Animation instance");
|
||||
// return jerry_number((double)janim->value);
|
||||
// }
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
// moduleBaseFunction(moduleAnimationReset) {
|
||||
// animation_t *anim = moduleAnimationGet(callInfo);
|
||||
// if(!anim) return moduleBaseThrow("Invalid Animation instance");
|
||||
// animationReset(anim);
|
||||
moduleBaseFunction(moduleAnimationUpdate) {
|
||||
jsAnimation_t *janim = moduleAnimationGetWrapper(callInfo);
|
||||
if(!janim) return moduleBaseThrow("Invalid Animation instance");
|
||||
if(argc < 1 || !jerry_value_is_number(args[0])) {
|
||||
return moduleBaseThrow("update() expects a number delta");
|
||||
}
|
||||
|
||||
// jerry_value_t key = jerry_string_sz("_completeFired");
|
||||
// jerry_value_t falseVal = jerry_boolean(false);
|
||||
// jerry_object_set(callInfo->this_value, key, falseVal);
|
||||
// jerry_value_free(key);
|
||||
// jerry_value_free(falseVal);
|
||||
float_t prevTime = janim->anim.time;
|
||||
animationUpdate(&janim->anim, (float_t)jerry_value_as_number(args[0]));
|
||||
|
||||
// return jerry_undefined();
|
||||
// }
|
||||
jerry_value_t propsKey = jerry_string_sz("properties");
|
||||
jerry_value_t propsArr = jerry_object_get(callInfo->this_value, propsKey);
|
||||
jerry_value_free(propsKey);
|
||||
if(jerry_value_is_array(propsArr)) {
|
||||
jerry_length_t len = jerry_array_length(propsArr);
|
||||
for(jerry_length_t p = 0; p < len; p++) {
|
||||
jerry_value_t propObj = jerry_object_get_index(propsArr, p);
|
||||
jerry_value_t result = moduleAnimationFireKeyframes(
|
||||
propObj, prevTime, janim->anim.time
|
||||
);
|
||||
jerry_value_free(propObj);
|
||||
if(jerry_value_is_exception(result)) {
|
||||
jerry_value_free(propsArr);
|
||||
return result;
|
||||
}
|
||||
jerry_value_free(result);
|
||||
}
|
||||
}
|
||||
jerry_value_free(propsArr);
|
||||
|
||||
// moduleBaseFunction(moduleAnimationGetComplete) {
|
||||
// animation_t *anim = moduleAnimationGet(callInfo);
|
||||
// return anim ? jerry_boolean(animationIsFinished(anim)) : jerry_boolean(false);
|
||||
// }
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
// moduleBaseFunction(moduleAnimationGetLoop) {
|
||||
// animation_t *anim = moduleAnimationGet(callInfo);
|
||||
// return anim ? jerry_boolean(animationIsLooping(anim)) : jerry_boolean(false);
|
||||
// }
|
||||
moduleBaseFunction(moduleAnimationReset) {
|
||||
animation_t *anim = moduleAnimationGet(callInfo);
|
||||
if(!anim) return moduleBaseThrow("Invalid Animation instance");
|
||||
animationReset(anim);
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
// moduleBaseFunction(moduleAnimationSetLoop) {
|
||||
// animation_t *anim = moduleAnimationGet(callInfo);
|
||||
// if(!anim) return moduleBaseThrow("Invalid Animation instance");
|
||||
// if(argc < 1) return moduleBaseThrow("Expected boolean");
|
||||
// if(jerry_value_is_true(args[0])) {
|
||||
// anim->flags |= ANIMATION_FLAG_LOOP_ENABLED;
|
||||
// } else {
|
||||
// anim->flags &= ~ANIMATION_FLAG_LOOP_ENABLED;
|
||||
// }
|
||||
// return jerry_undefined();
|
||||
// }
|
||||
moduleBaseFunction(moduleAnimationGetComplete) {
|
||||
animation_t *anim = moduleAnimationGet(callInfo);
|
||||
return anim ? jerry_boolean(animationIsFinished(anim)) : jerry_boolean(false);
|
||||
}
|
||||
|
||||
// moduleBaseFunction(moduleAnimationGetTime) {
|
||||
// animation_t *anim = moduleAnimationGet(callInfo);
|
||||
// return anim ? jerry_number((double)anim->time) : jerry_number(0.0);
|
||||
// }
|
||||
moduleBaseFunction(moduleAnimationGetLoop) {
|
||||
animation_t *anim = moduleAnimationGet(callInfo);
|
||||
return anim ? jerry_boolean(animationIsLooping(anim)) : jerry_boolean(false);
|
||||
}
|
||||
|
||||
// moduleBaseFunction(moduleAnimationGetDuration) {
|
||||
// animation_t *anim = moduleAnimationGet(callInfo);
|
||||
// return anim ? jerry_number((double)anim->duration) : jerry_number(0.0);
|
||||
// }
|
||||
moduleBaseFunction(moduleAnimationSetLoop) {
|
||||
animation_t *anim = moduleAnimationGet(callInfo);
|
||||
if(!anim) return moduleBaseThrow("Invalid Animation instance");
|
||||
if(argc < 1) return moduleBaseThrow("Expected boolean");
|
||||
if(jerry_value_is_true(args[0])) {
|
||||
anim->flags |= ANIMATION_FLAG_LOOP_ENABLED;
|
||||
} else {
|
||||
anim->flags &= ~ANIMATION_FLAG_LOOP_ENABLED;
|
||||
}
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleAnimationGetTime) {
|
||||
animation_t *anim = moduleAnimationGet(callInfo);
|
||||
return anim ? jerry_number((double)anim->time) : jerry_number(0.0);
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleAnimationGetDuration) {
|
||||
animation_t *anim = moduleAnimationGet(callInfo);
|
||||
return anim
|
||||
? jerry_number((double)animationGetDuration(anim))
|
||||
: jerry_number(0.0);
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleAnimationGetOnStart) {
|
||||
jsAnimation_t *janim = moduleAnimationGetWrapper(callInfo);
|
||||
if(!janim || !janim->onStartCallback) return jerry_null();
|
||||
return jerry_value_copy(janim->onStartCallback);
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleAnimationSetOnStart) {
|
||||
jsAnimation_t *janim = moduleAnimationGetWrapper(callInfo);
|
||||
if(!janim) return moduleBaseThrow("Invalid Animation instance");
|
||||
|
||||
if(janim->onStartCallback) {
|
||||
eventUnsubscribe(&janim->anim.onStart, jsAnimationOnStartBridge, janim);
|
||||
jerry_value_free(janim->onStartCallback);
|
||||
janim->onStartCallback = 0;
|
||||
}
|
||||
if(argc >= 1 && jerry_value_is_function(args[0])) {
|
||||
janim->onStartCallback = jerry_value_copy(args[0]);
|
||||
eventSubscribe(&janim->anim.onStart, jsAnimationOnStartBridge, janim);
|
||||
}
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleAnimationGetOnComplete) {
|
||||
jsAnimation_t *janim = moduleAnimationGetWrapper(callInfo);
|
||||
if(!janim || !janim->onCompleteCallback) return jerry_null();
|
||||
return jerry_value_copy(janim->onCompleteCallback);
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleAnimationSetOnComplete) {
|
||||
jsAnimation_t *janim = moduleAnimationGetWrapper(callInfo);
|
||||
if(!janim) return moduleBaseThrow("Invalid Animation instance");
|
||||
|
||||
if(janim->onCompleteCallback) {
|
||||
eventUnsubscribe(
|
||||
&janim->anim.onComplete, jsAnimationOnCompleteBridge, janim
|
||||
);
|
||||
jerry_value_free(janim->onCompleteCallback);
|
||||
janim->onCompleteCallback = 0;
|
||||
}
|
||||
if(argc >= 1 && jerry_value_is_function(args[0])) {
|
||||
janim->onCompleteCallback = jerry_value_copy(args[0]);
|
||||
eventSubscribe(&janim->anim.onComplete, jsAnimationOnCompleteBridge, janim);
|
||||
}
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
static void moduleAnimation(void) {
|
||||
// scriptProtoInit(
|
||||
// &MODULE_ANIMATION_PROTO,
|
||||
// "Animation",
|
||||
// sizeof(jsAnimation_t),
|
||||
// moduleAnimationConstructor
|
||||
// );
|
||||
scriptProtoInit(
|
||||
&MODULE_ANIMATION_PROP_PROTO, NULL, sizeof(jsAnimationProperty_t), NULL
|
||||
);
|
||||
MODULE_ANIMATION_PROP_PROTO.info.free_cb = jsAnimationPropertyFree;
|
||||
scriptProtoDefineProp(
|
||||
&MODULE_ANIMATION_PROP_PROTO, "value",
|
||||
moduleAnimationPropGetValue, NULL
|
||||
);
|
||||
|
||||
// scriptProtoDefineFunc(
|
||||
// &MODULE_ANIMATION_PROTO, "update", moduleAnimationUpdate
|
||||
// );
|
||||
// scriptProtoDefineFunc(
|
||||
// &MODULE_ANIMATION_PROTO, "getValue", moduleAnimationGetValue
|
||||
// );
|
||||
// scriptProtoDefineFunc(
|
||||
// &MODULE_ANIMATION_PROTO, "reset", moduleAnimationReset
|
||||
// );
|
||||
// scriptProtoDefineProp(
|
||||
// &MODULE_ANIMATION_PROTO, "complete",
|
||||
// moduleAnimationGetComplete, NULL
|
||||
// );
|
||||
// scriptProtoDefineProp(
|
||||
// &MODULE_ANIMATION_PROTO, "loop",
|
||||
// moduleAnimationGetLoop, moduleAnimationSetLoop
|
||||
// );
|
||||
// scriptProtoDefineProp(
|
||||
// &MODULE_ANIMATION_PROTO, "time",
|
||||
// moduleAnimationGetTime, NULL
|
||||
// );
|
||||
// scriptProtoDefineProp(
|
||||
// &MODULE_ANIMATION_PROTO, "duration",
|
||||
// moduleAnimationGetDuration, NULL
|
||||
// );
|
||||
scriptProtoInit(
|
||||
&MODULE_ANIMATION_PROTO,
|
||||
"Animation",
|
||||
sizeof(jsAnimation_t),
|
||||
moduleAnimationConstructor
|
||||
);
|
||||
MODULE_ANIMATION_PROTO.info.free_cb = jsAnimationFree;
|
||||
|
||||
scriptProtoDefineFunc(
|
||||
&MODULE_ANIMATION_PROTO, "update", moduleAnimationUpdate
|
||||
);
|
||||
scriptProtoDefineFunc(
|
||||
&MODULE_ANIMATION_PROTO, "reset", moduleAnimationReset
|
||||
);
|
||||
scriptProtoDefineProp(
|
||||
&MODULE_ANIMATION_PROTO, "complete",
|
||||
moduleAnimationGetComplete, NULL
|
||||
);
|
||||
scriptProtoDefineProp(
|
||||
&MODULE_ANIMATION_PROTO, "loop",
|
||||
moduleAnimationGetLoop, moduleAnimationSetLoop
|
||||
);
|
||||
scriptProtoDefineProp(
|
||||
&MODULE_ANIMATION_PROTO, "time",
|
||||
moduleAnimationGetTime, NULL
|
||||
);
|
||||
scriptProtoDefineProp(
|
||||
&MODULE_ANIMATION_PROTO, "duration",
|
||||
moduleAnimationGetDuration, NULL
|
||||
);
|
||||
scriptProtoDefineProp(
|
||||
&MODULE_ANIMATION_PROTO, "onStart",
|
||||
moduleAnimationGetOnStart, moduleAnimationSetOnStart
|
||||
);
|
||||
scriptProtoDefineProp(
|
||||
&MODULE_ANIMATION_PROTO, "onComplete",
|
||||
moduleAnimationGetOnComplete, moduleAnimationSetOnComplete
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
// 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 "event/event.h"
|
||||
#include "assert/assert.h"
|
||||
|
||||
#define MODULE_EVENT_POOL_MAX 32
|
||||
|
||||
typedef struct {
|
||||
jerry_value_t callback; // owned copy; slot is free when event == NULL
|
||||
event_t *event;
|
||||
} jsEventSub_t;
|
||||
|
||||
static jsEventSub_t MODULE_EVENT_POOL[MODULE_EVENT_POOL_MAX];
|
||||
|
||||
static void moduleEventBridge(void *params, void *user) {
|
||||
(void)params;
|
||||
jsEventSub_t *sub = (jsEventSub_t *)user;
|
||||
jerry_value_t ret = jerry_call(sub->callback, jerry_undefined(), NULL, 0);
|
||||
jerry_value_free(ret);
|
||||
}
|
||||
|
||||
static bool_t moduleEventCallbackEquals(
|
||||
const jerry_value_t a,
|
||||
const jerry_value_t b
|
||||
) {
|
||||
jerry_value_t result = jerry_binary_op(JERRY_BIN_OP_STRICT_EQUAL, a, b);
|
||||
bool_t eq = jerry_value_is_true(result);
|
||||
jerry_value_free(result);
|
||||
return eq;
|
||||
}
|
||||
|
||||
static void moduleEventJsSubscribe(event_t *event, jerry_value_t callback) {
|
||||
assertNotNull(event, "event must not be NULL");
|
||||
|
||||
for(uint8_t i = 0; i < MODULE_EVENT_POOL_MAX; i++) {
|
||||
if(MODULE_EVENT_POOL[i].event != event) continue;
|
||||
if(moduleEventCallbackEquals(MODULE_EVENT_POOL[i].callback, callback)) return;
|
||||
}
|
||||
|
||||
for(uint8_t i = 0; i < MODULE_EVENT_POOL_MAX; i++) {
|
||||
if(MODULE_EVENT_POOL[i].event != NULL) continue;
|
||||
MODULE_EVENT_POOL[i].callback = jerry_value_copy(callback);
|
||||
MODULE_EVENT_POOL[i].event = event;
|
||||
eventSubscribe(event, moduleEventBridge, &MODULE_EVENT_POOL[i]);
|
||||
return;
|
||||
}
|
||||
|
||||
assertUnreachable("MODULE_EVENT_POOL_MAX exceeded");
|
||||
}
|
||||
|
||||
static void moduleEventJsUnsubscribe(event_t *event, jerry_value_t callback) {
|
||||
assertNotNull(event, "event must not be NULL");
|
||||
|
||||
for(uint8_t i = 0; i < MODULE_EVENT_POOL_MAX; i++) {
|
||||
if(MODULE_EVENT_POOL[i].event != event) continue;
|
||||
if(!moduleEventCallbackEquals(MODULE_EVENT_POOL[i].callback, callback)) continue;
|
||||
|
||||
eventUnsubscribe(event, moduleEventBridge, &MODULE_EVENT_POOL[i]);
|
||||
jerry_value_free(MODULE_EVENT_POOL[i].callback);
|
||||
MODULE_EVENT_POOL[i].callback = 0;
|
||||
MODULE_EVENT_POOL[i].event = NULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "script/module/modulebase.h"
|
||||
#include "script/scriptproto.h"
|
||||
#include "script/module/math/modulevec2.h"
|
||||
#include "script/module/event/moduleEvent.h"
|
||||
#include "input/input.h"
|
||||
|
||||
static scriptproto_t MODULE_INPUT_PROTO;
|
||||
@@ -130,6 +131,54 @@ moduleBaseFunction(moduleInputAxis2D) {
|
||||
return moduleVec2Push(result);
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleInputOnPressed) {
|
||||
if(argc < 2) return moduleBaseThrow("Input.onPressed: expected (action, fn)");
|
||||
moduleBaseRequireNumber(0);
|
||||
moduleBaseRequireFunction(1);
|
||||
const inputaction_t action = (inputaction_t)jerry_value_as_number(args[0]);
|
||||
if(action <= INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) {
|
||||
return moduleBaseThrow("Input.onPressed: invalid action");
|
||||
}
|
||||
moduleEventJsSubscribe(&INPUT.actions[action].onPressed, args[1]);
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleInputOffPressed) {
|
||||
if(argc < 2) return moduleBaseThrow("Input.offPressed: expected (action, fn)");
|
||||
moduleBaseRequireNumber(0);
|
||||
moduleBaseRequireFunction(1);
|
||||
const inputaction_t action = (inputaction_t)jerry_value_as_number(args[0]);
|
||||
if(action <= INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) {
|
||||
return moduleBaseThrow("Input.offPressed: invalid action");
|
||||
}
|
||||
moduleEventJsUnsubscribe(&INPUT.actions[action].onPressed, args[1]);
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleInputOnReleased) {
|
||||
if(argc < 2) return moduleBaseThrow("Input.onReleased: expected (action, fn)");
|
||||
moduleBaseRequireNumber(0);
|
||||
moduleBaseRequireFunction(1);
|
||||
const inputaction_t action = (inputaction_t)jerry_value_as_number(args[0]);
|
||||
if(action <= INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) {
|
||||
return moduleBaseThrow("Input.onReleased: invalid action");
|
||||
}
|
||||
moduleEventJsSubscribe(&INPUT.actions[action].onReleased, args[1]);
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleInputOffReleased) {
|
||||
if(argc < 2) return moduleBaseThrow("Input.offReleased: expected (action, fn)");
|
||||
moduleBaseRequireNumber(0);
|
||||
moduleBaseRequireFunction(1);
|
||||
const inputaction_t action = (inputaction_t)jerry_value_as_number(args[0]);
|
||||
if(action <= INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) {
|
||||
return moduleBaseThrow("Input.offReleased: invalid action");
|
||||
}
|
||||
moduleEventJsUnsubscribe(&INPUT.actions[action].onReleased, args[1]);
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
static void moduleInput(void) {
|
||||
moduleBaseEval(INPUT_ACTION_SCRIPT);
|
||||
|
||||
@@ -174,4 +223,16 @@ static void moduleInput(void) {
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_INPUT_PROTO, "axis2D", moduleInputAxis2D
|
||||
);
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_INPUT_PROTO, "onPressed", moduleInputOnPressed
|
||||
);
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_INPUT_PROTO, "offPressed", moduleInputOffPressed
|
||||
);
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_INPUT_PROTO, "onReleased", moduleInputOnReleased
|
||||
);
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_INPUT_PROTO, "offReleased", moduleInputOffReleased
|
||||
);
|
||||
}
|
||||
@@ -25,7 +25,9 @@
|
||||
#include "script/module/engine/moduleengine.h"
|
||||
#include "script/module/item/moduleitem.h"
|
||||
#include "script/module/story/modulestory.h"
|
||||
#include "script/module/event/moduleEvent.h"
|
||||
#include "script/module/ui/moduletextbox.h"
|
||||
#include "script/module/ui/modulefullbox.h"
|
||||
|
||||
static void moduleRegister(void) {
|
||||
moduleInclude();
|
||||
@@ -48,4 +50,5 @@ static void moduleRegister(void) {
|
||||
moduleItem();
|
||||
moduleStory();
|
||||
moduleTextbox();
|
||||
moduleFullbox();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
// 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 "script/module/display/modulecolor.h"
|
||||
#include "script/module/event/moduleEvent.h"
|
||||
#include "ui/uifullbox.h"
|
||||
#include "animation/easing.h"
|
||||
|
||||
static scriptproto_t MODULE_FULLBOX_UNDER_PROTO;
|
||||
static scriptproto_t MODULE_FULLBOX_OVER_PROTO;
|
||||
|
||||
// Stored to allow getter to return the current callback without pool search.
|
||||
static jerry_value_t MODULE_FULLBOX_UNDER_CALLBACK = 0;
|
||||
static jerry_value_t MODULE_FULLBOX_OVER_CALLBACK = 0;
|
||||
|
||||
static easingtype_t moduleFullboxReadEasing(jerry_value_t val) {
|
||||
if(jerry_value_is_number(val)) {
|
||||
return (easingtype_t)(int32_t)jerry_value_as_number(val);
|
||||
}
|
||||
jerry_value_t typeKey = jerry_string_sz("type");
|
||||
jerry_value_t typeVal = jerry_object_get(val, typeKey);
|
||||
jerry_value_free(typeKey);
|
||||
easingtype_t type = EASING_LINEAR;
|
||||
if(jerry_value_is_number(typeVal)) {
|
||||
type = (easingtype_t)(int32_t)jerry_value_as_number(typeVal);
|
||||
}
|
||||
jerry_value_free(typeVal);
|
||||
return type;
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleFullboxUnderTransition) {
|
||||
if(argc < 4) {
|
||||
return moduleBaseThrow("FullboxUnder.transition: expected 4 arguments");
|
||||
}
|
||||
|
||||
color_t *from = (color_t*)scriptProtoGetValue(&MODULE_COLOR_PROTO, args[0]);
|
||||
if(!from) return moduleBaseThrow("FullboxUnder.transition: arg 0 must be a Color");
|
||||
|
||||
color_t *to = (color_t*)scriptProtoGetValue(&MODULE_COLOR_PROTO, args[1]);
|
||||
if(!to) return moduleBaseThrow("FullboxUnder.transition: arg 1 must be a Color");
|
||||
|
||||
if(!jerry_value_is_number(args[2])) {
|
||||
return moduleBaseThrow("FullboxUnder.transition: arg 2 must be a number");
|
||||
}
|
||||
|
||||
uiFullboxTransition(
|
||||
&UI_FULLBOX_UNDER,
|
||||
*from, *to,
|
||||
(float_t)jerry_value_as_number(args[2]),
|
||||
moduleFullboxReadEasing(args[3])
|
||||
);
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleFullboxOverTransition) {
|
||||
if(argc < 4) {
|
||||
return moduleBaseThrow("FullboxOver.transition: expected 4 arguments");
|
||||
}
|
||||
|
||||
color_t *from = (color_t*)scriptProtoGetValue(&MODULE_COLOR_PROTO, args[0]);
|
||||
if(!from) return moduleBaseThrow("FullboxOver.transition: arg 0 must be a Color");
|
||||
|
||||
color_t *to = (color_t*)scriptProtoGetValue(&MODULE_COLOR_PROTO, args[1]);
|
||||
if(!to) return moduleBaseThrow("FullboxOver.transition: arg 1 must be a Color");
|
||||
|
||||
if(!jerry_value_is_number(args[2])) {
|
||||
return moduleBaseThrow("FullboxOver.transition: arg 2 must be a number");
|
||||
}
|
||||
|
||||
uiFullboxTransition(
|
||||
&UI_FULLBOX_OVER,
|
||||
*from, *to,
|
||||
(float_t)jerry_value_as_number(args[2]),
|
||||
moduleFullboxReadEasing(args[3])
|
||||
);
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleFullboxUnderGetOnTransitionEnd) {
|
||||
(void)callInfo; (void)args; (void)argc;
|
||||
if(!MODULE_FULLBOX_UNDER_CALLBACK) return jerry_null();
|
||||
return jerry_value_copy(MODULE_FULLBOX_UNDER_CALLBACK);
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleFullboxUnderSetOnTransitionEnd) {
|
||||
(void)callInfo;
|
||||
if(MODULE_FULLBOX_UNDER_CALLBACK) {
|
||||
moduleEventJsUnsubscribe(
|
||||
&UI_FULLBOX_UNDER.onTransitionEnd, MODULE_FULLBOX_UNDER_CALLBACK
|
||||
);
|
||||
jerry_value_free(MODULE_FULLBOX_UNDER_CALLBACK);
|
||||
MODULE_FULLBOX_UNDER_CALLBACK = 0;
|
||||
}
|
||||
if(argc >= 1 && jerry_value_is_function(args[0])) {
|
||||
MODULE_FULLBOX_UNDER_CALLBACK = jerry_value_copy(args[0]);
|
||||
moduleEventJsSubscribe(&UI_FULLBOX_UNDER.onTransitionEnd, args[0]);
|
||||
}
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleFullboxOverGetOnTransitionEnd) {
|
||||
(void)callInfo; (void)args; (void)argc;
|
||||
if(!MODULE_FULLBOX_OVER_CALLBACK) return jerry_null();
|
||||
return jerry_value_copy(MODULE_FULLBOX_OVER_CALLBACK);
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleFullboxOverSetOnTransitionEnd) {
|
||||
(void)callInfo;
|
||||
if(MODULE_FULLBOX_OVER_CALLBACK) {
|
||||
moduleEventJsUnsubscribe(
|
||||
&UI_FULLBOX_OVER.onTransitionEnd, MODULE_FULLBOX_OVER_CALLBACK
|
||||
);
|
||||
jerry_value_free(MODULE_FULLBOX_OVER_CALLBACK);
|
||||
MODULE_FULLBOX_OVER_CALLBACK = 0;
|
||||
}
|
||||
if(argc >= 1 && jerry_value_is_function(args[0])) {
|
||||
MODULE_FULLBOX_OVER_CALLBACK = jerry_value_copy(args[0]);
|
||||
moduleEventJsSubscribe(&UI_FULLBOX_OVER.onTransitionEnd, args[0]);
|
||||
}
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
static void moduleFullbox(void) {
|
||||
scriptProtoInit(&MODULE_FULLBOX_UNDER_PROTO, "FullboxUnder", sizeof(uint8_t), NULL);
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_FULLBOX_UNDER_PROTO, "transition", moduleFullboxUnderTransition
|
||||
);
|
||||
scriptProtoDefineStaticProp(
|
||||
&MODULE_FULLBOX_UNDER_PROTO, "onTransitionEnd",
|
||||
moduleFullboxUnderGetOnTransitionEnd, moduleFullboxUnderSetOnTransitionEnd
|
||||
);
|
||||
|
||||
scriptProtoInit(&MODULE_FULLBOX_OVER_PROTO, "FullboxOver", sizeof(uint8_t), NULL);
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_FULLBOX_OVER_PROTO, "transition", moduleFullboxOverTransition
|
||||
);
|
||||
scriptProtoDefineStaticProp(
|
||||
&MODULE_FULLBOX_OVER_PROTO, "onTransitionEnd",
|
||||
moduleFullboxOverGetOnTransitionEnd, moduleFullboxOverSetOnTransitionEnd
|
||||
);
|
||||
}
|
||||
@@ -6,8 +6,12 @@
|
||||
#pragma once
|
||||
#include "script/module/modulebase.h"
|
||||
#include "script/scriptproto.h"
|
||||
#include "script/module/event/moduleEvent.h"
|
||||
#include "ui/uitextbox.h"
|
||||
|
||||
static jerry_value_t MODULE_TEXTBOX_PAGE_COMPLETE_CALLBACK = 0;
|
||||
static jerry_value_t MODULE_TEXTBOX_LAST_PAGE_CALLBACK = 0;
|
||||
|
||||
static scriptproto_t MODULE_TEXTBOX_PROTO;
|
||||
|
||||
moduleBaseFunction(moduleTextboxSetText) {
|
||||
@@ -77,6 +81,50 @@ moduleBaseFunction(moduleTextboxGetPageCount) {
|
||||
return jerry_number((double)UI_TEXTBOX.pageCount);
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleTextboxGetOnPageComplete) {
|
||||
(void)callInfo; (void)args; (void)argc;
|
||||
if(!MODULE_TEXTBOX_PAGE_COMPLETE_CALLBACK) return jerry_null();
|
||||
return jerry_value_copy(MODULE_TEXTBOX_PAGE_COMPLETE_CALLBACK);
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleTextboxSetOnPageComplete) {
|
||||
(void)callInfo;
|
||||
if(MODULE_TEXTBOX_PAGE_COMPLETE_CALLBACK) {
|
||||
moduleEventJsUnsubscribe(
|
||||
&UI_TEXTBOX.onPageComplete, MODULE_TEXTBOX_PAGE_COMPLETE_CALLBACK
|
||||
);
|
||||
jerry_value_free(MODULE_TEXTBOX_PAGE_COMPLETE_CALLBACK);
|
||||
MODULE_TEXTBOX_PAGE_COMPLETE_CALLBACK = 0;
|
||||
}
|
||||
if(argc >= 1 && jerry_value_is_function(args[0])) {
|
||||
MODULE_TEXTBOX_PAGE_COMPLETE_CALLBACK = jerry_value_copy(args[0]);
|
||||
moduleEventJsSubscribe(&UI_TEXTBOX.onPageComplete, args[0]);
|
||||
}
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleTextboxGetOnLastPage) {
|
||||
(void)callInfo; (void)args; (void)argc;
|
||||
if(!MODULE_TEXTBOX_LAST_PAGE_CALLBACK) return jerry_null();
|
||||
return jerry_value_copy(MODULE_TEXTBOX_LAST_PAGE_CALLBACK);
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleTextboxSetOnLastPage) {
|
||||
(void)callInfo;
|
||||
if(MODULE_TEXTBOX_LAST_PAGE_CALLBACK) {
|
||||
moduleEventJsUnsubscribe(
|
||||
&UI_TEXTBOX.onLastPage, MODULE_TEXTBOX_LAST_PAGE_CALLBACK
|
||||
);
|
||||
jerry_value_free(MODULE_TEXTBOX_LAST_PAGE_CALLBACK);
|
||||
MODULE_TEXTBOX_LAST_PAGE_CALLBACK = 0;
|
||||
}
|
||||
if(argc >= 1 && jerry_value_is_function(args[0])) {
|
||||
MODULE_TEXTBOX_LAST_PAGE_CALLBACK = jerry_value_copy(args[0]);
|
||||
moduleEventJsSubscribe(&UI_TEXTBOX.onLastPage, args[0]);
|
||||
}
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
static void moduleTextbox(void) {
|
||||
scriptProtoInit(
|
||||
&MODULE_TEXTBOX_PROTO, "Textbox", sizeof(uint8_t), NULL
|
||||
@@ -119,4 +167,12 @@ static void moduleTextbox(void) {
|
||||
&MODULE_TEXTBOX_PROTO, "pageCount",
|
||||
moduleTextboxGetPageCount, NULL
|
||||
);
|
||||
scriptProtoDefineStaticProp(
|
||||
&MODULE_TEXTBOX_PROTO, "onPageComplete",
|
||||
moduleTextboxGetOnPageComplete, moduleTextboxSetOnPageComplete
|
||||
);
|
||||
scriptProtoDefineStaticProp(
|
||||
&MODULE_TEXTBOX_PROTO, "onLastPage",
|
||||
moduleTextboxGetOnLastPage, moduleTextboxSetOnLastPage
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,5 +10,6 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
uifps.c
|
||||
uielement.c
|
||||
uiframe.c
|
||||
uifullbox.c
|
||||
uitextbox.c
|
||||
)
|
||||
@@ -11,6 +11,8 @@
|
||||
#include "display/spritebatch/spritebatch.h"
|
||||
#include "display/screen/screen.h"
|
||||
#include "ui/uielement.h"
|
||||
#include "ui/uifullbox.h"
|
||||
#include "time/time.h"
|
||||
#include "log/log.h"
|
||||
|
||||
ui_t UI;
|
||||
@@ -28,6 +30,8 @@ errorret_t uiInit(void) {
|
||||
}
|
||||
|
||||
void uiUpdate(void) {
|
||||
uiFullboxUpdate(&UI_FULLBOX_UNDER, TIME.delta);
|
||||
uiFullboxUpdate(&UI_FULLBOX_OVER, TIME.delta);
|
||||
}
|
||||
|
||||
errorret_t uiRender(void) {
|
||||
|
||||
@@ -12,12 +12,20 @@
|
||||
#include "ui/uifps.h"
|
||||
#include "engine/engine.h"
|
||||
#include "ui/uitextbox.h"
|
||||
#include "ui/uifullbox.h"
|
||||
|
||||
uielement_t UI_ELEMENTS[] = {
|
||||
// Fullbox under: above scene, below system UI.
|
||||
{ .type = UI_ELEMENT_TYPE_NATIVE, .native = { .draw = uiFullboxUnderDraw } },
|
||||
|
||||
{ .type = UI_ELEMENT_TYPE_SCRIPT, .script = { .script = "ui/test.js" } },
|
||||
|
||||
{ .type = UI_ELEMENT_TYPE_NATIVE, .native = { .draw = consoleDraw } },
|
||||
{ .type = UI_ELEMENT_TYPE_NATIVE, .native = { .draw = uiFPSDraw } },
|
||||
{ .type = UI_ELEMENT_TYPE_NATIVE, .native = { .draw = uiTextboxDraw } },
|
||||
{ .type = UI_ELEMENT_TYPE_SCRIPT, .script = { .script = "ui/test.js" } },
|
||||
|
||||
// Fullbox over: above absolutely everything.
|
||||
{ .type = UI_ELEMENT_TYPE_NATIVE, .native = { .draw = uiFullboxOverDraw } },
|
||||
|
||||
{ .type = UI_ELEMENT_TYPE_NULL },
|
||||
};
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "uifullbox.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
#include "display/screen/screen.h"
|
||||
#include "display/texture/texture.h"
|
||||
#include "display/spritebatch/spritebatch.h"
|
||||
#include "display/shader/shaderunlit.h"
|
||||
|
||||
uifullbox_t UI_FULLBOX_UNDER;
|
||||
uifullbox_t UI_FULLBOX_OVER;
|
||||
|
||||
void uiFullboxInit(uifullbox_t *fullbox) {
|
||||
assertNotNull(fullbox, "fullbox must not be NULL");
|
||||
memoryZero(fullbox, sizeof(uifullbox_t));
|
||||
eventInit(&fullbox->onTransitionEnd);
|
||||
}
|
||||
|
||||
void uiFullboxUpdate(uifullbox_t *fullbox, float_t delta) {
|
||||
assertNotNull(fullbox, "fullbox must not be NULL");
|
||||
if(fullbox->duration <= 0.0f || fullbox->time >= fullbox->duration) return;
|
||||
|
||||
fullbox->time += delta;
|
||||
if(fullbox->time >= fullbox->duration) {
|
||||
fullbox->time = fullbox->duration;
|
||||
eventInvoke(&fullbox->onTransitionEnd, fullbox);
|
||||
}
|
||||
}
|
||||
|
||||
static color_t uiFullboxGetColor(const uifullbox_t *fullbox) {
|
||||
if(fullbox->duration <= 0.0f || fullbox->time >= fullbox->duration) {
|
||||
return fullbox->toColor;
|
||||
}
|
||||
float_t t = easingApply(fullbox->easing, fullbox->time / fullbox->duration);
|
||||
return color4b(
|
||||
(uint8_t)((float_t)fullbox->fromColor.r + ((float_t)fullbox->toColor.r - (float_t)fullbox->fromColor.r) * t),
|
||||
(uint8_t)((float_t)fullbox->fromColor.g + ((float_t)fullbox->toColor.g - (float_t)fullbox->fromColor.g) * t),
|
||||
(uint8_t)((float_t)fullbox->fromColor.b + ((float_t)fullbox->toColor.b - (float_t)fullbox->fromColor.b) * t),
|
||||
(uint8_t)((float_t)fullbox->fromColor.a + ((float_t)fullbox->toColor.a - (float_t)fullbox->fromColor.a) * t)
|
||||
);
|
||||
}
|
||||
|
||||
errorret_t uiFullboxDraw(uifullbox_t *fullbox) {
|
||||
assertNotNull(fullbox, "fullbox must not be NULL");
|
||||
|
||||
color_t color = uiFullboxGetColor(fullbox);
|
||||
if(color.a == 0) errorOk();
|
||||
|
||||
errorChain(shaderSetTexture(&SHADER_UNLIT, SHADER_UNLIT_TEXTURE, &TEXTURE_WHITE));
|
||||
#if MESH_ENABLE_COLOR
|
||||
#else
|
||||
errorChain(shaderSetColor(&SHADER_UNLIT, SHADER_UNLIT_COLOR, color));
|
||||
#endif
|
||||
|
||||
errorChain(spriteBatchPush(
|
||||
0.0f, 0.0f,
|
||||
(float_t)SCREEN.width, (float_t)SCREEN.height,
|
||||
#if MESH_ENABLE_COLOR
|
||||
color,
|
||||
#endif
|
||||
0.0f, 0.0f, 1.0f, 1.0f
|
||||
));
|
||||
errorChain(spriteBatchFlush());
|
||||
errorOk();
|
||||
}
|
||||
|
||||
void uiFullboxTransition(
|
||||
uifullbox_t *fullbox,
|
||||
color_t from,
|
||||
color_t to,
|
||||
float_t duration,
|
||||
easingtype_t easing
|
||||
) {
|
||||
assertNotNull(fullbox, "fullbox must not be NULL");
|
||||
fullbox->fromColor = from;
|
||||
fullbox->toColor = to;
|
||||
fullbox->duration = duration;
|
||||
fullbox->time = 0.0f;
|
||||
fullbox->easing = easing;
|
||||
}
|
||||
|
||||
errorret_t uiFullboxUnderDraw(void) {
|
||||
return uiFullboxDraw(&UI_FULLBOX_UNDER);
|
||||
}
|
||||
|
||||
errorret_t uiFullboxOverDraw(void) {
|
||||
return uiFullboxDraw(&UI_FULLBOX_OVER);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "error/error.h"
|
||||
#include "display/color.h"
|
||||
#include "animation/easing.h"
|
||||
#include "event/event.h"
|
||||
|
||||
typedef struct {
|
||||
color_t fromColor;
|
||||
color_t toColor;
|
||||
float_t duration;
|
||||
float_t time;
|
||||
easingtype_t easing;
|
||||
event_t onTransitionEnd;
|
||||
} uifullbox_t;
|
||||
|
||||
extern uifullbox_t UI_FULLBOX_UNDER;
|
||||
extern uifullbox_t UI_FULLBOX_OVER;
|
||||
|
||||
/**
|
||||
* Initializes a fullbox, zeroing all fields.
|
||||
*
|
||||
* @param fullbox The fullbox to initialize.
|
||||
*/
|
||||
void uiFullboxInit(uifullbox_t *fullbox);
|
||||
|
||||
/**
|
||||
* Advances the fullbox transition. Fires onTransitionEnd once when the
|
||||
* transition completes. Safe to call when no transition is running.
|
||||
*
|
||||
* @param fullbox The fullbox to update.
|
||||
* @param delta Seconds elapsed since last update.
|
||||
*/
|
||||
void uiFullboxUpdate(uifullbox_t *fullbox, float_t delta);
|
||||
|
||||
/**
|
||||
* Renders the fullbox. Skipped entirely when the current alpha is zero.
|
||||
*
|
||||
* @param fullbox The fullbox to draw.
|
||||
* @return Any error that occurs.
|
||||
*/
|
||||
errorret_t uiFullboxDraw(uifullbox_t *fullbox);
|
||||
|
||||
/**
|
||||
* Begins a color transition.
|
||||
*
|
||||
* @param fullbox The fullbox to transition.
|
||||
* @param from Start color.
|
||||
* @param to End color.
|
||||
* @param duration Transition duration in seconds.
|
||||
* @param easing Easing function to apply.
|
||||
*/
|
||||
void uiFullboxTransition(
|
||||
uifullbox_t *fullbox,
|
||||
color_t from,
|
||||
color_t to,
|
||||
float_t duration,
|
||||
easingtype_t easing
|
||||
);
|
||||
|
||||
/**
|
||||
* Draw function for UI_FULLBOX_UNDER (registered in UI_ELEMENTS).
|
||||
*/
|
||||
errorret_t uiFullboxUnderDraw(void);
|
||||
|
||||
/**
|
||||
* Draw function for UI_FULLBOX_OVER (registered in UI_ELEMENTS).
|
||||
*/
|
||||
errorret_t uiFullboxOverDraw(void);
|
||||
+14
-1
@@ -9,6 +9,7 @@
|
||||
#include "util/string.h"
|
||||
#include "time/time.h"
|
||||
#include "input/input.h"
|
||||
#include "event/event.h"
|
||||
#include "display/screen/screen.h"
|
||||
#include "display/texture/texture.h"
|
||||
#include "display/spritebatch/spritebatch.h"
|
||||
@@ -44,6 +45,9 @@ errorret_t uiTextboxInit(void) {
|
||||
UI_TEXTBOX.frame.tileset.uv[1] = 1.0f / 3.0f;
|
||||
UI_TEXTBOX.frame.texture = &TEXTURE_WHITE;
|
||||
|
||||
eventInit(&UI_TEXTBOX.onPageComplete);
|
||||
eventInit(&UI_TEXTBOX.onLastPage);
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
@@ -153,7 +157,9 @@ errorret_t uiTextboxUpdate(void) {
|
||||
if(TIME.dynamicUpdate) errorOk();
|
||||
#endif
|
||||
|
||||
if(!uiTextboxPageIsComplete()) {
|
||||
bool_t wasComplete = uiTextboxPageIsComplete();
|
||||
|
||||
if(!wasComplete) {
|
||||
UI_TEXTBOX.scroll += UI_TEXTBOX_SCROLL_CHARS_PER_TICK;
|
||||
}
|
||||
|
||||
@@ -165,6 +171,13 @@ errorret_t uiTextboxUpdate(void) {
|
||||
}
|
||||
}
|
||||
|
||||
if(!wasComplete && uiTextboxPageIsComplete()) {
|
||||
eventInvoke(&UI_TEXTBOX.onPageComplete, &UI_TEXTBOX);
|
||||
if(!uiTextboxHasNextPage()) {
|
||||
eventInvoke(&UI_TEXTBOX.onLastPage, &UI_TEXTBOX);
|
||||
}
|
||||
}
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "uiframe.h"
|
||||
#include "display/text/text.h"
|
||||
#include "input/inputaction.h"
|
||||
#include "event/event.h"
|
||||
|
||||
#define UI_TEXTBOX_TEXT_MAX 1024
|
||||
#define UI_TEXTBOX_LINES_MAX 64
|
||||
@@ -40,6 +41,9 @@ typedef struct {
|
||||
int32_t currentPage;
|
||||
int32_t scroll;
|
||||
inputaction_t advanceAction;
|
||||
|
||||
event_t onPageComplete;
|
||||
event_t onLastPage;
|
||||
} uitextbox_t;
|
||||
|
||||
extern uitextbox_t UI_TEXTBOX;
|
||||
|
||||
Reference in New Issue
Block a user