@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.
Files changed (86) hide show
  1. package/LICENSE +21 -0
  2. package/Readme.md +53 -0
  3. package/dist/classes/bindings/toggle.d.ts +122 -0
  4. package/dist/classes/bindings/trigger.d.ts +79 -0
  5. package/dist/classes/bindings/value.d.ts +104 -0
  6. package/dist/classes/entity.d.ts +83 -0
  7. package/dist/classes/eventBus.d.ts +94 -0
  8. package/dist/classes/gameEngine.d.ts +57 -0
  9. package/dist/classes/input/gamepad.d.ts +94 -0
  10. package/dist/classes/input/keyboard.d.ts +66 -0
  11. package/dist/classes/input/mouse.d.ts +80 -0
  12. package/dist/classes/input/readers/gamepad.d.ts +77 -0
  13. package/dist/classes/input/readers/keyboard.d.ts +60 -0
  14. package/dist/classes/input/readers/mouse.d.ts +45 -0
  15. package/dist/classes/loggers/consoleBackend.d.ts +29 -0
  16. package/dist/classes/loggers/nullBackend.d.ts +14 -0
  17. package/dist/engines/scene.d.ts +11 -0
  18. package/dist/interfaces/action.d.ts +20 -0
  19. package/dist/interfaces/binding.d.ts +144 -0
  20. package/dist/interfaces/entity.d.ts +9 -0
  21. package/dist/interfaces/game.d.ts +26 -0
  22. package/dist/interfaces/input.d.ts +181 -0
  23. package/dist/interfaces/logger.d.ts +88 -0
  24. package/dist/managers/binding.d.ts +185 -0
  25. package/dist/managers/entity.d.ts +70 -0
  26. package/dist/managers/game.d.ts +20 -0
  27. package/dist/managers/input.d.ts +56 -0
  28. package/dist/managers/level.d.ts +55 -0
  29. package/dist/sage.d.ts +20 -0
  30. package/dist/sage.es.js +2208 -0
  31. package/dist/sage.es.js.map +1 -0
  32. package/dist/sage.umd.js +2 -0
  33. package/dist/sage.umd.js.map +1 -0
  34. package/dist/utils/capabilities.d.ts +2 -0
  35. package/dist/utils/graphics.d.ts +10 -0
  36. package/dist/utils/logger.d.ts +66 -0
  37. package/dist/utils/physics.d.ts +2 -0
  38. package/dist/utils/version.d.ts +5 -0
  39. package/docs/architecture.md +129 -0
  40. package/docs/behaviors.md +706 -0
  41. package/docs/binding_system.md +820 -0
  42. package/docs/design/input.md +86 -0
  43. package/docs/entity_system.md +538 -0
  44. package/docs/eventbus.md +225 -0
  45. package/docs/getting_started.md +264 -0
  46. package/docs/images/sage_logo.png +0 -0
  47. package/docs/images/sage_logo_shape.png +0 -0
  48. package/docs/overview.md +38 -0
  49. package/docs/physics_system.md +686 -0
  50. package/docs/scene_system.md +513 -0
  51. package/package.json +69 -0
  52. package/src/classes/bindings/toggle.ts +261 -0
  53. package/src/classes/bindings/trigger.ts +211 -0
  54. package/src/classes/bindings/value.ts +227 -0
  55. package/src/classes/entity.ts +256 -0
  56. package/src/classes/eventBus.ts +259 -0
  57. package/src/classes/gameEngine.ts +125 -0
  58. package/src/classes/input/gamepad.ts +388 -0
  59. package/src/classes/input/keyboard.ts +189 -0
  60. package/src/classes/input/mouse.ts +276 -0
  61. package/src/classes/input/readers/gamepad.ts +179 -0
  62. package/src/classes/input/readers/keyboard.ts +123 -0
  63. package/src/classes/input/readers/mouse.ts +133 -0
  64. package/src/classes/loggers/consoleBackend.ts +135 -0
  65. package/src/classes/loggers/nullBackend.ts +51 -0
  66. package/src/engines/scene.ts +112 -0
  67. package/src/images/sage_logo.svg +172 -0
  68. package/src/images/sage_logo_shape.svg +146 -0
  69. package/src/interfaces/action.ts +30 -0
  70. package/src/interfaces/binding.ts +191 -0
  71. package/src/interfaces/entity.ts +21 -0
  72. package/src/interfaces/game.ts +44 -0
  73. package/src/interfaces/input.ts +221 -0
  74. package/src/interfaces/logger.ts +118 -0
  75. package/src/managers/binding.ts +729 -0
  76. package/src/managers/entity.ts +252 -0
  77. package/src/managers/game.ts +111 -0
  78. package/src/managers/input.ts +233 -0
  79. package/src/managers/level.ts +261 -0
  80. package/src/sage.ts +119 -0
  81. package/src/types/global.d.ts +11 -0
  82. package/src/utils/capabilities.ts +16 -0
  83. package/src/utils/graphics.ts +148 -0
  84. package/src/utils/logger.ts +225 -0
  85. package/src/utils/physics.ts +16 -0
  86. 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
+ //----------------------------------------------------------------------------------------------------------------------