we ball I guess
This commit is contained in:
@@ -0,0 +1,417 @@
|
||||
# Dusk — Claude Code rules
|
||||
|
||||
## File headers
|
||||
Every C, H, and JS file starts with:
|
||||
|
||||
```c
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
```
|
||||
|
||||
JS files use `//` comment style instead.
|
||||
|
||||
---
|
||||
|
||||
## C conventions
|
||||
|
||||
### Types
|
||||
Always use the project-defined aliases instead of bare C primitives:
|
||||
|
||||
| Use | Not |
|
||||
|-----------|--------------|
|
||||
| `bool_t` | `bool` |
|
||||
| `int_t` | `int` |
|
||||
| `float_t` | `float` |
|
||||
| `char_t` | `char` |
|
||||
|
||||
Use `uint8_t`, `uint16_t`, `int32_t`, etc. for fixed-width integers.
|
||||
All struct and enum types end in `_t` (`animation_t`, `errorret_t`, …).
|
||||
|
||||
### Naming
|
||||
- **Functions** — snake_case, prefixed with their module:
|
||||
`assetLock()`, `entityPositionInit()`, `moduleAssetBatchCtor()`
|
||||
- **Struct fields** — camelCase: `keyframeCount`, `localPosition`
|
||||
- **Macros / constants** — UPPER_SNAKE_CASE:
|
||||
`ENTITY_ID_INVALID`, `ERROR_OK`, `COMPONENT_TYPE_COUNT`
|
||||
- **Files** — snake_case matching the primary type: `entityposition.c`,
|
||||
`moduleassetbatch.c`
|
||||
|
||||
### Header files (`.h`)
|
||||
- Use `#pragma once` — no include guards.
|
||||
- Declare every public function, `#define`, and `extern` global.
|
||||
- Write a JSDoc block (`/** … */`) above every declaration explaining
|
||||
purpose, `@param`s, and `@returns`.
|
||||
- Only include headers that the `.h` file itself strictly requires for
|
||||
the types it exposes. Move everything else to the `.c` file.
|
||||
Do not use forward declarations as a workaround — use the real
|
||||
include in the `.c` file instead.
|
||||
|
||||
### Implementation files (`.c`)
|
||||
- Contain function bodies only; no declarations.
|
||||
- Pull in whatever additional includes the implementation needs.
|
||||
|
||||
### Formatting
|
||||
- Hard-wrap all lines at **80 characters**.
|
||||
|
||||
### Error handling
|
||||
Return `errorret_t` from fallible functions. Use these macros:
|
||||
|
||||
```c
|
||||
errorOk(); // return success
|
||||
errorThrow("msg %d", val); // return failure with message
|
||||
errorChain(someCall()); // propagate failure, continue on success
|
||||
errorIsOk(ret) / errorIsNotOk(ret) // test a result
|
||||
errorCatch(ret); // handle + free an error
|
||||
```
|
||||
|
||||
Never return raw error codes or use `errno` for in-engine errors.
|
||||
|
||||
### Memory
|
||||
Use the project allocator — never raw `malloc`/`free`:
|
||||
|
||||
```c
|
||||
memoryAllocate(size) // allocate
|
||||
memoryFree(ptr) // free
|
||||
memoryZero(dest, size) // zero a block
|
||||
memoryCopy(dest, src, size) // copy
|
||||
```
|
||||
|
||||
### Asserts
|
||||
Prefer specific assert macros over bare `assert()`:
|
||||
|
||||
```c
|
||||
assertNotNull(ptr, "msg");
|
||||
assertTrue(cond, "msg");
|
||||
assertFalse(cond, "msg");
|
||||
assertUnreachable("msg");
|
||||
assertIsMainThread("msg");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Build system
|
||||
Each subdirectory has its own `CMakeLists.txt` that adds sources with:
|
||||
|
||||
```cmake
|
||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
myfile.c
|
||||
)
|
||||
```
|
||||
|
||||
Never add source files to the root `CMakeLists.txt` directly.
|
||||
|
||||
---
|
||||
|
||||
## Platform support
|
||||
|
||||
### Targets
|
||||
Set `DUSK_TARGET_SYSTEM` at CMake configure time to select a platform:
|
||||
|
||||
| `DUSK_TARGET_SYSTEM` | Macro defined | Platform |
|
||||
|----------------------|-------------------|------------------|
|
||||
| `linux` | `DUSK_LINUX` | Linux desktop |
|
||||
| `knulli` | `DUSK_KNULLI` | Knulli (handheld)|
|
||||
| `psp` | `DUSK_PSP` | Sony PSP |
|
||||
| `vita` | `DUSK_VITA` | PlayStation Vita |
|
||||
| `gamecube` | `DUSK_GAMECUBE` | Nintendo GameCube|
|
||||
| `wii` | `DUSK_WII` | Nintendo Wii |
|
||||
|
||||
### Layer structure
|
||||
```
|
||||
src/dusk/ core, platform-agnostic game logic
|
||||
src/duskgl/ OpenGL abstraction (Linux, Knulli, PSP, Vita)
|
||||
src/dusksdl2/ SDL2 window + input (Linux, Knulli, PSP, Vita)
|
||||
src/dusklinux/ Linux + Knulli platform impl
|
||||
src/duskpsp/ PSP platform impl
|
||||
src/duskvita/ Vita platform impl
|
||||
src/duskdolphin/ GameCube / Wii platform impl (no SDL2/OpenGL)
|
||||
```
|
||||
|
||||
Dolphin is the only target that bypasses SDL2 and OpenGL entirely —
|
||||
it uses native GameCube/Wii rendering and input APIs.
|
||||
|
||||
### Platform guards
|
||||
Use the compile-time macros for platform-specific code:
|
||||
|
||||
```c
|
||||
#ifdef DUSK_PSP
|
||||
// PSP-only path
|
||||
#elif defined(DUSK_GAMECUBE) || defined(DUSK_WII)
|
||||
// GameCube / Wii path
|
||||
#else
|
||||
// Generic / Linux fallback
|
||||
#endif
|
||||
```
|
||||
|
||||
Additional capability macros set per-target:
|
||||
`DUSK_SDL2`, `DUSK_OPENGL`, `DUSK_OPENGL_ES`, `DUSK_OPENGL_LEGACY`,
|
||||
`DUSK_INPUT_GAMEPAD`, `DUSK_INPUT_KEYBOARD`, `DUSK_INPUT_POINTER`,
|
||||
`DUSK_PLATFORM_ENDIAN_BIG` / `DUSK_PLATFORM_ENDIAN_LITTLE`.
|
||||
|
||||
### Abstraction pattern
|
||||
Platform-specific implementations are wired in via `#define` macros in
|
||||
each platform's `displayplatform.h` / `inputplatform.h` etc., which
|
||||
the core calls through. Functions that a platform does not support are
|
||||
simply left undefined — the core guards calls with `#ifdef`.
|
||||
|
||||
### Adding platform-specific code
|
||||
- Put it under `src/dusk<platform>/` in the matching subsystem folder.
|
||||
- Gate any core call-site with the appropriate `#ifdef DUSK_<PLATFORM>`
|
||||
or capability macro.
|
||||
- Keep the `src/dusk/` core free of platform ifdefs — delegate through
|
||||
the platform header macros instead.
|
||||
|
||||
---
|
||||
|
||||
## Adding a new asset loader type
|
||||
1. Add an enum value to `assetloadertype_t` (before `_COUNT`) in
|
||||
`src/dusk/asset/loader/assetloader.h`.
|
||||
2. Add fields to the input/loading/output unions in `assetloader.h`.
|
||||
3. Implement `assetXxxLoaderSync`, `assetXxxLoaderAsync`, and
|
||||
`assetXxxDispose` in a new `src/dusk/asset/loader/xxx/` directory.
|
||||
4. Register the three callbacks in `ASSET_LOADER_CALLBACKS[]` in
|
||||
`src/dusk/asset/loader/assetloader.c`.
|
||||
5. If user-facing, create a JS module (see below) and a `.d.ts` file.
|
||||
|
||||
---
|
||||
|
||||
## Adding a new entity component
|
||||
1. Create `src/dusk/entity/component/<category>/entityMyComp.h/.c` with
|
||||
struct `entityMyComp_t`, `entityMyCompInit()`, and optionally
|
||||
`entityMyCompDispose()`.
|
||||
2. Add the include to `src/dusk/entity/componentlist.h` header block.
|
||||
3. Add a row to `src/dusk/entity/componentlist.h`:
|
||||
```c
|
||||
X(MYCOMP, entityMyComp_t, myComp, entityMyCompInit, NULL, NULL)
|
||||
```
|
||||
This auto-generates the enum, union field, and definition entry.
|
||||
4. If JS-facing, create the script module and `.d.ts` (see below).
|
||||
|
||||
---
|
||||
|
||||
## Adding a new script (JS) module
|
||||
1. Create `src/dusk/script/module/<category>/moduleMyMod.h/.c`.
|
||||
- Declare `extern scriptproto_t MODULE_MYMOD_PROTO;` in the header.
|
||||
- Use `moduleBaseFunction(name)` to define JS-callable functions.
|
||||
- Register props/funcs in `moduleMyModInit()` with
|
||||
`scriptProtoDefineProp` / `scriptProtoDefineFunc` /
|
||||
`scriptProtoDefineStaticFunc`.
|
||||
2. `#include` the header in
|
||||
`src/dusk/script/module/modulelist.c` and call
|
||||
`moduleMyModInit()` in `moduleListInit()` (and `Dispose` in
|
||||
`moduleListDispose()`).
|
||||
3. For component modules also register in
|
||||
`src/dusk/script/module/entity/component/modulecomponentlist.c`
|
||||
so `entity.add()` returns the typed wrapper.
|
||||
4. Create `types/<category>/mymod.d.ts` and add a
|
||||
`/// <reference path="..." />` line to `types/index.d.ts`.
|
||||
|
||||
---
|
||||
|
||||
## Script module type declarations
|
||||
Whenever a `src/dusk/script/module/**/*.c` file is created or modified,
|
||||
check whether the corresponding `types/**/*.d.ts` needs updating and
|
||||
apply any changes before finishing the task.
|
||||
|
||||
---
|
||||
|
||||
## JavaScript (asset scripts)
|
||||
- Use `var` for module-level state; `const` for values that never
|
||||
change.
|
||||
- Always use semicolons.
|
||||
- Scene objects are plain objects (`var scene = {}`) with assigned
|
||||
methods.
|
||||
- Export via `module.exports = scene`.
|
||||
- Async scene init should use `async function` and `await`.
|
||||
|
||||
---
|
||||
|
||||
## Coding style
|
||||
|
||||
### Indentation
|
||||
2 spaces. No tabs.
|
||||
|
||||
### Keyword and operator spacing
|
||||
No space between a keyword or function name and its opening parenthesis:
|
||||
|
||||
```c
|
||||
if(!ptr) return;
|
||||
for(uint8_t i = 0; i < count; i++) {
|
||||
while(entry->state != DONE) {
|
||||
switch(type) {
|
||||
sizeof(assetbatch_t)
|
||||
memoryZero(ptr, size)
|
||||
```
|
||||
|
||||
Spaces around all binary operators and after every comma:
|
||||
|
||||
```c
|
||||
pos->flags |= ENTITY_POSITION_FLAG_WORLD_DIRTY;
|
||||
(size_t)end - (size_t)start
|
||||
foo(a, b, c)
|
||||
```
|
||||
|
||||
### Braces
|
||||
Opening brace on the **same line** as the statement (K&R style) for all
|
||||
constructs — functions, `if`, `else`, `for`, `while`, `switch`:
|
||||
|
||||
```c
|
||||
void assetEntryLock(assetentry_t *entry) {
|
||||
...
|
||||
}
|
||||
|
||||
if(dirty) {
|
||||
...
|
||||
} else {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### Guard returns
|
||||
Short guards go on one line with no braces:
|
||||
|
||||
```c
|
||||
if(!ptr) return;
|
||||
if(!b || !b->batch) return jerry_undefined();
|
||||
if(!(flags & DIRTY)) return;
|
||||
```
|
||||
|
||||
### Blank lines
|
||||
- One blank line between functions; no blank line at the start or end of
|
||||
a function body.
|
||||
- One blank line between logical blocks inside a function body.
|
||||
- No trailing blank lines at the end of a file.
|
||||
|
||||
### Pointer placement
|
||||
`*` is attached to the variable name, not the type:
|
||||
|
||||
```c
|
||||
assetentry_t *entry
|
||||
const char_t *name
|
||||
void *ptr
|
||||
uint8_t *d = (uint8_t *)dest;
|
||||
```
|
||||
|
||||
### Casts
|
||||
Space between cast and operand:
|
||||
|
||||
```c
|
||||
(assetbatch_t *)user
|
||||
(uint8_t *)dest
|
||||
(textureformat_t)v
|
||||
```
|
||||
|
||||
### Return
|
||||
No parentheses around the return value:
|
||||
|
||||
```c
|
||||
return ptr;
|
||||
return MEMORY_POINTERS_IN_USE;
|
||||
```
|
||||
|
||||
### switch / case
|
||||
`case` indented 2 spaces from `switch`; body indented 2 more from `case`:
|
||||
|
||||
```c
|
||||
switch(type) {
|
||||
case ASSET_LOADER_TYPE_TEXTURE:
|
||||
descs[i].input.texture = (textureformat_t)v;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
```
|
||||
|
||||
### Multi-line function signatures
|
||||
When parameters don't fit on one line, put each on its own line indented
|
||||
2 spaces; the closing `) {` (definition) or `);` (declaration) goes on
|
||||
its own line at column 0:
|
||||
|
||||
```c
|
||||
void assetEntryInit(
|
||||
assetentry_t *entry,
|
||||
const char_t *name,
|
||||
const assetloadertype_t type,
|
||||
assetloaderinput_t *input
|
||||
) {
|
||||
|
||||
errorret_t memoryCompare(
|
||||
const void *a,
|
||||
const void *b,
|
||||
const size_t size
|
||||
);
|
||||
```
|
||||
|
||||
### Structs and enums
|
||||
Anonymous inner struct or enum with a `typedef`, `_t` suffix, closing
|
||||
brace and name on the same line:
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
errorcode_t code;
|
||||
char_t *message;
|
||||
} errorstate_t;
|
||||
|
||||
typedef enum {
|
||||
ASSET_LOADER_TYPE_NULL,
|
||||
ASSET_LOADER_TYPE_COUNT
|
||||
} assetloadertype_t;
|
||||
```
|
||||
|
||||
### Designated initialisers
|
||||
Spaces inside braces; `.field = value`:
|
||||
|
||||
```c
|
||||
jsassetentry_t e = { .entry = entry };
|
||||
assetbatchloadedpend_t init = { .batch = batch };
|
||||
```
|
||||
|
||||
### Ternary operator
|
||||
Spaces around `?` and `:`:
|
||||
|
||||
```c
|
||||
const float val = psx > 0.0f ? pt[0][0] / psx : 0.0f;
|
||||
```
|
||||
|
||||
### const placement
|
||||
`const` before the type, `*` attached to the variable:
|
||||
|
||||
```c
|
||||
const char_t *name
|
||||
const void *src
|
||||
const size_t size
|
||||
```
|
||||
|
||||
### Comments in `.c` files
|
||||
- Block comments that describe a section use the divider style:
|
||||
```c
|
||||
/* ---- Public API ---- */
|
||||
```
|
||||
- Multi-line explanatory comments inside function bodies use `//` lines:
|
||||
```c
|
||||
// Script modules are freed; orphaned JS wrapper objects now get GC'd
|
||||
// so their finalizers fire before assetDispose() checks ref counts.
|
||||
jerry_heap_gc(JERRY_GC_PRESSURE_HIGH);
|
||||
```
|
||||
- Do not use `/* */` for inline or inline-block comments inside `.c`
|
||||
function bodies.
|
||||
|
||||
### Comments in `.h` files
|
||||
Every public declaration gets a Javadoc block (`/** … */`) with
|
||||
`@param` and `@returns` where relevant. Keep it on the lines immediately
|
||||
above the declaration with no blank line in between.
|
||||
|
||||
---
|
||||
|
||||
## Tests
|
||||
- Tests live in `test/` mirroring `src/dusk/` structure.
|
||||
- Use cmocka; include `dusktest.h`.
|
||||
- Test functions: `static void test_something(void **state)`.
|
||||
- After each test, assert `memoryGetAllocatedCount() == 0` to catch
|
||||
leaks.
|
||||
- Build with `-DDUSK_BUILD_TESTS=ON`.
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) 2026 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
const PLAYER_SPEED = 5.0;
|
||||
|
||||
var player = {};
|
||||
|
||||
var _entity, _position, _physics;
|
||||
|
||||
player.create = function(texEntry) {
|
||||
_entity = Entity.create();
|
||||
_position = _entity.add(Component.POSITION);
|
||||
_physics = _entity.add(Component.PHYSICS);
|
||||
|
||||
_physics.bodyType = Physics.DYNAMIC;
|
||||
_physics.shape = Physics.SHAPE_CUBE;
|
||||
_physics.gravityScale = 1.0;
|
||||
|
||||
var r = _entity.add(Component.RENDERABLE);
|
||||
r.texture = texEntry.texture;
|
||||
r.type = Renderable.SPRITEBATCH;
|
||||
r.color = new Color(220, 80, 80);
|
||||
// upright quad: (-0.5,0,0) → (0.5,1,0) in XY plane
|
||||
r.sprites = [[-0.5, 0, 0, 0.5, 1, 0, 0, 0, 1, 1]];
|
||||
|
||||
_position.localPosition = new Vec3(0, 1, 0);
|
||||
};
|
||||
|
||||
player.getPosition = function() {
|
||||
return _position;
|
||||
};
|
||||
|
||||
player.update = function() {
|
||||
if(!_physics) return;
|
||||
var vx = Input.axis(INPUT_ACTION_LEFT, INPUT_ACTION_RIGHT) * PLAYER_SPEED;
|
||||
var vz = Input.axis(INPUT_ACTION_DOWN, INPUT_ACTION_UP) * PLAYER_SPEED;
|
||||
// Preserve vertical velocity so gravity and landing work correctly.
|
||||
var vy = _physics.velocity.y;
|
||||
_physics.velocity = new Vec3(vx, vy, vz);
|
||||
};
|
||||
|
||||
player.dispose = function() {
|
||||
Entity.dispose(_entity);
|
||||
_entity = null;
|
||||
_position = null;
|
||||
_physics = null;
|
||||
};
|
||||
|
||||
module.exports = player;
|
||||
+50
-25
@@ -1,47 +1,72 @@
|
||||
var scene = {};
|
||||
// Copyright (c) 2026 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
var scene = {};
|
||||
var player = require('player.js');
|
||||
|
||||
var assets = AssetBatch([
|
||||
{ path: 'test.png', type: Asset.TYPE_TEXTURE, format: Texture.FORMAT_RGBA }
|
||||
]);
|
||||
|
||||
var cam;
|
||||
var camPos;
|
||||
var testEntity;
|
||||
var testPos;
|
||||
var testRenderable;
|
||||
var texEntry;
|
||||
// Pokemon DS-style camera: ~34 degrees elevation (atan(6/9)).
|
||||
// CAM_HEIGHT / CAM_DIST ratio controls the tilt — keep it under 0.7 for
|
||||
// the characteristically shallow DS angle.
|
||||
const CAM_HEIGHT = 6;
|
||||
const CAM_DIST = 9;
|
||||
|
||||
var cam, camPos;
|
||||
var floorEntity;
|
||||
|
||||
function updateCamera() {
|
||||
var pp = player.getPosition().worldPosition;
|
||||
// Position is offset above and behind the player; lookAt the exact player
|
||||
// world position so the player projects to the center pixel every frame.
|
||||
camPos.localPosition = new Vec3(pp.x, pp.y + CAM_HEIGHT, pp.z + CAM_DIST);
|
||||
camPos.lookAt(new Vec3(pp.x, pp.y, pp.z));
|
||||
}
|
||||
|
||||
scene.init = async function() {
|
||||
assets.lock();
|
||||
await assets.loaded();
|
||||
|
||||
Console.print('Scene Init');
|
||||
texEntry = assets.entry(0);
|
||||
var texEntry = assets.entry(0);
|
||||
|
||||
// Camera at (3, 3, 3) looking at origin
|
||||
// Camera
|
||||
cam = Entity.create();
|
||||
camPos = cam.add(Component.POSITION);
|
||||
cam.add(Component.CAMERA);
|
||||
camPos.localPosition = new Vec3(3, 3, 3);
|
||||
camPos.lookAt(new Vec3(0, 0, 0));
|
||||
|
||||
// Test entity with textured quad at origin
|
||||
testEntity = Entity.create();
|
||||
testPos = testEntity.add(Component.POSITION);
|
||||
testRenderable = testEntity.add(Component.RENDERABLE);
|
||||
// Floor — infinite static plane at Y=0. Rendered as a large flat blue
|
||||
// slab using the default SHADER_MATERIAL (no texture needed).
|
||||
floorEntity = Entity.create();
|
||||
var floorPos = floorEntity.add(Component.POSITION);
|
||||
var floorPhysics = floorEntity.add(Component.PHYSICS);
|
||||
floorPhysics.bodyType = Physics.STATIC;
|
||||
floorPhysics.shape = Physics.SHAPE_PLANE;
|
||||
|
||||
testRenderable.texture = texEntry.texture;
|
||||
testRenderable.type = Renderable.SPRITEBATCH;
|
||||
testRenderable.sprites = [
|
||||
[0, 0, 1, 1, 0, 1, 1, 0]
|
||||
];
|
||||
// testPos.localPosition = new Vec3(0, 0, 0);
|
||||
}
|
||||
var floorR = floorEntity.add(Component.RENDERABLE);
|
||||
floorR.color = Color.BLUE;
|
||||
floorPos.localScale = new Vec3(16, 0.2, 16);
|
||||
floorPos.localPosition = new Vec3(0, -0.1, 0);
|
||||
|
||||
// Player — spawns 1 unit above the floor so physics drops it cleanly.
|
||||
player.create(texEntry);
|
||||
|
||||
// Initialise camera at the correct angle from the player's spawn position.
|
||||
updateCamera();
|
||||
};
|
||||
|
||||
scene.update = function() {
|
||||
player.update();
|
||||
updateCamera();
|
||||
};
|
||||
|
||||
scene.dispose = function() {
|
||||
Console.print('Scene Dispose');
|
||||
player.dispose();
|
||||
Entity.dispose(floorEntity);
|
||||
Entity.dispose(cam);
|
||||
Entity.dispose(testEntity);
|
||||
assets.unlock();
|
||||
};
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ console_t CONSOLE;
|
||||
|
||||
void consoleInit(void) {
|
||||
memoryZero(&CONSOLE, sizeof(console_t));
|
||||
CONSOLE.visible = true;
|
||||
|
||||
#ifdef DUSK_CONSOLE_POSIX
|
||||
threadMutexInit(&CONSOLE.printMutex);
|
||||
|
||||
@@ -10,6 +10,7 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
)
|
||||
|
||||
# Subdirs
|
||||
add_subdirectory(animation)
|
||||
add_subdirectory(asset)
|
||||
add_subdirectory(console)
|
||||
add_subdirectory(display)
|
||||
@@ -17,7 +18,13 @@ add_subdirectory(engine)
|
||||
add_subdirectory(entity)
|
||||
add_subdirectory(event)
|
||||
add_subdirectory(input)
|
||||
add_subdirectory(item)
|
||||
add_subdirectory(locale)
|
||||
add_subdirectory(math)
|
||||
add_subdirectory(overworld)
|
||||
add_subdirectory(require)
|
||||
add_subdirectory(save)
|
||||
add_subdirectory(scene)
|
||||
add_subdirectory(system)
|
||||
add_subdirectory(story)
|
||||
add_subdirectory(system)
|
||||
add_subdirectory(ui)
|
||||
@@ -0,0 +1,10 @@
|
||||
# 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
|
||||
moduleanimation.c
|
||||
moduleeasing.c
|
||||
)
|
||||
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "moduleanimation.h"
|
||||
#include "animation/animation.h"
|
||||
|
||||
typedef struct {
|
||||
keyframe_t keyframes[MODULE_ANIMATION_KEYFRAME_MAX];
|
||||
animation_t anim;
|
||||
} moduleanimationdata_t;
|
||||
|
||||
scriptproto_t MODULE_ANIMATION_PROTO;
|
||||
|
||||
moduleBaseFunction(moduleAnimationCtor) {
|
||||
if(argc < 1 || !jerry_value_is_array(args[0])) {
|
||||
return moduleBaseThrow(
|
||||
"Animation: expected array of keyframe objects"
|
||||
);
|
||||
}
|
||||
uint32_t len = jerry_array_length(args[0]);
|
||||
if(len == 0) {
|
||||
return moduleBaseThrow(
|
||||
"Animation: keyframe array must not be empty"
|
||||
);
|
||||
}
|
||||
const uint16_t count = len > (uint32_t)MODULE_ANIMATION_KEYFRAME_MAX
|
||||
? (uint16_t)MODULE_ANIMATION_KEYFRAME_MAX
|
||||
: (uint16_t)len;
|
||||
|
||||
moduleanimationdata_t *d = (moduleanimationdata_t *)memoryAllocate(
|
||||
sizeof(moduleanimationdata_t)
|
||||
);
|
||||
|
||||
for(uint16_t i = 0; i < count; i++) {
|
||||
jerry_value_t elem = jerry_object_get_index(args[0], (uint32_t)i);
|
||||
jerry_value_t jtm = moduleBaseGetProp(elem, "time");
|
||||
jerry_value_t jvl = moduleBaseGetProp(elem, "value");
|
||||
jerry_value_t jea = moduleBaseGetProp(elem, "easing");
|
||||
|
||||
d->keyframes[i].time = moduleBaseValueFloat(jtm);
|
||||
d->keyframes[i].value = moduleBaseValueFloat(jvl);
|
||||
d->keyframes[i].easing = jerry_value_is_number(jea)
|
||||
? (easingtype_t)moduleBaseValueInt(jea)
|
||||
: EASING_LINEAR;
|
||||
|
||||
jerry_value_free(jea);
|
||||
jerry_value_free(jvl);
|
||||
jerry_value_free(jtm);
|
||||
jerry_value_free(elem);
|
||||
}
|
||||
|
||||
animationInit(&d->anim, d->keyframes, count);
|
||||
|
||||
jerry_object_set_native_ptr(
|
||||
callInfo->this_value, &MODULE_ANIMATION_PROTO.info, d
|
||||
);
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleAnimationGetValue) {
|
||||
moduleBaseRequireArgs(1);
|
||||
moduleBaseRequireNumber(0);
|
||||
moduleanimationdata_t *d = (moduleanimationdata_t *)scriptProtoGetValue(
|
||||
&MODULE_ANIMATION_PROTO, callInfo->this_value
|
||||
);
|
||||
if(!d) return jerry_undefined();
|
||||
return jerry_number(
|
||||
(double)animationGetValue(&d->anim, moduleBaseArgFloat(0))
|
||||
);
|
||||
}
|
||||
|
||||
void moduleAnimationInit(void) {
|
||||
scriptProtoInit(
|
||||
&MODULE_ANIMATION_PROTO, "Animation",
|
||||
sizeof(moduleanimationdata_t), moduleAnimationCtor
|
||||
);
|
||||
scriptProtoDefineFunc(
|
||||
&MODULE_ANIMATION_PROTO, "getValue", moduleAnimationGetValue
|
||||
);
|
||||
}
|
||||
|
||||
void moduleAnimationDispose(void) {
|
||||
scriptProtoDispose(&MODULE_ANIMATION_PROTO);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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 "util/memory.h"
|
||||
|
||||
/** Maximum keyframes per Animation instance. */
|
||||
#define MODULE_ANIMATION_KEYFRAME_MAX 64
|
||||
|
||||
extern scriptproto_t MODULE_ANIMATION_PROTO;
|
||||
|
||||
/**
|
||||
* new Animation(keyframes) — creates an Animation from a JS array of
|
||||
* `{time, value, easing?}` descriptor objects.
|
||||
*
|
||||
* @param args[0] Array of keyframe descriptors.
|
||||
*/
|
||||
moduleBaseFunction(moduleAnimationCtor);
|
||||
|
||||
/**
|
||||
* animation.getValue(time) — interpolates the animation at `time`.
|
||||
*
|
||||
* @param args[0] Time in seconds (number).
|
||||
* @return Interpolated float value.
|
||||
*/
|
||||
moduleBaseFunction(moduleAnimationGetValue);
|
||||
|
||||
/**
|
||||
* Initializes the Animation module and registers the Animation constructor.
|
||||
*/
|
||||
void moduleAnimationInit(void);
|
||||
|
||||
/**
|
||||
* Disposes the Animation module.
|
||||
*/
|
||||
void moduleAnimationDispose(void);
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "moduleeasing.h"
|
||||
#include "animation/easing.h"
|
||||
|
||||
scriptproto_t MODULE_EASING_PROTO;
|
||||
|
||||
moduleBaseFunction(moduleEasingApply) {
|
||||
moduleBaseRequireArgs(2);
|
||||
moduleBaseRequireNumber(0);
|
||||
moduleBaseRequireNumber(1);
|
||||
const int32_t type = moduleBaseArgInt(0);
|
||||
if(type < 0 || type >= (int32_t)EASING_COUNT) {
|
||||
return moduleBaseThrow("Easing.apply: invalid easing type");
|
||||
}
|
||||
const float_t t = moduleBaseArgFloat(1);
|
||||
return jerry_number((double)easingApply((easingtype_t)type, t));
|
||||
}
|
||||
|
||||
void moduleEasingInit(void) {
|
||||
scriptProtoInit(&MODULE_EASING_PROTO, "Easing", 0, NULL);
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_EASING_PROTO, "apply", moduleEasingApply
|
||||
);
|
||||
|
||||
moduleBaseSetInt("EASING_LINEAR", EASING_LINEAR);
|
||||
moduleBaseSetInt("EASING_IN_SINE", EASING_IN_SINE);
|
||||
moduleBaseSetInt("EASING_OUT_SINE", EASING_OUT_SINE);
|
||||
moduleBaseSetInt("EASING_IN_OUT_SINE", EASING_IN_OUT_SINE);
|
||||
moduleBaseSetInt("EASING_IN_QUAD", EASING_IN_QUAD);
|
||||
moduleBaseSetInt("EASING_OUT_QUAD", EASING_OUT_QUAD);
|
||||
moduleBaseSetInt("EASING_IN_OUT_QUAD", EASING_IN_OUT_QUAD);
|
||||
moduleBaseSetInt("EASING_IN_CUBIC", EASING_IN_CUBIC);
|
||||
moduleBaseSetInt("EASING_OUT_CUBIC", EASING_OUT_CUBIC);
|
||||
moduleBaseSetInt("EASING_IN_OUT_CUBIC", EASING_IN_OUT_CUBIC);
|
||||
moduleBaseSetInt("EASING_IN_QUART", EASING_IN_QUART);
|
||||
moduleBaseSetInt("EASING_OUT_QUART", EASING_OUT_QUART);
|
||||
moduleBaseSetInt("EASING_IN_OUT_QUART", EASING_IN_OUT_QUART);
|
||||
moduleBaseSetInt("EASING_IN_BACK", EASING_IN_BACK);
|
||||
moduleBaseSetInt("EASING_OUT_BACK", EASING_OUT_BACK);
|
||||
moduleBaseSetInt("EASING_IN_OUT_BACK", EASING_IN_OUT_BACK);
|
||||
}
|
||||
|
||||
void moduleEasingDispose(void) {
|
||||
scriptProtoDispose(&MODULE_EASING_PROTO);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 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"
|
||||
|
||||
extern scriptproto_t MODULE_EASING_PROTO;
|
||||
|
||||
/**
|
||||
* Easing.apply(type, t) — applies easing function `type` to normalized
|
||||
* time `t` and returns the eased value.
|
||||
*
|
||||
* @param args[0] Easing type constant (EASING_*).
|
||||
* @param args[1] Normalized input time in [0, 1].
|
||||
* @return Eased value as a number.
|
||||
*/
|
||||
moduleBaseFunction(moduleEasingApply);
|
||||
|
||||
/**
|
||||
* Initializes the Easing module and injects all EASING_* constants.
|
||||
*/
|
||||
void moduleEasingInit(void);
|
||||
|
||||
/**
|
||||
* Disposes the Easing module.
|
||||
*/
|
||||
void moduleEasingDispose(void);
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
#include "modulephysics.h"
|
||||
#include "util/memory.h"
|
||||
|
||||
scriptproto_t MODULE_PHYSICS_PROTO;
|
||||
|
||||
@@ -64,8 +65,29 @@ moduleBaseFunction(modulePhysicsSetShape) {
|
||||
moduleBaseRequireArgs(1);
|
||||
jscomponent_t *c = modulePhysicsSelf(callInfo);
|
||||
if(!c) return jerry_undefined();
|
||||
physicsshape_t shape = entityPhysicsGetShape(c->entityId, c->componentId);
|
||||
physicsshape_t shape;
|
||||
memoryZero(&shape, sizeof(physicsshape_t));
|
||||
shape.type = (physicshapetype_t)moduleBaseArgInt(0);
|
||||
switch(shape.type) {
|
||||
case PHYSICS_SHAPE_CUBE:
|
||||
shape.data.cube.halfExtents[0] = 0.5f;
|
||||
shape.data.cube.halfExtents[1] = 0.5f;
|
||||
shape.data.cube.halfExtents[2] = 0.5f;
|
||||
break;
|
||||
case PHYSICS_SHAPE_SPHERE:
|
||||
shape.data.sphere.radius = 0.5f;
|
||||
break;
|
||||
case PHYSICS_SHAPE_CAPSULE:
|
||||
shape.data.capsule.radius = 0.5f;
|
||||
shape.data.capsule.halfHeight = 0.5f;
|
||||
break;
|
||||
case PHYSICS_SHAPE_PLANE:
|
||||
shape.data.plane.normal[1] = 1.0f;
|
||||
shape.data.plane.distance = 0.0f;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
entityPhysicsSetShape(c->entityId, c->componentId, shape);
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# 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
|
||||
moduleitem.c
|
||||
modulebackpack.c
|
||||
)
|
||||
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "modulebackpack.h"
|
||||
#include "moduleitem.h"
|
||||
#include "item/backpack.h"
|
||||
|
||||
scriptproto_t MODULE_BACKPACK_PROTO;
|
||||
|
||||
moduleBaseFunction(moduleBackpackGetIsFull) {
|
||||
return jerry_boolean(inventoryIsFull(&BACKPACK));
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleBackpackGetCount) {
|
||||
moduleBaseRequireArgs(1);
|
||||
moduleItemRequireId(0, "Backpack.getCount");
|
||||
const itemid_t id = (itemid_t)moduleBaseArgInt(0);
|
||||
return jerry_number((double)inventoryGetCount(&BACKPACK, id));
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleBackpackHas) {
|
||||
moduleBaseRequireArgs(1);
|
||||
moduleItemRequireId(0, "Backpack.has");
|
||||
const itemid_t id = (itemid_t)moduleBaseArgInt(0);
|
||||
return jerry_boolean(inventoryItemExists(&BACKPACK, id));
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleBackpackIsItemFull) {
|
||||
moduleBaseRequireArgs(1);
|
||||
moduleItemRequireId(0, "Backpack.isItemFull");
|
||||
const itemid_t id = (itemid_t)moduleBaseArgInt(0);
|
||||
return jerry_boolean(inventoryItemFull(&BACKPACK, id));
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleBackpackSet) {
|
||||
moduleBaseRequireArgs(2);
|
||||
moduleItemRequireId(0, "Backpack.set");
|
||||
moduleBaseRequireNumber(1);
|
||||
const int32_t qty = moduleBaseArgInt(1);
|
||||
if(qty < 0 || qty > (int32_t)ITEM_STACK_QUANTITY_MAX) {
|
||||
return moduleBaseThrow("Backpack.set: quantity out of range (0-255)");
|
||||
}
|
||||
inventorySet(&BACKPACK, (itemid_t)moduleBaseArgInt(0), (uint8_t)qty);
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleBackpackAdd) {
|
||||
moduleBaseRequireArgs(2);
|
||||
moduleItemRequireId(0, "Backpack.add");
|
||||
moduleBaseRequireNumber(1);
|
||||
const int32_t qty = moduleBaseArgInt(1);
|
||||
if(qty < 1 || qty > (int32_t)ITEM_STACK_QUANTITY_MAX) {
|
||||
return moduleBaseThrow("Backpack.add: quantity out of range (1-255)");
|
||||
}
|
||||
inventoryAdd(&BACKPACK, (itemid_t)moduleBaseArgInt(0), (uint8_t)qty);
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleBackpackRemove) {
|
||||
moduleBaseRequireArgs(1);
|
||||
moduleItemRequireId(0, "Backpack.remove");
|
||||
inventoryRemove(&BACKPACK, (itemid_t)moduleBaseArgInt(0));
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleBackpackSort) {
|
||||
moduleBaseRequireArgs(1);
|
||||
moduleBaseRequireNumber(0);
|
||||
const inventorysort_t sortBy = (inventorysort_t)moduleBaseArgInt(0);
|
||||
if((int32_t)sortBy < 0 || sortBy >= INVENTORY_SORT_COUNT) {
|
||||
return moduleBaseThrow("Backpack.sort: invalid sort type");
|
||||
}
|
||||
const bool_t reverse = (argc > 1 && jerry_value_is_true(args[1]));
|
||||
inventorySort(&BACKPACK, sortBy, reverse);
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
void moduleBackpackInit(void) {
|
||||
scriptProtoInit(
|
||||
&MODULE_BACKPACK_PROTO, "Backpack", sizeof(uint8_t), NULL
|
||||
);
|
||||
|
||||
scriptProtoDefineStaticProp(
|
||||
&MODULE_BACKPACK_PROTO, "isFull", moduleBackpackGetIsFull, NULL
|
||||
);
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_BACKPACK_PROTO, "getCount", moduleBackpackGetCount
|
||||
);
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_BACKPACK_PROTO, "has", moduleBackpackHas
|
||||
);
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_BACKPACK_PROTO, "isItemFull", moduleBackpackIsItemFull
|
||||
);
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_BACKPACK_PROTO, "set", moduleBackpackSet
|
||||
);
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_BACKPACK_PROTO, "add", moduleBackpackAdd
|
||||
);
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_BACKPACK_PROTO, "remove", moduleBackpackRemove
|
||||
);
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_BACKPACK_PROTO, "sort", moduleBackpackSort
|
||||
);
|
||||
|
||||
moduleBaseSetInt(
|
||||
"INVENTORY_SORT_BY_ID", (int32_t)INVENTORY_SORT_BY_ID
|
||||
);
|
||||
moduleBaseSetInt(
|
||||
"INVENTORY_SORT_BY_TYPE", (int32_t)INVENTORY_SORT_BY_TYPE
|
||||
);
|
||||
}
|
||||
|
||||
void moduleBackpackDispose(void) {
|
||||
scriptProtoDispose(&MODULE_BACKPACK_PROTO);
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* 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"
|
||||
|
||||
extern scriptproto_t MODULE_BACKPACK_PROTO;
|
||||
|
||||
/**
|
||||
* Backpack.isFull — true when all storage slots are occupied.
|
||||
*/
|
||||
moduleBaseFunction(moduleBackpackGetIsFull);
|
||||
|
||||
/**
|
||||
* Backpack.getCount(itemId) — quantity of itemId (0 if absent).
|
||||
*
|
||||
* @param args[0] Item ID constant (ITEM_ID_*).
|
||||
*/
|
||||
moduleBaseFunction(moduleBackpackGetCount);
|
||||
|
||||
/**
|
||||
* Backpack.has(itemId) — true if itemId is present with quantity > 0.
|
||||
*
|
||||
* @param args[0] Item ID constant (ITEM_ID_*).
|
||||
*/
|
||||
moduleBaseFunction(moduleBackpackHas);
|
||||
|
||||
/**
|
||||
* Backpack.isItemFull(itemId) — true if the stack is at max quantity.
|
||||
*
|
||||
* @param args[0] Item ID constant (ITEM_ID_*).
|
||||
*/
|
||||
moduleBaseFunction(moduleBackpackIsItemFull);
|
||||
|
||||
/**
|
||||
* Backpack.set(itemId, quantity) — set quantity; removes when 0.
|
||||
*
|
||||
* @param args[0] Item ID constant (ITEM_ID_*).
|
||||
* @param args[1] Quantity 0–255.
|
||||
*/
|
||||
moduleBaseFunction(moduleBackpackSet);
|
||||
|
||||
/**
|
||||
* Backpack.add(itemId, quantity) — add quantity units of itemId.
|
||||
*
|
||||
* @param args[0] Item ID constant (ITEM_ID_*).
|
||||
* @param args[1] Quantity to add (1–255).
|
||||
*/
|
||||
moduleBaseFunction(moduleBackpackAdd);
|
||||
|
||||
/**
|
||||
* Backpack.remove(itemId) — removes itemId entirely from the backpack.
|
||||
*
|
||||
* @param args[0] Item ID constant (ITEM_ID_*).
|
||||
*/
|
||||
moduleBaseFunction(moduleBackpackRemove);
|
||||
|
||||
/**
|
||||
* Backpack.sort(sortBy[, reverse]) — sort the backpack contents.
|
||||
*
|
||||
* @param args[0] Sort type (INVENTORY_SORT_BY_ID or
|
||||
* INVENTORY_SORT_BY_TYPE).
|
||||
* @param args[1] Optional boolean; true to reverse order.
|
||||
*/
|
||||
moduleBaseFunction(moduleBackpackSort);
|
||||
|
||||
/**
|
||||
* Initializes the Backpack module, registers all Backpack methods, and
|
||||
* sets INVENTORY_SORT_BY_ID / INVENTORY_SORT_BY_TYPE as globals.
|
||||
*/
|
||||
void moduleBackpackInit(void);
|
||||
|
||||
/**
|
||||
* Disposes the Backpack module.
|
||||
*/
|
||||
void moduleBackpackDispose(void);
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "moduleitem.h"
|
||||
#include <string.h>
|
||||
|
||||
scriptproto_t MODULE_ITEM_PROTO;
|
||||
|
||||
moduleBaseFunction(moduleItemGetName) {
|
||||
moduleBaseRequireArgs(1);
|
||||
moduleItemRequireId(0, "Item.getName");
|
||||
const itemid_t id = (itemid_t)moduleBaseArgInt(0);
|
||||
return jerry_string_sz(ITEMS[id].name);
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleItemGetType) {
|
||||
moduleBaseRequireArgs(1);
|
||||
moduleItemRequireId(0, "Item.getType");
|
||||
const itemid_t id = (itemid_t)moduleBaseArgInt(0);
|
||||
return jerry_number((double)ITEMS[id].type);
|
||||
}
|
||||
|
||||
void moduleItemInit(void) {
|
||||
scriptProtoInit(&MODULE_ITEM_PROTO, "Item", sizeof(uint8_t), NULL);
|
||||
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_ITEM_PROTO, "getName", moduleItemGetName
|
||||
);
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_ITEM_PROTO, "getType", moduleItemGetType
|
||||
);
|
||||
|
||||
jerry_value_t result = jerry_eval(
|
||||
(const jerry_char_t *)ITEM_SCRIPT,
|
||||
strlen(ITEM_SCRIPT),
|
||||
JERRY_PARSE_NO_OPTS
|
||||
);
|
||||
jerry_value_free(result);
|
||||
}
|
||||
|
||||
void moduleItemDispose(void) {
|
||||
scriptProtoDispose(&MODULE_ITEM_PROTO);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* 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 "item/item.h"
|
||||
|
||||
extern scriptproto_t MODULE_ITEM_PROTO;
|
||||
|
||||
/**
|
||||
* Validates an itemid_t argument and returns a type error if invalid.
|
||||
*
|
||||
* @param i Argument index.
|
||||
* @param ctx Context string for error messages.
|
||||
*/
|
||||
#define moduleItemRequireId(i, ctx) do { \
|
||||
if(!jerry_value_is_number(args[(i)])) { \
|
||||
return moduleBaseThrow(ctx ": itemId must be a number"); \
|
||||
} \
|
||||
const itemid_t _id = (itemid_t)moduleBaseArgInt(i); \
|
||||
if(_id <= ITEM_ID_NULL || _id >= ITEM_ID_COUNT) { \
|
||||
return moduleBaseThrow(ctx ": invalid item ID"); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/**
|
||||
* Item.getName(itemId) — returns the item's string name.
|
||||
*
|
||||
* @param args[0] Item ID constant (ITEM_ID_*).
|
||||
*/
|
||||
moduleBaseFunction(moduleItemGetName);
|
||||
|
||||
/**
|
||||
* Item.getType(itemId) — returns the item's type constant.
|
||||
*
|
||||
* @param args[0] Item ID constant (ITEM_ID_*).
|
||||
*/
|
||||
moduleBaseFunction(moduleItemGetType);
|
||||
|
||||
/**
|
||||
* Initializes the Item module, registers Item.getName/getType, and
|
||||
* evaluates ITEM_SCRIPT to populate ITEM_ID_* and ITEM_TYPE_* constants
|
||||
* as globals in the script scope.
|
||||
*/
|
||||
void moduleItemInit(void);
|
||||
|
||||
/**
|
||||
* Disposes the Item module.
|
||||
*/
|
||||
void moduleItemDispose(void);
|
||||
@@ -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
|
||||
modulelocale.c
|
||||
)
|
||||
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "modulelocale.h"
|
||||
#include "asset/loader/locale/assetlocaleloader.h"
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MODULE_LOCALE_SUBSTITUTION_MAX 4
|
||||
#define MODULE_LOCALE_BUFFER_SIZE 512
|
||||
#define MODULE_LOCALE_ID_SIZE 256
|
||||
|
||||
scriptproto_t MODULE_LOCALE_PROTO;
|
||||
|
||||
moduleBaseFunction(moduleLocaleGetText) {
|
||||
moduleBaseRequireArgs(1);
|
||||
moduleBaseRequireString(0);
|
||||
|
||||
if(LOCALE.entry == NULL) {
|
||||
return moduleBaseThrow("Locale.getText: no locale loaded");
|
||||
}
|
||||
|
||||
char_t id[MODULE_LOCALE_ID_SIZE];
|
||||
moduleBaseToString(args[0], id, sizeof(id));
|
||||
|
||||
// Second arg is optional plural count.
|
||||
int32_t plural = 1;
|
||||
jerry_length_t subStart = 1;
|
||||
if(argc > 1 && jerry_value_is_number(args[1])) {
|
||||
plural = moduleBaseArgInt(1);
|
||||
subStart = 2;
|
||||
}
|
||||
|
||||
// Collect up to MODULE_LOCALE_SUBSTITUTION_MAX substitution args.
|
||||
assetlocalearg_t subArgs[MODULE_LOCALE_SUBSTITUTION_MAX];
|
||||
char_t subStrings[MODULE_LOCALE_SUBSTITUTION_MAX][128];
|
||||
size_t subCount = 0;
|
||||
|
||||
for(jerry_length_t i = subStart; i < argc; i++) {
|
||||
if(subCount >= MODULE_LOCALE_SUBSTITUTION_MAX) break;
|
||||
|
||||
if(jerry_value_is_string(args[i])) {
|
||||
moduleBaseToString(
|
||||
args[i], subStrings[subCount], sizeof(subStrings[subCount])
|
||||
);
|
||||
subArgs[subCount].type = ASSET_LOCALE_ARG_STRING;
|
||||
subArgs[subCount].stringValue = subStrings[subCount];
|
||||
} else if(jerry_value_is_number(args[i])) {
|
||||
double n = jerry_value_as_number(args[i]);
|
||||
if(floor(n) == n) {
|
||||
subArgs[subCount].type = ASSET_LOCALE_ARG_INT;
|
||||
subArgs[subCount].intValue = (int32_t)n;
|
||||
} else {
|
||||
subArgs[subCount].type = ASSET_LOCALE_ARG_FLOAT;
|
||||
subArgs[subCount].floatValue = (float_t)n;
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
subCount++;
|
||||
}
|
||||
|
||||
char_t buf[MODULE_LOCALE_BUFFER_SIZE];
|
||||
assetLocaleGetStringWithArgs(
|
||||
&LOCALE.entry->data.locale,
|
||||
id,
|
||||
plural,
|
||||
buf,
|
||||
sizeof(buf),
|
||||
subCount > 0 ? subArgs : NULL,
|
||||
subCount
|
||||
);
|
||||
|
||||
return jerry_string_sz(buf);
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleLocaleSetLocale) {
|
||||
moduleBaseRequireArgs(1);
|
||||
moduleBaseRequireString(0);
|
||||
|
||||
char_t name[64];
|
||||
moduleBaseToString(args[0], name, sizeof(name));
|
||||
|
||||
const localeinfo_t *locale = NULL;
|
||||
if(strcmp(name, LOCALE_EN_US.name) == 0) locale = &LOCALE_EN_US;
|
||||
|
||||
if(locale == NULL) {
|
||||
return moduleBaseThrow("Locale.setLocale: unknown locale name");
|
||||
}
|
||||
|
||||
localeManagerSetLocale(locale);
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
void moduleLocaleInit(void) {
|
||||
scriptProtoInit(
|
||||
&MODULE_LOCALE_PROTO, "Locale", sizeof(uint8_t), NULL
|
||||
);
|
||||
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_LOCALE_PROTO, "getText", moduleLocaleGetText
|
||||
);
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_LOCALE_PROTO, "setLocale", moduleLocaleSetLocale
|
||||
);
|
||||
}
|
||||
|
||||
void moduleLocaleDispose(void) {
|
||||
scriptProtoDispose(&MODULE_LOCALE_PROTO);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* 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 "locale/localemanager.h"
|
||||
|
||||
extern scriptproto_t MODULE_LOCALE_PROTO;
|
||||
|
||||
/**
|
||||
* Locale.getText(id[, pluralCount[, arg1, ...]]) — returns the
|
||||
* translated string for the given message ID.
|
||||
*
|
||||
* @param args[0] Message ID string.
|
||||
* @param args[1] Optional plural count (number). Defaults to 1.
|
||||
* @param args[2..5] Optional substitution args (string or number).
|
||||
*/
|
||||
moduleBaseFunction(moduleLocaleGetText);
|
||||
|
||||
/**
|
||||
* Locale.setLocale(name) — switches the active locale by name.
|
||||
*
|
||||
* @param args[0] Locale name string, e.g. "en-US".
|
||||
*/
|
||||
moduleBaseFunction(moduleLocaleSetLocale);
|
||||
|
||||
/**
|
||||
* Initializes the Locale module and registers Locale.getText/setLocale.
|
||||
*/
|
||||
void moduleLocaleInit(void);
|
||||
|
||||
/**
|
||||
* Disposes the Locale module.
|
||||
*/
|
||||
void moduleLocaleDispose(void);
|
||||
@@ -6,6 +6,8 @@
|
||||
*/
|
||||
|
||||
#include "modulelist.h"
|
||||
#include "script/module/animation/moduleanimation.h"
|
||||
#include "script/module/animation/moduleeasing.h"
|
||||
#include "script/module/asset/moduleasset.h"
|
||||
#include "script/module/console/moduleconsole.h"
|
||||
#include "script/module/display/modulecolor.h"
|
||||
@@ -16,13 +18,22 @@
|
||||
#include "script/module/engine/moduletimeout.h"
|
||||
#include "script/module/entity/moduleentity.h"
|
||||
#include "script/module/input/moduleinput.h"
|
||||
#include "script/module/item/moduleitem.h"
|
||||
#include "script/module/item/modulebackpack.h"
|
||||
#include "script/module/locale/modulelocale.h"
|
||||
#include "script/module/save/modulesave.h"
|
||||
#include "script/module/math/modulevec3.h"
|
||||
#include "script/module/overworld/moduleoverworld.h"
|
||||
#include "script/module/require/modulerequire.h"
|
||||
#include "script/module/scene/modulescene.h"
|
||||
#include "script/module/story/modulestory.h"
|
||||
#include "script/module/system/modulesystem.h"
|
||||
#include "script/module/ui/moduletextbox.h"
|
||||
|
||||
|
||||
void moduleListInit(void) {
|
||||
moduleAnimationInit();
|
||||
moduleEasingInit();
|
||||
moduleEventInit();
|
||||
moduleTextureInit();
|
||||
moduleColorInit();
|
||||
@@ -35,9 +46,16 @@ void moduleListInit(void) {
|
||||
moduleVec3Init();
|
||||
moduleEntityInit();
|
||||
moduleInputInit();
|
||||
moduleItemInit();
|
||||
moduleBackpackInit();
|
||||
moduleLocaleInit();
|
||||
moduleSaveInit();
|
||||
moduleOverworldInit();
|
||||
moduleRequireInit();
|
||||
moduleSceneInit();
|
||||
moduleStoryInit();
|
||||
moduleSystemInit();
|
||||
moduleTextboxInit();
|
||||
}
|
||||
|
||||
void moduleListUpdate(void) {
|
||||
@@ -46,9 +64,18 @@ void moduleListUpdate(void) {
|
||||
}
|
||||
|
||||
void moduleListDispose(void) {
|
||||
moduleTextboxDispose();
|
||||
moduleSystemDispose();
|
||||
moduleOverworldDispose();
|
||||
moduleEasingDispose();
|
||||
moduleAnimationDispose();
|
||||
moduleStoryDispose();
|
||||
moduleSceneDispose();
|
||||
moduleRequireDispose();
|
||||
moduleSaveDispose();
|
||||
moduleLocaleDispose();
|
||||
moduleBackpackDispose();
|
||||
moduleItemDispose();
|
||||
moduleInputDispose();
|
||||
moduleEntityDispose();
|
||||
moduleVec3Dispose();
|
||||
|
||||
@@ -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
|
||||
moduleoverworld.c
|
||||
)
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "moduleoverworld.h"
|
||||
#include "overworld/map.h"
|
||||
|
||||
scriptproto_t MODULE_OVERWORLD_PROTO;
|
||||
|
||||
moduleBaseFunction(moduleOverworldLoadMap) {
|
||||
moduleBaseRequireArgs(1);
|
||||
moduleBaseRequireString(0);
|
||||
char_t handle[MAP_HANDLE_MAX];
|
||||
moduleBaseToString(args[0], handle, (jerry_size_t)MAP_HANDLE_MAX);
|
||||
errorret_t err = mapLoad(handle);
|
||||
if(errorIsNotOk(err)) return moduleBaseThrowError(err);
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleOverworldIsLoaded) {
|
||||
return jerry_boolean(mapIsLoaded());
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleOverworldSetPosition) {
|
||||
moduleBaseRequireArgs(3);
|
||||
moduleBaseRequireNumber(0);
|
||||
moduleBaseRequireNumber(1);
|
||||
moduleBaseRequireNumber(2);
|
||||
tilepos_t pos;
|
||||
pos.x = (tileunit_t)moduleBaseArgInt(0);
|
||||
pos.y = (tileunit_t)moduleBaseArgInt(1);
|
||||
pos.z = (tileunit_t)moduleBaseArgInt(2);
|
||||
errorret_t err = mapPositionSet(pos);
|
||||
if(errorIsNotOk(err)) return moduleBaseThrowError(err);
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleOverworldUpdate) {
|
||||
errorret_t err = mapUpdate();
|
||||
if(errorIsNotOk(err)) return moduleBaseThrowError(err);
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleOverworldJsDispose) {
|
||||
mapDispose();
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
void moduleOverworldInit(void) {
|
||||
scriptProtoInit(&MODULE_OVERWORLD_PROTO, "Overworld", 0, NULL);
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_OVERWORLD_PROTO, "loadMap", moduleOverworldLoadMap
|
||||
);
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_OVERWORLD_PROTO, "isLoaded", moduleOverworldIsLoaded
|
||||
);
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_OVERWORLD_PROTO, "setPosition", moduleOverworldSetPosition
|
||||
);
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_OVERWORLD_PROTO, "update", moduleOverworldUpdate
|
||||
);
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_OVERWORLD_PROTO, "dispose", moduleOverworldJsDispose
|
||||
);
|
||||
}
|
||||
|
||||
void moduleOverworldDispose(void) {
|
||||
scriptProtoDispose(&MODULE_OVERWORLD_PROTO);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* 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"
|
||||
|
||||
extern scriptproto_t MODULE_OVERWORLD_PROTO;
|
||||
|
||||
/**
|
||||
* Overworld.loadMap(handle) — loads the map with the given handle string.
|
||||
* Disposes any currently loaded map first.
|
||||
*
|
||||
* @param args[0] Map handle string (max 31 chars).
|
||||
*/
|
||||
moduleBaseFunction(moduleOverworldLoadMap);
|
||||
|
||||
/**
|
||||
* Overworld.isLoaded() — returns true when a map is currently loaded.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
moduleBaseFunction(moduleOverworldIsLoaded);
|
||||
|
||||
/**
|
||||
* Overworld.setPosition(x, y, z) — slides the chunk window to the tile
|
||||
* position (x, y, z).
|
||||
*
|
||||
* @param args[0] Tile X (integer).
|
||||
* @param args[1] Tile Y (integer).
|
||||
* @param args[2] Tile Z (integer).
|
||||
*/
|
||||
moduleBaseFunction(moduleOverworldSetPosition);
|
||||
|
||||
/**
|
||||
* Overworld.update() — advances the map one tick.
|
||||
*/
|
||||
moduleBaseFunction(moduleOverworldUpdate);
|
||||
|
||||
/**
|
||||
* Overworld.dispose() — unloads the current map.
|
||||
*/
|
||||
moduleBaseFunction(moduleOverworldJsDispose);
|
||||
|
||||
/**
|
||||
* Initializes the Overworld module and registers the Overworld global.
|
||||
*/
|
||||
void moduleOverworldInit(void);
|
||||
|
||||
/**
|
||||
* Disposes the Overworld module.
|
||||
*/
|
||||
void moduleOverworldDispose(void);
|
||||
@@ -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
|
||||
modulesave.c
|
||||
)
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "modulesave.h"
|
||||
|
||||
scriptproto_t MODULE_SAVE_PROTO;
|
||||
|
||||
moduleBaseFunction(moduleSaveExists) {
|
||||
moduleBaseRequireArgs(1);
|
||||
moduleSaveRequireSlot(0, "Save.exists");
|
||||
return jerry_boolean(saveExists((uint8_t)moduleBaseArgInt(0)));
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleSaveLoad) {
|
||||
moduleBaseRequireArgs(1);
|
||||
moduleSaveRequireSlot(0, "Save.load");
|
||||
errorret_t err = saveLoad((uint8_t)moduleBaseArgInt(0));
|
||||
if(errorIsNotOk(err)) return moduleBaseThrowError(err);
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleSaveWrite) {
|
||||
moduleBaseRequireArgs(1);
|
||||
moduleSaveRequireSlot(0, "Save.write");
|
||||
errorret_t err = saveWrite((uint8_t)moduleBaseArgInt(0));
|
||||
if(errorIsNotOk(err)) return moduleBaseThrowError(err);
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleSaveDelete) {
|
||||
moduleBaseRequireArgs(1);
|
||||
moduleSaveRequireSlot(0, "Save.delete");
|
||||
errorret_t err = saveDelete((uint8_t)moduleBaseArgInt(0));
|
||||
if(errorIsNotOk(err)) return moduleBaseThrowError(err);
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
void moduleSaveInit(void) {
|
||||
scriptProtoInit(&MODULE_SAVE_PROTO, "Save", sizeof(uint8_t), NULL);
|
||||
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_SAVE_PROTO, "exists", moduleSaveExists
|
||||
);
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_SAVE_PROTO, "load", moduleSaveLoad
|
||||
);
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_SAVE_PROTO, "write", moduleSaveWrite
|
||||
);
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_SAVE_PROTO, "delete", moduleSaveDelete
|
||||
);
|
||||
}
|
||||
|
||||
void moduleSaveDispose(void) {
|
||||
scriptProtoDispose(&MODULE_SAVE_PROTO);
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* 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 "save/save.h"
|
||||
|
||||
extern scriptproto_t MODULE_SAVE_PROTO;
|
||||
|
||||
/**
|
||||
* Validates a save-slot argument and returns a type error if invalid.
|
||||
*
|
||||
* @param i Argument index.
|
||||
* @param ctx Context string for error messages.
|
||||
*/
|
||||
#define moduleSaveRequireSlot(i, ctx) do { \
|
||||
if(!jerry_value_is_number(args[(i)])) { \
|
||||
return moduleBaseThrow(ctx ": slot must be a number"); \
|
||||
} \
|
||||
const int32_t _s = moduleBaseArgInt(i); \
|
||||
if(_s < 0 || _s >= (int32_t)SAVE_FILE_COUNT_MAX) { \
|
||||
return moduleBaseThrow(ctx ": invalid save slot"); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/**
|
||||
* Save.exists(slot) — true if a save file is present for the slot.
|
||||
*
|
||||
* @param args[0] Save slot index (0 to SAVE_FILE_COUNT_MAX - 1).
|
||||
*/
|
||||
moduleBaseFunction(moduleSaveExists);
|
||||
|
||||
/**
|
||||
* Save.load(slot) — loads the save file for the given slot.
|
||||
*
|
||||
* @param args[0] Save slot index (0 to SAVE_FILE_COUNT_MAX - 1).
|
||||
*/
|
||||
moduleBaseFunction(moduleSaveLoad);
|
||||
|
||||
/**
|
||||
* Save.write(slot) — writes the save file for the given slot.
|
||||
*
|
||||
* @param args[0] Save slot index (0 to SAVE_FILE_COUNT_MAX - 1).
|
||||
*/
|
||||
moduleBaseFunction(moduleSaveWrite);
|
||||
|
||||
/**
|
||||
* Save.delete(slot) — deletes the save file for the given slot.
|
||||
*
|
||||
* @param args[0] Save slot index (0 to SAVE_FILE_COUNT_MAX - 1).
|
||||
*/
|
||||
moduleBaseFunction(moduleSaveDelete);
|
||||
|
||||
/**
|
||||
* Initializes the Save module and registers Save.exists/load/write/delete.
|
||||
*/
|
||||
void moduleSaveInit(void);
|
||||
|
||||
/**
|
||||
* Disposes the Save module.
|
||||
*/
|
||||
void moduleSaveDispose(void);
|
||||
@@ -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
|
||||
modulestory.c
|
||||
)
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "modulestory.h"
|
||||
#include <string.h>
|
||||
|
||||
scriptproto_t MODULE_STORY_PROTO;
|
||||
|
||||
moduleBaseFunction(moduleStoryGet) {
|
||||
moduleBaseRequireArgs(1);
|
||||
moduleStoryRequireFlag(0, "Story.get");
|
||||
const storyflag_t flag = (storyflag_t)moduleBaseArgInt(0);
|
||||
return jerry_number((double)storyFlagGet(flag));
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleStorySet) {
|
||||
moduleBaseRequireArgs(2);
|
||||
moduleStoryRequireFlag(0, "Story.set");
|
||||
moduleBaseRequireNumber(1);
|
||||
const int32_t val = moduleBaseArgInt(1);
|
||||
if(val < 0 || val > 255) {
|
||||
return moduleBaseThrow("Story.set: value out of range (0-255)");
|
||||
}
|
||||
storyFlagSet(
|
||||
(storyflag_t)moduleBaseArgInt(0),
|
||||
(storyflagvalue_t)val
|
||||
);
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
void moduleStoryInit(void) {
|
||||
scriptProtoInit(&MODULE_STORY_PROTO, "Story", sizeof(uint8_t), NULL);
|
||||
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_STORY_PROTO, "get", moduleStoryGet
|
||||
);
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_STORY_PROTO, "set", moduleStorySet
|
||||
);
|
||||
|
||||
jerry_value_t result = jerry_eval(
|
||||
(const jerry_char_t *)STORY_FLAG_SCRIPT,
|
||||
strlen(STORY_FLAG_SCRIPT),
|
||||
JERRY_PARSE_NO_OPTS
|
||||
);
|
||||
jerry_value_free(result);
|
||||
}
|
||||
|
||||
void moduleStoryDispose(void) {
|
||||
scriptProtoDispose(&MODULE_STORY_PROTO);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* 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 "story/storyflag.h"
|
||||
|
||||
extern scriptproto_t MODULE_STORY_PROTO;
|
||||
|
||||
/**
|
||||
* Validates a storyflag_t argument and returns a type error if invalid.
|
||||
*
|
||||
* @param i Argument index.
|
||||
* @param ctx Context string for error messages.
|
||||
*/
|
||||
#define moduleStoryRequireFlag(i, ctx) do { \
|
||||
if(!jerry_value_is_number(args[(i)])) { \
|
||||
return moduleBaseThrow(ctx ": flagId must be a number"); \
|
||||
} \
|
||||
const storyflag_t _f = (storyflag_t)moduleBaseArgInt(i); \
|
||||
if(_f <= STORY_FLAG_NULL || _f >= STORY_FLAG_COUNT) { \
|
||||
return moduleBaseThrow(ctx ": invalid story flag ID"); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/**
|
||||
* Story.get(flagId) — returns the current uint8 value of a story flag.
|
||||
*
|
||||
* @param args[0] Story flag constant (STORY_FLAG_*).
|
||||
*/
|
||||
moduleBaseFunction(moduleStoryGet);
|
||||
|
||||
/**
|
||||
* Story.set(flagId, value) — sets a story flag to a uint8 value.
|
||||
*
|
||||
* @param args[0] Story flag constant (STORY_FLAG_*).
|
||||
* @param args[1] Value 0–255.
|
||||
*/
|
||||
moduleBaseFunction(moduleStorySet);
|
||||
|
||||
/**
|
||||
* Initializes the Story module, registers Story.get/set, and evaluates
|
||||
* STORY_FLAG_SCRIPT to inject STORY_FLAG_* constants as globals.
|
||||
*/
|
||||
void moduleStoryInit(void);
|
||||
|
||||
/**
|
||||
* Disposes the Story module.
|
||||
*/
|
||||
void moduleStoryDispose(void);
|
||||
@@ -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
|
||||
moduletextbox.c
|
||||
)
|
||||
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "moduletextbox.h"
|
||||
#include "ui/uitextbox.h"
|
||||
#include "input/inputaction.h"
|
||||
|
||||
scriptproto_t MODULE_TEXTBOX_PROTO;
|
||||
|
||||
moduleBaseFunction(moduleTextboxSetText) {
|
||||
moduleBaseRequireArgs(1);
|
||||
moduleBaseRequireString(0);
|
||||
char_t buf[UI_TEXTBOX_TEXT_MAX];
|
||||
moduleBaseToString(args[0], buf, sizeof(buf));
|
||||
uiTextboxSetText(buf);
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleTextboxNextPage) {
|
||||
uiTextboxNextPage();
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleTextboxUpdate) {
|
||||
errorret_t err = uiTextboxUpdate();
|
||||
if(errorIsNotOk(err)) return moduleBaseThrowError(err);
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleTextboxDraw) {
|
||||
errorret_t err = uiTextboxDraw();
|
||||
if(errorIsNotOk(err)) return moduleBaseThrowError(err);
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleTextboxSetAdvanceAction) {
|
||||
moduleBaseRequireArgs(1);
|
||||
moduleBaseRequireNumber(0);
|
||||
const inputaction_t action = (inputaction_t)moduleBaseArgInt(0);
|
||||
if(action <= INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) {
|
||||
return moduleBaseThrow(
|
||||
"UITextbox.setAdvanceAction: invalid action"
|
||||
);
|
||||
}
|
||||
UI_TEXTBOX.advanceAction = action;
|
||||
return jerry_undefined();
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleTextboxGetIsPageComplete) {
|
||||
return jerry_boolean(uiTextboxPageIsComplete());
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleTextboxGetHasNextPage) {
|
||||
return jerry_boolean(uiTextboxHasNextPage());
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleTextboxGetCurrentPage) {
|
||||
return jerry_number((double)UI_TEXTBOX.currentPage);
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleTextboxGetPageCount) {
|
||||
return jerry_number((double)UI_TEXTBOX.pageCount);
|
||||
}
|
||||
|
||||
void moduleTextboxInit(void) {
|
||||
scriptProtoInit(
|
||||
&MODULE_TEXTBOX_PROTO, "UITextbox", sizeof(uint8_t), NULL
|
||||
);
|
||||
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_TEXTBOX_PROTO, "setText", moduleTextboxSetText
|
||||
);
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_TEXTBOX_PROTO, "nextPage", moduleTextboxNextPage
|
||||
);
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_TEXTBOX_PROTO, "update", moduleTextboxUpdate
|
||||
);
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_TEXTBOX_PROTO, "draw", moduleTextboxDraw
|
||||
);
|
||||
scriptProtoDefineStaticFunc(
|
||||
&MODULE_TEXTBOX_PROTO, "setAdvanceAction",
|
||||
moduleTextboxSetAdvanceAction
|
||||
);
|
||||
scriptProtoDefineStaticProp(
|
||||
&MODULE_TEXTBOX_PROTO, "isPageComplete",
|
||||
moduleTextboxGetIsPageComplete, NULL
|
||||
);
|
||||
scriptProtoDefineStaticProp(
|
||||
&MODULE_TEXTBOX_PROTO, "hasNextPage",
|
||||
moduleTextboxGetHasNextPage, NULL
|
||||
);
|
||||
scriptProtoDefineStaticProp(
|
||||
&MODULE_TEXTBOX_PROTO, "currentPage",
|
||||
moduleTextboxGetCurrentPage, NULL
|
||||
);
|
||||
scriptProtoDefineStaticProp(
|
||||
&MODULE_TEXTBOX_PROTO, "pageCount",
|
||||
moduleTextboxGetPageCount, NULL
|
||||
);
|
||||
}
|
||||
|
||||
void moduleTextboxDispose(void) {
|
||||
scriptProtoDispose(&MODULE_TEXTBOX_PROTO);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* 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"
|
||||
|
||||
extern scriptproto_t MODULE_TEXTBOX_PROTO;
|
||||
|
||||
/**
|
||||
* UITextbox.setText(text) — sets the textbox content and rebuilds layout.
|
||||
*
|
||||
* @param args[0] Null-terminated text string.
|
||||
*/
|
||||
moduleBaseFunction(moduleTextboxSetText);
|
||||
|
||||
/**
|
||||
* UITextbox.nextPage() — advances to the next page; no-op on last page.
|
||||
*/
|
||||
moduleBaseFunction(moduleTextboxNextPage);
|
||||
|
||||
/**
|
||||
* UITextbox.update() — advances the typewriter scroll by one tick.
|
||||
*/
|
||||
moduleBaseFunction(moduleTextboxUpdate);
|
||||
|
||||
/**
|
||||
* UITextbox.draw() — draws the frame and visible text for this frame.
|
||||
*/
|
||||
moduleBaseFunction(moduleTextboxDraw);
|
||||
|
||||
/**
|
||||
* UITextbox.setAdvanceAction(action) — sets the input action that
|
||||
* advances the textbox when held.
|
||||
*
|
||||
* @param args[0] Input action constant (INPUT_ACTION_*).
|
||||
*/
|
||||
moduleBaseFunction(moduleTextboxSetAdvanceAction);
|
||||
|
||||
/** @return true when the typewriter has fully revealed the current page. */
|
||||
moduleBaseFunction(moduleTextboxGetIsPageComplete);
|
||||
|
||||
/** @return true when at least one more page follows the current one. */
|
||||
moduleBaseFunction(moduleTextboxGetHasNextPage);
|
||||
|
||||
/** @return Zero-based index of the current page. */
|
||||
moduleBaseFunction(moduleTextboxGetCurrentPage);
|
||||
|
||||
/** @return Total number of pages for the current text. */
|
||||
moduleBaseFunction(moduleTextboxGetPageCount);
|
||||
|
||||
/**
|
||||
* Initializes the UITextbox module and registers all methods and
|
||||
* properties on the UITextbox global.
|
||||
*/
|
||||
void moduleTextboxInit(void);
|
||||
|
||||
/**
|
||||
* Disposes the UITextbox module.
|
||||
*/
|
||||
void moduleTextboxDispose(void);
|
||||
Vendored
+61
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
/** A single keyframe descriptor passed to the Animation constructor. */
|
||||
interface AnimationKeyframe {
|
||||
/** Time offset in seconds. */
|
||||
time: number;
|
||||
/** Value at this keyframe. */
|
||||
value: number;
|
||||
/** Easing type (EASING_* constant). Defaults to EASING_LINEAR. */
|
||||
easing?: number;
|
||||
}
|
||||
|
||||
/** Keyframe-based float animation. */
|
||||
interface AnimationInstance {
|
||||
/**
|
||||
* Interpolates the animation at the given time.
|
||||
* @param time Time in seconds.
|
||||
* @returns Interpolated value.
|
||||
*/
|
||||
getValue(time: number): number;
|
||||
}
|
||||
|
||||
/** Constructs a new Animation from an array of keyframe descriptors. */
|
||||
declare var Animation: {
|
||||
new (keyframes: AnimationKeyframe[]): AnimationInstance;
|
||||
};
|
||||
|
||||
/** Easing function utilities. */
|
||||
interface EasingNamespace {
|
||||
/**
|
||||
* Applies the given easing function to normalized time t.
|
||||
* @param type An EASING_* constant.
|
||||
* @param t Normalized time in [0, 1].
|
||||
* @returns Eased value in [0, 1].
|
||||
*/
|
||||
apply(type: number, t: number): number;
|
||||
}
|
||||
|
||||
declare var Easing: EasingNamespace;
|
||||
|
||||
declare var EASING_LINEAR: number;
|
||||
declare var EASING_IN_SINE: number;
|
||||
declare var EASING_OUT_SINE: number;
|
||||
declare var EASING_IN_OUT_SINE: number;
|
||||
declare var EASING_IN_QUAD: number;
|
||||
declare var EASING_OUT_QUAD: number;
|
||||
declare var EASING_IN_OUT_QUAD: number;
|
||||
declare var EASING_IN_CUBIC: number;
|
||||
declare var EASING_OUT_CUBIC: number;
|
||||
declare var EASING_IN_OUT_CUBIC: number;
|
||||
declare var EASING_IN_QUART: number;
|
||||
declare var EASING_OUT_QUART: number;
|
||||
declare var EASING_IN_OUT_QUART: number;
|
||||
declare var EASING_IN_BACK: number;
|
||||
declare var EASING_OUT_BACK: number;
|
||||
declare var EASING_IN_OUT_BACK: number;
|
||||
Vendored
+22
@@ -31,6 +31,28 @@
|
||||
/// <reference path="./asset/assetbatch.d.ts" />
|
||||
/// <reference path="./asset/asset.d.ts" />
|
||||
|
||||
// animation
|
||||
/// <reference path="./animation/animation.d.ts" />
|
||||
|
||||
// overworld
|
||||
/// <reference path="./overworld/overworld.d.ts" />
|
||||
|
||||
// item system
|
||||
/// <reference path="./item/item.d.ts" />
|
||||
/// <reference path="./item/backpack.d.ts" />
|
||||
|
||||
// story system
|
||||
/// <reference path="./story/story.d.ts" />
|
||||
|
||||
// locale
|
||||
/// <reference path="./locale/locale.d.ts" />
|
||||
|
||||
// save
|
||||
/// <reference path="./save/save.d.ts" />
|
||||
|
||||
// ui
|
||||
/// <reference path="./ui/textbox.d.ts" />
|
||||
|
||||
// engine systems
|
||||
/// <reference path="./console/console.d.ts" />
|
||||
/// <reference path="./engine/engine.d.ts" />
|
||||
|
||||
Vendored
+63
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
/** The player's item backpack (a fixed 20-slot inventory). */
|
||||
interface BackpackNamespace {
|
||||
/** `true` when all 20 storage slots are occupied by distinct items. */
|
||||
readonly isFull: boolean;
|
||||
|
||||
/**
|
||||
* Returns the quantity of `itemId` in the backpack (0 if absent).
|
||||
* @param itemId - An `ITEM_ID_*` constant.
|
||||
*/
|
||||
getCount(itemId: ItemId): number;
|
||||
|
||||
/**
|
||||
* Returns `true` if `itemId` is present with quantity greater than 0.
|
||||
* @param itemId - An `ITEM_ID_*` constant.
|
||||
*/
|
||||
has(itemId: ItemId): boolean;
|
||||
|
||||
/**
|
||||
* Returns `true` if the stack for `itemId` is at maximum quantity (255).
|
||||
* @param itemId - An `ITEM_ID_*` constant.
|
||||
*/
|
||||
isItemFull(itemId: ItemId): boolean;
|
||||
|
||||
/**
|
||||
* Sets the quantity of `itemId`; passing 0 removes the stack entirely.
|
||||
* @param itemId - An `ITEM_ID_*` constant.
|
||||
* @param quantity - New quantity, 0–255.
|
||||
*/
|
||||
set(itemId: ItemId, quantity: number): void;
|
||||
|
||||
/**
|
||||
* Adds `quantity` units of `itemId` to the backpack.
|
||||
* @param itemId - An `ITEM_ID_*` constant.
|
||||
* @param quantity - Amount to add, 1–255.
|
||||
*/
|
||||
add(itemId: ItemId, quantity: number): void;
|
||||
|
||||
/**
|
||||
* Removes `itemId` entirely from the backpack.
|
||||
* @param itemId - An `ITEM_ID_*` constant.
|
||||
*/
|
||||
remove(itemId: ItemId): void;
|
||||
|
||||
/**
|
||||
* Sorts the backpack contents.
|
||||
* @param sortBy - `INVENTORY_SORT_BY_ID` or `INVENTORY_SORT_BY_TYPE`.
|
||||
* @param reverse - Optional; pass `true` to reverse the sort order.
|
||||
*/
|
||||
sort(sortBy: number, reverse?: boolean): void;
|
||||
}
|
||||
|
||||
declare var Backpack: BackpackNamespace;
|
||||
|
||||
// Inventory sort constants — injected as globals by the engine at startup.
|
||||
declare var INVENTORY_SORT_BY_ID: number;
|
||||
declare var INVENTORY_SORT_BY_TYPE: number;
|
||||
Vendored
+39
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
/** Opaque type alias for item identifier constants. */
|
||||
type ItemId = number;
|
||||
|
||||
/** Opaque type alias for item type constants. */
|
||||
type ItemType = number;
|
||||
|
||||
/** Item data lookup. */
|
||||
interface ItemNamespace {
|
||||
/**
|
||||
* Returns the string name for the given item ID.
|
||||
* @param itemId - An `ITEM_ID_*` constant.
|
||||
*/
|
||||
getName(itemId: ItemId): string;
|
||||
|
||||
/**
|
||||
* Returns the type constant for the given item ID.
|
||||
* @param itemId - An `ITEM_ID_*` constant.
|
||||
* @returns An `ITEM_TYPE_*` constant.
|
||||
*/
|
||||
getType(itemId: ItemId): ItemType;
|
||||
}
|
||||
|
||||
declare var Item: ItemNamespace;
|
||||
|
||||
// Item ID constants — injected as globals by the engine at startup.
|
||||
declare var ITEM_ID_POTION: ItemId;
|
||||
declare var ITEM_ID_POTATO: ItemId;
|
||||
declare var ITEM_ID_APPLE: ItemId;
|
||||
|
||||
// Item type constants — injected as globals by the engine at startup.
|
||||
declare var ITEM_TYPE_MEDICINE: ItemType;
|
||||
declare var ITEM_TYPE_FOOD: ItemType;
|
||||
Vendored
+31
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
/** Locale string lookup and language switching. */
|
||||
interface LocaleNamespace {
|
||||
/**
|
||||
* Returns the translated string for the given message ID.
|
||||
*
|
||||
* @param id - PO message ID, e.g. `"ITEM_POTION_NAME"`.
|
||||
* @param pluralCount - Optional count used to select the plural form.
|
||||
* Defaults to 1 (singular).
|
||||
* @param args - Optional substitution values (`%s`, `%d`, `%f`).
|
||||
*/
|
||||
getText(
|
||||
id: string,
|
||||
pluralCount?: number,
|
||||
...args: Array<string | number>
|
||||
): string;
|
||||
|
||||
/**
|
||||
* Switches the active locale.
|
||||
* @param name - Locale name string, e.g. `"en-US"`.
|
||||
*/
|
||||
setLocale(name: string): void;
|
||||
}
|
||||
|
||||
declare var Locale: LocaleNamespace;
|
||||
Vendored
+40
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
/** Map loading and world-position management. */
|
||||
interface OverworldNamespace {
|
||||
/**
|
||||
* Loads the map with the given handle, disposing any currently loaded map.
|
||||
* @param handle Map handle string (max 31 chars).
|
||||
*/
|
||||
loadMap(handle: string): void;
|
||||
|
||||
/**
|
||||
* Returns `true` when a map is currently loaded.
|
||||
*/
|
||||
isLoaded(): boolean;
|
||||
|
||||
/**
|
||||
* Slides the loaded chunk window to the given tile-space position.
|
||||
* @param x Tile X.
|
||||
* @param y Tile Y.
|
||||
* @param z Tile Z.
|
||||
*/
|
||||
setPosition(x: number, y: number, z: number): void;
|
||||
|
||||
/**
|
||||
* Advances the map one tick. Call once per frame.
|
||||
*/
|
||||
update(): void;
|
||||
|
||||
/**
|
||||
* Unloads the current map.
|
||||
*/
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
declare var Overworld: OverworldNamespace;
|
||||
Vendored
+2
-3
@@ -11,9 +11,8 @@
|
||||
* Modules are cached after their first load. Subsequent calls with the same
|
||||
* resolved path return the cached exports without re-executing the file.
|
||||
*
|
||||
* Path rules: `"./foo"` / `"../foo"` resolve relative to the calling script's
|
||||
* directory; any other string resolves from the archive root. `.js` is
|
||||
* appended automatically when missing.
|
||||
* Path rules: the string is passed verbatim to the asset loader and resolved
|
||||
* from the archive root. The `.js` extension must be included explicitly.
|
||||
*
|
||||
* @example
|
||||
* const NPC = require('./entities/NPC');
|
||||
|
||||
Vendored
+38
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
/** Slot-based save file management. */
|
||||
interface SaveNamespace {
|
||||
/**
|
||||
* Returns `true` if a save file is present for the given slot.
|
||||
* @param slot - Save slot index (0–2).
|
||||
*/
|
||||
exists(slot: number): boolean;
|
||||
|
||||
/**
|
||||
* Loads the save file for the given slot from persistent storage.
|
||||
* Throws if the load fails.
|
||||
* @param slot - Save slot index (0–2).
|
||||
*/
|
||||
load(slot: number): void;
|
||||
|
||||
/**
|
||||
* Writes the save file for the given slot to persistent storage.
|
||||
* Throws if the write fails.
|
||||
* @param slot - Save slot index (0–2).
|
||||
*/
|
||||
write(slot: number): void;
|
||||
|
||||
/**
|
||||
* Deletes the save file for the given slot from persistent storage.
|
||||
* Throws if the delete fails.
|
||||
* @param slot - Save slot index (0–2).
|
||||
*/
|
||||
delete(slot: number): void;
|
||||
}
|
||||
|
||||
declare var Save: SaveNamespace;
|
||||
Vendored
+30
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
/** Opaque type alias for story flag identifier constants. */
|
||||
type StoryFlag = number;
|
||||
|
||||
/** Story flag read/write access. */
|
||||
interface StoryNamespace {
|
||||
/**
|
||||
* Returns the current value of a story flag (0–255).
|
||||
* @param flagId - A `STORY_FLAG_*` constant.
|
||||
*/
|
||||
get(flagId: StoryFlag): number;
|
||||
|
||||
/**
|
||||
* Sets a story flag to a new value.
|
||||
* @param flagId - A `STORY_FLAG_*` constant.
|
||||
* @param value - New value, 0–255.
|
||||
*/
|
||||
set(flagId: StoryFlag, value: number): void;
|
||||
}
|
||||
|
||||
declare var Story: StoryNamespace;
|
||||
|
||||
// Story flag constants — injected as globals by the engine at startup.
|
||||
declare var STORY_FLAG_TEST: StoryFlag;
|
||||
Vendored
+50
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
/** VN-style typewriter textbox singleton. */
|
||||
interface UITextboxNamespace {
|
||||
/** `true` when the typewriter has fully revealed the current page. */
|
||||
readonly isPageComplete: boolean;
|
||||
/** `true` when at least one more page follows the current one. */
|
||||
readonly hasNextPage: boolean;
|
||||
/** Zero-based index of the current page. */
|
||||
readonly currentPage: number;
|
||||
/** Total number of pages for the current text. */
|
||||
readonly pageCount: number;
|
||||
|
||||
/**
|
||||
* Sets the textbox content and rebuilds the word-wrap layout.
|
||||
* Resets to page 0 and scroll 0.
|
||||
* @param text - Text to display (max 1024 chars).
|
||||
*/
|
||||
setText(text: string): void;
|
||||
|
||||
/**
|
||||
* Advances to the next page; no-op on the last page.
|
||||
*/
|
||||
nextPage(): void;
|
||||
|
||||
/**
|
||||
* Advances the typewriter scroll by one tick.
|
||||
* Call once per frame before `draw()`.
|
||||
*/
|
||||
update(): void;
|
||||
|
||||
/**
|
||||
* Draws the frame and currently visible text.
|
||||
* Call once per frame after `update()`.
|
||||
*/
|
||||
draw(): void;
|
||||
|
||||
/**
|
||||
* Sets the input action that auto-advances the textbox when held.
|
||||
* @param action - An `INPUT_ACTION_*` constant.
|
||||
*/
|
||||
setAdvanceAction(action: InputAction): void;
|
||||
}
|
||||
|
||||
declare var UITextbox: UITextboxNamespace;
|
||||
Reference in New Issue
Block a user