Cutscene tests

This commit is contained in:
2026-07-02 11:18:36 -05:00
parent 01d89cf22c
commit 7b98e40ccf
15 changed files with 49 additions and 245 deletions
-21
View File
@@ -1,21 +0,0 @@
// Copyright (c) 2026 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
const platformNames = {
[System.PLATFORM_LINUX]: 'Linux',
[System.PLATFORM_KNULLI]: 'Knulli',
[System.PLATFORM_PSP]: 'PSP',
[System.PLATFORM_GAMECUBE]: 'GameCube',
[System.PLATFORM_WII]: 'Wii',
};
Console.print('Platform: ' + (platformNames[System.platform] || 'Unknown'));
UIFullboxOver.setColor(Color.BLACK);
requireAsync('testscene.js').then(Scene.set).catch(err => {
Console.print('Error loading scene: ' + err);
Engine.exit();
});
-63
View File
@@ -1,63 +0,0 @@
// Copyright (c) 2026 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
const PLAYER_SPEED = 5.0;
// 1 world unit = 16 pixels.
const PIXEL_SCALE = 1.0 / 16.0;
// Player sprite is 32x32 px (test.png dimensions).
const PLAYER_W = 32 * PIXEL_SCALE;
const PLAYER_H = 32 * PIXEL_SCALE;
var player = {};
player.getAssets = () => {
return [
{ path: 'test.png', type: Asset.TYPE_TEXTURE, format: Texture.FORMAT_RGBA }
];
}
player.init = function(scene) {
var texture = scene.assets.getAssetByPath('test.png');
Console.print('Player init: got texture ' + texture);
_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 = texture.texture;
r.type = Renderable.SPRITEBATCH;
r.color = new Color(220, 80, 80);
// Upright quad centered on X, bottom-aligned on Y.
r.sprites = [[-PLAYER_W/2, 0, 0, PLAYER_W/2, PLAYER_H, 0, 0, 1, 1, 0]];
_position.localPosition = new Vec3(0, PLAYER_H, 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;
-42
View File
@@ -1,42 +0,0 @@
// Copyright (c) 2026 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
var scene = {};
// 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;
scene.init = async function() {
// Camera
scene.cam = Entity.create();
var camPos = scene.cam.add(Component.POSITION);
var cam = scene.cam.add(Component.CAMERA);
camPos.localPosition = new Vec3(3, 3, 3);
camPos.lookAt(new Vec3(0, 0, 0));
// Floor - large flat slab, no texture needed.
scene.floor = Entity.create();
var floorPos = scene.floor.add(Component.POSITION);
var floorR = scene.floor.add(Component.RENDERABLE);
floorR.type = Renderable.SHADER_MATERIAL;
floorR.color = Color.BLUE;
// floorPos.localScale = new Vec3(16, 0.2, 16);
// floorPos.localPosition = new Vec3(0, -0.1, 0);
await UIFullboxOver.transition(Color.BLACK, Color.TRANSPARENT, 1.0);
};
scene.update = function() {
};
scene.dispose = function() {
Entity.dispose(scene.floor);
Entity.dispose(scene.cam);
};
module.exports = scene;
-6
View File
@@ -1,6 +0,0 @@
module = {
render() {
Text.draw(0, 0, "Hello World");
SpriteBatch.flush();
}
};
-1
View File
@@ -8,7 +8,6 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC PUBLIC
rpg.c rpg.c
rpgcamera.c rpgcamera.c
rpgtextbox.c
) )
# Subdirs # Subdirs
+2
View File
@@ -7,8 +7,10 @@
#pragma once #pragma once
#include "rpg/cutscene/item/cutsceneitem.h" #include "rpg/cutscene/item/cutsceneitem.h"
#include "cutscenepause.h"
typedef struct cutscene_s { typedef struct cutscene_s {
const cutsceneitem_t *items; const cutsceneitem_t *items;
uint8_t itemCount; uint8_t itemCount;
cutscenepause_t pause;
} cutscene_t; } cutscene_t;
+20
View File
@@ -0,0 +1,20 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
typedef uint8_t cutscenepause_t;
#define CUTSCENE_PAUSE_NPC ((cutscenepause_t)(1 << 0))
#define CUTSCENE_PAUSE_PLAYER ((cutscenepause_t)(1 << 1))
#define CUTSCENE_PAUSE_WORLD ((cutscenepause_t)(1 << 2))
#define CUTSCENE_PAUSE_DEFAULT \
((cutscenepause_t)(CUTSCENE_PAUSE_NPC | CUTSCENE_PAUSE_PLAYER))
#define CUTSCENE_PAUSE_ALL \
((cutscenepause_t)(CUTSCENE_PAUSE_NPC | CUTSCENE_PAUSE_PLAYER | CUTSCENE_PAUSE_WORLD))
+4 -7
View File
@@ -9,16 +9,13 @@
#include "cutsceneentitymove.h" #include "cutsceneentitymove.h"
#include "input/input.h" #include "input/input.h"
#include "time/time.h" #include "time/time.h"
#include "ui/rpg/uitextboxmain.h"
void cutsceneItemStart(const cutsceneitem_t *item, cutsceneitemdata_t *data) { void cutsceneItemStart(const cutsceneitem_t *item, cutsceneitemdata_t *data) {
switch(item->type) { switch(item->type) {
case CUTSCENE_ITEM_TYPE_TEXT: { case CUTSCENE_ITEM_TYPE_TEXT:
rpgTextboxShow( uiTextboxMainSetText(item->text.text);
item->text.position,
item->text.text
);
break; break;
}
case CUTSCENE_ITEM_TYPE_WAIT: case CUTSCENE_ITEM_TYPE_WAIT:
data->wait = item->wait; data->wait = item->wait;
@@ -44,7 +41,7 @@ void cutsceneItemStart(const cutsceneitem_t *item, cutsceneitemdata_t *data) {
void cutsceneItemUpdate(const cutsceneitem_t *item, cutsceneitemdata_t *data) { void cutsceneItemUpdate(const cutsceneitem_t *item, cutsceneitemdata_t *data) {
switch(item->type) { switch(item->type) {
case CUTSCENE_ITEM_TYPE_TEXT: case CUTSCENE_ITEM_TYPE_TEXT:
if(rpgTextboxIsVisible()) return; if(uiTextboxMainIsActive()) return;
cutsceneSystemNext(); cutsceneSystemNext();
break; break;
+4 -3
View File
@@ -6,9 +6,10 @@
*/ */
#pragma once #pragma once
#include "rpg/rpgtextbox.h" #include "dusk.h"
#define CUTSCENE_TEXT_MAX_CHARS 256
typedef struct { typedef struct {
char_t text[RPG_TEXTBOX_MAX_CHARS]; char_t text[CUTSCENE_TEXT_MAX_CHARS];
rpgtextboxpos_t position;
} cutscenetext_t; } cutscenetext_t;
+5 -4
View File
@@ -9,9 +9,9 @@
#include "rpg/cutscene/cutscenesystem.h" #include "rpg/cutscene/cutscenesystem.h"
static const cutsceneitem_t TEST_CUTSCENE_ONE_ITEMS[] = { static const cutsceneitem_t TEST_CUTSCENE_ONE_ITEMS[] = {
{ .type = CUTSCENE_ITEM_TYPE_TEXT, .text = { .text = "This is a test cutscene.", .position = RPG_TEXTBOX_POS_BOTTOM } }, { .type = CUTSCENE_ITEM_TYPE_TEXT, .text = { .text = "This is a test cutscene." } },
{ .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = 2.0f }, { .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = 0.5f },
{ .type = CUTSCENE_ITEM_TYPE_TEXT, .text = { .text = "It has multiple lines of text.\nAnd waits in between.", .position = RPG_TEXTBOX_POS_TOP } }, { .type = CUTSCENE_ITEM_TYPE_TEXT, .text = { .text = "It has multiple lines of text.\nAnd waits in between." } },
}; };
static const cutscene_t TEST_CUTSCENE_ONE = { static const cutscene_t TEST_CUTSCENE_ONE = {
@@ -20,11 +20,12 @@ static const cutscene_t TEST_CUTSCENE_ONE = {
}; };
static const cutsceneitem_t TEST_CUTSCENE_TWO_ITEMS[] = { static const cutsceneitem_t TEST_CUTSCENE_TWO_ITEMS[] = {
{ .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = 1.0f }, { .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = 0.1f },
{ .type = CUTSCENE_ITEM_TYPE_CUTSCENE, .cutscene = &TEST_CUTSCENE_ONE }, { .type = CUTSCENE_ITEM_TYPE_CUTSCENE, .cutscene = &TEST_CUTSCENE_ONE },
}; };
static const cutscene_t TEST_CUTSCENE = { static const cutscene_t TEST_CUTSCENE = {
.items = TEST_CUTSCENE_TWO_ITEMS, .items = TEST_CUTSCENE_TWO_ITEMS,
.pause = CUTSCENE_PAUSE_NPC,
.itemCount = sizeof(TEST_CUTSCENE_TWO_ITEMS) / sizeof(cutsceneitem_t) .itemCount = sizeof(TEST_CUTSCENE_TWO_ITEMS) / sizeof(cutsceneitem_t)
}; };
+5 -1
View File
@@ -7,9 +7,9 @@
#include "rpg/entity/entity.h" #include "rpg/entity/entity.h"
#include "assert/assert.h" #include "assert/assert.h"
#include "rpg/cutscene/cutscenesystem.h"
#include "rpg/cutscene/scene/testcutscene.h" #include "rpg/cutscene/scene/testcutscene.h"
#include "rpg/rpgtextbox.h"
const npcmovecallback_t NPC_MOVE_CALLBACKS[NPC_MOVE_TYPE_COUNT] = { const npcmovecallback_t NPC_MOVE_CALLBACKS[NPC_MOVE_TYPE_COUNT] = {
[NPC_MOVE_TYPE_NULL] = { 0 }, [NPC_MOVE_TYPE_NULL] = { 0 },
@@ -50,6 +50,10 @@ void npcSetMoveType(entity_t *entity, const npcmovetype_t moveType) {
void npcMovement(entity_t *entity) { void npcMovement(entity_t *entity) {
assertNotNull(entity, "Entity pointer cannot be NULL"); assertNotNull(entity, "Entity pointer cannot be NULL");
if(
CUTSCENE_SYSTEM.scene != NULL &&
(CUTSCENE_SYSTEM.scene->pause & CUTSCENE_PAUSE_NPC)
) return;
npc_t *npc = &entity->data.npc; npc_t *npc = &entity->data.npc;
if(npc->interactState != NPC_INTERACT_STATE_NONE) return; if(npc->interactState != NPC_INTERACT_STATE_NONE) return;
+5
View File
@@ -12,6 +12,7 @@
#include "time/time.h" #include "time/time.h"
#include "ui/focus/uifocus.h" #include "ui/focus/uifocus.h"
#include "ui/frame/uisettings.h" #include "ui/frame/uisettings.h"
#include "rpg/cutscene/cutscenesystem.h"
void playerInit(entity_t *entity) { void playerInit(entity_t *entity) {
assertNotNull(entity, "Entity pointer cannot be NULL"); assertNotNull(entity, "Entity pointer cannot be NULL");
@@ -23,6 +24,10 @@ bool_t playerCanInteract(entity_t *entity) {
void playerInput(entity_t *entity) { void playerInput(entity_t *entity) {
assertNotNull(entity, "Entity pointer cannot be NULL"); assertNotNull(entity, "Entity pointer cannot be NULL");
if(
CUTSCENE_SYSTEM.scene != NULL &&
(CUTSCENE_SYSTEM.scene->pause & CUTSCENE_PAUSE_PLAYER)
) return;
// Toggle settings on pause // Toggle settings on pause
if(uiSettingsIsOpen() && inputPressed(INPUT_ACTION_PAUSE)) { if(uiSettingsIsOpen() && inputPressed(INPUT_ACTION_PAUSE)) {
+3 -5
View File
@@ -9,9 +9,9 @@
#include "entity/entity.h" #include "entity/entity.h"
#include "rpg/overworld/map.h" #include "rpg/overworld/map.h"
#include "rpg/cutscene/cutscenesystem.h" #include "rpg/cutscene/cutscenesystem.h"
#include "rpg/cutscene/scene/testcutscene.h"
#include "time/time.h" #include "time/time.h"
#include "rpgcamera.h" #include "rpgcamera.h"
#include "rpgtextbox.h"
#include "util/memory.h" #include "util/memory.h"
#include "util/string.h" #include "util/string.h"
#include "assert/assert.h" #include "assert/assert.h"
@@ -25,8 +25,6 @@ errorret_t rpgInit(void) {
errorChain(mapInit()); errorChain(mapInit());
rpgCameraInit(); rpgCameraInit();
rpgTextboxInit();
// Init world // Init world
errorChain(mapPositionSet((chunkpos_t){ 0, 0, 0 })); errorChain(mapPositionSet((chunkpos_t){ 0, 0, 0 }));
@@ -50,8 +48,8 @@ errorret_t rpgInit(void) {
entityInit(npc, ENTITY_TYPE_NPC); entityInit(npc, ENTITY_TYPE_NPC);
npcSetMoveType(npc, NPC_MOVE_TYPE_PATH); npcSetMoveType(npc, NPC_MOVE_TYPE_PATH);
npc->position = (worldpos_t){ 8, 8, 1 }; npc->position = (worldpos_t){ 8, 8, 1 };
npc->interact.type = ENTITY_INTERACT_PRINT; npc->interact.type = ENTITY_INTERACT_CUTSCENE;
npc->interact.data.message = "hello world"; npc->interact.data.cutscene = &TEST_CUTSCENE;
{ {
chunkpos_t cp; chunkpos_t cp;
worldPosToChunkPos(&npc->position, &cp); worldPosToChunkPos(&npc->position, &cp);
-39
View File
@@ -1,39 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "rpgtextbox.h"
#include "util/memory.h"
#include "util/string.h"
#include "assert/assert.h"
rpgtextbox_t RPG_TEXTBOX;
void rpgTextboxInit() {
memoryZero(&RPG_TEXTBOX, sizeof(rpgtextbox_t));
}
void rpgTextboxShow(
const rpgtextboxpos_t position,
const char_t *text
) {
RPG_TEXTBOX.position = position;
RPG_TEXTBOX.visible = true;
stringCopy(
RPG_TEXTBOX.text,
text,
RPG_TEXTBOX_MAX_CHARS
);
}
void rpgTextboxHide() {
RPG_TEXTBOX.visible = false;
}
bool_t rpgTextboxIsVisible() {
return RPG_TEXTBOX.visible;
}
-52
View File
@@ -1,52 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
#define RPG_TEXTBOX_MAX_CHARS 256
typedef enum {
RPG_TEXTBOX_POS_TOP,
RPG_TEXTBOX_POS_BOTTOM,
} rpgtextboxpos_t;
typedef struct {
rpgtextboxpos_t position;
bool_t visible;
char_t text[RPG_TEXTBOX_MAX_CHARS];
} rpgtextbox_t;
extern rpgtextbox_t RPG_TEXTBOX;
/**
* Initializes the RPG textbox.
*/
void rpgTextboxInit();
/**
* Shows the RPG textbox at a specified position.
*
* @param position The position to show the textbox at.
* @param text The text to display in the textbox (copied).
*/
void rpgTextboxShow(
const rpgtextboxpos_t position,
const char_t *text
);
/**
* Hides the RPG textbox.
*/
void rpgTextboxHide();
/**
* Checks if the RPG textbox is currently visible.
*
* @return true if the textbox is visible, false otherwise.
*/
bool_t rpgTextboxIsVisible();