@skewedaspect/sage 0.3.0
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/LICENSE +21 -0
- package/Readme.md +53 -0
- package/dist/classes/bindings/toggle.d.ts +122 -0
- package/dist/classes/bindings/trigger.d.ts +79 -0
- package/dist/classes/bindings/value.d.ts +104 -0
- package/dist/classes/entity.d.ts +83 -0
- package/dist/classes/eventBus.d.ts +94 -0
- package/dist/classes/gameEngine.d.ts +57 -0
- package/dist/classes/input/gamepad.d.ts +94 -0
- package/dist/classes/input/keyboard.d.ts +66 -0
- package/dist/classes/input/mouse.d.ts +80 -0
- package/dist/classes/input/readers/gamepad.d.ts +77 -0
- package/dist/classes/input/readers/keyboard.d.ts +60 -0
- package/dist/classes/input/readers/mouse.d.ts +45 -0
- package/dist/classes/loggers/consoleBackend.d.ts +29 -0
- package/dist/classes/loggers/nullBackend.d.ts +14 -0
- package/dist/engines/scene.d.ts +11 -0
- package/dist/interfaces/action.d.ts +20 -0
- package/dist/interfaces/binding.d.ts +144 -0
- package/dist/interfaces/entity.d.ts +9 -0
- package/dist/interfaces/game.d.ts +26 -0
- package/dist/interfaces/input.d.ts +181 -0
- package/dist/interfaces/logger.d.ts +88 -0
- package/dist/managers/binding.d.ts +185 -0
- package/dist/managers/entity.d.ts +70 -0
- package/dist/managers/game.d.ts +20 -0
- package/dist/managers/input.d.ts +56 -0
- package/dist/managers/level.d.ts +55 -0
- package/dist/sage.d.ts +20 -0
- package/dist/sage.es.js +2208 -0
- package/dist/sage.es.js.map +1 -0
- package/dist/sage.umd.js +2 -0
- package/dist/sage.umd.js.map +1 -0
- package/dist/utils/capabilities.d.ts +2 -0
- package/dist/utils/graphics.d.ts +10 -0
- package/dist/utils/logger.d.ts +66 -0
- package/dist/utils/physics.d.ts +2 -0
- package/dist/utils/version.d.ts +5 -0
- package/docs/architecture.md +129 -0
- package/docs/behaviors.md +706 -0
- package/docs/binding_system.md +820 -0
- package/docs/design/input.md +86 -0
- package/docs/entity_system.md +538 -0
- package/docs/eventbus.md +225 -0
- package/docs/getting_started.md +264 -0
- package/docs/images/sage_logo.png +0 -0
- package/docs/images/sage_logo_shape.png +0 -0
- package/docs/overview.md +38 -0
- package/docs/physics_system.md +686 -0
- package/docs/scene_system.md +513 -0
- package/package.json +69 -0
- package/src/classes/bindings/toggle.ts +261 -0
- package/src/classes/bindings/trigger.ts +211 -0
- package/src/classes/bindings/value.ts +227 -0
- package/src/classes/entity.ts +256 -0
- package/src/classes/eventBus.ts +259 -0
- package/src/classes/gameEngine.ts +125 -0
- package/src/classes/input/gamepad.ts +388 -0
- package/src/classes/input/keyboard.ts +189 -0
- package/src/classes/input/mouse.ts +276 -0
- package/src/classes/input/readers/gamepad.ts +179 -0
- package/src/classes/input/readers/keyboard.ts +123 -0
- package/src/classes/input/readers/mouse.ts +133 -0
- package/src/classes/loggers/consoleBackend.ts +135 -0
- package/src/classes/loggers/nullBackend.ts +51 -0
- package/src/engines/scene.ts +112 -0
- package/src/images/sage_logo.svg +172 -0
- package/src/images/sage_logo_shape.svg +146 -0
- package/src/interfaces/action.ts +30 -0
- package/src/interfaces/binding.ts +191 -0
- package/src/interfaces/entity.ts +21 -0
- package/src/interfaces/game.ts +44 -0
- package/src/interfaces/input.ts +221 -0
- package/src/interfaces/logger.ts +118 -0
- package/src/managers/binding.ts +729 -0
- package/src/managers/entity.ts +252 -0
- package/src/managers/game.ts +111 -0
- package/src/managers/input.ts +233 -0
- package/src/managers/level.ts +261 -0
- package/src/sage.ts +119 -0
- package/src/types/global.d.ts +11 -0
- package/src/utils/capabilities.ts +16 -0
- package/src/utils/graphics.ts +148 -0
- package/src/utils/logger.ts +225 -0
- package/src/utils/physics.ts +16 -0
- package/src/utils/version.ts +11 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
2
|
+
// Console Logger Backend
|
|
3
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
4
|
+
|
|
5
|
+
import type { LogLevel, LoggingBackend } from '../../interfaces/logger.ts';
|
|
6
|
+
|
|
7
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
type ColorLevel = LogLevel | 'timer';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A logging backend that logs to the console.
|
|
13
|
+
*/
|
|
14
|
+
export class ConsoleBackend implements LoggingBackend
|
|
15
|
+
{
|
|
16
|
+
private timers : Map<string, number>;
|
|
17
|
+
|
|
18
|
+
constructor()
|
|
19
|
+
{
|
|
20
|
+
this.timers = new Map();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Get the CSS style for a specific log level
|
|
25
|
+
*/
|
|
26
|
+
private getStyleForLevel(level : ColorLevel) : string
|
|
27
|
+
{
|
|
28
|
+
// CSS styles for browser console
|
|
29
|
+
const styles : Record<ColorLevel, string> = {
|
|
30
|
+
trace: 'color: #999999', // Dim gray
|
|
31
|
+
debug: 'color: #00AAAA', // Cyan
|
|
32
|
+
info: 'color: #00AA00', // Green
|
|
33
|
+
warn: 'color: #AAAA00', // Yellow
|
|
34
|
+
error: 'color: #AA0000', // Red
|
|
35
|
+
timer: 'color: #AA00AA', // Magenta
|
|
36
|
+
none: 'color: inherit', // Default
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
return styles[level] || styles.none;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Format a log message with category, timestamp and log level
|
|
44
|
+
*/
|
|
45
|
+
private formatMessage(category : string, level : ColorLevel) : [string, string, string, string]
|
|
46
|
+
{
|
|
47
|
+
// Format time as HH:MM:SS AM/PM
|
|
48
|
+
const now = new Date();
|
|
49
|
+
const hours = now.getHours() % 12 || 12; // Convert 0 to 12 for 12 AM
|
|
50
|
+
const minutes = now.getMinutes().toString()
|
|
51
|
+
.padStart(2, '0');
|
|
52
|
+
const seconds = now.getSeconds().toString()
|
|
53
|
+
.padStart(2, '0');
|
|
54
|
+
const ampm = now.getHours() >= 12 ? 'PM' : 'AM';
|
|
55
|
+
const timestamp = `[${ hours }:${ minutes }:${ seconds } ${ ampm }]`;
|
|
56
|
+
|
|
57
|
+
const upperLevel = level.toUpperCase();
|
|
58
|
+
|
|
59
|
+
// Return format strings and styling that will be used with console methods
|
|
60
|
+
return [
|
|
61
|
+
`${ timestamp } %c${ upperLevel }%c (${ category }): `, // Format string with placeholders
|
|
62
|
+
this.getStyleForLevel(level), // Level style
|
|
63
|
+
'', // Default style
|
|
64
|
+
'', // Reset style
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
trace(category : string, message : string, ...args : any[]) : void
|
|
69
|
+
{
|
|
70
|
+
const [ format, defaultStyle, levelStyle, resetStyle ] = this.formatMessage(category, 'trace');
|
|
71
|
+
console.debug(format, defaultStyle, levelStyle, resetStyle, message, ...args);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
debug(category : string, message : string, ...args : any[]) : void
|
|
75
|
+
{
|
|
76
|
+
const [ format, defaultStyle, levelStyle, resetStyle ] = this.formatMessage(category, 'debug');
|
|
77
|
+
console.debug(format, defaultStyle, levelStyle, resetStyle, message, ...args);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
info(category : string, message : string, ...args : any[]) : void
|
|
81
|
+
{
|
|
82
|
+
const [ format, defaultStyle, levelStyle, resetStyle ] = this.formatMessage(category, 'info');
|
|
83
|
+
console.info(format, defaultStyle, levelStyle, resetStyle, message, ...args);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
warn(category : string, message : string, ...args : any[]) : void
|
|
87
|
+
{
|
|
88
|
+
const [ format, defaultStyle, levelStyle, resetStyle ] = this.formatMessage(category, 'warn');
|
|
89
|
+
console.warn(format, defaultStyle, levelStyle, resetStyle, message, ...args);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
error(category : string, message : string, ...args : any[]) : void
|
|
93
|
+
{
|
|
94
|
+
const [ format, defaultStyle, levelStyle, resetStyle ] = this.formatMessage(category, 'error');
|
|
95
|
+
console.error(format, defaultStyle, levelStyle, resetStyle, message, ...args);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Start a timer with the specified label.
|
|
100
|
+
*/
|
|
101
|
+
time(category : string, label : string) : void
|
|
102
|
+
{
|
|
103
|
+
const timerKey = `${ category }:${ label }`;
|
|
104
|
+
this.timers.set(timerKey, performance.now());
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* End a timer and log the elapsed time.
|
|
109
|
+
*/
|
|
110
|
+
timeEnd(category : string, label : string) : void
|
|
111
|
+
{
|
|
112
|
+
const timerKey = `${ category }:${ label }`;
|
|
113
|
+
const startTime = this.timers.get(timerKey);
|
|
114
|
+
|
|
115
|
+
if(startTime !== undefined)
|
|
116
|
+
{
|
|
117
|
+
const duration = performance.now() - startTime;
|
|
118
|
+
this.timers.delete(timerKey);
|
|
119
|
+
|
|
120
|
+
const [ format, defaultStyle, levelStyle, resetStyle ] = this.formatMessage(category, 'timer');
|
|
121
|
+
console.info(
|
|
122
|
+
`${ format } Timer '${ label }' completed in ${ duration.toFixed(2) }ms`,
|
|
123
|
+
defaultStyle,
|
|
124
|
+
levelStyle,
|
|
125
|
+
resetStyle
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
else
|
|
129
|
+
{
|
|
130
|
+
console.warn(`[${ category }]: Timer '${ label }' does not exist`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
2
|
+
// Null Logger Backend
|
|
3
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
4
|
+
|
|
5
|
+
import type { LoggingBackend } from '../../interfaces/logger.ts';
|
|
6
|
+
|
|
7
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A logging backend that does nothing (discards all logs).
|
|
11
|
+
* Useful for production environments or when you want to completely disable logging.
|
|
12
|
+
*/
|
|
13
|
+
export class NullBackend implements LoggingBackend
|
|
14
|
+
{
|
|
15
|
+
trace(_category : string, _message : string, ..._args : any[]) : void
|
|
16
|
+
{
|
|
17
|
+
// Do nothing
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
debug(_category : string, _message : string, ..._args : any[]) : void
|
|
21
|
+
{
|
|
22
|
+
// Do nothing
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
info(_category : string, _message : string, ..._args : any[]) : void
|
|
26
|
+
{
|
|
27
|
+
// Do nothing
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
warn(_category : string, _message : string, ..._args : any[]) : void
|
|
31
|
+
{
|
|
32
|
+
// Do nothing
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
error(_category : string, _message : string, ..._args : any[]) : void
|
|
36
|
+
{
|
|
37
|
+
// Do nothing
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
time(_category : string, _label : string) : void
|
|
41
|
+
{
|
|
42
|
+
// Do nothing
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
timeEnd(_category : string, _label : string) : void
|
|
46
|
+
{
|
|
47
|
+
// Do nothing
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
2
|
+
// Scene Engine
|
|
3
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
AbstractEngine,
|
|
7
|
+
FreeCamera,
|
|
8
|
+
HavokPlugin,
|
|
9
|
+
HemisphericLight,
|
|
10
|
+
MeshBuilder,
|
|
11
|
+
PhysicsAggregate,
|
|
12
|
+
PhysicsShapeType,
|
|
13
|
+
Scene,
|
|
14
|
+
Vector3,
|
|
15
|
+
} from '@babylonjs/core';
|
|
16
|
+
import type { GameCanvas } from '../interfaces/game.ts';
|
|
17
|
+
import type { LoggerInterface } from '../interfaces/logger.ts';
|
|
18
|
+
import { type LoggingUtility, SAGELogger } from '../utils/logger.ts';
|
|
19
|
+
|
|
20
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
export class SceneEngine
|
|
23
|
+
{
|
|
24
|
+
private _engine : AbstractEngine;
|
|
25
|
+
private _physics : HavokPlugin;
|
|
26
|
+
private _log : LoggerInterface;
|
|
27
|
+
|
|
28
|
+
constructor(engine : AbstractEngine, physics : HavokPlugin, logger ?: LoggingUtility)
|
|
29
|
+
{
|
|
30
|
+
this._engine = engine;
|
|
31
|
+
this._physics = physics;
|
|
32
|
+
this._log = logger?.getLogger('SceneEngine') || new SAGELogger('SceneEngine');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private async _buildDemoScene(scene : Scene, canvas : GameCanvas) : Promise<void>
|
|
36
|
+
{
|
|
37
|
+
this._log.debug('Building demo scene...');
|
|
38
|
+
|
|
39
|
+
// This creates and positions a free camera (non-mesh)
|
|
40
|
+
this._log.trace('Creating camera...');
|
|
41
|
+
const camera = new FreeCamera('camera1', new Vector3(0, 5, -10), scene);
|
|
42
|
+
|
|
43
|
+
// This targets the camera to scene origin
|
|
44
|
+
camera.setTarget(Vector3.Zero());
|
|
45
|
+
|
|
46
|
+
// This attaches the camera to the canvas
|
|
47
|
+
camera.attachControl(canvas, true);
|
|
48
|
+
|
|
49
|
+
// This creates a light, aiming 0,1,0 - to the sky (non-mesh)
|
|
50
|
+
this._log.trace('Creating light...');
|
|
51
|
+
const light = new HemisphericLight('light', new Vector3(0, 1, 0), scene);
|
|
52
|
+
|
|
53
|
+
// Default intensity is 1. Let's dim the light a small amount
|
|
54
|
+
light.intensity = 0.7;
|
|
55
|
+
|
|
56
|
+
// Our built-in 'sphere' shape.
|
|
57
|
+
this._log.trace('Creating sphere...');
|
|
58
|
+
const sphere = MeshBuilder.CreateSphere('sphere', { diameter: 2, segments: 32 }, scene);
|
|
59
|
+
|
|
60
|
+
// Move the sphere upward at 4 units
|
|
61
|
+
sphere.position.y = 4;
|
|
62
|
+
|
|
63
|
+
// Our built-in 'ground' shape.
|
|
64
|
+
this._log.trace('Creating ground...');
|
|
65
|
+
const ground = MeshBuilder.CreateGround('ground', { width: 10, height: 10 }, scene);
|
|
66
|
+
|
|
67
|
+
// Create a sphere shape and the associated body. Size will be determined automatically.
|
|
68
|
+
this._log.trace('Adding physics to sphere...');
|
|
69
|
+
// @ts-expect-error Need these, or they don't show up in the scene.
|
|
70
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
71
|
+
const sphereAggregate = new PhysicsAggregate(
|
|
72
|
+
sphere,
|
|
73
|
+
PhysicsShapeType.SPHERE,
|
|
74
|
+
{ mass: 1, restitution: 0.75 },
|
|
75
|
+
scene
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
// Create a static box shape.
|
|
79
|
+
this._log.trace('Adding physics to ground...');
|
|
80
|
+
// @ts-expect-error Need these, or they don't show up in the scene.
|
|
81
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
82
|
+
const groundAggregate = new PhysicsAggregate(ground, PhysicsShapeType.BOX, { mass: 0 }, scene);
|
|
83
|
+
|
|
84
|
+
this._log.debug('Demo scene built successfully');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// TODO: This needs more logic to handle different scenes
|
|
88
|
+
async loadScene(canvas : GameCanvas) : Promise<Scene>
|
|
89
|
+
{
|
|
90
|
+
this._log.info('Loading scene...');
|
|
91
|
+
this._log.time('sceneLoad');
|
|
92
|
+
|
|
93
|
+
// Create a new scene
|
|
94
|
+
this._log.debug('Creating new scene...');
|
|
95
|
+
const scene = new Scene(this._engine);
|
|
96
|
+
|
|
97
|
+
// TODO: Some scenes may not need physics, or may need different gravity settings
|
|
98
|
+
// Initialize the scene with physics
|
|
99
|
+
this._log.debug('Enabling physics with gravity (0, -9.8, 0)...');
|
|
100
|
+
scene.enablePhysics(new Vector3(0, -9.8, 0), this._physics);
|
|
101
|
+
|
|
102
|
+
// TODO: Need to set up a real scene...
|
|
103
|
+
await this._buildDemoScene(scene, canvas);
|
|
104
|
+
|
|
105
|
+
this._log.timeEnd('sceneLoad');
|
|
106
|
+
this._log.info('Scene loaded successfully');
|
|
107
|
+
|
|
108
|
+
return scene;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
//----------------------------------------------------------------------------------------------------------------------
|