pixospritz-core 0.10.1 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -286
- package/dist/bundle.js +13 -3
- package/dist/bundle.js.map +1 -1
- package/dist/style.css +1 -0
- package/package.json +43 -44
- package/src/components/WebGLView.jsx +318 -0
- package/src/css/pixos.css +372 -0
- package/src/engine/actions/animate.js +41 -0
- package/src/engine/actions/changezone.js +135 -0
- package/src/engine/actions/chat.js +109 -0
- package/src/engine/actions/dialogue.js +90 -0
- package/src/engine/actions/face.js +22 -0
- package/src/engine/actions/greeting.js +28 -0
- package/src/engine/actions/interact.js +86 -0
- package/src/engine/actions/move.js +67 -0
- package/src/engine/actions/patrol.js +109 -0
- package/src/engine/actions/prompt.js +185 -0
- package/src/engine/actions/script.js +42 -0
- package/src/engine/core/audio/AudioSystem.js +543 -0
- package/src/engine/core/cutscene/PxcPlayer.js +956 -0
- package/src/engine/core/cutscene/manager.js +243 -0
- package/src/engine/core/database/index.js +75 -0
- package/src/engine/core/debug/index.js +371 -0
- package/src/engine/core/hud/index.js +765 -0
- package/src/engine/core/index.js +540 -0
- package/src/engine/core/input/gamepad/Controller.js +71 -0
- package/src/engine/core/input/gamepad/ControllerButtons.js +231 -0
- package/src/engine/core/input/gamepad/ControllerStick.js +173 -0
- package/src/engine/core/input/gamepad/index.js +592 -0
- package/src/engine/core/input/keyboard.js +196 -0
- package/src/engine/core/input/manager.js +485 -0
- package/src/engine/core/input/mouse.js +203 -0
- package/src/engine/core/input/touch.js +175 -0
- package/src/engine/core/mode/manager.js +199 -0
- package/src/engine/core/net/manager.js +535 -0
- package/src/engine/core/queue/action.js +83 -0
- package/src/engine/core/queue/event.js +82 -0
- package/src/engine/core/queue/index.js +44 -0
- package/src/engine/core/queue/loadable.js +33 -0
- package/src/engine/core/render/CameraEffects.js +494 -0
- package/src/engine/core/render/FrustumCuller.js +417 -0
- package/src/engine/core/render/LODManager.js +285 -0
- package/src/engine/core/render/ParticleManager.js +529 -0
- package/src/engine/core/render/TextureAtlas.js +465 -0
- package/src/engine/core/render/camera.js +338 -0
- package/src/engine/core/render/light.js +197 -0
- package/src/engine/core/render/manager.js +1079 -0
- package/src/engine/core/render/shaders.js +110 -0
- package/src/engine/core/render/skybox.js +342 -0
- package/src/engine/core/resource/manager.js +133 -0
- package/src/engine/core/resource/object.js +611 -0
- package/src/engine/core/resource/texture.js +103 -0
- package/src/engine/core/resource/tileset.js +177 -0
- package/src/engine/core/scene/avatar.js +215 -0
- package/src/engine/core/scene/speech.js +138 -0
- package/src/engine/core/scene/sprite.js +702 -0
- package/src/engine/core/scene/spritz.js +189 -0
- package/src/engine/core/scene/world.js +681 -0
- package/src/engine/core/scene/zone.js +1167 -0
- package/src/engine/core/store/index.js +110 -0
- package/src/engine/dynamic/animatedSprite.js +64 -0
- package/src/engine/dynamic/animatedTile.js +98 -0
- package/src/engine/dynamic/avatar.js +110 -0
- package/src/engine/dynamic/map.js +174 -0
- package/src/engine/dynamic/sprite.js +255 -0
- package/src/engine/dynamic/spritz.js +119 -0
- package/src/engine/events/EventSystem.js +609 -0
- package/src/engine/events/camera.js +142 -0
- package/src/engine/events/chat.js +75 -0
- package/src/engine/events/menu.js +186 -0
- package/src/engine/scripting/CallbackManager.js +514 -0
- package/src/engine/scripting/PixoScriptInterpreter.js +81 -0
- package/src/engine/scripting/PixoScriptLibrary.js +704 -0
- package/src/engine/shaders/effects/index.js +450 -0
- package/src/engine/shaders/fs.js +222 -0
- package/src/engine/shaders/particles/fs.js +41 -0
- package/src/engine/shaders/particles/vs.js +61 -0
- package/src/engine/shaders/picker/fs.js +34 -0
- package/src/engine/shaders/picker/init.js +62 -0
- package/src/engine/shaders/picker/vs.js +42 -0
- package/src/engine/shaders/pxsl/README.md +250 -0
- package/src/engine/shaders/pxsl/index.js +25 -0
- package/src/engine/shaders/pxsl/library.js +608 -0
- package/src/engine/shaders/pxsl/manager.js +338 -0
- package/src/engine/shaders/pxsl/specification.js +363 -0
- package/src/engine/shaders/pxsl/transpiler.js +753 -0
- package/src/engine/shaders/skybox/cosmic/fs.js +147 -0
- package/src/engine/shaders/skybox/cosmic/vs.js +23 -0
- package/src/engine/shaders/skybox/matrix/fs.js +127 -0
- package/src/engine/shaders/skybox/matrix/vs.js +23 -0
- package/src/engine/shaders/skybox/morning/fs.js +109 -0
- package/src/engine/shaders/skybox/morning/vs.js +23 -0
- package/src/engine/shaders/skybox/neon/fs.js +119 -0
- package/src/engine/shaders/skybox/neon/vs.js +23 -0
- package/src/engine/shaders/skybox/sky/fs.js +114 -0
- package/src/engine/shaders/skybox/sky/vs.js +23 -0
- package/src/engine/shaders/skybox/sunset/fs.js +101 -0
- package/src/engine/shaders/skybox/sunset/vs.js +23 -0
- package/src/engine/shaders/transition/blur/fs.js +42 -0
- package/src/engine/shaders/transition/blur/vs.js +26 -0
- package/src/engine/shaders/transition/cross/fs.js +36 -0
- package/src/engine/shaders/transition/cross/vs.js +26 -0
- package/src/engine/shaders/transition/crossBlur/fs.js +41 -0
- package/src/engine/shaders/transition/crossBlur/vs.js +25 -0
- package/src/engine/shaders/transition/dissolve/fs.js +78 -0
- package/src/engine/shaders/transition/dissolve/vs.js +24 -0
- package/src/engine/shaders/transition/fade/fs.js +31 -0
- package/src/engine/shaders/transition/fade/vs.js +27 -0
- package/src/engine/shaders/transition/iris/fs.js +52 -0
- package/src/engine/shaders/transition/iris/vs.js +24 -0
- package/src/engine/shaders/transition/pixelate/fs.js +44 -0
- package/src/engine/shaders/transition/pixelate/vs.js +24 -0
- package/src/engine/shaders/transition/slide/fs.js +53 -0
- package/src/engine/shaders/transition/slide/vs.js +24 -0
- package/src/engine/shaders/transition/swirl/fs.js +39 -0
- package/src/engine/shaders/transition/swirl/vs.js +26 -0
- package/src/engine/shaders/transition/wipe/fs.js +50 -0
- package/src/engine/shaders/transition/wipe/vs.js +24 -0
- package/src/engine/shaders/vs.js +60 -0
- package/src/engine/utils/CameraController.js +506 -0
- package/src/engine/utils/ObjHelper.js +551 -0
- package/src/engine/utils/debug-logger.js +110 -0
- package/src/engine/utils/enums.js +305 -0
- package/src/engine/utils/generator.js +156 -0
- package/src/engine/utils/index.js +21 -0
- package/src/engine/utils/loaders/ActionLoader.js +77 -0
- package/src/engine/utils/loaders/AudioLoader.js +157 -0
- package/src/engine/utils/loaders/EventLoader.js +66 -0
- package/src/engine/utils/loaders/ObjectLoader.js +67 -0
- package/src/engine/utils/loaders/SpriteLoader.js +77 -0
- package/src/engine/utils/loaders/TilesetLoader.js +103 -0
- package/src/engine/utils/loaders/index.js +21 -0
- package/src/engine/utils/math/matrix4.js +367 -0
- package/src/engine/utils/math/vector.js +458 -0
- package/src/engine/utils/obj/_old_js/index.js +46 -0
- package/src/engine/utils/obj/_old_js/layout.js +308 -0
- package/src/engine/utils/obj/_old_js/material.js +711 -0
- package/src/engine/utils/obj/_old_js/mesh.js +761 -0
- package/src/engine/utils/obj/_old_js/utils.js +647 -0
- package/src/engine/utils/obj/index.js +24 -0
- package/src/engine/utils/obj/js/index.js +277 -0
- package/src/engine/utils/obj/js/loader.js +232 -0
- package/src/engine/utils/obj/layout.js +246 -0
- package/src/engine/utils/obj/material.js +665 -0
- package/src/engine/utils/obj/mesh.js +657 -0
- package/src/engine/utils/obj/ts/index.ts +72 -0
- package/src/engine/utils/obj/ts/layout.ts +265 -0
- package/src/engine/utils/obj/ts/material.ts +760 -0
- package/src/engine/utils/obj/ts/mesh.ts +785 -0
- package/src/engine/utils/obj/ts/utils.ts +501 -0
- package/src/engine/utils/obj/utils.js +428 -0
- package/src/engine/utils/resources.js +18 -0
- package/src/index.jsx +55 -0
- package/src/spritz/player.js +18 -0
- package/src/spritz/readme.md +18 -0
- package/LICENSE +0 -437
- package/dist/bundle.js.LICENSE.txt +0 -31
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/* *\
|
|
2
|
+
** ----------------------------------------------- **
|
|
3
|
+
** Calliope - Pixos Game Engine **
|
|
4
|
+
** ----------------------------------------------- **
|
|
5
|
+
** Copyright (c) 2020-2025 - Kyle Derby MacInnis **
|
|
6
|
+
** **
|
|
7
|
+
** Any unauthorized distribution or transfer **
|
|
8
|
+
** of this work is strictly prohibited. **
|
|
9
|
+
** **
|
|
10
|
+
** All Rights Reserved. **
|
|
11
|
+
** ----------------------------------------------- **
|
|
12
|
+
\* */
|
|
13
|
+
|
|
14
|
+
export default {
|
|
15
|
+
// Initialize Dialogue Object
|
|
16
|
+
init: function (text, scrolling = true, options = {}) {
|
|
17
|
+
this.engine = this.sprite.engine;
|
|
18
|
+
this.text = text; // holds queue of dialogue
|
|
19
|
+
this.displayText = typeof text === 'string' ? text : this.text.shift(); // current statement
|
|
20
|
+
this.scrolling = scrolling;
|
|
21
|
+
this.options = options;
|
|
22
|
+
this.completed = false;
|
|
23
|
+
this.lastText = false;
|
|
24
|
+
this.speechOutput = true;
|
|
25
|
+
this.lastKey = new Date().getTime();
|
|
26
|
+
this.loaded = true;
|
|
27
|
+
},
|
|
28
|
+
// Update & Scroll
|
|
29
|
+
tick: function (time) {
|
|
30
|
+
if (!this.loaded) return;
|
|
31
|
+
// Check for Dialogue Completion (TODO - manual triggers + scroll / sections)
|
|
32
|
+
if (this.options && this.options.autoclose) {
|
|
33
|
+
this.endTime = this.endTime ? this.endTime : this.options.endTime ?? new Date().getTime() + (this.options.duration * 1000 ?? 10000); // 10 seconds default if autoclose
|
|
34
|
+
if (time > this.endTime) {
|
|
35
|
+
this.completed = true;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Handle Input
|
|
39
|
+
this.checkInput(time);
|
|
40
|
+
|
|
41
|
+
// Dialogue
|
|
42
|
+
this.sprite.speak(this.displayText, false, this);
|
|
43
|
+
|
|
44
|
+
// Callback on Completion
|
|
45
|
+
if (this.completed && this.options.onClose) {
|
|
46
|
+
if (this.sprite.speech.clearHud) {
|
|
47
|
+
this.sprite.speech.clearHud();
|
|
48
|
+
}
|
|
49
|
+
this.options.onClose();
|
|
50
|
+
}
|
|
51
|
+
return this.completed;
|
|
52
|
+
},
|
|
53
|
+
// Handle Keyboard
|
|
54
|
+
checkInput: function (time) {
|
|
55
|
+
if (time > this.lastKey + 100) {
|
|
56
|
+
switch (this.engine.keyboard.lastPressedCode()) {
|
|
57
|
+
case 'Escape':
|
|
58
|
+
this.sprite.speak(false);
|
|
59
|
+
this.completed = true; // toggle
|
|
60
|
+
break;
|
|
61
|
+
case 'Enter':
|
|
62
|
+
if (typeof this.text === 'string' || this.text.length === 0) {
|
|
63
|
+
this.sprite.speak(false);
|
|
64
|
+
this.completed = true;
|
|
65
|
+
} else {
|
|
66
|
+
this.completed = false;
|
|
67
|
+
this.displayText = this.text.shift();
|
|
68
|
+
window.speechSynthesis.cancel();
|
|
69
|
+
this.speechOutput = true;
|
|
70
|
+
this.sprite.speak(this.displayText, false, this);
|
|
71
|
+
}
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
// gamepad
|
|
75
|
+
if (this.engine.gamepad.keyPressed('a')) {
|
|
76
|
+
if (typeof this.text === 'string' || this.text.length === 0) {
|
|
77
|
+
this.sprite.speak(false);
|
|
78
|
+
this.completed = true;
|
|
79
|
+
} else {
|
|
80
|
+
this.completed = false;
|
|
81
|
+
this.displayText = this.text.shift();
|
|
82
|
+
window.speechSynthesis.cancel();
|
|
83
|
+
this.speechOutput = true;
|
|
84
|
+
this.sprite.speak(this.displayText, false, this);
|
|
85
|
+
}
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/* *\
|
|
2
|
+
** ----------------------------------------------- **
|
|
3
|
+
** Calliope - Pixos Game Engine **
|
|
4
|
+
** ----------------------------------------------- **
|
|
5
|
+
** Copyright (c) 2020-2025 - Kyle Derby MacInnis **
|
|
6
|
+
** **
|
|
7
|
+
** Any unauthorized distribution or transfer **
|
|
8
|
+
** of this work is strictly prohibited. **
|
|
9
|
+
** **
|
|
10
|
+
** All Rights Reserved. **
|
|
11
|
+
** ----------------------------------------------- **
|
|
12
|
+
\* */
|
|
13
|
+
|
|
14
|
+
export default {
|
|
15
|
+
init: function (facing) {
|
|
16
|
+
this.facing = facing;
|
|
17
|
+
},
|
|
18
|
+
tick: function (time) {
|
|
19
|
+
if (this.facing && this.facing != this.sprite.facing) this.sprite.setFacing(this.facing);
|
|
20
|
+
return true;
|
|
21
|
+
},
|
|
22
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/* *\
|
|
2
|
+
** ----------------------------------------------- **
|
|
3
|
+
** Calliope - Pixos Game Engine **
|
|
4
|
+
** ----------------------------------------------- **
|
|
5
|
+
** Copyright (c) 2020-2025 - Kyle Derby MacInnis **
|
|
6
|
+
** **
|
|
7
|
+
** Any unauthorized distribution or transfer **
|
|
8
|
+
** of this work is strictly prohibited. **
|
|
9
|
+
** **
|
|
10
|
+
** All Rights Reserved. **
|
|
11
|
+
** ----------------------------------------------- **
|
|
12
|
+
\* */
|
|
13
|
+
|
|
14
|
+
export default {
|
|
15
|
+
// Initialize Dialogue Object
|
|
16
|
+
init: function (greeting, options = {}) {
|
|
17
|
+
this.engine = this.sprite.engine;
|
|
18
|
+
this.greeting = greeting;
|
|
19
|
+
this.options = options;
|
|
20
|
+
this.completed = false;
|
|
21
|
+
},
|
|
22
|
+
// Update & Scroll
|
|
23
|
+
tick: function (time) {
|
|
24
|
+
if (!this.loaded) return;
|
|
25
|
+
this.sprite.setGreeting(this.text);
|
|
26
|
+
return true;
|
|
27
|
+
},
|
|
28
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/* *\
|
|
2
|
+
** ----------------------------------------------- **
|
|
3
|
+
** Calliope - Pixos Game Engine **
|
|
4
|
+
** ----------------------------------------------- **
|
|
5
|
+
** Copyright (c) 2020-2025 - Kyle Derby MacInnis **
|
|
6
|
+
** **
|
|
7
|
+
** Any unauthorized distribution or transfer **
|
|
8
|
+
** of this work is strictly prohibited. **
|
|
9
|
+
** **
|
|
10
|
+
** All Rights Reserved. **
|
|
11
|
+
** ----------------------------------------------- **
|
|
12
|
+
\* */
|
|
13
|
+
|
|
14
|
+
import { Vector } from '@Engine/utils/math/vector.js';
|
|
15
|
+
import { Direction } from '@Engine/utils/enums.js';
|
|
16
|
+
|
|
17
|
+
export default {
|
|
18
|
+
init: async function (from, facing, world) {
|
|
19
|
+
this.world = world;
|
|
20
|
+
this.from = new Vector(...from);
|
|
21
|
+
this.facing = facing;
|
|
22
|
+
this.offset = Direction.toOffset(facing);
|
|
23
|
+
this.lastKey = new Date().getTime();
|
|
24
|
+
this.completed = false;
|
|
25
|
+
// Determine Tile
|
|
26
|
+
this.to = [from[0] + this.offset[0], from[1] + this.offset[1]];
|
|
27
|
+
// Check for Sprites at that point
|
|
28
|
+
this.zone = world.zoneContaining(...this.to);
|
|
29
|
+
// Trigger interaction on Sprite
|
|
30
|
+
this.spriteList = this.zone.spriteList.filter((sprite) => sprite.pos.x === this.to[0] && sprite.pos.y === this.to[1]);
|
|
31
|
+
this.objectList = this.zone.objectList.filter((object) => object.pos.x === this.to[0] && object.pos.y === this.to[1]);
|
|
32
|
+
// -- pass through reference to "finish()" callback
|
|
33
|
+
this.finish = this.finish.bind(this);
|
|
34
|
+
// Trigger
|
|
35
|
+
this.interact();
|
|
36
|
+
},
|
|
37
|
+
// Trigger interactions in sprites
|
|
38
|
+
interact: async function () {
|
|
39
|
+
if (this.spriteList.length === 0 && this.objectList.length === 0) this.completed = true;
|
|
40
|
+
// objects
|
|
41
|
+
await Promise.all(this.objectList.map(async (object) => {
|
|
42
|
+
let faceChange = object.faceDir(Direction.reverse(this.facing));
|
|
43
|
+
if (faceChange) {
|
|
44
|
+
object.addAction(faceChange); // face towards avatar
|
|
45
|
+
}
|
|
46
|
+
return object.interact ? await this.zone.objectDict[object.id].interact(this.sprite, this.finish) : null;
|
|
47
|
+
}));
|
|
48
|
+
// sprite
|
|
49
|
+
await Promise.all(this.spriteList.map(async (sprite) => {
|
|
50
|
+
let faceChange = sprite.faceDir(Direction.reverse(this.facing));
|
|
51
|
+
if (faceChange) {
|
|
52
|
+
sprite.addAction(faceChange); // face towards avatar
|
|
53
|
+
}
|
|
54
|
+
return sprite.interact ? await this.zone.spriteDict[sprite.id].interact(this.sprite, this.finish) : null;
|
|
55
|
+
}));
|
|
56
|
+
},
|
|
57
|
+
// Callback to clear interaction
|
|
58
|
+
finish: function (result) {
|
|
59
|
+
if (result) this.completed = true;
|
|
60
|
+
},
|
|
61
|
+
// check input and completion
|
|
62
|
+
tick: function (time) {
|
|
63
|
+
if (!this.loaded) return;
|
|
64
|
+
this.checkInput(time);
|
|
65
|
+
return this.completed; // loop
|
|
66
|
+
},
|
|
67
|
+
// Handle Keyboard
|
|
68
|
+
checkInput: function (time) {
|
|
69
|
+
if (time > this.lastKey + Math.max(this.length, 200)) {
|
|
70
|
+
switch (this.sprite.engine.keyboard.lastPressed('q')) {
|
|
71
|
+
// close dialogue on q key press
|
|
72
|
+
case 'q':
|
|
73
|
+
// Needs to Cancel the Interaction on the Affected Sprite as well
|
|
74
|
+
this.completed = true; // toggle
|
|
75
|
+
break;
|
|
76
|
+
default:
|
|
77
|
+
this.lastKey = new Date().getTime();
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// gamepad
|
|
82
|
+
if (this.sprite.engine.gamepad.keyPressed('a')) {
|
|
83
|
+
this.completed = true;
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/* *\
|
|
2
|
+
** ----------------------------------------------- **
|
|
3
|
+
** Calliope - Pixos Game Engine **
|
|
4
|
+
** ----------------------------------------------- **
|
|
5
|
+
** Copyright (c) 2020-2025 - Kyle Derby MacInnis **
|
|
6
|
+
** **
|
|
7
|
+
** Any unauthorized distribution or transfer **
|
|
8
|
+
** of this work is strictly prohibited. **
|
|
9
|
+
** **
|
|
10
|
+
** All Rights Reserved. **
|
|
11
|
+
** ----------------------------------------------- **
|
|
12
|
+
\* */
|
|
13
|
+
|
|
14
|
+
import { Vector, set, lerp } from '@Engine/utils/math/vector.js';
|
|
15
|
+
import { Direction } from '@Engine/utils/enums.js';
|
|
16
|
+
|
|
17
|
+
export default {
|
|
18
|
+
init: function (from, to, length, zone) {
|
|
19
|
+
this.zone = zone;
|
|
20
|
+
this.from = new Vector(...from);
|
|
21
|
+
this.to = new Vector(...to);
|
|
22
|
+
this.facing = Direction.fromOffset([Math.round(to.x - from.x), Math.round(to.y - from.y)]);
|
|
23
|
+
this.length = length;
|
|
24
|
+
this.spriteList = this.zone.spriteList.filter((sprite) => sprite.pos.x === this.to.x && sprite.pos.y === this.to.y);
|
|
25
|
+
},
|
|
26
|
+
// move
|
|
27
|
+
tick: function (time) {
|
|
28
|
+
if (!this.loaded) return;
|
|
29
|
+
// Set facing
|
|
30
|
+
if (this.facing && this.facing != this.sprite.facing) this.sprite.setFacing(this.facing);
|
|
31
|
+
// Transition & Move
|
|
32
|
+
let endTime = this.startTime + this.length;
|
|
33
|
+
let frac = (time - this.startTime) / this.length;
|
|
34
|
+
if (time >= endTime) {
|
|
35
|
+
this.sprite.pos.x = this.to.x;
|
|
36
|
+
this.sprite.pos.y = this.to.y;
|
|
37
|
+
let hx = this.sprite.pos.x + this.sprite.hotspotOffset.x;
|
|
38
|
+
let hy = this.sprite.pos.y + this.sprite.hotspotOffset.y;
|
|
39
|
+
this.sprite.pos.z = this.sprite.zone.getHeight(hx, hy);
|
|
40
|
+
frac = 1;
|
|
41
|
+
this.onStep();
|
|
42
|
+
// Get next frame
|
|
43
|
+
let newFrame = Math.floor(frac * 4);
|
|
44
|
+
if (newFrame != this.sprite.animFrame) this.sprite.setFrame(newFrame);
|
|
45
|
+
return time >= endTime;
|
|
46
|
+
} else {
|
|
47
|
+
this.sprite.pos.x = this.from.x + frac * (this.to.x - this.from.x);
|
|
48
|
+
this.sprite.pos.y = this.from.y + frac * (this.to.y - this.from.y);
|
|
49
|
+
let hx = this.sprite.pos.x + this.sprite.hotspotOffset.x;
|
|
50
|
+
let hy = this.sprite.pos.y + this.sprite.hotspotOffset.y;
|
|
51
|
+
this.sprite.pos.z = this.sprite.zone.getHeight(hx, hy);
|
|
52
|
+
// Get next frame
|
|
53
|
+
let newFrame = Math.floor(frac * 4);
|
|
54
|
+
if (newFrame != this.sprite.animFrame) this.sprite.setFrame(newFrame);
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
},
|
|
58
|
+
// Trigger interactions in sprite when finished moving
|
|
59
|
+
onStep: async function () {
|
|
60
|
+
if (this.spriteList.length === 0) this.completed = true;
|
|
61
|
+
await Promise.all(
|
|
62
|
+
this.spriteList.map(async (sprite) => {
|
|
63
|
+
return sprite.onStep ? await sprite.onStep(sprite, sprite) : null;
|
|
64
|
+
})
|
|
65
|
+
);
|
|
66
|
+
},
|
|
67
|
+
};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/* *\
|
|
2
|
+
** ----------------------------------------------- **
|
|
3
|
+
** Calliope - Pixos Game Engine **
|
|
4
|
+
** ----------------------------------------------- **
|
|
5
|
+
** Copyright (c) 2020-2025 - Kyle Derby MacInnis **
|
|
6
|
+
** **
|
|
7
|
+
** Any unauthorized distribution or transfer **
|
|
8
|
+
** of this work is strictly prohibited. **
|
|
9
|
+
** **
|
|
10
|
+
** All Rights Reserved. **
|
|
11
|
+
** ----------------------------------------------- **
|
|
12
|
+
\* */
|
|
13
|
+
|
|
14
|
+
import { Vector } from '@Engine/utils/math/vector.js';
|
|
15
|
+
import { Direction } from '@Engine/utils/enums.js';
|
|
16
|
+
import { ActionLoader } from '@Engine/utils/loaders/index.js';
|
|
17
|
+
|
|
18
|
+
export default {
|
|
19
|
+
init: async function (from, to, moveLength, zone) {
|
|
20
|
+
this.zone = zone;
|
|
21
|
+
this.from = new Vector(...from);
|
|
22
|
+
this.to = new Vector(...to);
|
|
23
|
+
this.lastKey = new Date().getTime();
|
|
24
|
+
this.completed = false;
|
|
25
|
+
this.direction = 1;
|
|
26
|
+
this.audio = await this.zone.engine.resourceManager.audioLoader.loadFromZip(this.sprite.zip, this.sprite.patrolSound ?? 'sewer-beat.mp3', true);
|
|
27
|
+
// Determine Path to Walk
|
|
28
|
+
[this.hasMoves, this.moveList] = this.sprite.zone.world.pathFind(from, to);
|
|
29
|
+
if (!this.hasMoves) {
|
|
30
|
+
this.completed = true; // no path - do not patrol
|
|
31
|
+
}
|
|
32
|
+
this.moveIndex = 1; // holds index position
|
|
33
|
+
this.moveLength = moveLength; // length of time per move
|
|
34
|
+
|
|
35
|
+
if (this.zone.audio) this.zone.audio.pauseAudio();
|
|
36
|
+
if (this.audio) this.audio.playAudio();
|
|
37
|
+
},
|
|
38
|
+
tick: function (time) {
|
|
39
|
+
if (!this.loaded) return;
|
|
40
|
+
this.checkInput(time);
|
|
41
|
+
// load up moves - todo (improve this and make it less manual)
|
|
42
|
+
let endTime = this.startTime + this.moveLength;
|
|
43
|
+
if (time > endTime) {
|
|
44
|
+
let move = this.moveList[this.moveIndex];
|
|
45
|
+
if (this.moveList.length > 2) {
|
|
46
|
+
// last position (for facing)
|
|
47
|
+
let last =
|
|
48
|
+
this.moveIndex == 0
|
|
49
|
+
? this.moveList[this.moveIndex + 1]
|
|
50
|
+
: this.moveIndex + 1 >= this.moveList.length
|
|
51
|
+
? this.moveList[this.moveList.length - 1]
|
|
52
|
+
: this.moveList[this.moveIndex - 1];
|
|
53
|
+
let facing = Direction.fromOffset([Math.round(move[0] - last[0]), Math.round(move[1] - last[1])]);
|
|
54
|
+
// Check for zone change
|
|
55
|
+
if (!this.sprite.zone.isInZone(move[0], move[1])) {
|
|
56
|
+
let zone = this.sprite.zone.world.zoneContaining(move[0], move[1]);
|
|
57
|
+
if (!zone || !zone.loaded || !zone.isWalkable(move[0], move[1], Direction.reverse(facing))) {
|
|
58
|
+
this.currentAction = this.sprite.faceDir(facing);
|
|
59
|
+
} else {
|
|
60
|
+
this.currentAction = new ActionLoader(
|
|
61
|
+
this.sprite.engine,
|
|
62
|
+
'changezone',
|
|
63
|
+
[this.sprite.zone.id, this.sprite.pos.toArray(), zone.id, move, this.moveLength],
|
|
64
|
+
this.sprite
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
// Load Next move
|
|
69
|
+
this.currentAction = new ActionLoader(this.sprite.engine, 'move', [last, move, this.moveLength, this.zone], this.sprite);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (this.sprite.facing !== facing) {
|
|
73
|
+
this.currentAction = this.sprite.faceDir(facing);
|
|
74
|
+
// return this.completed;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (this.currentAction) {
|
|
78
|
+
this.currentAction.facing = facing;
|
|
79
|
+
this.sprite.addAction(Promise.resolve(this.currentAction)).then(() => {});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// stop when done
|
|
83
|
+
if (this.moveIndex + this.direction >= this.moveList.length) {
|
|
84
|
+
this.direction *= -1;
|
|
85
|
+
this.completed = true;
|
|
86
|
+
if (this.zone.audio) this.zone.audio.playAudio();
|
|
87
|
+
if (this.audio) this.audio.pauseAudio();
|
|
88
|
+
}
|
|
89
|
+
this.moveIndex += this.direction;
|
|
90
|
+
this.startTime = time;
|
|
91
|
+
}
|
|
92
|
+
return this.completed; // loop
|
|
93
|
+
},
|
|
94
|
+
// Handle Keyboard
|
|
95
|
+
checkInput: function (time) {
|
|
96
|
+
if (time > this.lastKey + Math.max(this.moveLength, 200)) {
|
|
97
|
+
switch (this.sprite.engine.keyboard.lastPressed('q')) {
|
|
98
|
+
// quit on q key press
|
|
99
|
+
case 'q':
|
|
100
|
+
if (this.zone.audio) this.zone.audio.playAudio();
|
|
101
|
+
if (this.audio) this.audio.pauseAudio();
|
|
102
|
+
this.completed = true; // toggle
|
|
103
|
+
default:
|
|
104
|
+
this.lastKey = new Date().getTime();
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
};
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/* *\
|
|
2
|
+
** ----------------------------------------------- **
|
|
3
|
+
** Calliope - Pixos Game Engine **
|
|
4
|
+
** ----------------------------------------------- **
|
|
5
|
+
** Copyright (c) 2020-2025 - Kyle Derby MacInnis **
|
|
6
|
+
** **
|
|
7
|
+
** Any unauthorized distribution or transfer **
|
|
8
|
+
** of this work is strictly prohibited. **
|
|
9
|
+
** **
|
|
10
|
+
** All Rights Reserved. **
|
|
11
|
+
** ----------------------------------------------- **
|
|
12
|
+
\* */
|
|
13
|
+
|
|
14
|
+
export default {
|
|
15
|
+
// Initialize Dialogue Object
|
|
16
|
+
init: function (menu, activeMenus, scrolling = true, options = {}) {
|
|
17
|
+
this.engine = this.sprite.engine;
|
|
18
|
+
this.text = '';
|
|
19
|
+
this.scrolling = scrolling;
|
|
20
|
+
this.line = 0;
|
|
21
|
+
this.options = options;
|
|
22
|
+
this.completed = false;
|
|
23
|
+
this.lastKey = new Date().getTime();
|
|
24
|
+
this.listenerId = this.engine.gamepad.attachListener(this.hookListener());
|
|
25
|
+
this.touches = [];
|
|
26
|
+
this.menuDict = menu ?? {};
|
|
27
|
+
this.activeMenus = activeMenus ?? [];
|
|
28
|
+
this.selectedMenu = {};
|
|
29
|
+
this.selectedMenuId = null;
|
|
30
|
+
this.isTouched = false;
|
|
31
|
+
},
|
|
32
|
+
// Update & Scroll
|
|
33
|
+
tick: function (time) {
|
|
34
|
+
if (!this.loaded) return;
|
|
35
|
+
// Check for Dialogue Completion (TODO - manual triggers + scroll / sections)
|
|
36
|
+
if (this.options && this.options.autoclose) {
|
|
37
|
+
this.endTime = this.endTime ? this.endTime : this.options.endTime ?? new Date().getTime() + 10000; // 10 seconds default if autoclose
|
|
38
|
+
if (time > this.endTime) {
|
|
39
|
+
this.completed = true;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Handle Input
|
|
44
|
+
this.checkInput(time);
|
|
45
|
+
// Draw Active Menus to Screen
|
|
46
|
+
Object.keys(this.menuDict)
|
|
47
|
+
.filter((key) => this.activeMenus.includes(key))
|
|
48
|
+
.map((id) => {
|
|
49
|
+
let section = this.menuDict[id];
|
|
50
|
+
let colors = section.colours;
|
|
51
|
+
if (section.active) {
|
|
52
|
+
colors['background'] = '#555';
|
|
53
|
+
}
|
|
54
|
+
this.engine.hud.drawButton(section.text, section.x, section.y, section.w, section.h, section.colours);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
this.textbox = this.engine.hud.scrollText(this.prompt + this.text, this.scrolling, this.options);
|
|
58
|
+
|
|
59
|
+
if (this.completed) {
|
|
60
|
+
this.unhookListener();
|
|
61
|
+
// this.engine.hud.clearHud();
|
|
62
|
+
}
|
|
63
|
+
return this.completed;
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
unhookListener: function () {
|
|
67
|
+
// remove listener
|
|
68
|
+
this.engine.gamepad.removeListener(this.listenerId);
|
|
69
|
+
this.listenerId = null;
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
hookListener: function () {
|
|
73
|
+
/**
|
|
74
|
+
* Normalize event to extract touches array.
|
|
75
|
+
* Works with mouse events, touch events, and pre-processed events from WebGLView.
|
|
76
|
+
*/
|
|
77
|
+
const normalizeTouches = (e) => {
|
|
78
|
+
// Pre-computed canvas coordinates from WebGLView
|
|
79
|
+
if (e.canvasX !== undefined && e.canvasY !== undefined) {
|
|
80
|
+
return [{ x: e.canvasX, y: e.canvasY, identifier: 'normalized' }];
|
|
81
|
+
}
|
|
82
|
+
// Touch events
|
|
83
|
+
if (e.touches && e.touches.length > 0) {
|
|
84
|
+
return Array.from(e.touches).map(t => ({ x: t.clientX, y: t.clientY, identifier: t.identifier }));
|
|
85
|
+
}
|
|
86
|
+
// Touch end uses changedTouches
|
|
87
|
+
if (e.changedTouches && e.changedTouches.length > 0) {
|
|
88
|
+
return Array.from(e.changedTouches).map(t => ({ x: t.clientX, y: t.clientY, identifier: t.identifier }));
|
|
89
|
+
}
|
|
90
|
+
// Mouse events
|
|
91
|
+
if (e.clientX !== undefined && e.clientY !== undefined) {
|
|
92
|
+
return [{ x: e.clientX, y: e.clientY, identifier: 'mouse' }];
|
|
93
|
+
}
|
|
94
|
+
// Already normalized (legacy format)
|
|
95
|
+
if (e.touches) {
|
|
96
|
+
return e.touches;
|
|
97
|
+
}
|
|
98
|
+
return [];
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
let touchstart = (e) => {
|
|
102
|
+
const touches = normalizeTouches(e);
|
|
103
|
+
this.isTouched = true;
|
|
104
|
+
this.touches = touches;
|
|
105
|
+
if (this.isTouched && this.touches.length > 0 && this.lastKey + 100 < new Date().getTime()) {
|
|
106
|
+
let x = this.touches[0].x;
|
|
107
|
+
let y = this.touches[0].y;
|
|
108
|
+
let self = this;
|
|
109
|
+
self.activeMenus
|
|
110
|
+
.filter((key) => {
|
|
111
|
+
let w = self.menuDict[key];
|
|
112
|
+
if (x < w.x + w.w && x > w.x && y < w.y + w.h && y > w.y) {
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
return false;
|
|
116
|
+
})
|
|
117
|
+
.map((key) => {
|
|
118
|
+
let w = self.menuDict[key];
|
|
119
|
+
if (w.trigger) w.trigger(this);
|
|
120
|
+
if (w.children) {
|
|
121
|
+
self.activeMenus = w.children;
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
let touchmove = (e) => {
|
|
127
|
+
this.touches = normalizeTouches(e);
|
|
128
|
+
};
|
|
129
|
+
let touchend = (e) => {
|
|
130
|
+
this.isTouched = false;
|
|
131
|
+
this.touches = normalizeTouches(e);
|
|
132
|
+
};
|
|
133
|
+
let mousemove = touchmove;
|
|
134
|
+
let mousedown = touchstart;
|
|
135
|
+
let mouseup = touchend;
|
|
136
|
+
return {
|
|
137
|
+
touchstart,
|
|
138
|
+
touchmove,
|
|
139
|
+
touchend,
|
|
140
|
+
mousedown,
|
|
141
|
+
mousemove,
|
|
142
|
+
mouseup,
|
|
143
|
+
};
|
|
144
|
+
},
|
|
145
|
+
// Handle Keyboard & Mouse & Touch
|
|
146
|
+
checkInput: function (time) {
|
|
147
|
+
// TODO -- Reimplement mouse / touch handler support
|
|
148
|
+
|
|
149
|
+
// TOOD -- Is there a cleaner way to do this? I feel like this should be less hacky
|
|
150
|
+
// Keyboard
|
|
151
|
+
if (time > this.lastKey + 200) {
|
|
152
|
+
let skipChar = false;
|
|
153
|
+
switch (this.engine.keyboard.lastPressedCode()) {
|
|
154
|
+
case 'Escape':
|
|
155
|
+
this.completed = true;
|
|
156
|
+
skipChar = true;
|
|
157
|
+
break;
|
|
158
|
+
case 'Backspace':
|
|
159
|
+
let arr = this.text.split('');
|
|
160
|
+
arr.pop();
|
|
161
|
+
this.text = arr.join('');
|
|
162
|
+
this.lastKey = time;
|
|
163
|
+
skipChar = true;
|
|
164
|
+
break;
|
|
165
|
+
case 'Enter':
|
|
166
|
+
this.sprite.setGreeting(this.text);
|
|
167
|
+
if (this.sprite.speech.clearHud) this.sprite.speech.clearHud();
|
|
168
|
+
this.speechbox = this.sprite.speech.scrollText(this.text);
|
|
169
|
+
this.sprite.speech.loadImage();
|
|
170
|
+
this.completed = true;
|
|
171
|
+
skipChar = true;
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
// debounce keypresses
|
|
175
|
+
// write to chat box
|
|
176
|
+
if (!skipChar) {
|
|
177
|
+
let char = this.engine.keyboard.lastPressedKey();
|
|
178
|
+
if (char) {
|
|
179
|
+
this.lastKey = time;
|
|
180
|
+
this.text += '' + char;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/* *\
|
|
2
|
+
** ----------------------------------------------- **
|
|
3
|
+
** Calliope - Pixos Game Engine **
|
|
4
|
+
** ----------------------------------------------- **
|
|
5
|
+
** Copyright (c) 2020-2025 - Kyle Derby MacInnis **
|
|
6
|
+
** **
|
|
7
|
+
** Any unauthorized distribution or transfer **
|
|
8
|
+
** of this work is strictly prohibited. **
|
|
9
|
+
** **
|
|
10
|
+
** All Rights Reserved. **
|
|
11
|
+
** ----------------------------------------------- **
|
|
12
|
+
\* */
|
|
13
|
+
|
|
14
|
+
import { debug } from '@Engine/utils/debug-logger.js';
|
|
15
|
+
|
|
16
|
+
export default {
|
|
17
|
+
init: function (triggerId, zone, onCompleted = null) {
|
|
18
|
+
this.zone = zone;
|
|
19
|
+
this.world = zone.world;
|
|
20
|
+
this.lastKey = new Date().getTime();
|
|
21
|
+
this.completed = false;
|
|
22
|
+
this.onCompleted = () => debug('Script', 'script finished');
|
|
23
|
+
if (onCompleted) this.onCompleted = onCompleted;
|
|
24
|
+
// Determine Tile
|
|
25
|
+
this.triggerId = triggerId;
|
|
26
|
+
// Trigger
|
|
27
|
+
this.triggerScript();
|
|
28
|
+
},
|
|
29
|
+
// Trigger interactions in sprites
|
|
30
|
+
triggerScript: function () {
|
|
31
|
+
if (!this.triggerId) this.completed = true;
|
|
32
|
+
Promise.all(this.zone.scripts.filter((x) => x.id === this.triggerId).map(async (x) => await x.trigger.call(this.zone))).then(() => {
|
|
33
|
+
this.completed = true;
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
// check input and completion
|
|
37
|
+
tick: function (time) {
|
|
38
|
+
if (!this.loaded) return;
|
|
39
|
+
if (this.completed) this.onCompleted();
|
|
40
|
+
return this.completed; // loop
|
|
41
|
+
},
|
|
42
|
+
};
|