Add some script modules

This commit is contained in:
2026-06-02 12:55:32 -05:00
parent 0f8b629e20
commit 82c300b077
12 changed files with 1189 additions and 0 deletions
@@ -0,0 +1,145 @@
/**
* 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/entity/modulecomponent.h"
#include "entity/component/overworld/entityinteractable.h"
#include <stdlib.h>
static scriptproto_t MODULE_INTERACTABLE_PROTO;
moduleBaseFunction(moduleInteractableCtor) {
(void)callInfo; (void)args; (void)argc;
return moduleBaseThrow("Interactable cannot be instantiated with new");
}
static inline jscomponent_t *moduleInteractableSelf(
const jerry_call_info_t *callInfo
) {
return (jscomponent_t *)scriptProtoGetValue(
&MODULE_INTERACTABLE_PROTO, callInfo->this_value
);
}
static void moduleInteractableCb(
const entityid_t entityId,
const componentid_t componentId,
void *user
) {
(void)entityId; (void)componentId;
jerry_value_t fn = *((jerry_value_t *)user);
jerry_value_t ret = jerry_call(fn, jerry_undefined(), NULL, 0);
jerry_value_free(ret);
}
moduleBaseFunction(moduleInteractableGetEntity) {
jscomponent_t *c = moduleInteractableSelf(callInfo);
if(!c) return jerry_undefined();
return jerry_number((double)c->entityId);
}
moduleBaseFunction(moduleInteractableGetId) {
jscomponent_t *c = moduleInteractableSelf(callInfo);
if(!c) return jerry_undefined();
return jerry_number((double)c->componentId);
}
/*
* onInteract getter — reads back the pinned JS function stored on this._cb.
*/
moduleBaseFunction(moduleInteractableGetOnInteract) {
(void)args; (void)argc;
jerry_value_t key = jerry_string_sz("_cb");
jerry_value_t val = jerry_object_get(callInfo->this_value, key);
jerry_value_free(key);
return val;
}
/*
* onInteract setter — pins the JS function on this._cb for GC safety, and
* registers a C trampoline that calls it when the player interacts.
* Passing null/undefined clears the callback.
*/
moduleBaseFunction(moduleInteractableSetOnInteract) {
jscomponent_t *c = moduleInteractableSelf(callInfo);
if(!c) return jerry_undefined();
entityinteractable_t *d = entityInteractableGet(c->entityId, c->componentId);
if(!d) return jerry_undefined();
/* Free previously stored JS reference */
if(d->user) {
jerry_value_free(*((jerry_value_t *)d->user));
free(d->user);
d->user = NULL;
}
jerry_value_t pin = (argc > 0) ? args[0] : jerry_undefined();
jerry_value_t pinKey = jerry_string_sz("_cb");
if(jerry_value_is_function(pin)) {
jerry_value_t *stored = (jerry_value_t *)malloc(sizeof(jerry_value_t));
*stored = jerry_value_copy(pin);
entityInteractableSetCallback(
c->entityId, c->componentId, moduleInteractableCb, stored
);
jerry_object_set(callInfo->this_value, pinKey, pin);
} else {
entityInteractableSetCallback(c->entityId, c->componentId, NULL, NULL);
jerry_value_t undef = jerry_undefined();
jerry_object_set(callInfo->this_value, pinKey, undef);
jerry_value_free(undef);
}
jerry_value_free(pinKey);
return jerry_undefined();
}
moduleBaseFunction(moduleInteractableTrigger) {
(void)args; (void)argc;
jscomponent_t *c = moduleInteractableSelf(callInfo);
if(!c) return jerry_undefined();
entityInteractableTrigger(c->entityId, c->componentId);
return jerry_undefined();
}
moduleBaseFunction(moduleInteractableToString) {
jscomponent_t *c = moduleInteractableSelf(callInfo);
if(!c) return jerry_string_sz("Interactable:invalid");
char_t buf[32];
snprintf(buf, sizeof(buf), "Interactable(%u)", (unsigned)c->componentId);
return jerry_string_sz(buf);
}
static void moduleInteractableInit(void) {
scriptProtoInit(
&MODULE_INTERACTABLE_PROTO, "Interactable",
sizeof(jscomponent_t), moduleInteractableCtor
);
scriptProtoDefineProp(
&MODULE_INTERACTABLE_PROTO, "entity", moduleInteractableGetEntity, NULL
);
scriptProtoDefineProp(
&MODULE_INTERACTABLE_PROTO, "id", moduleInteractableGetId, NULL
);
scriptProtoDefineProp(
&MODULE_INTERACTABLE_PROTO, "onInteract",
moduleInteractableGetOnInteract, moduleInteractableSetOnInteract
);
scriptProtoDefineFunc(
&MODULE_INTERACTABLE_PROTO, "trigger", moduleInteractableTrigger
);
scriptProtoDefineToString(
&MODULE_INTERACTABLE_PROTO, moduleInteractableToString
);
}
static void moduleInteractableDispose(void) {
scriptProtoDispose(&MODULE_INTERACTABLE_PROTO);
}
@@ -8,7 +8,12 @@
#pragma once
#include "script/module/entity/modulecomponent.h"
#include "camera/modulecamera.h"
#include "interactable/moduleinteractable.h"
#include "overworld/moduleoverworld.h"
#include "overworldcamera/moduleoverworldcamera.h"
#include "overworldtrigger/moduleoverworldtrigger.h"
#include "physics/modulephysics.h"
#include "player/moduleplayer.h"
#include "position/moduleposition.h"
#include "renderable/modulerenderable.h"
#include "trigger/moduletrigger.h"
@@ -24,8 +29,18 @@ static jerry_value_t moduleComponentListCreateInstance(
switch(type) {
case COMPONENT_TYPE_CAMERA:
return scriptProtoCreateValue(&MODULE_CAMERA_PROTO, comp);
case COMPONENT_TYPE_INTERACTABLE:
return scriptProtoCreateValue(&MODULE_INTERACTABLE_PROTO, comp);
case COMPONENT_TYPE_OVERWORLD:
return scriptProtoCreateValue(&MODULE_OVERWORLD_PROTO, comp);
case COMPONENT_TYPE_OVERWORLD_CAMERA:
return scriptProtoCreateValue(&MODULE_OVERWORLD_CAMERA_PROTO, comp);
case COMPONENT_TYPE_OVERWORLD_TRIGGER:
return scriptProtoCreateValue(&MODULE_OVERWORLD_TRIGGER_PROTO, comp);
case COMPONENT_TYPE_PHYSICS:
return scriptProtoCreateValue(&MODULE_PHYSICS_PROTO, comp);
case COMPONENT_TYPE_PLAYER:
return scriptProtoCreateValue(&MODULE_PLAYER_PROTO, comp);
case COMPONENT_TYPE_POSITION:
return scriptProtoCreateValue(&MODULE_POSITION_PROTO, comp);
case COMPONENT_TYPE_RENDERABLE:
@@ -39,7 +54,12 @@ static jerry_value_t moduleComponentListCreateInstance(
static void moduleComponentListInit(void) {
moduleCameraInit();
moduleInteractableInit();
moduleOverworldInit();
moduleOverworldCameraInit();
moduleOverworldTriggerInit();
modulePhysicsInit();
modulePlayerInit();
modulePositionInit();
moduleRenderableInit();
moduleTriggerInit();
@@ -49,6 +69,11 @@ static void moduleComponentListDispose(void) {
moduleTriggerDispose();
moduleRenderableDispose();
modulePositionDispose();
modulePlayerDispose();
modulePhysicsDispose();
moduleOverworldTriggerDispose();
moduleOverworldCameraDispose();
moduleOverworldDispose();
moduleInteractableDispose();
moduleCameraDispose();
}
@@ -0,0 +1,168 @@
/**
* 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/entity/modulecomponent.h"
#include "entity/component/overworld/entityoverworld.h"
#include "overworld/facingdir.h"
static scriptproto_t MODULE_OVERWORLD_PROTO;
moduleBaseFunction(moduleOverworldCtor) {
(void)callInfo; (void)args; (void)argc;
return moduleBaseThrow("Overworld cannot be instantiated with new");
}
static inline jscomponent_t *moduleOverworldSelf(
const jerry_call_info_t *callInfo
) {
return (jscomponent_t *)scriptProtoGetValue(
&MODULE_OVERWORLD_PROTO, callInfo->this_value
);
}
moduleBaseFunction(moduleOverworldGetEntity) {
jscomponent_t *c = moduleOverworldSelf(callInfo);
if(!c) return jerry_undefined();
return jerry_number((double)c->entityId);
}
moduleBaseFunction(moduleOverworldGetId) {
jscomponent_t *c = moduleOverworldSelf(callInfo);
if(!c) return jerry_undefined();
return jerry_number((double)c->componentId);
}
moduleBaseFunction(moduleOverworldGetType) {
jscomponent_t *c = moduleOverworldSelf(callInfo);
if(!c) return jerry_undefined();
entityoverworld_t *o = entityOverworldGet(c->entityId, c->componentId);
if(!o) return jerry_undefined();
return jerry_number((double)o->type);
}
moduleBaseFunction(moduleOverworldSetType) {
moduleBaseRequireArgs(1);
jscomponent_t *c = moduleOverworldSelf(callInfo);
if(!c) return jerry_undefined();
entityOverworldSetType(
c->entityId, c->componentId,
(entityoverworldtype_t)moduleBaseArgInt(0)
);
return jerry_undefined();
}
moduleBaseFunction(moduleOverworldGetFacing) {
jscomponent_t *c = moduleOverworldSelf(callInfo);
if(!c) return jerry_undefined();
entityoverworld_t *o = entityOverworldGet(c->entityId, c->componentId);
if(!o) return jerry_undefined();
return jerry_number((double)o->facing);
}
moduleBaseFunction(moduleOverworldSetFacing) {
moduleBaseRequireArgs(1);
jscomponent_t *c = moduleOverworldSelf(callInfo);
if(!c) return jerry_undefined();
entityoverworld_t *o = entityOverworldGet(c->entityId, c->componentId);
if(!o) return jerry_undefined();
o->facing = (facingdir_t)moduleBaseArgInt(0);
return jerry_undefined();
}
moduleBaseFunction(moduleOverworldGetRenderCompId) {
jscomponent_t *c = moduleOverworldSelf(callInfo);
if(!c) return jerry_undefined();
entityoverworld_t *o = entityOverworldGet(c->entityId, c->componentId);
if(!o) return jerry_undefined();
return jerry_number((double)o->renderCompId);
}
moduleBaseFunction(moduleOverworldGetPhysCompId) {
jscomponent_t *c = moduleOverworldSelf(callInfo);
if(!c) return jerry_undefined();
entityoverworld_t *o = entityOverworldGet(c->entityId, c->componentId);
if(!o) return jerry_undefined();
return jerry_number((double)o->physCompId);
}
moduleBaseFunction(moduleOverworldToString) {
jscomponent_t *c = moduleOverworldSelf(callInfo);
if(!c) return jerry_string_sz("Overworld:invalid");
char_t buf[32];
snprintf(buf, sizeof(buf), "Overworld(%u)", (unsigned)c->componentId);
return jerry_string_sz(buf);
}
static void moduleOverworldInit(void) {
scriptProtoInit(
&MODULE_OVERWORLD_PROTO, "Overworld",
sizeof(jscomponent_t), moduleOverworldCtor
);
scriptProtoDefineProp(
&MODULE_OVERWORLD_PROTO, "entity", moduleOverworldGetEntity, NULL
);
scriptProtoDefineProp(
&MODULE_OVERWORLD_PROTO, "id", moduleOverworldGetId, NULL
);
scriptProtoDefineProp(
&MODULE_OVERWORLD_PROTO, "type",
moduleOverworldGetType, moduleOverworldSetType
);
scriptProtoDefineProp(
&MODULE_OVERWORLD_PROTO, "facing",
moduleOverworldGetFacing, moduleOverworldSetFacing
);
scriptProtoDefineProp(
&MODULE_OVERWORLD_PROTO, "renderComponentId",
moduleOverworldGetRenderCompId, NULL
);
scriptProtoDefineProp(
&MODULE_OVERWORLD_PROTO, "physicsComponentId",
moduleOverworldGetPhysCompId, NULL
);
scriptProtoDefineToString(&MODULE_OVERWORLD_PROTO, moduleOverworldToString);
jerry_value_t ctor = MODULE_OVERWORLD_PROTO.constructor;
struct { const char_t *name; int val; } types[] = {
{ "PLAYER", OVERWORLD_ENTITY_TYPE_PLAYER },
{ "NPC", OVERWORLD_ENTITY_TYPE_NPC },
};
for(int i = 0; i < 2; i++) {
jerry_value_t k = jerry_string_sz(types[i].name);
jerry_value_t v = jerry_number((double)types[i].val);
jerry_object_set(ctor, k, v);
jerry_value_free(v);
jerry_value_free(k);
}
struct { const char_t *name; int val; } dirs[] = {
{ "FACING_DOWN", FACING_DIR_DOWN },
{ "FACING_UP", FACING_DIR_UP },
{ "FACING_LEFT", FACING_DIR_LEFT },
{ "FACING_RIGHT", FACING_DIR_RIGHT },
{ "FACING_SOUTH", FACING_DIR_SOUTH },
{ "FACING_NORTH", FACING_DIR_NORTH },
{ "FACING_WEST", FACING_DIR_WEST },
{ "FACING_EAST", FACING_DIR_EAST },
};
for(int i = 0; i < 8; i++) {
jerry_value_t k = jerry_string_sz(dirs[i].name);
jerry_value_t v = jerry_number((double)dirs[i].val);
jerry_object_set(ctor, k, v);
jerry_value_free(v);
jerry_value_free(k);
}
}
static void moduleOverworldDispose(void) {
scriptProtoDispose(&MODULE_OVERWORLD_PROTO);
}
@@ -0,0 +1,201 @@
/**
* 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/math/modulevec3.h"
#include "script/module/entity/modulecomponent.h"
#include "entity/component/overworld/entityoverworldcamera.h"
static scriptproto_t MODULE_OVERWORLD_CAMERA_PROTO;
moduleBaseFunction(moduleOverworldCameraCtor) {
(void)callInfo; (void)args; (void)argc;
return moduleBaseThrow("OverworldCamera cannot be instantiated with new");
}
static inline jscomponent_t *moduleOverworldCameraSelf(
const jerry_call_info_t *callInfo
) {
return (jscomponent_t *)scriptProtoGetValue(
&MODULE_OVERWORLD_CAMERA_PROTO, callInfo->this_value
);
}
moduleBaseFunction(moduleOverworldCameraGetEntity) {
jscomponent_t *c = moduleOverworldCameraSelf(callInfo);
if(!c) return jerry_undefined();
return jerry_number((double)c->entityId);
}
moduleBaseFunction(moduleOverworldCameraGetId) {
jscomponent_t *c = moduleOverworldCameraSelf(callInfo);
if(!c) return jerry_undefined();
return jerry_number((double)c->componentId);
}
moduleBaseFunction(moduleOverworldCameraGetTargetEntity) {
jscomponent_t *c = moduleOverworldCameraSelf(callInfo);
if(!c) return jerry_undefined();
entityoverworldcamera_t *cam = entityOverworldCameraGet(c->entityId, c->componentId);
if(!cam) return jerry_undefined();
return jerry_number((double)cam->targetEntityId);
}
moduleBaseFunction(moduleOverworldCameraSetTargetEntity) {
moduleBaseRequireArgs(1);
jscomponent_t *c = moduleOverworldCameraSelf(callInfo);
if(!c) return jerry_undefined();
entityoverworldcamera_t *cam = entityOverworldCameraGet(c->entityId, c->componentId);
if(!cam) return jerry_undefined();
cam->targetEntityId = (entityid_t)moduleBaseArgInt(0);
return jerry_undefined();
}
moduleBaseFunction(moduleOverworldCameraGetTargetPosComp) {
jscomponent_t *c = moduleOverworldCameraSelf(callInfo);
if(!c) return jerry_undefined();
entityoverworldcamera_t *cam = entityOverworldCameraGet(c->entityId, c->componentId);
if(!cam) return jerry_undefined();
return jerry_number((double)cam->targetPosCompId);
}
moduleBaseFunction(moduleOverworldCameraSetTargetPosComp) {
moduleBaseRequireArgs(1);
jscomponent_t *c = moduleOverworldCameraSelf(callInfo);
if(!c) return jerry_undefined();
entityoverworldcamera_t *cam = entityOverworldCameraGet(c->entityId, c->componentId);
if(!cam) return jerry_undefined();
cam->targetPosCompId = (componentid_t)moduleBaseArgInt(0);
return jerry_undefined();
}
moduleBaseFunction(moduleOverworldCameraGetTargetOffset) {
jscomponent_t *c = moduleOverworldCameraSelf(callInfo);
if(!c) return jerry_undefined();
entityoverworldcamera_t *cam = entityOverworldCameraGet(c->entityId, c->componentId);
if(!cam) return jerry_undefined();
return moduleVec3Push(cam->targetOffset);
}
moduleBaseFunction(moduleOverworldCameraSetTargetOffset) {
moduleBaseRequireArgs(1);
jscomponent_t *c = moduleOverworldCameraSelf(callInfo);
if(!c) return jerry_undefined();
float_t *v = moduleVec3From(args[0]);
if(!v) return moduleBaseThrow("OverworldCamera.targetOffset: expected Vec3");
entityoverworldcamera_t *cam = entityOverworldCameraGet(c->entityId, c->componentId);
if(!cam) return jerry_undefined();
glm_vec3_copy(v, cam->targetOffset);
return jerry_undefined();
}
moduleBaseFunction(moduleOverworldCameraGetEyeOffset) {
jscomponent_t *c = moduleOverworldCameraSelf(callInfo);
if(!c) return jerry_undefined();
entityoverworldcamera_t *cam = entityOverworldCameraGet(c->entityId, c->componentId);
if(!cam) return jerry_undefined();
return moduleVec3Push(cam->eyeOffset);
}
moduleBaseFunction(moduleOverworldCameraSetEyeOffset) {
moduleBaseRequireArgs(1);
jscomponent_t *c = moduleOverworldCameraSelf(callInfo);
if(!c) return jerry_undefined();
float_t *v = moduleVec3From(args[0]);
if(!v) return moduleBaseThrow("OverworldCamera.eyeOffset: expected Vec3");
entityoverworldcamera_t *cam = entityOverworldCameraGet(c->entityId, c->componentId);
if(!cam) return jerry_undefined();
glm_vec3_copy(v, cam->eyeOffset);
return jerry_undefined();
}
moduleBaseFunction(moduleOverworldCameraGetScale) {
jscomponent_t *c = moduleOverworldCameraSelf(callInfo);
if(!c) return jerry_undefined();
entityoverworldcamera_t *cam = entityOverworldCameraGet(c->entityId, c->componentId);
if(!cam) return jerry_undefined();
return jerry_number((double)cam->scale);
}
moduleBaseFunction(moduleOverworldCameraSetScale) {
moduleBaseRequireArgs(1);
jscomponent_t *c = moduleOverworldCameraSelf(callInfo);
if(!c) return jerry_undefined();
entityoverworldcamera_t *cam = entityOverworldCameraGet(c->entityId, c->componentId);
if(!cam) return jerry_undefined();
cam->scale = moduleBaseArgFloat(0);
return jerry_undefined();
}
moduleBaseFunction(moduleOverworldCameraSetTarget) {
moduleBaseRequireArgs(2);
jscomponent_t *c = moduleOverworldCameraSelf(callInfo);
if(!c) return jerry_undefined();
entityOverworldCameraSetTarget(
c->entityId, c->componentId,
(entityid_t)moduleBaseArgInt(0),
(componentid_t)moduleBaseArgInt(1)
);
return jerry_undefined();
}
moduleBaseFunction(moduleOverworldCameraToString) {
jscomponent_t *c = moduleOverworldCameraSelf(callInfo);
if(!c) return jerry_string_sz("OverworldCamera:invalid");
char_t buf[32];
snprintf(buf, sizeof(buf), "OverworldCamera(%u)", (unsigned)c->componentId);
return jerry_string_sz(buf);
}
static void moduleOverworldCameraInit(void) {
scriptProtoInit(
&MODULE_OVERWORLD_CAMERA_PROTO, "OverworldCamera",
sizeof(jscomponent_t), moduleOverworldCameraCtor
);
scriptProtoDefineProp(
&MODULE_OVERWORLD_CAMERA_PROTO, "entity",
moduleOverworldCameraGetEntity, NULL
);
scriptProtoDefineProp(
&MODULE_OVERWORLD_CAMERA_PROTO, "id",
moduleOverworldCameraGetId, NULL
);
scriptProtoDefineProp(
&MODULE_OVERWORLD_CAMERA_PROTO, "targetEntity",
moduleOverworldCameraGetTargetEntity, moduleOverworldCameraSetTargetEntity
);
scriptProtoDefineProp(
&MODULE_OVERWORLD_CAMERA_PROTO, "targetPositionComponent",
moduleOverworldCameraGetTargetPosComp, moduleOverworldCameraSetTargetPosComp
);
scriptProtoDefineProp(
&MODULE_OVERWORLD_CAMERA_PROTO, "targetOffset",
moduleOverworldCameraGetTargetOffset, moduleOverworldCameraSetTargetOffset
);
scriptProtoDefineProp(
&MODULE_OVERWORLD_CAMERA_PROTO, "eyeOffset",
moduleOverworldCameraGetEyeOffset, moduleOverworldCameraSetEyeOffset
);
scriptProtoDefineProp(
&MODULE_OVERWORLD_CAMERA_PROTO, "scale",
moduleOverworldCameraGetScale, moduleOverworldCameraSetScale
);
scriptProtoDefineFunc(
&MODULE_OVERWORLD_CAMERA_PROTO, "setTarget",
moduleOverworldCameraSetTarget
);
scriptProtoDefineToString(
&MODULE_OVERWORLD_CAMERA_PROTO, moduleOverworldCameraToString
);
}
static void moduleOverworldCameraDispose(void) {
scriptProtoDispose(&MODULE_OVERWORLD_CAMERA_PROTO);
}
@@ -0,0 +1,374 @@
/**
* 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/math/modulevec3.h"
#include "script/module/entity/modulecomponent.h"
#include "entity/component/overworld/entityoverworldtrigger.h"
#include <stdlib.h>
static scriptproto_t MODULE_OVERWORLD_TRIGGER_PROTO;
/**
* Heap-allocated struct stored in entityoverworldtrigger_t.user.
* Holds jerry_value_t copies of the four JS callback functions.
*/
typedef struct {
jerry_value_t onEnter;
jerry_value_t onExit;
jerry_value_t onStay;
jerry_value_t onOutside;
} jsoverworldtriggercbs_t;
moduleBaseFunction(moduleOverworldTriggerCtor) {
(void)callInfo; (void)args; (void)argc;
return moduleBaseThrow("OverworldTrigger cannot be instantiated with new");
}
static inline jscomponent_t *moduleOverworldTriggerSelf(
const jerry_call_info_t *callInfo
) {
return (jscomponent_t *)scriptProtoGetValue(
&MODULE_OVERWORLD_TRIGGER_PROTO, callInfo->this_value
);
}
/** Lazily allocates the callback struct and stores it in the trigger's user. */
static jsoverworldtriggercbs_t *moduleOverworldTriggerGetCbs(
entityoverworldtrigger_t *t
) {
if(t->user) return (jsoverworldtriggercbs_t *)t->user;
jsoverworldtriggercbs_t *cbs = (jsoverworldtriggercbs_t *)malloc(
sizeof(jsoverworldtriggercbs_t)
);
cbs->onEnter = jerry_undefined();
cbs->onExit = jerry_undefined();
cbs->onStay = jerry_undefined();
cbs->onOutside = jerry_undefined();
t->user = (void *)cbs;
return cbs;
}
static void moduleOverworldTriggerOnEnterCb(
const entityid_t entityId,
const componentid_t componentId,
void *user
) {
(void)entityId; (void)componentId;
jsoverworldtriggercbs_t *cbs = (jsoverworldtriggercbs_t *)user;
if(!jerry_value_is_function(cbs->onEnter)) return;
jerry_value_t ret = jerry_call(cbs->onEnter, jerry_undefined(), NULL, 0);
jerry_value_free(ret);
}
static void moduleOverworldTriggerOnExitCb(
const entityid_t entityId,
const componentid_t componentId,
void *user
) {
(void)entityId; (void)componentId;
jsoverworldtriggercbs_t *cbs = (jsoverworldtriggercbs_t *)user;
if(!jerry_value_is_function(cbs->onExit)) return;
jerry_value_t ret = jerry_call(cbs->onExit, jerry_undefined(), NULL, 0);
jerry_value_free(ret);
}
static void moduleOverworldTriggerOnStayCb(
const entityid_t entityId,
const componentid_t componentId,
void *user
) {
(void)entityId; (void)componentId;
jsoverworldtriggercbs_t *cbs = (jsoverworldtriggercbs_t *)user;
if(!jerry_value_is_function(cbs->onStay)) return;
jerry_value_t ret = jerry_call(cbs->onStay, jerry_undefined(), NULL, 0);
jerry_value_free(ret);
}
static void moduleOverworldTriggerOnOutsideCb(
const entityid_t entityId,
const componentid_t componentId,
void *user
) {
(void)entityId; (void)componentId;
jsoverworldtriggercbs_t *cbs = (jsoverworldtriggercbs_t *)user;
if(!jerry_value_is_function(cbs->onOutside)) return;
jerry_value_t ret = jerry_call(cbs->onOutside, jerry_undefined(), NULL, 0);
jerry_value_free(ret);
}
// ---- entity / id ----
moduleBaseFunction(moduleOverworldTriggerGetEntity) {
jscomponent_t *c = moduleOverworldTriggerSelf(callInfo);
if(!c) return jerry_undefined();
return jerry_number((double)c->entityId);
}
moduleBaseFunction(moduleOverworldTriggerGetId) {
jscomponent_t *c = moduleOverworldTriggerSelf(callInfo);
if(!c) return jerry_undefined();
return jerry_number((double)c->componentId);
}
// ---- min / max ----
moduleBaseFunction(moduleOverworldTriggerGetMin) {
jscomponent_t *c = moduleOverworldTriggerSelf(callInfo);
if(!c) return jerry_undefined();
entityoverworldtrigger_t *t = entityOverworldTriggerGet(c->entityId, c->componentId);
if(!t) return jerry_undefined();
return moduleVec3Push(t->min);
}
moduleBaseFunction(moduleOverworldTriggerSetMin) {
moduleBaseRequireArgs(1);
jscomponent_t *c = moduleOverworldTriggerSelf(callInfo);
if(!c) return jerry_undefined();
float_t *v = moduleVec3From(args[0]);
if(!v) return moduleBaseThrow("OverworldTrigger.min: expected Vec3");
entityoverworldtrigger_t *t = entityOverworldTriggerGet(c->entityId, c->componentId);
if(!t) return jerry_undefined();
glm_vec3_copy(v, t->min);
return jerry_undefined();
}
moduleBaseFunction(moduleOverworldTriggerGetMax) {
jscomponent_t *c = moduleOverworldTriggerSelf(callInfo);
if(!c) return jerry_undefined();
entityoverworldtrigger_t *t = entityOverworldTriggerGet(c->entityId, c->componentId);
if(!t) return jerry_undefined();
return moduleVec3Push(t->max);
}
moduleBaseFunction(moduleOverworldTriggerSetMax) {
moduleBaseRequireArgs(1);
jscomponent_t *c = moduleOverworldTriggerSelf(callInfo);
if(!c) return jerry_undefined();
float_t *v = moduleVec3From(args[0]);
if(!v) return moduleBaseThrow("OverworldTrigger.max: expected Vec3");
entityoverworldtrigger_t *t = entityOverworldTriggerGet(c->entityId, c->componentId);
if(!t) return jerry_undefined();
glm_vec3_copy(v, t->max);
return jerry_undefined();
}
// ---- playerInside ----
moduleBaseFunction(moduleOverworldTriggerGetPlayerInside) {
jscomponent_t *c = moduleOverworldTriggerSelf(callInfo);
if(!c) return jerry_undefined();
entityoverworldtrigger_t *t = entityOverworldTriggerGet(c->entityId, c->componentId);
if(!t) return jerry_undefined();
return jerry_boolean(t->playerInside);
}
// ---- setBounds ----
moduleBaseFunction(moduleOverworldTriggerSetBounds) {
moduleBaseRequireArgs(2);
jscomponent_t *c = moduleOverworldTriggerSelf(callInfo);
if(!c) return jerry_undefined();
float_t *minV = moduleVec3From(args[0]);
float_t *maxV = moduleVec3From(args[1]);
if(!minV) return moduleBaseThrow("OverworldTrigger.setBounds: expected Vec3 for min");
if(!maxV) return moduleBaseThrow("OverworldTrigger.setBounds: expected Vec3 for max");
entityOverworldTriggerSetBounds(c->entityId, c->componentId, minV, maxV);
return jerry_undefined();
}
// ---- callback helpers ----
/*
* Pins the JS function on this._onXxx for GC safety, stores a copied
* jerry_value_t reference in the cbs struct for the C trampoline, and
* wires up the C callback pointer. Passing null/undefined clears it.
*/
static void moduleOverworldTriggerSetCb(
const jerry_call_info_t *callInfo,
const jerry_value_t *newArgs,
const jerry_length_t newArgc,
jerry_value_t *slot,
entityoverworldtriggercallback_t *cslot,
entityoverworldtriggercallback_t trampolineFn,
const char_t *pinKey
) {
jerry_value_free(*slot);
jerry_value_t pin = (newArgc > 0) ? newArgs[0] : jerry_undefined();
jerry_value_t keyVal = jerry_string_sz(pinKey);
if(jerry_value_is_function(pin)) {
*slot = jerry_value_copy(pin);
*cslot = trampolineFn;
jerry_object_set(callInfo->this_value, keyVal, pin);
} else {
*slot = jerry_undefined();
*cslot = NULL;
jerry_value_t undef = jerry_undefined();
jerry_object_set(callInfo->this_value, keyVal, undef);
jerry_value_free(undef);
}
jerry_value_free(keyVal);
}
static jerry_value_t moduleOverworldTriggerGetPinnedCb(
const jerry_call_info_t *callInfo,
const char_t *pinKey
) {
jerry_value_t key = jerry_string_sz(pinKey);
jerry_value_t val = jerry_object_get(callInfo->this_value, key);
jerry_value_free(key);
return val;
}
// ---- onEnter ----
moduleBaseFunction(moduleOverworldTriggerGetOnEnter) {
(void)args; (void)argc;
return moduleOverworldTriggerGetPinnedCb(callInfo, "_onEnter");
}
moduleBaseFunction(moduleOverworldTriggerSetOnEnter) {
jscomponent_t *c = moduleOverworldTriggerSelf(callInfo);
if(!c) return jerry_undefined();
entityoverworldtrigger_t *t = entityOverworldTriggerGet(c->entityId, c->componentId);
if(!t) return jerry_undefined();
jsoverworldtriggercbs_t *cbs = moduleOverworldTriggerGetCbs(t);
moduleOverworldTriggerSetCb(
callInfo, args, argc,
&cbs->onEnter, &t->onEnter,
moduleOverworldTriggerOnEnterCb, "_onEnter"
);
return jerry_undefined();
}
// ---- onExit ----
moduleBaseFunction(moduleOverworldTriggerGetOnExit) {
(void)args; (void)argc;
return moduleOverworldTriggerGetPinnedCb(callInfo, "_onExit");
}
moduleBaseFunction(moduleOverworldTriggerSetOnExit) {
jscomponent_t *c = moduleOverworldTriggerSelf(callInfo);
if(!c) return jerry_undefined();
entityoverworldtrigger_t *t = entityOverworldTriggerGet(c->entityId, c->componentId);
if(!t) return jerry_undefined();
jsoverworldtriggercbs_t *cbs = moduleOverworldTriggerGetCbs(t);
moduleOverworldTriggerSetCb(
callInfo, args, argc,
&cbs->onExit, &t->onExit,
moduleOverworldTriggerOnExitCb, "_onExit"
);
return jerry_undefined();
}
// ---- onStay ----
moduleBaseFunction(moduleOverworldTriggerGetOnStay) {
(void)args; (void)argc;
return moduleOverworldTriggerGetPinnedCb(callInfo, "_onStay");
}
moduleBaseFunction(moduleOverworldTriggerSetOnStay) {
jscomponent_t *c = moduleOverworldTriggerSelf(callInfo);
if(!c) return jerry_undefined();
entityoverworldtrigger_t *t = entityOverworldTriggerGet(c->entityId, c->componentId);
if(!t) return jerry_undefined();
jsoverworldtriggercbs_t *cbs = moduleOverworldTriggerGetCbs(t);
moduleOverworldTriggerSetCb(
callInfo, args, argc,
&cbs->onStay, &t->onStay,
moduleOverworldTriggerOnStayCb, "_onStay"
);
return jerry_undefined();
}
// ---- onOutside ----
moduleBaseFunction(moduleOverworldTriggerGetOnOutside) {
(void)args; (void)argc;
return moduleOverworldTriggerGetPinnedCb(callInfo, "_onOutside");
}
moduleBaseFunction(moduleOverworldTriggerSetOnOutside) {
jscomponent_t *c = moduleOverworldTriggerSelf(callInfo);
if(!c) return jerry_undefined();
entityoverworldtrigger_t *t = entityOverworldTriggerGet(c->entityId, c->componentId);
if(!t) return jerry_undefined();
jsoverworldtriggercbs_t *cbs = moduleOverworldTriggerGetCbs(t);
moduleOverworldTriggerSetCb(
callInfo, args, argc,
&cbs->onOutside, &t->onOutside,
moduleOverworldTriggerOnOutsideCb, "_onOutside"
);
return jerry_undefined();
}
moduleBaseFunction(moduleOverworldTriggerToString) {
jscomponent_t *c = moduleOverworldTriggerSelf(callInfo);
if(!c) return jerry_string_sz("OverworldTrigger:invalid");
char_t buf[32];
snprintf(buf, sizeof(buf), "OverworldTrigger(%u)", (unsigned)c->componentId);
return jerry_string_sz(buf);
}
static void moduleOverworldTriggerInit(void) {
scriptProtoInit(
&MODULE_OVERWORLD_TRIGGER_PROTO, "OverworldTrigger",
sizeof(jscomponent_t), moduleOverworldTriggerCtor
);
scriptProtoDefineProp(
&MODULE_OVERWORLD_TRIGGER_PROTO, "entity",
moduleOverworldTriggerGetEntity, NULL
);
scriptProtoDefineProp(
&MODULE_OVERWORLD_TRIGGER_PROTO, "id",
moduleOverworldTriggerGetId, NULL
);
scriptProtoDefineProp(
&MODULE_OVERWORLD_TRIGGER_PROTO, "min",
moduleOverworldTriggerGetMin, moduleOverworldTriggerSetMin
);
scriptProtoDefineProp(
&MODULE_OVERWORLD_TRIGGER_PROTO, "max",
moduleOverworldTriggerGetMax, moduleOverworldTriggerSetMax
);
scriptProtoDefineProp(
&MODULE_OVERWORLD_TRIGGER_PROTO, "playerInside",
moduleOverworldTriggerGetPlayerInside, NULL
);
scriptProtoDefineFunc(
&MODULE_OVERWORLD_TRIGGER_PROTO, "setBounds",
moduleOverworldTriggerSetBounds
);
scriptProtoDefineProp(
&MODULE_OVERWORLD_TRIGGER_PROTO, "onEnter",
moduleOverworldTriggerGetOnEnter, moduleOverworldTriggerSetOnEnter
);
scriptProtoDefineProp(
&MODULE_OVERWORLD_TRIGGER_PROTO, "onExit",
moduleOverworldTriggerGetOnExit, moduleOverworldTriggerSetOnExit
);
scriptProtoDefineProp(
&MODULE_OVERWORLD_TRIGGER_PROTO, "onStay",
moduleOverworldTriggerGetOnStay, moduleOverworldTriggerSetOnStay
);
scriptProtoDefineProp(
&MODULE_OVERWORLD_TRIGGER_PROTO, "onOutside",
moduleOverworldTriggerGetOnOutside, moduleOverworldTriggerSetOnOutside
);
scriptProtoDefineToString(
&MODULE_OVERWORLD_TRIGGER_PROTO, moduleOverworldTriggerToString
);
}
static void moduleOverworldTriggerDispose(void) {
scriptProtoDispose(&MODULE_OVERWORLD_TRIGGER_PROTO);
}
@@ -0,0 +1,110 @@
/**
* 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/entity/modulecomponent.h"
#include "entity/component/overworld/entityplayer.h"
static scriptproto_t MODULE_PLAYER_PROTO;
moduleBaseFunction(modulePlayerCtor) {
(void)callInfo; (void)args; (void)argc;
return moduleBaseThrow("Player cannot be instantiated with new");
}
static inline jscomponent_t *modulePlayerSelf(
const jerry_call_info_t *callInfo
) {
return (jscomponent_t *)scriptProtoGetValue(
&MODULE_PLAYER_PROTO, callInfo->this_value
);
}
moduleBaseFunction(modulePlayerGetEntity) {
jscomponent_t *c = modulePlayerSelf(callInfo);
if(!c) return jerry_undefined();
return jerry_number((double)c->entityId);
}
moduleBaseFunction(modulePlayerGetId) {
jscomponent_t *c = modulePlayerSelf(callInfo);
if(!c) return jerry_undefined();
return jerry_number((double)c->componentId);
}
moduleBaseFunction(modulePlayerGetSpeed) {
jscomponent_t *c = modulePlayerSelf(callInfo);
if(!c) return jerry_undefined();
entityplayer_t *p = entityPlayerGet(c->entityId, c->componentId);
if(!p) return jerry_undefined();
return jerry_number((double)p->speed);
}
moduleBaseFunction(modulePlayerSetSpeed) {
moduleBaseRequireArgs(1);
jscomponent_t *c = modulePlayerSelf(callInfo);
if(!c) return jerry_undefined();
entityplayer_t *p = entityPlayerGet(c->entityId, c->componentId);
if(!p) return jerry_undefined();
p->speed = moduleBaseArgFloat(0);
return jerry_undefined();
}
moduleBaseFunction(modulePlayerGetRunSpeed) {
jscomponent_t *c = modulePlayerSelf(callInfo);
if(!c) return jerry_undefined();
entityplayer_t *p = entityPlayerGet(c->entityId, c->componentId);
if(!p) return jerry_undefined();
return jerry_number((double)p->runSpeed);
}
moduleBaseFunction(modulePlayerSetRunSpeed) {
moduleBaseRequireArgs(1);
jscomponent_t *c = modulePlayerSelf(callInfo);
if(!c) return jerry_undefined();
entityplayer_t *p = entityPlayerGet(c->entityId, c->componentId);
if(!p) return jerry_undefined();
p->runSpeed = moduleBaseArgFloat(0);
return jerry_undefined();
}
moduleBaseFunction(modulePlayerToString) {
jscomponent_t *c = modulePlayerSelf(callInfo);
if(!c) return jerry_string_sz("Player:invalid");
char_t buf[32];
snprintf(buf, sizeof(buf), "Player(%u)", (unsigned)c->componentId);
return jerry_string_sz(buf);
}
static void modulePlayerInit(void) {
scriptProtoInit(
&MODULE_PLAYER_PROTO, "Player",
sizeof(jscomponent_t), modulePlayerCtor
);
scriptProtoDefineProp(
&MODULE_PLAYER_PROTO, "entity", modulePlayerGetEntity, NULL
);
scriptProtoDefineProp(
&MODULE_PLAYER_PROTO, "id", modulePlayerGetId, NULL
);
scriptProtoDefineProp(
&MODULE_PLAYER_PROTO, "speed",
modulePlayerGetSpeed, modulePlayerSetSpeed
);
scriptProtoDefineProp(
&MODULE_PLAYER_PROTO, "runSpeed",
modulePlayerGetRunSpeed, modulePlayerSetRunSpeed
);
scriptProtoDefineToString(&MODULE_PLAYER_PROTO, modulePlayerToString);
}
static void modulePlayerDispose(void) {
scriptProtoDispose(&MODULE_PLAYER_PROTO);
}
+26
View File
@@ -0,0 +1,26 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
/**
* Interactable component. Fires a callback when the player activates it.
*
* Assign a JS function to `onInteract` to handle the event from script.
* Call `trigger()` to fire the callback imperatively from C or JS.
*/
interface Interactable extends Component {
/** Called when the player interacts with this entity. Set to null to clear. */
onInteract: (() => void) | null;
/** Fires the registered callback immediately (no-op if none is set). */
trigger(): void;
toString(): string;
}
interface InteractableConstructor {
new(): never;
}
declare var Interactable: InteractableConstructor;
+41
View File
@@ -0,0 +1,41 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
/** Overworld character component (player or NPC). */
interface Overworld extends Component {
/**
* Entity type — `Overworld.PLAYER` or `Overworld.NPC`.
*/
type: number;
/**
* Facing direction — one of the `Overworld.FACING_*` constants.
*/
facing: number;
/** Component ID of the linked Renderable, or INVALID if none. */
readonly renderComponentId: number;
/** Component ID of the linked Physics body, or INVALID if none. */
readonly physicsComponentId: number;
toString(): string;
}
interface OverworldConstructor {
readonly PLAYER: number;
readonly NPC: number;
readonly FACING_DOWN: number;
readonly FACING_UP: number;
readonly FACING_LEFT: number;
readonly FACING_RIGHT: number;
readonly FACING_SOUTH: number;
readonly FACING_NORTH: number;
readonly FACING_WEST: number;
readonly FACING_EAST: number;
new(): never;
}
declare var Overworld: OverworldConstructor;
+35
View File
@@ -0,0 +1,35 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
/**
* Overworld follow-camera component.
* Smoothly tracks a target entity's Position each frame.
*/
interface OverworldCamera extends Component {
/** Entity ID to follow. */
targetEntity: number;
/** Position component ID on the target entity. */
targetPositionComponent: number;
/** World-space offset added to the target's position before placing the camera. */
targetOffset: Vec3;
/** Eye-space offset applied on top of targetOffset. */
eyeOffset: Vec3;
/** Orthographic scale factor (larger = wider view). */
scale: number;
/**
* Convenience setter — equivalent to assigning `targetEntity` and
* `targetPositionComponent` individually.
*/
setTarget(targetEntityId: number, targetPositionComponentId: number): void;
toString(): string;
}
interface OverworldCameraConstructor {
new(): never;
}
declare var OverworldCamera: OverworldCameraConstructor;
+38
View File
@@ -0,0 +1,38 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
/**
* Overworld AABB trigger. Fires callbacks as the player enters, stays inside,
* exits, or remains outside the defined bounding box each frame.
*/
interface OverworldTrigger extends Component {
/** Minimum corner of the trigger AABB. */
min: Vec3;
/** Maximum corner of the trigger AABB. */
max: Vec3;
/** `true` while the player is inside the trigger bounds. */
readonly playerInside: boolean;
/** Fired once when the player first enters the bounds. */
onEnter: (() => void) | null;
/** Fired once when the player leaves the bounds. */
onExit: (() => void) | null;
/** Fired every frame while the player remains inside the bounds. */
onStay: (() => void) | null;
/** Fired every frame while the player remains outside the bounds. */
onOutside: (() => void) | null;
/** Convenience setter for both corners at once. */
setBounds(min: Vec3, max: Vec3): void;
toString(): string;
}
interface OverworldTriggerConstructor {
new(): never;
}
declare var OverworldTrigger: OverworldTriggerConstructor;
+21
View File
@@ -0,0 +1,21 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
/** Player movement component. Controls walk and run speeds. */
interface Player extends Component {
/** Walk speed in world units per second. */
speed: number;
/** Run speed in world units per second. */
runSpeed: number;
toString(): string;
}
interface PlayerConstructor {
new(): never;
}
declare var Player: PlayerConstructor;
+5
View File
@@ -36,7 +36,12 @@
// entity / components
/// <reference path="./entity/component.d.ts" />
/// <reference path="./entity/component/camera.d.ts" />
/// <reference path="./entity/component/interactable.d.ts" />
/// <reference path="./entity/component/overworld.d.ts" />
/// <reference path="./entity/component/overworldcamera.d.ts" />
/// <reference path="./entity/component/overworldtrigger.d.ts" />
/// <reference path="./entity/component/physics.d.ts" />
/// <reference path="./entity/component/player.d.ts" />
/// <reference path="./entity/component/position.d.ts" />
/// <reference path="./entity/component/renderable.d.ts" />
/// <reference path="./entity/component/trigger.d.ts" />