Cutscene tests
This commit is contained in:
@@ -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();
|
||||
});
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -1,6 +0,0 @@
|
||||
module = {
|
||||
render() {
|
||||
Text.draw(0, 0, "Hello World");
|
||||
SpriteBatch.flush();
|
||||
}
|
||||
};
|
||||
@@ -8,7 +8,6 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
rpg.c
|
||||
rpgcamera.c
|
||||
rpgtextbox.c
|
||||
)
|
||||
|
||||
# Subdirs
|
||||
|
||||
@@ -7,8 +7,10 @@
|
||||
|
||||
#pragma once
|
||||
#include "rpg/cutscene/item/cutsceneitem.h"
|
||||
#include "cutscenepause.h"
|
||||
|
||||
typedef struct cutscene_s {
|
||||
const cutsceneitem_t *items;
|
||||
uint8_t itemCount;
|
||||
cutscenepause_t pause;
|
||||
} cutscene_t;
|
||||
@@ -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))
|
||||
@@ -9,16 +9,13 @@
|
||||
#include "cutsceneentitymove.h"
|
||||
#include "input/input.h"
|
||||
#include "time/time.h"
|
||||
#include "ui/rpg/uitextboxmain.h"
|
||||
|
||||
void cutsceneItemStart(const cutsceneitem_t *item, cutsceneitemdata_t *data) {
|
||||
switch(item->type) {
|
||||
case CUTSCENE_ITEM_TYPE_TEXT: {
|
||||
rpgTextboxShow(
|
||||
item->text.position,
|
||||
item->text.text
|
||||
);
|
||||
case CUTSCENE_ITEM_TYPE_TEXT:
|
||||
uiTextboxMainSetText(item->text.text);
|
||||
break;
|
||||
}
|
||||
|
||||
case CUTSCENE_ITEM_TYPE_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) {
|
||||
switch(item->type) {
|
||||
case CUTSCENE_ITEM_TYPE_TEXT:
|
||||
if(rpgTextboxIsVisible()) return;
|
||||
if(uiTextboxMainIsActive()) return;
|
||||
cutsceneSystemNext();
|
||||
break;
|
||||
|
||||
|
||||
@@ -6,9 +6,10 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "rpg/rpgtextbox.h"
|
||||
#include "dusk.h"
|
||||
|
||||
#define CUTSCENE_TEXT_MAX_CHARS 256
|
||||
|
||||
typedef struct {
|
||||
char_t text[RPG_TEXTBOX_MAX_CHARS];
|
||||
rpgtextboxpos_t position;
|
||||
char_t text[CUTSCENE_TEXT_MAX_CHARS];
|
||||
} cutscenetext_t;
|
||||
@@ -9,9 +9,9 @@
|
||||
#include "rpg/cutscene/cutscenesystem.h"
|
||||
|
||||
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_WAIT, .wait = 2.0f },
|
||||
{ .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 = "This is a test cutscene." } },
|
||||
{ .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." } },
|
||||
};
|
||||
|
||||
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[] = {
|
||||
{ .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 },
|
||||
};
|
||||
|
||||
static const cutscene_t TEST_CUTSCENE = {
|
||||
.items = TEST_CUTSCENE_TWO_ITEMS,
|
||||
.pause = CUTSCENE_PAUSE_NPC,
|
||||
.itemCount = sizeof(TEST_CUTSCENE_TWO_ITEMS) / sizeof(cutsceneitem_t)
|
||||
};
|
||||
@@ -7,9 +7,9 @@
|
||||
|
||||
#include "rpg/entity/entity.h"
|
||||
#include "assert/assert.h"
|
||||
#include "rpg/cutscene/cutscenesystem.h"
|
||||
|
||||
#include "rpg/cutscene/scene/testcutscene.h"
|
||||
#include "rpg/rpgtextbox.h"
|
||||
|
||||
const npcmovecallback_t NPC_MOVE_CALLBACKS[NPC_MOVE_TYPE_COUNT] = {
|
||||
[NPC_MOVE_TYPE_NULL] = { 0 },
|
||||
@@ -50,6 +50,10 @@ void npcSetMoveType(entity_t *entity, const npcmovetype_t moveType) {
|
||||
|
||||
void npcMovement(entity_t *entity) {
|
||||
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;
|
||||
if(npc->interactState != NPC_INTERACT_STATE_NONE) return;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "time/time.h"
|
||||
#include "ui/focus/uifocus.h"
|
||||
#include "ui/frame/uisettings.h"
|
||||
#include "rpg/cutscene/cutscenesystem.h"
|
||||
|
||||
void playerInit(entity_t *entity) {
|
||||
assertNotNull(entity, "Entity pointer cannot be NULL");
|
||||
@@ -23,6 +24,10 @@ bool_t playerCanInteract(entity_t *entity) {
|
||||
|
||||
void playerInput(entity_t *entity) {
|
||||
assertNotNull(entity, "Entity pointer cannot be NULL");
|
||||
if(
|
||||
CUTSCENE_SYSTEM.scene != NULL &&
|
||||
(CUTSCENE_SYSTEM.scene->pause & CUTSCENE_PAUSE_PLAYER)
|
||||
) return;
|
||||
|
||||
// Toggle settings on pause
|
||||
if(uiSettingsIsOpen() && inputPressed(INPUT_ACTION_PAUSE)) {
|
||||
|
||||
+3
-5
@@ -9,9 +9,9 @@
|
||||
#include "entity/entity.h"
|
||||
#include "rpg/overworld/map.h"
|
||||
#include "rpg/cutscene/cutscenesystem.h"
|
||||
#include "rpg/cutscene/scene/testcutscene.h"
|
||||
#include "time/time.h"
|
||||
#include "rpgcamera.h"
|
||||
#include "rpgtextbox.h"
|
||||
#include "util/memory.h"
|
||||
#include "util/string.h"
|
||||
#include "assert/assert.h"
|
||||
@@ -25,8 +25,6 @@ errorret_t rpgInit(void) {
|
||||
errorChain(mapInit());
|
||||
|
||||
rpgCameraInit();
|
||||
rpgTextboxInit();
|
||||
|
||||
// Init world
|
||||
errorChain(mapPositionSet((chunkpos_t){ 0, 0, 0 }));
|
||||
|
||||
@@ -50,8 +48,8 @@ errorret_t rpgInit(void) {
|
||||
entityInit(npc, ENTITY_TYPE_NPC);
|
||||
npcSetMoveType(npc, NPC_MOVE_TYPE_PATH);
|
||||
npc->position = (worldpos_t){ 8, 8, 1 };
|
||||
npc->interact.type = ENTITY_INTERACT_PRINT;
|
||||
npc->interact.data.message = "hello world";
|
||||
npc->interact.type = ENTITY_INTERACT_CUTSCENE;
|
||||
npc->interact.data.cutscene = &TEST_CUTSCENE;
|
||||
{
|
||||
chunkpos_t cp;
|
||||
worldPosToChunkPos(&npc->position, &cp);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
Reference in New Issue
Block a user