zx-kit 0.7.0 → 0.8.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.
@@ -0,0 +1,87 @@
1
+ import type { Sprite } from './sprite.js';
2
+ import type { TileMap } from './tilemap.js';
3
+ /**
4
+ * Axis-aligned bounding rectangle in game pixels.
5
+ */
6
+ export interface Rect {
7
+ x: number;
8
+ y: number;
9
+ w: number;
10
+ h: number;
11
+ }
12
+ /**
13
+ * Returns the bounding `Rect` for a sprite (always `CELL × CELL`).
14
+ * Position is taken from `sprite.x / sprite.y` without rounding —
15
+ * use this for precise overlap tests during the same frame the sprite moved.
16
+ */
17
+ export declare function spriteRect(sprite: Sprite): Rect;
18
+ /**
19
+ * Returns `true` when rectangles `a` and `b` overlap (share at least one pixel).
20
+ * Touching edges (shared border, zero overlap area) returns `false`.
21
+ *
22
+ * @example
23
+ * if (rectsOverlap(spriteRect(bullet), spriteRect(enemy))) { hitEnemy() }
24
+ */
25
+ export declare function rectsOverlap(a: Rect, b: Rect): boolean;
26
+ /**
27
+ * Shorthand for `rectsOverlap(spriteRect(a), spriteRect(b))`.
28
+ *
29
+ * @example
30
+ * if (spritesOverlap(player, coin)) collectCoin()
31
+ */
32
+ export declare function spritesOverlap(a: Sprite, b: Sprite): boolean;
33
+ /**
34
+ * Returns `true` when the game pixel at `(px, py)` falls inside a solid tile.
35
+ * Converts pixel coords to tile coords via integer division by `CELL`.
36
+ * Out-of-bounds pixels are treated as solid (map boundary is an implicit wall).
37
+ *
38
+ * @example
39
+ * if (isSolidAt(map, player.x, player.y + CELL)) { player.vy = 0 } // on floor
40
+ */
41
+ export declare function isSolidAt(map: TileMap, px: number, py: number): boolean;
42
+ /**
43
+ * Resolves a proposed horizontal movement for a sprite against solid tiles.
44
+ * Tests the leading edge of the sprite's bounding box at the target x position.
45
+ * Returns the clamped x coordinate and collision flags.
46
+ *
47
+ * Call BEFORE `resolveY` so each axis is resolved independently.
48
+ *
49
+ * @param sprite - Sprite being moved (uses current `sprite.y` for vertical extent)
50
+ * @param map - Tile map to test solidity against
51
+ * @param newX - Proposed new x position (after `moveSprite`)
52
+ *
53
+ * @example
54
+ * moveSprite(player, dt)
55
+ * const rx = resolveX(player, map, player.x)
56
+ * player.x = rx.x
57
+ * if (rx.hitLeft || rx.hitRight) player.vx = 0
58
+ */
59
+ export declare function resolveX(sprite: Sprite, map: TileMap, newX: number): {
60
+ x: number;
61
+ hitLeft: boolean;
62
+ hitRight: boolean;
63
+ };
64
+ /**
65
+ * Resolves a proposed vertical movement for a sprite against solid tiles.
66
+ * Tests the leading edge of the sprite's bounding box at the target y position.
67
+ * Returns the clamped y coordinate and collision flags.
68
+ *
69
+ * Typical platformer pattern: `hitBottom` means the sprite landed on a floor.
70
+ * Zero out `vy` on `hitBottom` and `hitTop` to prevent tunnelling.
71
+ *
72
+ * @param sprite - Sprite being moved (uses current `sprite.x` for horizontal extent)
73
+ * @param map - Tile map to test solidity against
74
+ * @param newY - Proposed new y position (after `moveSprite`)
75
+ *
76
+ * @example
77
+ * const ry = resolveY(player, map, player.y)
78
+ * player.y = ry.y
79
+ * if (ry.hitBottom) { player.vy = 0; onGround = true }
80
+ * if (ry.hitTop) { player.vy = 0 }
81
+ */
82
+ export declare function resolveY(sprite: Sprite, map: TileMap, newY: number): {
83
+ y: number;
84
+ hitTop: boolean;
85
+ hitBottom: boolean;
86
+ };
87
+ //# sourceMappingURL=collision.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collision.d.ts","sourceRoot":"","sources":["../src/collision.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAE3C;;GAEG;AACH,MAAM,WAAW,IAAI;IACnB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;CACV;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAE/C;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,GAAG,OAAO,CAKtD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAE5D;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAEvE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,QAAQ,CACtB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,MAAM,GACX;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAyBpD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,QAAQ,CACtB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,MAAM,GACX;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAwBpD"}
@@ -0,0 +1,127 @@
1
+ import { CELL } from './palette.js';
2
+ /**
3
+ * Returns the bounding `Rect` for a sprite (always `CELL × CELL`).
4
+ * Position is taken from `sprite.x / sprite.y` without rounding —
5
+ * use this for precise overlap tests during the same frame the sprite moved.
6
+ */
7
+ export function spriteRect(sprite) {
8
+ return { x: sprite.x, y: sprite.y, w: CELL, h: CELL };
9
+ }
10
+ /**
11
+ * Returns `true` when rectangles `a` and `b` overlap (share at least one pixel).
12
+ * Touching edges (shared border, zero overlap area) returns `false`.
13
+ *
14
+ * @example
15
+ * if (rectsOverlap(spriteRect(bullet), spriteRect(enemy))) { hitEnemy() }
16
+ */
17
+ export function rectsOverlap(a, b) {
18
+ return a.x < b.x + b.w &&
19
+ a.x + a.w > b.x &&
20
+ a.y < b.y + b.h &&
21
+ a.y + a.h > b.y;
22
+ }
23
+ /**
24
+ * Shorthand for `rectsOverlap(spriteRect(a), spriteRect(b))`.
25
+ *
26
+ * @example
27
+ * if (spritesOverlap(player, coin)) collectCoin()
28
+ */
29
+ export function spritesOverlap(a, b) {
30
+ return rectsOverlap(spriteRect(a), spriteRect(b));
31
+ }
32
+ /**
33
+ * Returns `true` when the game pixel at `(px, py)` falls inside a solid tile.
34
+ * Converts pixel coords to tile coords via integer division by `CELL`.
35
+ * Out-of-bounds pixels are treated as solid (map boundary is an implicit wall).
36
+ *
37
+ * @example
38
+ * if (isSolidAt(map, player.x, player.y + CELL)) { player.vy = 0 } // on floor
39
+ */
40
+ export function isSolidAt(map, px, py) {
41
+ return map.isSolid(Math.floor(px / CELL), Math.floor(py / CELL));
42
+ }
43
+ /**
44
+ * Resolves a proposed horizontal movement for a sprite against solid tiles.
45
+ * Tests the leading edge of the sprite's bounding box at the target x position.
46
+ * Returns the clamped x coordinate and collision flags.
47
+ *
48
+ * Call BEFORE `resolveY` so each axis is resolved independently.
49
+ *
50
+ * @param sprite - Sprite being moved (uses current `sprite.y` for vertical extent)
51
+ * @param map - Tile map to test solidity against
52
+ * @param newX - Proposed new x position (after `moveSprite`)
53
+ *
54
+ * @example
55
+ * moveSprite(player, dt)
56
+ * const rx = resolveX(player, map, player.x)
57
+ * player.x = rx.x
58
+ * if (rx.hitLeft || rx.hitRight) player.vx = 0
59
+ */
60
+ export function resolveX(sprite, map, newX) {
61
+ const y0 = sprite.y;
62
+ const y1 = sprite.y + CELL - 1;
63
+ let x = newX;
64
+ let hitLeft = false;
65
+ let hitRight = false;
66
+ if (newX < sprite.x) {
67
+ // Moving left — check left edge
68
+ if (isSolidAt(map, newX, y0) || isSolidAt(map, newX, y1)) {
69
+ // Clamp to in-bounds before computing tile column (avoids negative col from OOB overshoot)
70
+ const safeX = Math.max(0, newX);
71
+ x = (Math.floor(safeX / CELL) + 1) * CELL;
72
+ hitLeft = true;
73
+ }
74
+ }
75
+ else if (newX > sprite.x) {
76
+ // Moving right — check right edge
77
+ if (isSolidAt(map, newX + CELL - 1, y0) || isSolidAt(map, newX + CELL - 1, y1)) {
78
+ const safeEdge = Math.min(map.cols * CELL - 1, newX + CELL - 1);
79
+ x = Math.floor(safeEdge / CELL) * CELL - CELL;
80
+ hitRight = true;
81
+ }
82
+ }
83
+ return { x, hitLeft, hitRight };
84
+ }
85
+ /**
86
+ * Resolves a proposed vertical movement for a sprite against solid tiles.
87
+ * Tests the leading edge of the sprite's bounding box at the target y position.
88
+ * Returns the clamped y coordinate and collision flags.
89
+ *
90
+ * Typical platformer pattern: `hitBottom` means the sprite landed on a floor.
91
+ * Zero out `vy` on `hitBottom` and `hitTop` to prevent tunnelling.
92
+ *
93
+ * @param sprite - Sprite being moved (uses current `sprite.x` for horizontal extent)
94
+ * @param map - Tile map to test solidity against
95
+ * @param newY - Proposed new y position (after `moveSprite`)
96
+ *
97
+ * @example
98
+ * const ry = resolveY(player, map, player.y)
99
+ * player.y = ry.y
100
+ * if (ry.hitBottom) { player.vy = 0; onGround = true }
101
+ * if (ry.hitTop) { player.vy = 0 }
102
+ */
103
+ export function resolveY(sprite, map, newY) {
104
+ const x0 = sprite.x;
105
+ const x1 = sprite.x + CELL - 1;
106
+ let y = newY;
107
+ let hitTop = false;
108
+ let hitBottom = false;
109
+ if (newY < sprite.y) {
110
+ // Moving up — check top edge
111
+ if (isSolidAt(map, x0, newY) || isSolidAt(map, x1, newY)) {
112
+ const safeY = Math.max(0, newY);
113
+ y = (Math.floor(safeY / CELL) + 1) * CELL;
114
+ hitTop = true;
115
+ }
116
+ }
117
+ else if (newY > sprite.y) {
118
+ // Moving down — check bottom edge
119
+ if (isSolidAt(map, x0, newY + CELL - 1) || isSolidAt(map, x1, newY + CELL - 1)) {
120
+ const safeEdge = Math.min(map.rows * CELL - 1, newY + CELL - 1);
121
+ y = Math.floor(safeEdge / CELL) * CELL - CELL;
122
+ hitBottom = true;
123
+ }
124
+ }
125
+ return { y, hitTop, hitBottom };
126
+ }
127
+ //# sourceMappingURL=collision.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collision.js","sourceRoot":"","sources":["../src/collision.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -5,4 +5,6 @@ export * from './audio.js';
5
5
  export * from './input.js';
