zx-kit 0.20.0 → 0.22.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/README.md +184 -16
- package/dist/collision.d.ts +82 -0
- package/dist/collision.d.ts.map +1 -1
- package/dist/collision.js +117 -0
- package/dist/collision.js.map +1 -1
- package/dist/i18n.d.ts +43 -0
- package/dist/i18n.d.ts.map +1 -0
- package/dist/i18n.js +48 -0
- package/dist/i18n.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ The goal is simple: **it should look and sound like a Spectrum, but run like a m
|
|
|
27
27
|
- **Canvas renderer** — pixel-perfect scaled rendering, sprite flipping, text drawing, CRT scanline overlay, animated border flashing
|
|
28
28
|
- **Tile map engine** — scrollable maps, O(1) id-index, smart seasonal background swapping, solid-tile collision queries
|
|
29
29
|
- **Free-roaming sprites** — position, velocity, gravity, `flipX` caching, transparent or opaque background
|
|
30
|
-
- **
|
|
30
|
+
- **Three-tier collision** — AABB overlap tests, generic rect-vs-tile wall resolution (any sprite size), and pixel-precise mask overlap with O(pixels) sorted-merge intersection — no allocations per frame
|
|
31
31
|
- **Keyboard input** — configurable key-repeat, single-consume action flags, instant state reset on phase transitions
|
|
32
32
|
- **ZX-style UI widgets** — progress bars with managed lifetime, boxes, frames, panel titles
|
|
33
33
|
- **Typed save / load** — persistent saves via `localStorage` with schema versioning, migrations, slot enumeration, in-memory throttling, and discriminated Result types for every failure mode
|
|
@@ -539,7 +539,7 @@ requestAnimationFrame(loop)
|
|
|
539
539
|
| [`ui.ts`](#uits--ui-widgets) | Boxes, frames, panel titles, progress bars + instrumentation widgets (dotted grids, segmented bars, fluid tanks, dials, text compass) |
|
|
540
540
|
| [`input.ts`](#inputts--keyboard-input) | Movement with key-repeat, action flags, state reset |
|
|
541
541
|
| [`sprite.ts`](#spritets--free-roaming-sprites) | Sprites: position, velocity, gravity, flip, render |
|
|
542
|
-
| [`collision.ts`](#collisionts--
|
|
542
|
+
| [`collision.ts`](#collisionts--collision-detection) | AABB overlap + rect-based tile resolution, pixel-precise mask overlap and tile checks |
|
|
543
543
|
| [`animation.ts`](#animationts--frame-timer--tween) | Frame-timer for sprite strips, position tween between two points |
|
|
544
544
|
| [`camera.ts`](#camerats--scrolling-camera) | Viewport that follows a target with lerp + deadzone, world-bounds clamping |
|
|
545
545
|
| [`scene.ts`](#scenets--scene-manager) | Stack-based scene manager with onEnter/onExit/onPause/onResume hooks |
|
|
@@ -1412,9 +1412,17 @@ renderSprite(ctx, player)
|
|
|
1412
1412
|
|
|
1413
1413
|
---
|
|
1414
1414
|
|
|
1415
|
-
## `collision.ts` —
|
|
1415
|
+
## `collision.ts` — Collision Detection
|
|
1416
1416
|
|
|
1417
|
-
|
|
1417
|
+
Three tiers of collision detection, from broad-phase to pixel-precise:
|
|
1418
|
+
|
|
1419
|
+
| Tier | Functions | Use case |
|
|
1420
|
+
|------|-----------|----------|
|
|
1421
|
+
| **AABB** | `rectsOverlap`, `spritesOverlap`, `spriteRect`, `bitmapRect` | Fast broad-phase hit tests |
|
|
1422
|
+
| **Rect-vs-tile** | `isSolidAt`, `resolveRectX`, `resolveRectY`, `resolveX`, `resolveY` | Wall resolution for any sprite size |
|
|
1423
|
+
| **Pixel-precise** | `bitmapPixelMask`, `masksOverlap`, `pixelSolidCount` | Exact opaque-pixel overlap tests |
|
|
1424
|
+
|
|
1425
|
+
Pick the tier that matches your accuracy need. AABB is O(1) and correct for most cases. Pixel-precise costs O(opaque pixels) but eliminates false positives for non-rectangular sprites.
|
|
1418
1426
|
|
|
1419
1427
|
### `Rect` interface
|
|
1420
1428
|
|
|
@@ -1422,19 +1430,36 @@ Axis-aligned bounding box overlap tests and sprite-vs-tile-map wall resolution.
|
|
|
1422
1430
|
interface Rect { x: number; y: number; w: number; h: number }
|
|
1423
1431
|
```
|
|
1424
1432
|
|
|
1425
|
-
|
|
1433
|
+
An axis-aligned bounding rectangle in game pixels.
|
|
1434
|
+
|
|
1435
|
+
---
|
|
1436
|
+
|
|
1437
|
+
### AABB overlap tests
|
|
1438
|
+
|
|
1439
|
+
#### `spriteRect(sprite): Rect`
|
|
1426
1440
|
|
|
1427
1441
|
Returns the `CELL × CELL` bounding box of a sprite at its current position.
|
|
1428
1442
|
|
|
1429
|
-
|
|
1443
|
+
#### `bitmapRect(bitmap, x, y): Rect`
|
|
1444
|
+
|
|
1445
|
+
Returns the bounding box for any `Bitmap` at `(x, y)`. Correct for bitmaps of any size — 16×24, 32×32, 96×128 — not just `CELL × CELL`.
|
|
1446
|
+
|
|
1447
|
+
```ts
|
|
1448
|
+
const heroRect = bitmapRect(HERO_BMP, hero.x, hero.y)
|
|
1449
|
+
const enemyRect = bitmapRect(ENEMY_BMP, enemy.x, enemy.y)
|
|
1450
|
+
if (rectsOverlap(heroRect, enemyRect)) damage(hero)
|
|
1451
|
+
```
|
|
1452
|
+
|
|
1453
|
+
#### `rectsOverlap(a, b): boolean`
|
|
1430
1454
|
|
|
1431
|
-
Returns `true` when two rectangles share at least one pixel. Touching edges return `false`.
|
|
1455
|
+
Returns `true` when two rectangles share at least one pixel. Touching edges (zero-area overlap) return `false`.
|
|
1432
1456
|
|
|
1433
1457
|
```ts
|
|
1434
1458
|
rectsOverlap(spriteRect(bullet), spriteRect(enemy)) // hit test
|
|
1459
|
+
rectsOverlap(bitmapRect(HERO_BMP, hx, hy), bitmapRect(SWORD_BMP, sx, sy))
|
|
1435
1460
|
```
|
|
1436
1461
|
|
|
1437
|
-
|
|
1462
|
+
#### `spritesOverlap(a, b): boolean`
|
|
1438
1463
|
|
|
1439
1464
|
Shorthand: `rectsOverlap(spriteRect(a), spriteRect(b))`.
|
|
1440
1465
|
|
|
@@ -1443,13 +1468,46 @@ if (spritesOverlap(player, coin)) collectCoin()
|
|
|
1443
1468
|
if (enemies.some(e => spritesOverlap(player, e))) loseLife()
|
|
1444
1469
|
```
|
|
1445
1470
|
|
|
1446
|
-
|
|
1471
|
+
---
|
|
1472
|
+
|
|
1473
|
+
### Rect-vs-tile resolution
|
|
1474
|
+
|
|
1475
|
+
#### `isSolidAt(map, px, py): boolean`
|
|
1476
|
+
|
|
1477
|
+
Tests whether the game-pixel `(px, py)` falls inside a solid tile. Out-of-bounds pixels return `true` (implicit solid boundary). Converts to tile coords via `Math.floor(px / CELL)`.
|
|
1447
1478
|
|
|
1448
|
-
|
|
1479
|
+
```ts
|
|
1480
|
+
if (isSolidAt(map, player.x, player.y + CELL)) { player.vy = 0 } // on floor
|
|
1481
|
+
```
|
|
1482
|
+
|
|
1483
|
+
#### `resolveRectX(rect, map, newX): { x, hitLeft, hitRight }`
|
|
1449
1484
|
|
|
1450
|
-
|
|
1485
|
+
Generic horizontal resolver for any axis-aligned rectangle. Checks every tile row the rectangle spans — so a 16×24 hero correctly detects walls in the middle rows, not just the top and bottom corners.
|
|
1451
1486
|
|
|
1452
|
-
|
|
1487
|
+
Returns the clamped x and collision flags. On collision, the rectangle is placed flush against the wall.
|
|
1488
|
+
|
|
1489
|
+
```ts
|
|
1490
|
+
const rect = bitmapRect(HERO_BMP, hero.x, hero.y)
|
|
1491
|
+
const r = resolveRectX(rect, map, hero.x + dx)
|
|
1492
|
+
hero.x = r.x
|
|
1493
|
+
if (r.hitLeft || r.hitRight) hero.vx = 0
|
|
1494
|
+
```
|
|
1495
|
+
|
|
1496
|
+
#### `resolveRectY(rect, map, newY): { y, hitTop, hitBottom }`
|
|
1497
|
+
|
|
1498
|
+
Generic vertical resolver for any axis-aligned rectangle. Checks every tile column the rectangle spans — so a wide wagon detects the floor across its full footprint.
|
|
1499
|
+
|
|
1500
|
+
```ts
|
|
1501
|
+
const rect = bitmapRect(HERO_BMP, hero.x, hero.y)
|
|
1502
|
+
const r = resolveRectY(rect, map, hero.y + dy)
|
|
1503
|
+
hero.y = r.y
|
|
1504
|
+
if (r.hitBottom) { hero.vy = 0; onGround = true }
|
|
1505
|
+
if (r.hitTop) { hero.vy = 0 }
|
|
1506
|
+
```
|
|
1507
|
+
|
|
1508
|
+
#### `resolveX(sprite, map, newX): { x, hitLeft, hitRight }`
|
|
1509
|
+
|
|
1510
|
+
Resolves a proposed horizontal move for an 8×8 sprite. Thin wrapper over `resolveRectX` — preserved for the common CELL-sized sprite case.
|
|
1453
1511
|
|
|
1454
1512
|
```ts
|
|
1455
1513
|
const { x, hitLeft, hitRight } = resolveX(player, map, player.x)
|
|
@@ -1457,9 +1515,9 @@ player.x = x
|
|
|
1457
1515
|
if (hitLeft || hitRight) player.vx = 0
|
|
1458
1516
|
```
|
|
1459
1517
|
|
|
1460
|
-
|
|
1518
|
+
#### `resolveY(sprite, map, newY): { y, hitTop, hitBottom }`
|
|
1461
1519
|
|
|
1462
|
-
Resolves a proposed vertical move
|
|
1520
|
+
Resolves a proposed vertical move for an 8×8 sprite. Thin wrapper over `resolveRectY`.
|
|
1463
1521
|
|
|
1464
1522
|
- `hitBottom` — landed on a floor (use for jump ground detection)
|
|
1465
1523
|
- `hitTop` — bumped a ceiling
|
|
@@ -1473,6 +1531,114 @@ if (hitTop) { player.vy = 0 }
|
|
|
1473
1531
|
|
|
1474
1532
|
---
|
|
1475
1533
|
|
|
1534
|
+
### Pixel-precise collision
|
|
1535
|
+
|
|
1536
|
+
AABB and rect-vs-tile use the full bounding box. This is almost always correct — but it fails for non-rectangular sprites in edge cases: a circular character standing on a ledge, a diamond-shaped projectile grazing a corner, a tall hero with narrow feet that shouldn't trigger floor detection when hanging over a gap.
|
|
1537
|
+
|
|
1538
|
+
Pixel-precise collision solves this by working with the actual opaque pixels of a bitmap, not its enclosing rectangle.
|
|
1539
|
+
|
|
1540
|
+
```
|
|
1541
|
+
AABB (16px wide): ████████████████ → fires when any part of the box overlaps
|
|
1542
|
+
a tile, even if the sprite itself clears it
|
|
1543
|
+
pixelSolidCount: ···██····██···· → only the real foot pixels are checked —
|
|
1544
|
+
Dizzy hanging over a platform edge doesn't
|
|
1545
|
+
feel magically glued to empty air
|
|
1546
|
+
```
|
|
1547
|
+
|
|
1548
|
+
#### `PixelMask` interface
|
|
1549
|
+
|
|
1550
|
+
```ts
|
|
1551
|
+
interface PixelMask {
|
|
1552
|
+
readonly width: number
|
|
1553
|
+
readonly height: number
|
|
1554
|
+
readonly rows: readonly (readonly number[])[] // per-row sorted opaque column indices
|
|
1555
|
+
readonly totalPixels: number
|
|
1556
|
+
}
|
|
1557
|
+
```
|
|
1558
|
+
|
|
1559
|
+
Pre-computed per-row opaque pixel data for a `Bitmap`. Build once with `bitmapPixelMask`; reuse every frame. Each `rows[r]` is a sorted array of column indices where that row has a set bit. Empty rows have zero-length arrays — never `undefined`.
|
|
1560
|
+
|
|
1561
|
+
```
|
|
1562
|
+
// Example: 16×16 circular sprite
|
|
1563
|
+
mask.rows[0] → [6, 7, 8, 9] // narrow top
|
|
1564
|
+
mask.rows[7] → [0, 1, 2, ..., 15] // full-width middle
|
|
1565
|
+
mask.rows[11] → [3, 4, 10, 11] // only feet
|
|
1566
|
+
mask.rows[14] → [] // below feet, empty
|
|
1567
|
+
```
|
|
1568
|
+
|
|
1569
|
+
The immutability guarantee matters: the mask is derived from immutable `Bitmap` data. Pre-compute once and store alongside the bitmap definition.
|
|
1570
|
+
|
|
1571
|
+
#### `bitmapPixelMask(bitmap): PixelMask`
|
|
1572
|
+
|
|
1573
|
+
Extracts a pixel mask from a `Bitmap`. Reads each row's bit data (bit 7 = leftmost pixel) and collects column indices of set (opaque) pixels into sorted arrays. Counts `totalPixels` for overlap severity calculations.
|
|
1574
|
+
|
|
1575
|
+
```ts
|
|
1576
|
+
// Pre-compute at module load time — not inside the game loop
|
|
1577
|
+
const HERO_MASK = bitmapPixelMask(HERO_BMP)
|
|
1578
|
+
const ENEMY_MASK = bitmapPixelMask(ENEMY_BMP)
|
|
1579
|
+
const BULLET_MASK = bitmapPixelMask(BULLET_BMP)
|
|
1580
|
+
```
|
|
1581
|
+
|
|
1582
|
+
**Bitmap width must be a multiple of 8.** Bitmaps with width `w` require `w * height / 8` bytes of data — standard `createBitmap` enforces this.
|
|
1583
|
+
|
|
1584
|
+
#### `masksOverlap(a, ax, ay, b, bx, by): number`
|
|
1585
|
+
|
|
1586
|
+
Counts opaque pixels of mask `a` at `(ax, ay)` that overlap with opaque pixels of mask `b` at `(bx, by)`.
|
|
1587
|
+
|
|
1588
|
+
Returns **0** when there is no pixel-level overlap. Any value **> 0** is a pixel-perfect collision. The count itself carries semantic meaning: use it for overlap severity — damage scaling, knock-back strength, or as a threshold to ignore grazing touches.
|
|
1589
|
+
|
|
1590
|
+
Uses sorted-merge intersection per row: O(opaque pixels), no allocations per call. Safe to call every frame for multiple pairs.
|
|
1591
|
+
|
|
1592
|
+
```ts
|
|
1593
|
+
// Simple hit test
|
|
1594
|
+
if (masksOverlap(BULLET_MASK, bullet.x, bullet.y, ENEMY_MASK, enemy.x, enemy.y) > 0) {
|
|
1595
|
+
destroyEnemy()
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
// Severity — require a real overlap, not just a 1-pixel graze
|
|
1599
|
+
const overlap = masksOverlap(SWORD_MASK, sx, sy, HERO_MASK, hx, hy)
|
|
1600
|
+
if (overlap >= 3) {
|
|
1601
|
+
takeDamage(Math.round(overlap / SWORD_MASK.totalPixels * 10))
|
|
1602
|
+
}
|
|
1603
|
+
```
|
|
1604
|
+
|
|
1605
|
+
Masks of different sizes work without any special handling — the overlap window is clipped to the intersection region automatically.
|
|
1606
|
+
|
|
1607
|
+
#### `pixelSolidCount(mask, mx, my, map): number`
|
|
1608
|
+
|
|
1609
|
+
Counts opaque pixels of a mask at `(mx, my)` that sit on solid tiles in a `TileMap`. Pixel-precise replacement for AABB-based ground / wall checks.
|
|
1610
|
+
|
|
1611
|
+
Solves the "character standing on a platform edge" problem: a round sprite with narrow feet can hang over the edge — only the real foot pixels are checked against the tile map, not the full bounding box.
|
|
1612
|
+
|
|
1613
|
+
```ts
|
|
1614
|
+
const HERO_MASK = bitmapPixelMask(HERO_BMP)
|
|
1615
|
+
|
|
1616
|
+
// Check if standing — test 1 px below current foot position
|
|
1617
|
+
const standing = pixelSolidCount(HERO_MASK, hero.x, hero.y + 1, map) > 0
|
|
1618
|
+
|
|
1619
|
+
// Check wall to the right — test 1 px past right edge
|
|
1620
|
+
const wallRight = pixelSolidCount(HERO_MASK, hero.x + 1, hero.y, map) > 0
|
|
1621
|
+
|
|
1622
|
+
// How many foot pixels are actually on solid ground? Use as grip factor
|
|
1623
|
+
const groundContact = pixelSolidCount(HERO_MASK, hero.x, hero.y + 1, map)
|
|
1624
|
+
```
|
|
1625
|
+
|
|
1626
|
+
When `groundContact` is 0, a circle-shaped hero hanging over a tile edge won't trigger `hitBottom` in `resolveRectY` — the pixel-check and AABB-check intentionally disagree, and you pick which one to trust for each gameplay mechanic.
|
|
1627
|
+
|
|
1628
|
+
---
|
|
1629
|
+
|
|
1630
|
+
### Choosing the right tier
|
|
1631
|
+
|
|
1632
|
+
| Situation | Recommended tier |
|
|
1633
|
+
|-----------|-----------------|
|
|
1634
|
+
| Player touches any part of a coin | `spritesOverlap` — AABB is exact when both sprites are `CELL × CELL` |
|
|
1635
|
+
| Large hero (16×24) walks into a wall | `resolveRectX` / `resolveRectY` — checks all tile rows the sprite spans |
|
|
1636
|
+
| Round sprite on a platform edge | `pixelSolidCount` — only real foot pixels count |
|
|
1637
|
+
| Bullet vs. irregular boss sprite | `masksOverlap` — pixel-precise, returns overlap count for damage |
|
|
1638
|
+
| Off-road detection for a truck with a bumpy silhouette | `pixelSolidCount` / custom mask loop — checks each opaque pixel against road boundary |
|
|
1639
|
+
|
|
1640
|
+
---
|
|
1641
|
+
|
|
1476
1642
|
## `animation.ts` — Frame Timer & Tween
|
|
1477
1643
|
|
|
1478
1644
|
Two small primitives for time-based animation:
|
|
@@ -2057,8 +2223,10 @@ zx-kit/
|
|
|
2057
2223
|
│ ├── tilemap.ts # createTileMap, Tile, Viewport, TileMap
|
|
2058
2224
|
│ ├── sprite.ts # createSprite, moveSprite, applyGravity,
|
|
2059
2225
|
│ │ # renderSprite, Sprite
|
|
2060
|
-
│ └── collision.ts # spriteRect, rectsOverlap, spritesOverlap,
|
|
2061
|
-
│ # isSolidAt, resolveX, resolveY,
|
|
2226
|
+
│ └── collision.ts # spriteRect, bitmapRect, rectsOverlap, spritesOverlap,
|
|
2227
|
+
│ # isSolidAt, resolveRectX, resolveRectY, resolveX, resolveY,
|
|
2228
|
+
│ # bitmapPixelMask, masksOverlap, pixelSolidCount,
|
|
2229
|
+
│ # Rect, PixelMask
|
|
2062
2230
|
└── dist/ # compiled output (npm run build)
|
|
2063
2231
|
├── index.js
|
|
2064
2232
|
├── index.d.ts
|
package/dist/collision.d.ts
CHANGED
|
@@ -85,6 +85,88 @@ export declare function resolveRectY(rect: Rect, map: TileMap, newY: number): {
|
|
|
85
85
|
hitTop: boolean;
|
|
86
86
|
hitBottom: boolean;
|
|
87
87
|
};
|
|
88
|
+
/**
|
|
89
|
+
* Pre-computed per-row opaque pixel data for a {@link Bitmap}.
|
|
90
|
+
* Build once with {@link bitmapPixelMask}, reuse every frame.
|
|
91
|
+
*
|
|
92
|
+
* Each row is a sorted array of column indices where the bitmap has a set bit.
|
|
93
|
+
* Empty rows have zero-length arrays — never `undefined`.
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```
|
|
97
|
+
* // 16×16 circular sprite:
|
|
98
|
+
* mask.rows[0] → [6, 7, 8, 9] // narrow top
|
|
99
|
+
* mask.rows[7] → [0, 1, 2, ..., 15] // full-width middle
|
|
100
|
+
* mask.rows[11] → [3, 4, 10, 11] // only feet
|
|
101
|
+
* mask.rows[14] → [] // below feet, empty
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export interface PixelMask {
|
|
105
|
+
readonly width: number;
|
|
106
|
+
readonly height: number;
|
|
107
|
+
readonly rows: readonly (readonly number[])[];
|
|
108
|
+
readonly totalPixels: number;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Extract a pixel mask from a {@link Bitmap}.
|
|
112
|
+
* Reads each row's bit data (bit 7 = leftmost pixel) and collects column
|
|
113
|
+
* indices of set (opaque) pixels into sorted arrays.
|
|
114
|
+
*
|
|
115
|
+
* Pre-compute once per sprite definition — the result is immutable and
|
|
116
|
+
* derived from immutable bitmap data.
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```ts
|
|
120
|
+
* const HERO_MASK = bitmapPixelMask(HERO_BMP)
|
|
121
|
+
* // Now use with masksOverlap() or pixelSolidCount()
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
export declare function bitmapPixelMask(bitmap: Bitmap): PixelMask;
|
|
125
|
+
/**
|
|
126
|
+
* Count opaque pixels of mask `a` at `(ax, ay)` that overlap with
|
|
127
|
+
* opaque pixels of mask `b` at `(bx, by)`.
|
|
128
|
+
*
|
|
129
|
+
* Returns 0 when no overlap. Any value > 0 means pixel-perfect collision.
|
|
130
|
+
* The count itself is useful for overlap severity — e.g. damage scaling.
|
|
131
|
+
*
|
|
132
|
+
* Uses sorted-merge intersection per row — O(pixels) total, no allocations.
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```ts
|
|
136
|
+
* const BULLET = bitmapPixelMask(BULLET_BMP)
|
|
137
|
+
* const ENEMY = bitmapPixelMask(ENEMY_BMP)
|
|
138
|
+
*
|
|
139
|
+
* if (masksOverlap(BULLET, bx, by, ENEMY, ex, ey) > 0) {
|
|
140
|
+
* destroyEnemy()
|
|
141
|
+
* }
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
export declare function masksOverlap(a: PixelMask, ax: number, ay: number, b: PixelMask, bx: number, by: number): number;
|
|
145
|
+
/**
|
|
146
|
+
* Count opaque pixels of a mask at `(mx, my)` that sit on solid tiles
|
|
147
|
+
* in a {@link TileMap}. Pixel-precise replacement for AABB-based checks.
|
|
148
|
+
*
|
|
149
|
+
* Solves the "character standing on a platform edge" problem: a round
|
|
150
|
+
* sprite with narrow feet can hang over the edge — only real foot pixels
|
|
151
|
+
* are checked, not the full bounding box.
|
|
152
|
+
*
|
|
153
|
+
* ```
|
|
154
|
+
* AABB (16px wide): ████████████████ → full-width overlap check
|
|
155
|
+
* pixelSolidCount: ···██····██···· → only feet matter
|
|
156
|
+
* ```
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* ```ts
|
|
160
|
+
* const HERO_MASK = bitmapPixelMask(HERO_BMP)
|
|
161
|
+
*
|
|
162
|
+
* // Check if standing: test 1px below current position
|
|
163
|
+
* const standing = pixelSolidCount(HERO_MASK, hero.x, hero.y + 1, map) > 0
|
|
164
|
+
*
|
|
165
|
+
* // Check wall to the right
|
|
166
|
+
* const wallRight = pixelSolidCount(HERO_MASK, hero.x + 1, hero.y, map) > 0
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
export declare function pixelSolidCount(mask: PixelMask, mx: number, my: number, map: TileMap): number;
|
|
88
170
|
/**
|
|
89
171
|
* Resolves a proposed horizontal movement for an 8×8 sprite against solid tiles.
|
|
90
172
|
* Thin wrapper over {@link resolveRectX} preserved for backward compatibility.
|
package/dist/collision.d.ts.map
CHANGED
|
@@ -1 +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;AAC3C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,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;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAErE;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAEvE;AAwBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,IAAI,EACV,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,MAAM,GACX;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAuBpD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,IAAI,EACV,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,MAAM,GACX;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAuBpD;AAED;;;;;;;;;;;GAWG;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,CAEpD;AAED;;;;;;;;;;GAUG;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,CAEpD"}
|
|
1
|
+
{"version":3,"file":"collision.d.ts","sourceRoot":"","sources":["../src/collision.ts"],"names":[],"mappings":"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"}
|
package/dist/collision.js
CHANGED
|
@@ -144,6 +144,123 @@ export function resolveRectY(rect, map, newY) {
|
|
|
144
144
|
}
|
|
145
145
|
return { y, hitTop, hitBottom };
|
|
146
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Extract a pixel mask from a {@link Bitmap}.
|
|
149
|
+
* Reads each row's bit data (bit 7 = leftmost pixel) and collects column
|
|
150
|
+
* indices of set (opaque) pixels into sorted arrays.
|
|
151
|
+
*
|
|
152
|
+
* Pre-compute once per sprite definition — the result is immutable and
|
|
153
|
+
* derived from immutable bitmap data.
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```ts
|
|
157
|
+
* const HERO_MASK = bitmapPixelMask(HERO_BMP)
|
|
158
|
+
* // Now use with masksOverlap() or pixelSolidCount()
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
export function bitmapPixelMask(bitmap) {
|
|
162
|
+
const bytesPerRow = bitmap.width / 8;
|
|
163
|
+
const rows = [];
|
|
164
|
+
let total = 0;
|
|
165
|
+
for (let row = 0; row < bitmap.height; row++) {
|
|
166
|
+
const cols = [];
|
|
167
|
+
for (let col = 0; col < bitmap.width; col++) {
|
|
168
|
+
const byteIdx = row * bytesPerRow + Math.floor(col / 8);
|
|
169
|
+
const bitIdx = 7 - (col % 8);
|
|
170
|
+
if (bitmap.data[byteIdx] & (1 << bitIdx))
|
|
171
|
+
cols.push(col);
|
|
172
|
+
}
|
|
173
|
+
rows.push(cols);
|
|
174
|
+
total += cols.length;
|
|
175
|
+
}
|
|
176
|
+
return { width: bitmap.width, height: bitmap.height, rows, totalPixels: total };
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Count opaque pixels of mask `a` at `(ax, ay)` that overlap with
|
|
180
|
+
* opaque pixels of mask `b` at `(bx, by)`.
|
|
181
|
+
*
|
|
182
|
+
* Returns 0 when no overlap. Any value > 0 means pixel-perfect collision.
|
|
183
|
+
* The count itself is useful for overlap severity — e.g. damage scaling.
|
|
184
|
+
*
|
|
185
|
+
* Uses sorted-merge intersection per row — O(pixels) total, no allocations.
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* ```ts
|
|
189
|
+
* const BULLET = bitmapPixelMask(BULLET_BMP)
|
|
190
|
+
* const ENEMY = bitmapPixelMask(ENEMY_BMP)
|
|
191
|
+
*
|
|
192
|
+
* if (masksOverlap(BULLET, bx, by, ENEMY, ex, ey) > 0) {
|
|
193
|
+
* destroyEnemy()
|
|
194
|
+
* }
|
|
195
|
+
* ```
|
|
196
|
+
*/
|
|
197
|
+
export function masksOverlap(a, ax, ay, b, bx, by) {
|
|
198
|
+
const top = Math.max(ay, by);
|
|
199
|
+
const bot = Math.min(ay + a.height, by + b.height);
|
|
200
|
+
if (top >= bot)
|
|
201
|
+
return 0;
|
|
202
|
+
if (ax >= bx + b.width || ax + a.width <= bx)
|
|
203
|
+
return 0;
|
|
204
|
+
let count = 0;
|
|
205
|
+
for (let y = top; y < bot; y++) {
|
|
206
|
+
const rowA = a.rows[y - ay];
|
|
207
|
+
const rowB = b.rows[y - by];
|
|
208
|
+
if (rowA.length === 0 || rowB.length === 0)
|
|
209
|
+
continue;
|
|
210
|
+
let i = 0, j = 0;
|
|
211
|
+
while (i < rowA.length && j < rowB.length) {
|
|
212
|
+
const ca = rowA[i] + ax;
|
|
213
|
+
const cb = rowB[j] + bx;
|
|
214
|
+
if (ca === cb) {
|
|
215
|
+
count++;
|
|
216
|
+
i++;
|
|
217
|
+
j++;
|
|
218
|
+
}
|
|
219
|
+
else if (ca < cb)
|
|
220
|
+
i++;
|
|
221
|
+
else
|
|
222
|
+
j++;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return count;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Count opaque pixels of a mask at `(mx, my)` that sit on solid tiles
|
|
229
|
+
* in a {@link TileMap}. Pixel-precise replacement for AABB-based checks.
|
|
230
|
+
*
|
|
231
|
+
* Solves the "character standing on a platform edge" problem: a round
|
|
232
|
+
* sprite with narrow feet can hang over the edge — only real foot pixels
|
|
233
|
+
* are checked, not the full bounding box.
|
|
234
|
+
*
|
|
235
|
+
* ```
|
|
236
|
+
* AABB (16px wide): ████████████████ → full-width overlap check
|
|
237
|
+
* pixelSolidCount: ···██····██···· → only feet matter
|
|
238
|
+
* ```
|
|
239
|
+
*
|
|
240
|
+
* @example
|
|
241
|
+
* ```ts
|
|
242
|
+
* const HERO_MASK = bitmapPixelMask(HERO_BMP)
|
|
243
|
+
*
|
|
244
|
+
* // Check if standing: test 1px below current position
|
|
245
|
+
* const standing = pixelSolidCount(HERO_MASK, hero.x, hero.y + 1, map) > 0
|
|
246
|
+
*
|
|
247
|
+
* // Check wall to the right
|
|
248
|
+
* const wallRight = pixelSolidCount(HERO_MASK, hero.x + 1, hero.y, map) > 0
|
|
249
|
+
* ```
|
|
250
|
+
*/
|
|
251
|
+
export function pixelSolidCount(mask, mx, my, map) {
|
|
252
|
+
let count = 0;
|
|
253
|
+
for (let row = 0; row < mask.height; row++) {
|
|
254
|
+
const worldY = my + row;
|
|
255
|
+
const tileY = Math.floor(worldY / CELL);
|
|
256
|
+
for (const col of mask.rows[row]) {
|
|
257
|
+
if (map.isSolid(Math.floor((mx + col) / CELL), tileY))
|
|
258
|
+
count++;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return count;
|
|
262
|
+
}
|
|
263
|
+
// ─── AABB sprite wrappers (backward-compatible) ─────────────────────────────
|
|
147
264
|
/**
|
|
148
265
|
* Resolves a proposed horizontal movement for an 8×8 sprite against solid tiles.
|
|
149
266
|
* Thin wrapper over {@link resolveRectX} preserved for backward compatibility.
|
package/dist/collision.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collision.js","sourceRoot":"","sources":["../src/collision.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"collision.js","sourceRoot":"","sources":["../src/collision.ts"],"names":[],"mappings":"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"}
|
package/dist/i18n.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* i18n.ts — locale selection helper.
|
|
3
|
+
*
|
|
4
|
+
* A tiny, dependency-free utility for picking a string pack at runtime
|
|
5
|
+
* based on a language code. Designed so any zx-kit game can ship with
|
|
6
|
+
* multiple translations and switch them via a single config flag.
|
|
7
|
+
*
|
|
8
|
+
* USAGE
|
|
9
|
+
* ─────
|
|
10
|
+
* Each locale exports the same shape — usually as named string constants
|
|
11
|
+
* and template functions (e.g. `STR_DEPTH = (m) => \`D:${m}M\``).
|
|
12
|
+
* Import each locale module as a namespace and hand them to `pickLocale`:
|
|
13
|
+
*
|
|
14
|
+
* import * as en from './strings.ts' // default English
|
|
15
|
+
* import * as sk from './strings.sk.ts' // Slovak
|
|
16
|
+
* import * as ru from './strings.ru.ts' // Russian
|
|
17
|
+
* import { LANGUAGE_CODE } from './config.ts'
|
|
18
|
+
*
|
|
19
|
+
* export const L = pickLocale(en, { sk, ru }, LANGUAGE_CODE)
|
|
20
|
+
*
|
|
21
|
+
* Consumers then read `L.STR_DEPTH(120)` etc. — same name, swapped values.
|
|
22
|
+
*
|
|
23
|
+
* SELECTION RULES
|
|
24
|
+
* ───────────────
|
|
25
|
+
* - `code` null / undefined / empty → returns `defaultLocale`
|
|
26
|
+
* - `code` matches a key in `locales` → returns that locale
|
|
27
|
+
* (case-insensitive: 'SK' === 'sk')
|
|
28
|
+
* - `code` doesn't match anything → returns `defaultLocale`
|
|
29
|
+
*
|
|
30
|
+
* The default key (typically 'en') doesn't need to live in the `locales`
|
|
31
|
+
* map — passing 'en' simply falls through to `defaultLocale`. This keeps
|
|
32
|
+
* the English source-of-truth file at the conventional `strings.ts` path
|
|
33
|
+
* without forcing a `strings.en.ts` rename.
|
|
34
|
+
*/
|
|
35
|
+
/**
|
|
36
|
+
* Pick a locale object from a map by code, with fallback to a default.
|
|
37
|
+
*
|
|
38
|
+
* Generic over `T` so every entry in `locales` is type-checked against the
|
|
39
|
+
* shape of `defaultLocale` — adding a new translation that misses a key
|
|
40
|
+
* (or has a wrong signature) becomes a compile error.
|
|
41
|
+
*/
|
|
42
|
+
export declare function pickLocale<T>(defaultLocale: T, locales: Record<string, T>, code: string | null | undefined): T;
|
|
43
|
+
//# sourceMappingURL=i18n.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../src/i18n.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAC1B,aAAa,EAAE,CAAC,EAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,EAC1B,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC9B,CAAC,CAIH"}
|
package/dist/i18n.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* i18n.ts — locale selection helper.
|
|
3
|
+
*
|
|
4
|
+
* A tiny, dependency-free utility for picking a string pack at runtime
|
|
5
|
+
* based on a language code. Designed so any zx-kit game can ship with
|
|
6
|
+
* multiple translations and switch them via a single config flag.
|
|
7
|
+
*
|
|
8
|
+
* USAGE
|
|
9
|
+
* ─────
|
|
10
|
+
* Each locale exports the same shape — usually as named string constants
|
|
11
|
+
* and template functions (e.g. `STR_DEPTH = (m) => \`D:${m}M\``).
|
|
12
|
+
* Import each locale module as a namespace and hand them to `pickLocale`:
|
|
13
|
+
*
|
|
14
|
+
* import * as en from './strings.ts' // default English
|
|
15
|
+
* import * as sk from './strings.sk.ts' // Slovak
|
|
16
|
+
* import * as ru from './strings.ru.ts' // Russian
|
|
17
|
+
* import { LANGUAGE_CODE } from './config.ts'
|
|
18
|
+
*
|
|
19
|
+
* export const L = pickLocale(en, { sk, ru }, LANGUAGE_CODE)
|
|
20
|
+
*
|
|
21
|
+
* Consumers then read `L.STR_DEPTH(120)` etc. — same name, swapped values.
|
|
22
|
+
*
|
|
23
|
+
* SELECTION RULES
|
|
24
|
+
* ───────────────
|
|
25
|
+
* - `code` null / undefined / empty → returns `defaultLocale`
|
|
26
|
+
* - `code` matches a key in `locales` → returns that locale
|
|
27
|
+
* (case-insensitive: 'SK' === 'sk')
|
|
28
|
+
* - `code` doesn't match anything → returns `defaultLocale`
|
|
29
|
+
*
|
|
30
|
+
* The default key (typically 'en') doesn't need to live in the `locales`
|
|
31
|
+
* map — passing 'en' simply falls through to `defaultLocale`. This keeps
|
|
32
|
+
* the English source-of-truth file at the conventional `strings.ts` path
|
|
33
|
+
* without forcing a `strings.en.ts` rename.
|
|
34
|
+
*/
|
|
35
|
+
/**
|
|
36
|
+
* Pick a locale object from a map by code, with fallback to a default.
|
|
37
|
+
*
|
|
38
|
+
* Generic over `T` so every entry in `locales` is type-checked against the
|
|
39
|
+
* shape of `defaultLocale` — adding a new translation that misses a key
|
|
40
|
+
* (or has a wrong signature) becomes a compile error.
|
|
41
|
+
*/
|
|
42
|
+
export function pickLocale(defaultLocale, locales, code) {
|
|
43
|
+
if (!code)
|
|
44
|
+
return defaultLocale;
|
|
45
|
+
const normalised = code.toLowerCase();
|
|
46
|
+
return locales[normalised] ?? defaultLocale;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=i18n.js.map
|
package/dist/i18n.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i18n.js","sourceRoot":"","sources":["../src/i18n.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CACxB,aAAgB,EAChB,OAA0B,EAC1B,IAA+B;IAE/B,IAAI,CAAC,IAAI;QAAE,OAAO,aAAa,CAAA;IAC/B,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE,CAAA;IACrC,OAAO,OAAO,CAAC,UAAU,CAAC,IAAI,aAAa,CAAA;AAC7C,CAAC"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,SAAS,CAAA;AACvB,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;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,SAAS,CAAA;AACvB,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;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,WAAW,CAAA"}
|
package/dist/index.js
CHANGED
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,SAAS,CAAA;AACvB,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;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,SAAS,CAAA;AACvB,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;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,WAAW,CAAA"}
|