@dryanovski/gamefoo 0.2.3 → 0.2.5

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 (84) hide show
  1. package/dist/core/animate.js +147 -0
  2. package/dist/core/asset.js +74 -0
  3. package/dist/core/behaviour.js +88 -0
  4. package/dist/core/behaviours/collidable.js +186 -0
  5. package/dist/core/behaviours/control.js +75 -0
  6. package/dist/core/behaviours/healtkit.js +153 -0
  7. package/dist/core/behaviours/sprite_render.js +193 -0
  8. package/dist/core/camera.js +134 -0
  9. package/dist/core/engine.d.ts +1 -1
  10. package/dist/core/engine.d.ts.map +1 -1
  11. package/dist/core/engine.js +527 -0
  12. package/dist/core/fonts/font_bitmap.js +205 -0
  13. package/dist/core/fonts/font_bitmap_prebuild.js +137 -0
  14. package/dist/core/fonts/internal/font_3x5.js +169 -0
  15. package/dist/core/fonts/internal/font_4x6.js +171 -0
  16. package/dist/core/fonts/internal/font_5x5.js +129 -0
  17. package/dist/core/fonts/internal/font_6x8.js +171 -0
  18. package/dist/core/fonts/internal/font_8x13.js +171 -0
  19. package/dist/core/fonts/internal/font_8x8.js +171 -0
  20. package/dist/core/game_object_register.js +134 -0
  21. package/dist/core/input.js +170 -0
  22. package/dist/core/sprite.js +222 -0
  23. package/dist/core/utils/perlin_noise.js +183 -0
  24. package/dist/core/world.js +304 -0
  25. package/dist/debug/monitor.js +47 -0
  26. package/dist/decorators/index.js +1 -0
  27. package/dist/decorators/log.js +42 -0
  28. package/dist/entities/dynamic_entity.js +99 -0
  29. package/dist/entities/entity.js +283 -0
  30. package/dist/entities/player.js +93 -0
  31. package/dist/entities/text.js +62 -0
  32. package/dist/index.d.ts +1 -1
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +47 -29
  35. package/dist/subsystems/camera_system.d.ts +2 -2
  36. package/dist/subsystems/camera_system.d.ts.map +1 -1
  37. package/dist/subsystems/camera_system.js +41 -0
  38. package/dist/subsystems/collision_system.js +17 -0
  39. package/dist/subsystems/monitor_system.js +20 -0
  40. package/dist/subsystems/object_system.d.ts +1 -1
  41. package/dist/subsystems/object_system.d.ts.map +1 -1
  42. package/dist/subsystems/object_system.js +29 -0
  43. package/dist/subsystems/types.d.ts +1 -1
  44. package/dist/subsystems/types.d.ts.map +1 -1
  45. package/dist/subsystems/types.js +0 -0
  46. package/dist/types.js +10 -0
  47. package/package.json +17 -6
  48. package/src/core/animate.ts +159 -0
  49. package/src/core/asset.ts +76 -0
  50. package/src/core/behaviour.ts +145 -0
  51. package/src/core/behaviours/collidable.ts +296 -0
  52. package/src/core/behaviours/control.ts +80 -0
  53. package/src/core/behaviours/healtkit.ts +166 -0
  54. package/src/core/behaviours/sprite_render.ts +216 -0
  55. package/src/core/camera.ts +145 -0
  56. package/src/core/engine.ts +607 -0
  57. package/src/core/fonts/font_bitmap.ts +232 -0
  58. package/src/core/fonts/font_bitmap_prebuild.ts +141 -0
  59. package/src/core/fonts/internal/font_3x5.ts +178 -0
  60. package/src/core/fonts/internal/font_4x6.ts +180 -0
  61. package/src/core/fonts/internal/font_5x5.ts +137 -0
  62. package/src/core/fonts/internal/font_6x8.ts +180 -0
  63. package/src/core/fonts/internal/font_8x13.ts +180 -0
  64. package/src/core/fonts/internal/font_8x8.ts +180 -0
  65. package/src/core/game_object_register.ts +146 -0
  66. package/src/core/input.ts +182 -0
  67. package/src/core/sprite.ts +339 -0
  68. package/src/core/utils/perlin_noise.ts +196 -0
  69. package/src/core/world.ts +331 -0
  70. package/src/debug/monitor.ts +60 -0
  71. package/src/decorators/index.ts +1 -0
  72. package/src/decorators/log.ts +45 -0
  73. package/src/entities/dynamic_entity.ts +106 -0
  74. package/src/entities/entity.ts +322 -0
  75. package/src/entities/player.ts +99 -0
  76. package/src/entities/text.ts +72 -0
  77. package/src/index.ts +51 -0
  78. package/src/subsystems/camera_system.ts +52 -0
  79. package/src/subsystems/collision_system.ts +21 -0
  80. package/src/subsystems/monitor_system.ts +26 -0
  81. package/src/subsystems/object_system.ts +37 -0
  82. package/src/subsystems/types.ts +46 -0
  83. package/src/types.ts +178 -0
  84. package/dist/index.js.map +0 -9