6
6
  export * from './ui.js';
7
7
  export * from './tilemap.js';
8
+ export * from './sprite.js';
9
+ export * from './collision.js';
8
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,SAAS,CAAA;AACvB,cAAc,cAAc,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,SAAS,CAAA;AACvB,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA"}
package/dist/index.js CHANGED
@@ -5,4 +5,6 @@ export * from './audio.js';
5
5
  export * from './input.js';
6
6
  export * from './ui.js';
7
7
  export * from './tilemap.js';
8
+ export * from './sprite.js';
9
+ export * from './collision.js';
8
10
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,SAAS,CAAA;AACvB,cAAc,cAAc,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,SAAS,CAAA;AACvB,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA"}
@@ -0,0 +1,81 @@
1
+ import { type SpectrumColor } from './palette.js';
2
+ /**
3
+ * A game entity with position, velocity, and an 8×8 bitmap.
4
+ * Use `createSprite` to create, `moveSprite` / `applyGravity` to update each frame,
5
+ * and `renderSprite` to draw.
6
+ */
7
+ export interface Sprite {
8
+ /** Horizontal position in game pixels (float allowed — rounded on render). */
9
+ x: number;
10
+ /** Vertical position in game pixels (float allowed — rounded on render). */
11
+ y: number;
12
+ /** Horizontal velocity in pixels per millisecond. */
13
+ vx: number;
14
+ /** Vertical velocity in pixels per millisecond. */
15
+ vy: number;
16
+ /** 8-byte sprite bitmap — one byte per row, bit 7 = leftmost pixel. */
17
+ bitmap: Uint8Array;
18
+ /** Foreground colour (`C.*` palette value). */
19
+ ink: SpectrumColor;
20
+ /**
21
+ * Background colour, or `null` for a transparent background.
22
+ * Transparent sprites draw only ink pixels — useful over scrolling backgrounds.
23
+ */
24
+ paper: SpectrumColor | null;
25
+ /** When `true` the sprite is rendered mirrored horizontally. Bitmap is cached. */
26
+ flipX: boolean;
27
+ /** When `false` `renderSprite` skips this sprite entirely. */
28
+ visible: boolean;
29
+ }
30
+ /**
31
+ * Creates a `Sprite` at position (0, 0) with zero velocity.
32
+ *
33
+ * @param bitmap - 8-byte sprite definition
34
+ * @param ink - Foreground colour
35
+ * @param paper - Background colour, or `null` for transparent (default `null`)
36
+ *
37
+ * @example
38
+ * const player = createSprite(PLAYER_BITMAP, C.B_CYAN)
39
+ * const bullet = createSprite(BULLET_BITMAP, C.B_WHITE, C.BLACK)
40
+ */
41
+ export declare function createSprite(bitmap: Uint8Array, ink: SpectrumColor, paper?: SpectrumColor | null): Sprite;
42
+ /**
43
+ * Advances the sprite position by `vx * dt` and `vy * dt`.
44
+ * Call once per frame before collision resolution.
45
+ *
46
+ * @param sprite - Sprite to update
47
+ * @param dt - Elapsed time in milliseconds since last frame
48
+ *
49
+ * @example
50
+ * // Game loop
51
+ * moveSprite(player, dt)
52
+ * const { x, hitBottom } = resolveY(player, map, player.y)
53
+ * player.y = y
54
+ * if (hitBottom) player.vy = 0
55
+ */
56
+ export declare function moveSprite(sprite: Sprite, dt: number): void;
57
+ /**
58
+ * Adds `gravity * dt` to the sprite's vertical velocity.
59
+ * Call once per frame before `moveSprite`.
60
+ *
61
+ * @param sprite - Sprite to affect
62
+ * @param gravity - Acceleration in pixels per millisecond² (e.g. `0.003` for gentle gravity)
63
+ * @param dt - Elapsed time in milliseconds
64
+ *
65
+ * @example
66
+ * applyGravity(player, 0.003, dt)
67
+ * moveSprite(player, dt)
68
+ */
69
+ export declare function applyGravity(sprite: Sprite, gravity: number, dt: number): void;
70
+ /**
71
+ * Draws the sprite at its current position (rounded to nearest pixel).
72
+ * Does nothing when `sprite.visible === false`.
73
+ * Respects `sprite.paper` (transparent when `null`) and `sprite.flipX`.
74
+ *
75
+ * Must be called after all position updates and collision resolution for the frame.
76
+ *
77
+ * @param ctx - Canvas 2D context (same one used for the rest of the frame)
78
+ * @param sprite - Sprite to render
79
+ */
80
+ export declare function renderSprite(ctx: CanvasRenderingContext2D, sprite: Sprite): void;
81
+ //# sourceMappingURL=sprite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sprite.d.ts","sourceRoot":"","sources":["../src/sprite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,cAAc,CAAA;AAGjD;;;;GAIG;AACH,MAAM,WAAW,MAAM;IACrB,8EAA8E;IAC9E,CAAC,EAAE,MAAM,CAAA;IACT,4EAA4E;IAC5E,CAAC,EAAE,MAAM,CAAA;IACT,qDAAqD;IACrD,EAAE,EAAE,MAAM,CAAA;IACV,mDAAmD;IACnD,EAAE,EAAE,MAAM,CAAA;IACV,uEAAuE;IACvE,MAAM,EAAE,UAAU,CAAA;IAClB,+CAA+C;IAC/C,GAAG,EAAE,aAAa,CAAA;IAClB;;;OAGG;IACH,KAAK,EAAE,aAAa,GAAG,IAAI,CAAA;IAC3B,kFAAkF;IAClF,KAAK,EAAE,OAAO,CAAA;IACd,8DAA8D;IAC9D,OAAO,EAAE,OAAO,CAAA;CACjB;AAcD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,aAAa,EAClB,KAAK,GAAE,aAAa,GAAG,IAAW,GACjC,MAAM,CAER;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAG3D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAE9E;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,wBAAwB,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAiBhF"}
package/dist/sprite.js ADDED
@@ -0,0 +1,91 @@
1
+ import {} from './palette.js';
2
+ import { mirrorSprite, drawSprite as _drawSprite } from './renderer.js';
3
+ // Lazy cache: avoids creating a new mirrored Uint8Array every frame
4
+ const _flipCache = new WeakMap();
5
+ function getFlipped(bm) {
6
+ let flipped = _flipCache.get(bm);
7
+ if (!flipped) {
8
+ flipped = mirrorSprite(bm);
9
+ _flipCache.set(bm, flipped);
10
+ }
11
+ return flipped;
12
+ }
13
+ /**
14
+ * Creates a `Sprite` at position (0, 0) with zero velocity.
15
+ *
16
+ * @param bitmap - 8-byte sprite definition
17
+ * @param ink - Foreground colour
18
+ * @param paper - Background colour, or `null` for transparent (default `null`)
19
+ *
20
+ * @example
21
+ * const player = createSprite(PLAYER_BITMAP, C.B_CYAN)
22
+ * const bullet = createSprite(BULLET_BITMAP, C.B_WHITE, C.BLACK)
23
+ */
24
+ export function createSprite(bitmap, ink, paper = null) {
25
+ return { x: 0, y: 0, vx: 0, vy: 0, bitmap, ink, paper, flipX: false, visible: true };
26
+ }
27
+ /**
28
+ * Advances the sprite position by `vx * dt` and `vy * dt`.
29
+ * Call once per frame before collision resolution.
30
+ *
31
+ * @param sprite - Sprite to update
32
+ * @param dt - Elapsed time in milliseconds since last frame
33
+ *
34
+ * @example
35
+ * // Game loop
36
+ * moveSprite(player, dt)
37
+ * const { x, hitBottom } = resolveY(player, map, player.y)
38
+ * player.y = y
39
+ * if (hitBottom) player.vy = 0
40
+ */
41
+ export function moveSprite(sprite, dt) {
42
+ sprite.x += sprite.vx * dt;
43
+ sprite.y += sprite.vy * dt;
44
+ }
45
+ /**
46
+ * Adds `gravity * dt` to the sprite's vertical velocity.
47
+ * Call once per frame before `moveSprite`.
48
+ *
49
+ * @param sprite - Sprite to affect
50
+ * @param gravity - Acceleration in pixels per millisecond² (e.g. `0.003` for gentle gravity)
51
+ * @param dt - Elapsed time in milliseconds
52
+ *
53
+ * @example
54
+ * applyGravity(player, 0.003, dt)
55
+ * moveSprite(player, dt)
56
+ */
57
+ export function applyGravity(sprite, gravity, dt) {
58
+ sprite.vy += gravity * dt;
59
+ }
60
+ /**
61
+ * Draws the sprite at its current position (rounded to nearest pixel).
62
+ * Does nothing when `sprite.visible === false`.
63
+ * Respects `sprite.paper` (transparent when `null`) and `sprite.flipX`.
64
+ *
65
+ * Must be called after all position updates and collision resolution for the frame.
66
+ *
67
+ * @param ctx - Canvas 2D context (same one used for the rest of the frame)
68
+ * @param sprite - Sprite to render
69
+ */
70
+ export function renderSprite(ctx, sprite) {
71
+ if (!sprite.visible)
72
+ return;
73
+ const x = Math.round(sprite.x);
74
+ const y = Math.round(sprite.y);
75
+ const bm = sprite.flipX ? getFlipped(sprite.bitmap) : sprite.bitmap;
76
+ if (sprite.paper !== null) {
77
+ _drawSprite(ctx, bm, x, y, sprite.ink, sprite.paper);
78
+ }
79
+ else {
80
+ // Transparent background — draw only ink pixels
81
+ ctx.fillStyle = sprite.ink;
82
+ for (let row = 0; row < 8; row++) {
83
+ const byte = bm[row];
84
+ for (let bit = 0; bit < 8; bit++) {
85
+ if (byte & (0x80 >> bit))
86
+ ctx.fillRect(x + bit, y + row, 1, 1);
87
+ }
88
+ }
89
+ }
90
+ }
91
+ //# sourceMappingURL=sprite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sprite.js","sourceRoot":"","sources":["../src/sprite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,MAAM,cAAc,CAAA;AACjD,OAAO,EAAE,YAAY,EAAE,UAAU,IAAI,WAAW,EAAE,MAAM,eAAe,CAAA;AA+BvE,oEAAoE;AACpE,MAAM,UAAU,GAAG,IAAI,OAAO,EAA0B,CAAA;AAExD,SAAS,UAAU,CAAC,EAAc;IAChC,IAAI,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IAChC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,GAAG,YAAY,CAAC,EAAE,CAAC,CAAA;QAC1B,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,CAAC,CAAA;IAC7B,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,YAAY,CAC1B,MAAkB,EAClB,GAAkB,EAClB,QAA8B,IAAI;IAElC,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;AACtF,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,UAAU,CAAC,MAAc,EAAE,EAAU;IACnD,MAAM,CAAC,CAAC,IAAI,MAAM,CAAC,EAAE,GAAG,EAAE,CAAA;IAC1B,MAAM,CAAC,CAAC,IAAI,MAAM,CAAC,EAAE,GAAG,EAAE,CAAA;AAC5B,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,YAAY,CAAC,MAAc,EAAE,OAAe,EAAE,EAAU;IACtE,MAAM,CAAC,EAAE,IAAI,OAAO,GAAG,EAAE,CAAA;AAC3B,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,YAAY,CAAC,GAA6B,EAAE,MAAc;IACxE,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,OAAM;IAC3B,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;IAC9B,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;IAC9B,MAAM,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAA;IACnE,IAAI,MAAM,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;QAC1B,WAAW,CAAC,GAAG,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAA;IACtD,CAAC;SAAM,CAAC;QACN,gDAAgD;QAChD,GAAG,CAAC,SAAS,GAAG,MAAM,CAAC,GAAG,CAAA;QAC1B,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC;YACjC,MAAM,IAAI,GAAG,EAAE,CAAC,GAAG,CAAC,CAAA;YACpB,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC;gBACjC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC;oBAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;YAChE,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zx-kit",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/zrebec/zx-kit.git"