@@ -0,0 +1,216 @@
1
+ import type Entity from "../../entities/entity";
2
+ import type { Vector2 } from "../../types";
3
+ import { Behaviour } from "../behaviour";
4
+ import type Sprite from "../sprite";
5
+
6
+ /**
7
+ * Sprite animation renderer that can be attached to any {@link Entity}.
8
+ *
9
+ * `SpriteRender` plays named animations defined in a {@link Sprite}
10
+ * sheet, advancing frames based on each animation's `duration` and
11
+ * `loop` settings. It supports horizontal flipping for left/right
12
+ * facing and an optional pixel offset for fine-tuning draw position.
13
+ *
14
+ * @category Behaviours
15
+ * @since 0.1.0
16
+ *
17
+ * @example Attaching and playing an animation
18
+ * ```ts
19
+ * import { Asset, Sprite, SpriteRender, Player } from "gamefoo";
20
+ *
21
+ * const image = await Asset.load("hero.png");
22
+ * const sheet = new Sprite(image, 32, 32, {
23
+ * idle: { frames: [0, 1], duration: 0.25, loop: true },
24
+ * run: { frames: [2, 3, 4, 5], duration: 0.1, loop: true },
25
+ * });
26
+ *
27
+ * const player = new Player("hero", 100, 100, 32, 32);
28
+ * const sr = new SpriteRender(player, sheet);
29
+ * player.attachBehaviour(sr);
30
+ *
31
+ * sr.play("idle");
32
+ * ```
33
+ *
34
+ * @example Flipping for directional facing
35
+ * ```ts
36
+ * if (velocity.x < 0) {
37
+ * spriteRender.setFlipX(true);
38
+ * } else if (velocity.x > 0) {
39
+ * spriteRender.setFlipX(false);
40
+ * }
41
+ * ```
42
+ *
43
+ * @see {@link Sprite} — spritesheet metadata
44
+ * @see {@link Asset} — image loader
45
+ * @see {@link Behaviour} — abstract base class
46
+ */
47
+ export default class SpriteRender extends Behaviour<Entity> {
48
+ /** @inheritDoc */
49
+ readonly type = "sprite";
50
+
51
+ /** The spritesheet this renderer draws from. */
52
+ private sheet: Sprite;
53
+
54
+ /**
55
+ * Name of the currently playing animation, or `null` if stopped.
56
+ *
57
+ * @defaultValue `null`
58
+ */
59
+ private currentFrame: string | null = null;
60
+
61
+ /**
62
+ * Index into the current animation's `frames` array.
63
+ *
64
+ * @defaultValue `0`
65
+ */
66
+ private currentFrameIndex: number = 0;
67
+
68
+ /**
69
+ * Seconds accumulated towards the next frame advance.
70
+ *
71
+ * @defaultValue `0`
72
+ */
73
+ private elapsedTime: number = 0;
74
+
75
+ /**
76
+ * Whether the sprite is drawn mirrored horizontally.
77
+ *
78
+ * @defaultValue `false`
79
+ */
80
+ private flipX: boolean = false;
81
+
82
+ /**
83
+ * Pixel offset applied to the draw position, relative to the
84
+ * entity's origin.
85
+ *
86
+ * @defaultValue `{ x: 0, y: 0 }`
87
+ */
88
+ public offset: Vector2 = { x: 0, y: 0 };
89
+
90
+ /**
91
+ * Creates a sprite renderer bound to the given entity and sheet.
92
+ *
93
+ * @param owner - The entity whose position determines where the
94
+ * sprite is drawn.
95
+ * @param sheet - A {@link Sprite} containing the image and animation
96
+ * definitions.
97
+ */
98
+ constructor(owner: Entity, sheet: Sprite) {
99
+ super(owner);
100
+ this.sheet = sheet;
101
+ }
102
+
103
+ /**
104
+ * Starts (or switches to) the named animation.
105
+ *
106
+ * If the requested animation is already playing, the call is a no-op
107
+ * so the current playback position is preserved.
108
+ *
109
+ * @param animation - Name matching a key in
110
+ * {@link Sprite.animations}.
111
+ *
112
+ * @example
113
+ * ```ts
114
+ * spriteRender.play("run");
115
+ * ```
116
+ */
117
+ play(animation: string): void {
118
+ if (this.currentFrame === animation) {
119
+ return;
120
+ }
121
+ this.currentFrame = animation;
122
+ this.currentFrameIndex = 0;
123
+ this.elapsedTime = 0;
124
+ }
125
+
126
+ /**
127
+ * Stops the current animation and resets playback state.
128
+ *
129
+ * After calling `stop`, nothing is drawn until {@link SpriteRender.play}
130
+ * is called again.
131
+ */
132
+ stop(): void {
133
+ this.currentFrame = null;
134
+ this.currentFrameIndex = 0;
135
+ this.elapsedTime = 0;
136
+ }
137
+
138
+ /**
139
+ * Enables or disables horizontal flipping.
140
+ *
141
+ * Useful for mirroring a character sprite when facing left.
142
+ *
143
+ * @param flip - `true` to mirror horizontally, `false` for normal.
144
+ */
145
+ setFlipX(flip: boolean): void {
146
+ this.flipX = flip;
147
+ }
148
+
149
+ /**
150
+ * Advances the animation clock and moves to the next frame when the
151
+ * animation's `duration` has elapsed.
152
+ *
153
+ * If the animation does not loop, it holds on the last frame.
154
+ *
155
+ * @param deltaTime - Seconds elapsed since the previous frame.
156
+ */
157
+ update(deltaTime: number): void {
158
+ if (!this.currentFrame) {
159
+ return;
160
+ }
161
+
162
+ const animation = this.sheet.animations.get(this.currentFrame);
163
+
164
+ if (!animation) {
165
+ console.warn(`Animation "${this.currentFrame}" not found in sprite sheet.`);
166
+ return;
167
+ }
168
+
169
+ this.elapsedTime += deltaTime;
170
+
171
+ if (this.elapsedTime >= animation.duration) {
172
+ this.elapsedTime -= animation.duration;
173
+ this.currentFrameIndex++;
174
+
175
+ if (this.currentFrameIndex >= animation.frames.length) {
176
+ this.currentFrameIndex = animation.loop ? 0 : animation.frames.length - 1;
177
+ }
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Draws the current animation frame to the canvas.
183
+ *
184
+ * Respects {@link SpriteRender.flipX} by temporarily mirroring the
185
+ * canvas transform, and applies {@link SpriteRender.offset} to the
186
+ * draw position.
187
+ *
188
+ * @param ctx - The canvas 2-D rendering context.
189
+ */
190
+ override render(ctx: CanvasRenderingContext2D): void {
191
+ if (!this.currentFrame) {
192
+ return;
193
+ }
194
+
195
+ const animation = this.sheet.animations.get(this.currentFrame);
196
+
197
+ if (!animation) {
198
+ return;
199
+ }
200
+ const frameKey = animation.frames[this.currentFrameIndex]!;
201
+ const { x, y, width, height } = this.sheet.getFrameRect(frameKey);
202
+ const pos = this.owner.getPosition();
203
+ const drawX = pos.x + this.offset.x;
204
+ const drawY = pos.y + this.offset.y;
205
+ const size = this.owner.getSize();
206
+
207
+ if (this.flipX) {
208
+ ctx.save();
209
+ ctx.scale(-1, 1);
210
+ ctx.drawImage(this.sheet.image, x, y, width, height, -(drawX + size.width), drawY, size.width, size.height);
211
+ ctx.restore();
212
+ } else {
213
+ ctx.drawImage(this.sheet.image, x, y, width, height, drawX, drawY, size.width, size.height);
214
+ }
215
+ }
216
+ }
@@ -0,0 +1,145 @@
1
+ import type { Vector2 } from "../types";
2
+
3
+ /**
4
+ * A 2-D viewport camera that tracks a target position within the game
5
+ * world.
6
+ *
7
+ * The camera stores its own `(x, y)` centre and viewport dimensions.
8
+ * Each frame the {@link Engine} calls {@link Camera.follow} with the
9
+ * player's position so the viewport stays centred on the action.
10
+ *
11
+ * @category Core
12
+ * @since 0.1.0
13
+ *
14
+ * @example Basic usage inside the engine
15
+ * ```ts
16
+ * const camera = new Camera(800, 600);
17
+ * camera.follow(player.getPosition());
18
+ *
19
+ * const view = camera.getViewRect();
20
+ * // view → { x: px - 400, y: py - 300, width: 800, height: 600 }
21
+ * ```
22
+ *
23
+ * @example Manual camera control
24
+ * ```ts
25
+ * const camera = new Camera(800, 600);
26
+ * camera.moveTo({ x: 0, y: 0 }); // jump to origin
27
+ * console.log(camera.getPosition()); // { x: 0, y: 0 }
28
+ * ```
29
+ *
30
+ * @see {@link Engine} — owns and drives the camera each frame
31
+ */
32
+ export default class Camera {
33
+ /** Current X coordinate of the camera centre. */
34
+ private x: number = 0;
35
+
36
+ /** Current Y coordinate of the camera centre. */
37
+ private y: number = 0;
38
+
39
+ /** Viewport width in pixels. */
40
+ private width: number;
41
+
42
+ /** Viewport height in pixels. */
43
+ private height: number;
44
+
45
+ /**
46
+ * Creates a camera with the given viewport dimensions.
47
+ *
48
+ * @param width - Viewport width in pixels.
49
+ * @param height - Viewport height in pixels.
50
+ */
51
+ constructor(width: number, height: number) {
52
+ this.width = width;
53
+ this.height = height;
54
+ }
55
+
56
+ /**
57
+ * Centres the camera on a target position.
58
+ *
59
+ * Called by the engine every frame when a player is set.
60
+ *
61
+ * @param target - The world-space position to track.
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * camera.follow(player.getPosition());
66
+ * ```
67
+ */
68
+ follow(target: Vector2): void {
69
+ this.x = target.x;
70
+ this.y = target.y;
71
+ }
72
+
73
+ /**
74
+ * Instantly teleports the camera to a specific position.
75
+ *
76
+ * Functionally identical to {@link Camera.follow} but communicates
77
+ * intent more clearly for one-shot repositioning (e.g. scene
78
+ * transitions).
79
+ *
80
+ * @param target - The world-space position to jump to.
81
+ *
82
+ * @example
83
+ * ```ts
84
+ * camera.moveTo({ x: 500, y: 300 });
85
+ * ```
86
+ */
87
+ moveTo(target: Vector2): void {
88
+ this.x = target.x;
89
+ this.y = target.y;
90
+ }
91
+
92
+ /**
93
+ * Returns the current centre position of the camera.
94
+ *
95
+ * @returns A new {@link Vector2} copy of the camera centre.
96
+ */
97
+ getPosition(): Vector2 {
98
+ return { x: this.x, y: this.y };
99
+ }
100
+
101
+ /**
102
+ * Computes the axis-aligned rectangle that represents the visible
103
+ * area in world-space.
104
+ *
105
+ * The rectangle is centred on the camera's current position.
106
+ *
107
+ * @returns An object with `x` (left edge), `y` (top edge), `width`,
108
+ * and `height`.
109
+ *
110
+ * @example
111
+ * ```ts
112
+ * const rect = camera.getViewRect();
113
+ * // Use rect to cull off-screen objects
114
+ * if (entity.x < rect.x || entity.x > rect.x + rect.width) {
115
+ * return; // off-screen — skip rendering
116
+ * }
117
+ * ```
118
+ */
119
+ getViewRect(): { x: number; y: number; width: number; height: number } {
120
+ return {
121
+ x: this.x - this.width / 2,
122
+ y: this.y - this.height / 2,
123
+ width: this.width,
124
+ height: this.height,
125
+ };
126
+ }
127
+
128
+ /**
129
+ * Updates the viewport dimensions, e.g. after a window resize.
130
+ *
131
+ * @param width - New viewport width in pixels.
132
+ * @param height - New viewport height in pixels.
133
+ *
134
+ * @example
135
+ * ```ts
136
+ * window.addEventListener("resize", () => {
137
+ * camera.resize(window.innerWidth, window.innerHeight);
138
+ * });
139
+ * ```
140
+ */
141
+ resize(width: number, height: number): void {
142
+ this.width = width;
143
+ this.height = height;
144
+ }
145
+ }