@sadhaka/loom-engine 0.10.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 +344 -0
- package/dist/animation/animation-clip.d.ts +12 -0
- package/dist/animation/animation-clip.d.ts.map +1 -0
- package/dist/animation/animation-clip.js +85 -0
- package/dist/animation/animation-clip.js.map +1 -0
- package/dist/animation/animation-state-pool.d.ts +25 -0
- package/dist/animation/animation-state-pool.d.ts.map +1 -0
- package/dist/animation/animation-state-pool.js +113 -0
- package/dist/animation/animation-state-pool.js.map +1 -0
- package/dist/asset/sprite-sheet-loader.d.ts +41 -0
- package/dist/asset/sprite-sheet-loader.d.ts.map +1 -0
- package/dist/asset/sprite-sheet-loader.js +313 -0
- package/dist/asset/sprite-sheet-loader.js.map +1 -0
- package/dist/audio/audio-bus.d.ts +43 -0
- package/dist/audio/audio-bus.d.ts.map +1 -0
- package/dist/audio/audio-bus.js +258 -0
- package/dist/audio/audio-bus.js.map +1 -0
- package/dist/combat/mob-catalog.d.ts +29 -0
- package/dist/combat/mob-catalog.d.ts.map +1 -0
- package/dist/combat/mob-catalog.js +104 -0
- package/dist/combat/mob-catalog.js.map +1 -0
- package/dist/components/health.d.ts +28 -0
- package/dist/components/health.d.ts.map +1 -0
- package/dist/components/health.js +150 -0
- package/dist/components/health.js.map +1 -0
- package/dist/components/interactable.d.ts +30 -0
- package/dist/components/interactable.d.ts.map +1 -0
- package/dist/components/interactable.js +94 -0
- package/dist/components/interactable.js.map +1 -0
- package/dist/components/particle-emitter.d.ts +62 -0
- package/dist/components/particle-emitter.d.ts.map +1 -0
- package/dist/components/particle-emitter.js +193 -0
- package/dist/components/particle-emitter.js.map +1 -0
- package/dist/components/pursue.d.ts +23 -0
- package/dist/components/pursue.d.ts.map +1 -0
- package/dist/components/pursue.js +96 -0
- package/dist/components/pursue.js.map +1 -0
- package/dist/components/ranged-attack.d.ts +44 -0
- package/dist/components/ranged-attack.d.ts.map +1 -0
- package/dist/components/ranged-attack.js +120 -0
- package/dist/components/ranged-attack.js.map +1 -0
- package/dist/components/sprite.d.ts +27 -0
- package/dist/components/sprite.d.ts.map +1 -0
- package/dist/components/sprite.js +122 -0
- package/dist/components/sprite.js.map +1 -0
- package/dist/components/transform.d.ts +30 -0
- package/dist/components/transform.d.ts.map +1 -0
- package/dist/components/transform.js +150 -0
- package/dist/components/transform.js.map +1 -0
- package/dist/director/director-bridge.d.ts +22 -0
- package/dist/director/director-bridge.d.ts.map +1 -0
- package/dist/director/director-bridge.js +23 -0
- package/dist/director/director-bridge.js.map +1 -0
- package/dist/director/director-encounter-system.d.ts +21 -0
- package/dist/director/director-encounter-system.d.ts.map +1 -0
- package/dist/director/director-encounter-system.js +128 -0
- package/dist/director/director-encounter-system.js.map +1 -0
- package/dist/director/director-system.d.ts +19 -0
- package/dist/director/director-system.d.ts.map +1 -0
- package/dist/director/director-system.js +179 -0
- package/dist/director/director-system.js.map +1 -0
- package/dist/director/event-envelope.d.ts +144 -0
- package/dist/director/event-envelope.d.ts.map +1 -0
- package/dist/director/event-envelope.js +108 -0
- package/dist/director/event-envelope.js.map +1 -0
- package/dist/director/knot-context-resource.d.ts +25 -0
- package/dist/director/knot-context-resource.d.ts.map +1 -0
- package/dist/director/knot-context-resource.js +152 -0
- package/dist/director/knot-context-resource.js.map +1 -0
- package/dist/director/mock-director-bridge.d.ts +18 -0
- package/dist/director/mock-director-bridge.d.ts.map +1 -0
- package/dist/director/mock-director-bridge.js +75 -0
- package/dist/director/mock-director-bridge.js.map +1 -0
- package/dist/director/snapshot-recovery.d.ts +37 -0
- package/dist/director/snapshot-recovery.d.ts.map +1 -0
- package/dist/director/snapshot-recovery.js +180 -0
- package/dist/director/snapshot-recovery.js.map +1 -0
- package/dist/director/sse-director-bridge.d.ts +42 -0
- package/dist/director/sse-director-bridge.d.ts.map +1 -0
- package/dist/director/sse-director-bridge.js +280 -0
- package/dist/director/sse-director-bridge.js.map +1 -0
- package/dist/engine.d.ts +25 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +166 -0
- package/dist/engine.js.map +1 -0
- package/dist/entity.d.ts +17 -0
- package/dist/entity.d.ts.map +1 -0
- package/dist/entity.js +77 -0
- package/dist/entity.js.map +1 -0
- package/dist/index.d.ts +85 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +64 -0
- package/dist/index.js.map +1 -0
- package/dist/input/input-manager.d.ts +91 -0
- package/dist/input/input-manager.d.ts.map +1 -0
- package/dist/input/input-manager.js +349 -0
- package/dist/input/input-manager.js.map +1 -0
- package/dist/input/tap-to-walk.d.ts +27 -0
- package/dist/input/tap-to-walk.d.ts.map +1 -0
- package/dist/input/tap-to-walk.js +118 -0
- package/dist/input/tap-to-walk.js.map +1 -0
- package/dist/input/virtual-dpad.d.ts +34 -0
- package/dist/input/virtual-dpad.d.ts.map +1 -0
- package/dist/input/virtual-dpad.js +267 -0
- package/dist/input/virtual-dpad.js.map +1 -0
- package/dist/renderer/camera.d.ts +26 -0
- package/dist/renderer/camera.d.ts.map +1 -0
- package/dist/renderer/camera.js +39 -0
- package/dist/renderer/camera.js.map +1 -0
- package/dist/renderer/canvas2d-device.d.ts +26 -0
- package/dist/renderer/canvas2d-device.d.ts.map +1 -0
- package/dist/renderer/canvas2d-device.js +252 -0
- package/dist/renderer/canvas2d-device.js.map +1 -0
- package/dist/renderer/graphics-device.d.ts +36 -0
- package/dist/renderer/graphics-device.d.ts.map +1 -0
- package/dist/renderer/graphics-device.js +12 -0
- package/dist/renderer/graphics-device.js.map +1 -0
- package/dist/renderer/iso-projection.d.ts +11 -0
- package/dist/renderer/iso-projection.d.ts.map +1 -0
- package/dist/renderer/iso-projection.js +59 -0
- package/dist/renderer/iso-projection.js.map +1 -0
- package/dist/resources.d.ts +28 -0
- package/dist/resources.d.ts.map +1 -0
- package/dist/resources.js +53 -0
- package/dist/resources.js.map +1 -0
- package/dist/system.d.ts +14 -0
- package/dist/system.d.ts.map +1 -0
- package/dist/system.js +25 -0
- package/dist/system.js.map +1 -0
- package/dist/systems/animation-system.d.ts +8 -0
- package/dist/systems/animation-system.d.ts.map +1 -0
- package/dist/systems/animation-system.js +77 -0
- package/dist/systems/animation-system.js.map +1 -0
- package/dist/systems/attack-system.d.ts +17 -0
- package/dist/systems/attack-system.d.ts.map +1 -0
- package/dist/systems/attack-system.js +94 -0
- package/dist/systems/attack-system.js.map +1 -0
- package/dist/systems/damage-system.d.ts +18 -0
- package/dist/systems/damage-system.d.ts.map +1 -0
- package/dist/systems/damage-system.js +77 -0
- package/dist/systems/damage-system.js.map +1 -0
- package/dist/systems/input-system.d.ts +7 -0
- package/dist/systems/input-system.d.ts.map +1 -0
- package/dist/systems/input-system.js +27 -0
- package/dist/systems/input-system.js.map +1 -0
- package/dist/systems/interaction-system.d.ts +23 -0
- package/dist/systems/interaction-system.d.ts.map +1 -0
- package/dist/systems/interaction-system.js +120 -0
- package/dist/systems/interaction-system.js.map +1 -0
- package/dist/systems/particle-emitter-system.d.ts +8 -0
- package/dist/systems/particle-emitter-system.d.ts.map +1 -0
- package/dist/systems/particle-emitter-system.js +161 -0
- package/dist/systems/particle-emitter-system.js.map +1 -0
- package/dist/systems/particle-render-system.d.ts +7 -0
- package/dist/systems/particle-render-system.d.ts.map +1 -0
- package/dist/systems/particle-render-system.js +53 -0
- package/dist/systems/particle-render-system.js.map +1 -0
- package/dist/systems/particle-simulation-system.d.ts +8 -0
- package/dist/systems/particle-simulation-system.d.ts.map +1 -0
- package/dist/systems/particle-simulation-system.js +45 -0
- package/dist/systems/particle-simulation-system.js.map +1 -0
- package/dist/systems/projectile-render-system.d.ts +7 -0
- package/dist/systems/projectile-render-system.d.ts.map +1 -0
- package/dist/systems/projectile-render-system.js +35 -0
- package/dist/systems/projectile-render-system.js.map +1 -0
- package/dist/systems/projectile-system.d.ts +7 -0
- package/dist/systems/projectile-system.d.ts.map +1 -0
- package/dist/systems/projectile-system.js +114 -0
- package/dist/systems/projectile-system.js.map +1 -0
- package/dist/systems/pursue-system.d.ts +7 -0
- package/dist/systems/pursue-system.d.ts.map +1 -0
- package/dist/systems/pursue-system.js +83 -0
- package/dist/systems/pursue-system.js.map +1 -0
- package/dist/systems/ranged-attack-system.d.ts +7 -0
- package/dist/systems/ranged-attack-system.d.ts.map +1 -0
- package/dist/systems/ranged-attack-system.js +98 -0
- package/dist/systems/ranged-attack-system.js.map +1 -0
- package/dist/systems/sprite-render-system.d.ts +9 -0
- package/dist/systems/sprite-render-system.d.ts.map +1 -0
- package/dist/systems/sprite-render-system.js +108 -0
- package/dist/systems/sprite-render-system.js.map +1 -0
- package/dist/systems/veil-budget-system.d.ts +7 -0
- package/dist/systems/veil-budget-system.d.ts.map +1 -0
- package/dist/systems/veil-budget-system.js +37 -0
- package/dist/systems/veil-budget-system.js.map +1 -0
- package/dist/util/color.d.ts +19 -0
- package/dist/util/color.d.ts.map +1 -0
- package/dist/util/color.js +45 -0
- package/dist/util/color.js.map +1 -0
- package/dist/util/math.d.ts +26 -0
- package/dist/util/math.d.ts.map +1 -0
- package/dist/util/math.js +47 -0
- package/dist/util/math.js.map +1 -0
- package/dist/util/typed-arrays.d.ts +7 -0
- package/dist/util/typed-arrays.d.ts.map +1 -0
- package/dist/util/typed-arrays.js +42 -0
- package/dist/util/typed-arrays.js.map +1 -0
- package/dist/vfx/particle-pool.d.ts +61 -0
- package/dist/vfx/particle-pool.d.ts.map +1 -0
- package/dist/vfx/particle-pool.js +204 -0
- package/dist/vfx/particle-pool.js.map +1 -0
- package/dist/vfx/projectile-pool.d.ts +56 -0
- package/dist/vfx/projectile-pool.d.ts.map +1 -0
- package/dist/vfx/projectile-pool.js +157 -0
- package/dist/vfx/projectile-pool.js.map +1 -0
- package/dist/world.d.ts +23 -0
- package/dist/world.d.ts.map +1 -0
- package/dist/world.js +101 -0
- package/dist/world.js.map +1 -0
- package/dist/zone/zone-catalog.d.ts +17 -0
- package/dist/zone/zone-catalog.d.ts.map +1 -0
- package/dist/zone/zone-catalog.js +116 -0
- package/dist/zone/zone-catalog.js.map +1 -0
- package/dist/zone/zone-state.d.ts +18 -0
- package/dist/zone/zone-state.d.ts.map +1 -0
- package/dist/zone/zone-state.js +52 -0
- package/dist/zone/zone-state.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
// VirtualDpad - on-screen 4-direction touch overlay that injects WASD
|
|
2
|
+
// key events into an InputManager. Lets every input-consuming system
|
|
3
|
+
// (movement, interaction, menu nav) keep reading keysHeld with no
|
|
4
|
+
// branching for "is this a touch device?" - the keys are already there.
|
|
5
|
+
//
|
|
6
|
+
// Layout: bottom-left of the viewport, a 3x3 grid where the four
|
|
7
|
+
// edge cells are directional buttons (up = KeyW, down = KeyS,
|
|
8
|
+
// left = KeyA, right = KeyD) and the center cell is a deadzone /
|
|
9
|
+
// label. Corners are inert. The grid is fixed-position so it stays
|
|
10
|
+
// pinned regardless of page scroll.
|
|
11
|
+
//
|
|
12
|
+
// Multi-touch: each direction tracks the set of touch identifiers
|
|
13
|
+
// currently pressing it. KeyDown injects only when the count goes
|
|
14
|
+
// 0->1; KeyUp injects only when it returns to 0. Fingers can slide
|
|
15
|
+
// off (touchcancel) without leaving a phantom held key.
|
|
16
|
+
//
|
|
17
|
+
// Visibility: by default the overlay only mounts when
|
|
18
|
+
// `'ontouchstart' in window` is true (or navigator.maxTouchPoints > 0).
|
|
19
|
+
// Callers can override via opts.visible for tests / forced display.
|
|
20
|
+
const KEY_FOR_DIRECTION = {
|
|
21
|
+
up: 'KeyW',
|
|
22
|
+
down: 'KeyS',
|
|
23
|
+
left: 'KeyA',
|
|
24
|
+
right: 'KeyD',
|
|
25
|
+
};
|
|
26
|
+
export class VirtualDpad {
|
|
27
|
+
opts;
|
|
28
|
+
root = null;
|
|
29
|
+
buttons = null;
|
|
30
|
+
mounted = false;
|
|
31
|
+
visible;
|
|
32
|
+
constructor(opts) {
|
|
33
|
+
this.opts = opts;
|
|
34
|
+
this.visible = opts.visible ?? VirtualDpad.detectTouchSupport();
|
|
35
|
+
}
|
|
36
|
+
// Heuristic: any modern phone / tablet exposes ontouchstart on
|
|
37
|
+
// window. iPad with iPadOS 13+ pretends to be desktop Safari but
|
|
38
|
+
// still reports navigator.maxTouchPoints > 0. Either signal is
|
|
39
|
+
// enough to consider the device touch-capable.
|
|
40
|
+
static detectTouchSupport(win) {
|
|
41
|
+
const w = win ?? (typeof window !== 'undefined' ? window : undefined);
|
|
42
|
+
if (!w)
|
|
43
|
+
return false;
|
|
44
|
+
if ('ontouchstart' in w)
|
|
45
|
+
return true;
|
|
46
|
+
const nav = w.navigator;
|
|
47
|
+
if (nav && typeof nav.maxTouchPoints === 'number' && nav.maxTouchPoints > 0)
|
|
48
|
+
return true;
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
isMounted() {
|
|
52
|
+
return this.mounted;
|
|
53
|
+
}
|
|
54
|
+
isVisible() {
|
|
55
|
+
return this.visible;
|
|
56
|
+
}
|
|
57
|
+
// Attach the overlay DOM. No-op on desktop (visible=false), so the
|
|
58
|
+
// caller can mount unconditionally and let the class decide.
|
|
59
|
+
mount() {
|
|
60
|
+
if (this.mounted)
|
|
61
|
+
return;
|
|
62
|
+
if (!this.visible)
|
|
63
|
+
return;
|
|
64
|
+
const doc = this.opts.document ?? (typeof document !== 'undefined' ? document : undefined);
|
|
65
|
+
if (!doc)
|
|
66
|
+
return;
|
|
67
|
+
const parent = this.opts.parent ?? doc.body;
|
|
68
|
+
if (!parent)
|
|
69
|
+
return;
|
|
70
|
+
const root = doc.createElement('div');
|
|
71
|
+
root.className = 'loom-virtual-dpad';
|
|
72
|
+
this.styleRoot(root);
|
|
73
|
+
const buttons = {
|
|
74
|
+
up: this.buildButton(doc, 'up', '▲'),
|
|
75
|
+
down: this.buildButton(doc, 'down', '▼'),
|
|
76
|
+
left: this.buildButton(doc, 'left', '◀'),
|
|
77
|
+
right: this.buildButton(doc, 'right', '▶'),
|
|
78
|
+
};
|
|
79
|
+
// 3x3 grid. Corners are inert spacers - cleaner press surface
|
|
80
|
+
// than a single round pad and easier to thumb on phones.
|
|
81
|
+
const grid = doc.createElement('div');
|
|
82
|
+
this.styleGrid(grid);
|
|
83
|
+
grid.appendChild(this.spacer(doc));
|
|
84
|
+
grid.appendChild(buttons.up.el);
|
|
85
|
+
grid.appendChild(this.spacer(doc));
|
|
86
|
+
grid.appendChild(buttons.left.el);
|
|
87
|
+
grid.appendChild(this.deadzone(doc));
|
|
88
|
+
grid.appendChild(buttons.right.el);
|
|
89
|
+
grid.appendChild(this.spacer(doc));
|
|
90
|
+
grid.appendChild(buttons.down.el);
|
|
91
|
+
grid.appendChild(this.spacer(doc));
|
|
92
|
+
root.appendChild(grid);
|
|
93
|
+
parent.appendChild(root);
|
|
94
|
+
this.root = root;
|
|
95
|
+
this.buttons = buttons;
|
|
96
|
+
this.attachListeners();
|
|
97
|
+
this.mounted = true;
|
|
98
|
+
}
|
|
99
|
+
// Remove the overlay and release any held WASD keys it had injected.
|
|
100
|
+
unmount() {
|
|
101
|
+
if (!this.mounted)
|
|
102
|
+
return;
|
|
103
|
+
if (this.buttons) {
|
|
104
|
+
for (const dir of Object.keys(this.buttons)) {
|
|
105
|
+
const b = this.buttons[dir];
|
|
106
|
+
if (b.active.size > 0) {
|
|
107
|
+
b.active.clear();
|
|
108
|
+
this.opts.inputManager.injectKeyUp(KEY_FOR_DIRECTION[dir]);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (this.root && this.root.parentNode) {
|
|
113
|
+
this.root.parentNode.removeChild(this.root);
|
|
114
|
+
}
|
|
115
|
+
this.root = null;
|
|
116
|
+
this.buttons = null;
|
|
117
|
+
this.mounted = false;
|
|
118
|
+
}
|
|
119
|
+
// Synthetic press / release for tests. Bypasses DOM listeners but
|
|
120
|
+
// exercises the same accounting as a real touch.
|
|
121
|
+
pressDirection(dir, touchId = -1) {
|
|
122
|
+
const b = this.requireButton(dir);
|
|
123
|
+
const wasActive = b.active.size > 0;
|
|
124
|
+
b.active.add(touchId);
|
|
125
|
+
if (!wasActive) {
|
|
126
|
+
this.opts.inputManager.injectKeyDown(KEY_FOR_DIRECTION[dir]);
|
|
127
|
+
if (this.opts.onPress)
|
|
128
|
+
this.opts.onPress(dir);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
releaseDirection(dir, touchId = -1) {
|
|
132
|
+
const b = this.requireButton(dir);
|
|
133
|
+
if (!b.active.delete(touchId))
|
|
134
|
+
return;
|
|
135
|
+
if (b.active.size === 0) {
|
|
136
|
+
this.opts.inputManager.injectKeyUp(KEY_FOR_DIRECTION[dir]);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// Number of unique touch IDs currently pressing a direction. Test
|
|
140
|
+
// helper; production code should read InputManager.snapshot().
|
|
141
|
+
pressedTouchCount(dir) {
|
|
142
|
+
if (!this.buttons)
|
|
143
|
+
return 0;
|
|
144
|
+
return this.buttons[dir].active.size;
|
|
145
|
+
}
|
|
146
|
+
requireButton(dir) {
|
|
147
|
+
if (!this.buttons) {
|
|
148
|
+
// pressDirection / releaseDirection used before mount. Build a
|
|
149
|
+
// headless button record so tests can drive synthetic presses
|
|
150
|
+
// without DOM mounting.
|
|
151
|
+
const headless = {
|
|
152
|
+
up: { el: null, active: new Set() },
|
|
153
|
+
down: { el: null, active: new Set() },
|
|
154
|
+
left: { el: null, active: new Set() },
|
|
155
|
+
right: { el: null, active: new Set() },
|
|
156
|
+
};
|
|
157
|
+
this.buttons = headless;
|
|
158
|
+
}
|
|
159
|
+
return this.buttons[dir];
|
|
160
|
+
}
|
|
161
|
+
buildButton(doc, dir, glyph) {
|
|
162
|
+
const el = doc.createElement('div');
|
|
163
|
+
el.className = 'loom-virtual-dpad-btn loom-virtual-dpad-' + dir;
|
|
164
|
+
el.textContent = glyph;
|
|
165
|
+
el.setAttribute('role', 'button');
|
|
166
|
+
el.setAttribute('aria-label', 'Move ' + dir);
|
|
167
|
+
this.styleButton(el);
|
|
168
|
+
return { el, active: new Set() };
|
|
169
|
+
}
|
|
170
|
+
spacer(doc) {
|
|
171
|
+
const el = doc.createElement('div');
|
|
172
|
+
el.className = 'loom-virtual-dpad-spacer';
|
|
173
|
+
el.style.pointerEvents = 'none';
|
|
174
|
+
return el;
|
|
175
|
+
}
|
|
176
|
+
deadzone(doc) {
|
|
177
|
+
const el = doc.createElement('div');
|
|
178
|
+
el.className = 'loom-virtual-dpad-deadzone';
|
|
179
|
+
el.style.pointerEvents = 'none';
|
|
180
|
+
el.style.display = 'flex';
|
|
181
|
+
el.style.alignItems = 'center';
|
|
182
|
+
el.style.justifyContent = 'center';
|
|
183
|
+
el.style.color = 'rgba(214, 198, 148, 0.45)';
|
|
184
|
+
el.style.fontSize = '10px';
|
|
185
|
+
el.style.letterSpacing = '0.08em';
|
|
186
|
+
el.style.textTransform = 'uppercase';
|
|
187
|
+
el.style.fontFamily = 'ui-monospace, Consolas, monospace';
|
|
188
|
+
el.textContent = 'move';
|
|
189
|
+
return el;
|
|
190
|
+
}
|
|
191
|
+
styleRoot(root) {
|
|
192
|
+
const s = root.style;
|
|
193
|
+
s.position = 'fixed';
|
|
194
|
+
// env() falls back to 12px when safe-area-inset is unsupported.
|
|
195
|
+
s.bottom = 'calc(12px + env(safe-area-inset-bottom, 0px))';
|
|
196
|
+
s.left = 'calc(12px + env(safe-area-inset-left, 0px))';
|
|
197
|
+
s.zIndex = '9000';
|
|
198
|
+
s.userSelect = 'none';
|
|
199
|
+
s.touchAction = 'none';
|
|
200
|
+
s.setProperty('-webkit-user-select', 'none');
|
|
201
|
+
}
|
|
202
|
+
styleGrid(grid) {
|
|
203
|
+
const s = grid.style;
|
|
204
|
+
s.display = 'grid';
|
|
205
|
+
s.gridTemplateColumns = '56px 56px 56px';
|
|
206
|
+
s.gridTemplateRows = '56px 56px 56px';
|
|
207
|
+
s.gap = '4px';
|
|
208
|
+
}
|
|
209
|
+
styleButton(btn) {
|
|
210
|
+
const s = btn.style;
|
|
211
|
+
s.display = 'flex';
|
|
212
|
+
s.alignItems = 'center';
|
|
213
|
+
s.justifyContent = 'center';
|
|
214
|
+
s.background = 'rgba(20, 17, 13, 0.72)';
|
|
215
|
+
s.border = '1px solid rgba(214, 198, 148, 0.35)';
|
|
216
|
+
s.borderRadius = '6px';
|
|
217
|
+
s.color = '#d6c694';
|
|
218
|
+
s.fontSize = '20px';
|
|
219
|
+
s.fontWeight = '700';
|
|
220
|
+
s.touchAction = 'none';
|
|
221
|
+
s.cursor = 'pointer';
|
|
222
|
+
s.userSelect = 'none';
|
|
223
|
+
s.setProperty('-webkit-user-select', 'none');
|
|
224
|
+
s.setProperty('-webkit-tap-highlight-color', 'transparent');
|
|
225
|
+
}
|
|
226
|
+
attachListeners() {
|
|
227
|
+
if (!this.buttons)
|
|
228
|
+
return;
|
|
229
|
+
const dirs = ['up', 'down', 'left', 'right'];
|
|
230
|
+
for (const dir of dirs) {
|
|
231
|
+
const b = this.buttons[dir];
|
|
232
|
+
// touchstart / touchend / touchcancel cover the touch case.
|
|
233
|
+
// mousedown / mouseup are added too so a desktop tester with a
|
|
234
|
+
// visible pad (opts.visible = true) can drive it from a mouse.
|
|
235
|
+
b.el.addEventListener('touchstart', (e) => {
|
|
236
|
+
const te = e;
|
|
237
|
+
for (let i = 0; i < te.changedTouches.length; i++) {
|
|
238
|
+
const t = te.changedTouches[i];
|
|
239
|
+
if (t)
|
|
240
|
+
this.pressDirection(dir, t.identifier);
|
|
241
|
+
}
|
|
242
|
+
te.preventDefault();
|
|
243
|
+
}, { passive: false });
|
|
244
|
+
const endHandler = (e) => {
|
|
245
|
+
const te = e;
|
|
246
|
+
for (let i = 0; i < te.changedTouches.length; i++) {
|
|
247
|
+
const t = te.changedTouches[i];
|
|
248
|
+
if (t)
|
|
249
|
+
this.releaseDirection(dir, t.identifier);
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
b.el.addEventListener('touchend', endHandler);
|
|
253
|
+
b.el.addEventListener('touchcancel', endHandler);
|
|
254
|
+
b.el.addEventListener('mousedown', (e) => {
|
|
255
|
+
this.pressDirection(dir, -1);
|
|
256
|
+
e.preventDefault();
|
|
257
|
+
});
|
|
258
|
+
// mouseup is on window so a press-then-drag-off-and-release
|
|
259
|
+
// doesn't leave a phantom held key.
|
|
260
|
+
const win = (typeof window !== 'undefined' ? window : undefined);
|
|
261
|
+
if (win) {
|
|
262
|
+
win.addEventListener('mouseup', () => this.releaseDirection(dir, -1));
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
//# sourceMappingURL=virtual-dpad.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"virtual-dpad.js","sourceRoot":"","sources":["../../src/input/virtual-dpad.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Rect } from '../util/math.js';
|
|
2
|
+
export interface CameraView {
|
|
3
|
+
centerX: number;
|
|
4
|
+
centerY: number;
|
|
5
|
+
zoom: number;
|
|
6
|
+
rotation: number;
|
|
7
|
+
viewportWidth: number;
|
|
8
|
+
viewportHeight: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function createCamera(viewportWidth: number, viewportHeight: number): CameraView;
|
|
11
|
+
export declare function getCameraViewRect(cam: CameraView, out: Rect): Rect;
|
|
12
|
+
export declare function worldToScreen(cam: CameraView, worldX: number, worldY: number, out: {
|
|
13
|
+
x: number;
|
|
14
|
+
y: number;
|
|
15
|
+
}): {
|
|
16
|
+
x: number;
|
|
17
|
+
y: number;
|
|
18
|
+
};
|
|
19
|
+
export declare function screenToWorld(cam: CameraView, screenX: number, screenY: number, out: {
|
|
20
|
+
x: number;
|
|
21
|
+
y: number;
|
|
22
|
+
}): {
|
|
23
|
+
x: number;
|
|
24
|
+
y: number;
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=camera.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"camera.d.ts","sourceRoot":"","sources":["../../src/renderer/camera.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C,MAAM,WAAW,UAAU;IAEzB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAEhB,IAAI,EAAE,MAAM,CAAC;IAEb,QAAQ,EAAE,MAAM,CAAC;IAEjB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,YAAY,CAAC,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,UAAU,CAStF;AAID,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,GAAG,IAAI,CAQlE;AAID,wBAAgB,aAAa,CAC3B,GAAG,EAAE,UAAU,EACf,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,GAAG,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5B;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAI1B;AAED,wBAAgB,aAAa,CAC3B,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,GAAG,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5B;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAI1B"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Camera for the Loom Engine.
|
|
2
|
+
//
|
|
3
|
+
// 2D camera with optional zoom + rotation. Iso projection happens
|
|
4
|
+
// in iso-projection.ts; this camera produces the screen-space view
|
|
5
|
+
// rect that systems use for frustum culling.
|
|
6
|
+
export function createCamera(viewportWidth, viewportHeight) {
|
|
7
|
+
return {
|
|
8
|
+
centerX: 0,
|
|
9
|
+
centerY: 0,
|
|
10
|
+
zoom: 1,
|
|
11
|
+
rotation: 0,
|
|
12
|
+
viewportWidth,
|
|
13
|
+
viewportHeight,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
// World-space rect that the camera currently sees. Used for frustum
|
|
17
|
+
// culling. Rotation is ignored in v1 (engine ships axis-aligned).
|
|
18
|
+
export function getCameraViewRect(cam, out) {
|
|
19
|
+
const halfW = cam.viewportWidth / cam.zoom / 2;
|
|
20
|
+
const halfH = cam.viewportHeight / cam.zoom / 2;
|
|
21
|
+
out.x = cam.centerX - halfW;
|
|
22
|
+
out.y = cam.centerY - halfH;
|
|
23
|
+
out.width = halfW * 2;
|
|
24
|
+
out.height = halfH * 2;
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
// World-space coords -> screen-space coords. Iso transform happens
|
|
28
|
+
// before this; this is the pure camera transform.
|
|
29
|
+
export function worldToScreen(cam, worldX, worldY, out) {
|
|
30
|
+
out.x = (worldX - cam.centerX) * cam.zoom + cam.viewportWidth / 2;
|
|
31
|
+
out.y = (worldY - cam.centerY) * cam.zoom + cam.viewportHeight / 2;
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
export function screenToWorld(cam, screenX, screenY, out) {
|
|
35
|
+
out.x = (screenX - cam.viewportWidth / 2) / cam.zoom + cam.centerX;
|
|
36
|
+
out.y = (screenY - cam.viewportHeight / 2) / cam.zoom + cam.centerY;
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=camera.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"camera.js","sourceRoot":"","sources":["../../src/renderer/camera.ts"],"names":[],"mappings":"AAAA,8BAA8B;AAC9B,EAAE;AACF,kEAAkE;AAClE,mEAAmE;AACnE,6CAA6C;AAiB7C,MAAM,UAAU,YAAY,CAAC,aAAqB,EAAE,cAAsB;IACxE,OAAO;QACL,OAAO,EAAE,CAAC;QACV,OAAO,EAAE,CAAC;QACV,IAAI,EAAE,CAAC;QACP,QAAQ,EAAE,CAAC;QACX,aAAa;QACb,cAAc;KACf,CAAC;AACJ,CAAC;AAED,oEAAoE;AACpE,kEAAkE;AAClE,MAAM,UAAU,iBAAiB,CAAC,GAAe,EAAE,GAAS;IAC1D,MAAM,KAAK,GAAG,GAAG,CAAC,aAAa,GAAG,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC;IAC/C,MAAM,KAAK,GAAG,GAAG,CAAC,cAAc,GAAG,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC;IAChD,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,OAAO,GAAG,KAAK,CAAC;IAC5B,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,OAAO,GAAG,KAAK,CAAC;IAC5B,GAAG,CAAC,KAAK,GAAG,KAAK,GAAG,CAAC,CAAC;IACtB,GAAG,CAAC,MAAM,GAAG,KAAK,GAAG,CAAC,CAAC;IACvB,OAAO,GAAG,CAAC;AACb,CAAC;AAED,mEAAmE;AACnE,kDAAkD;AAClD,MAAM,UAAU,aAAa,CAC3B,GAAe,EACf,MAAc,EACd,MAAc,EACd,GAA6B;IAE7B,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,aAAa,GAAG,CAAC,CAAC;IAClE,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,cAAc,GAAG,CAAC,CAAC;IACnE,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,GAAe,EACf,OAAe,EACf,OAAe,EACf,GAA6B;IAE7B,GAAG,CAAC,CAAC,GAAG,CAAC,OAAO,GAAG,GAAG,CAAC,aAAa,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC;IACnE,GAAG,CAAC,CAAC,GAAG,CAAC,OAAO,GAAG,GAAG,CAAC,cAAc,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC;IACpE,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ColorRGBA } from '../util/color.js';
|
|
2
|
+
import { type IGraphicsDevice, type AtlasHandle, type AtlasDescriptor, type TextStyle } from './graphics-device.js';
|
|
3
|
+
import { type CameraView } from './camera.js';
|
|
4
|
+
export declare class Canvas2DDevice implements IGraphicsDevice {
|
|
5
|
+
readonly canvas: HTMLCanvasElement;
|
|
6
|
+
readonly viewportWidth: number;
|
|
7
|
+
readonly viewportHeight: number;
|
|
8
|
+
private ctx;
|
|
9
|
+
private atlases;
|
|
10
|
+
private nextAtlasHandle;
|
|
11
|
+
private camera;
|
|
12
|
+
private drawCallCount;
|
|
13
|
+
private particleDisc;
|
|
14
|
+
constructor(canvas: HTMLCanvasElement);
|
|
15
|
+
beginFrame(): void;
|
|
16
|
+
endFrame(): void;
|
|
17
|
+
setCamera(cam: Readonly<CameraView>): void;
|
|
18
|
+
registerAtlas(desc: AtlasDescriptor): AtlasHandle;
|
|
19
|
+
releaseAtlas(handle: AtlasHandle): void;
|
|
20
|
+
drawSprite(worldX: number, worldY: number, worldZ: number, atlas: AtlasHandle, frame: number, tint?: Readonly<ColorRGBA>): void;
|
|
21
|
+
drawTile(tileX: number, tileY: number, atlas: AtlasHandle, frame: number): void;
|
|
22
|
+
drawText(worldX: number, worldY: number, text: string, style: TextStyle): void;
|
|
23
|
+
drawParticle(worldX: number, worldY: number, worldZ: number, size: number, color: Readonly<ColorRGBA>, additive: boolean): void;
|
|
24
|
+
getDrawCallCount(): number;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=canvas2d-device.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvas2d-device.d.ts","sourceRoot":"","sources":["../../src/renderer/canvas2d-device.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,SAAS,EACf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,UAAU,EAEhB,MAAM,aAAa,CAAC;AA0CrB,qBAAa,cAAe,YAAW,eAAe;IACpD,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;IACnC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAEhC,OAAO,CAAC,GAAG,CAA2B;IACtC,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,eAAe,CAAa;IACpC,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,aAAa,CAAa;IAElC,OAAO,CAAC,YAAY,CAAkC;gBAE1C,MAAM,EAAE,iBAAiB;IAcrC,UAAU,IAAI,IAAI;IAQlB,QAAQ,IAAI,IAAI;IAIhB,SAAS,CAAC,GAAG,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,IAAI;IAM1C,aAAa,CAAC,IAAI,EAAE,eAAe,GAAG,WAAW;IAMjD,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAQvC,UAAU,CACR,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,WAAW,EAClB,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,QAAQ,CAAC,SAAS,CAAC,GACzB,IAAI;IA8CP,QAAQ,CACN,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,WAAW,EAClB,KAAK,EAAE,MAAM,GACZ,IAAI;IA4BP,QAAQ,CACN,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS,GACf,IAAI;IAoBP,YAAY,CACV,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,QAAQ,CAAC,SAAS,CAAC,EAC1B,QAAQ,EAAE,OAAO,GAChB,IAAI;IAsEP,gBAAgB,IAAI,MAAM;CAG3B"}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
// Canvas2D backend for the Loom Engine.
|
|
2
|
+
//
|
|
3
|
+
// Primary backend for v1. Iso world coords -> screen coords happens
|
|
4
|
+
// inside the device. drawImage is the workhorse; sorting is the
|
|
5
|
+
// caller's responsibility (the render-graph stage submits sprites
|
|
6
|
+
// in depth-sort order).
|
|
7
|
+
//
|
|
8
|
+
// Babylon.js inspires the ThinEngine split: this file is the lean
|
|
9
|
+
// GPU-talking core. Higher-level scene logic lives elsewhere. See
|
|
10
|
+
// PRIOR-ART.md.
|
|
11
|
+
import { rgbaToCssString } from '../util/color.js';
|
|
12
|
+
import { worldToScreen, } from './camera.js';
|
|
13
|
+
import { ISO_HALF_W, ISO_HALF_H, ISO_Z_SCALE, } from './iso-projection.js';
|
|
14
|
+
const SCRATCH_VEC2 = { x: 0, y: 0 };
|
|
15
|
+
// Pre-baked particle disc - white-to-transparent radial gradient at
|
|
16
|
+
// 64px diameter on an offscreen canvas. drawParticle uses this with
|
|
17
|
+
// drawImage instead of allocating a fresh CanvasGradient per call.
|
|
18
|
+
// Additive blend with globalAlpha = color.a gives correct brightness
|
|
19
|
+
// per particle. For non-additive tinted particles the gradient path
|
|
20
|
+
// is preserved as a fallback (RGB tinting under straight alpha
|
|
21
|
+
// requires a tinted disc per color, which would need a per-color
|
|
22
|
+
// cache; the demo + Phase 4 emitters are all additive so this fast
|
|
23
|
+
// path covers the hot case).
|
|
24
|
+
const PARTICLE_DISC_SIZE = 64;
|
|
25
|
+
function bakeParticleDisc() {
|
|
26
|
+
if (typeof document === 'undefined')
|
|
27
|
+
return null;
|
|
28
|
+
const c = document.createElement('canvas');
|
|
29
|
+
c.width = PARTICLE_DISC_SIZE;
|
|
30
|
+
c.height = PARTICLE_DISC_SIZE;
|
|
31
|
+
const ctx = c.getContext('2d');
|
|
32
|
+
if (!ctx)
|
|
33
|
+
return null;
|
|
34
|
+
const center = PARTICLE_DISC_SIZE / 2;
|
|
35
|
+
const grad = ctx.createRadialGradient(center, center, 0, center, center, center);
|
|
36
|
+
grad.addColorStop(0, 'rgba(255,255,255,1)');
|
|
37
|
+
grad.addColorStop(1, 'rgba(255,255,255,0)');
|
|
38
|
+
ctx.fillStyle = grad;
|
|
39
|
+
ctx.beginPath();
|
|
40
|
+
ctx.arc(center, center, center, 0, Math.PI * 2);
|
|
41
|
+
ctx.fill();
|
|
42
|
+
return c;
|
|
43
|
+
}
|
|
44
|
+
export class Canvas2DDevice {
|
|
45
|
+
canvas;
|
|
46
|
+
viewportWidth;
|
|
47
|
+
viewportHeight;
|
|
48
|
+
ctx;
|
|
49
|
+
atlases = [];
|
|
50
|
+
nextAtlasHandle = 0;
|
|
51
|
+
camera = null;
|
|
52
|
+
drawCallCount = 0;
|
|
53
|
+
// Lazy-initialized; null in headless / non-DOM contexts.
|
|
54
|
+
particleDisc = null;
|
|
55
|
+
constructor(canvas) {
|
|
56
|
+
this.canvas = canvas;
|
|
57
|
+
this.viewportWidth = canvas.width;
|
|
58
|
+
this.viewportHeight = canvas.height;
|
|
59
|
+
const ctx = canvas.getContext('2d', { alpha: false });
|
|
60
|
+
if (!ctx) {
|
|
61
|
+
throw new Error('Canvas2DDevice: failed to acquire 2D context');
|
|
62
|
+
}
|
|
63
|
+
this.ctx = ctx;
|
|
64
|
+
// Pixel-art friendly defaults. Future style configurability
|
|
65
|
+
// belongs in a device options object; v1 ships pixel-art-first.
|
|
66
|
+
this.ctx.imageSmoothingEnabled = false;
|
|
67
|
+
}
|
|
68
|
+
beginFrame() {
|
|
69
|
+
this.drawCallCount = 0;
|
|
70
|
+
// Clear to opaque black. Higher-level stages can paint a sky
|
|
71
|
+
// color in their own clear stage if they want a different bg.
|
|
72
|
+
this.ctx.fillStyle = '#000';
|
|
73
|
+
this.ctx.fillRect(0, 0, this.viewportWidth, this.viewportHeight);
|
|
74
|
+
}
|
|
75
|
+
endFrame() {
|
|
76
|
+
// Immediate-mode backend - nothing to flush.
|
|
77
|
+
}
|
|
78
|
+
setCamera(cam) {
|
|
79
|
+
// Store a reference rather than copying. The caller owns the
|
|
80
|
+
// camera object; we only read it inside draw* calls.
|
|
81
|
+
this.camera = cam;
|
|
82
|
+
}
|
|
83
|
+
registerAtlas(desc) {
|
|
84
|
+
const handle = this.nextAtlasHandle++;
|
|
85
|
+
this.atlases[handle] = { desc, released: false };
|
|
86
|
+
return handle;
|
|
87
|
+
}
|
|
88
|
+
releaseAtlas(handle) {
|
|
89
|
+
const slot = this.atlases[handle];
|
|
90
|
+
if (slot) {
|
|
91
|
+
slot.released = true;
|
|
92
|
+
this.atlases[handle] = null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
drawSprite(worldX, worldY, worldZ, atlas, frame, tint) {
|
|
96
|
+
const slot = this.atlases[atlas];
|
|
97
|
+
if (!slot || slot.released)
|
|
98
|
+
return;
|
|
99
|
+
const f = slot.desc.frames[frame];
|
|
100
|
+
if (!f)
|
|
101
|
+
return;
|
|
102
|
+
const cam = this.camera;
|
|
103
|
+
if (!cam)
|
|
104
|
+
return;
|
|
105
|
+
// Iso projection: world (x,y,z) -> iso (sx,sy) before camera.
|
|
106
|
+
const isoX = (worldX - worldY) * ISO_HALF_W;
|
|
107
|
+
const isoY = (worldX + worldY) * ISO_HALF_H - worldZ * ISO_Z_SCALE;
|
|
108
|
+
// Camera transform: iso world -> screen.
|
|
109
|
+
worldToScreen(cam, isoX, isoY, SCRATCH_VEC2);
|
|
110
|
+
const screenX = SCRATCH_VEC2.x;
|
|
111
|
+
const screenY = SCRATCH_VEC2.y;
|
|
112
|
+
// Draw centered horizontally, anchored at the bottom (sprite's
|
|
113
|
+
// feet on the iso projection point). This is the standard iso
|
|
114
|
+
// sprite anchor.
|
|
115
|
+
const dw = f.w * cam.zoom;
|
|
116
|
+
const dh = f.h * cam.zoom;
|
|
117
|
+
const dx = screenX - dw / 2;
|
|
118
|
+
const dy = screenY - dh;
|
|
119
|
+
if (tint) {
|
|
120
|
+
this.ctx.save();
|
|
121
|
+
this.ctx.globalAlpha = tint.a;
|
|
122
|
+
// Canvas2D doesn't tint drawImage natively. For pixel-perfect
|
|
123
|
+
// tint we'd need to pre-bake tinted variants in an offscreen
|
|
124
|
+
// canvas. For v1 we use globalAlpha + a colored overlay
|
|
125
|
+
// composite. Optimization deferred.
|
|
126
|
+
this.ctx.drawImage(slot.desc.image, f.x, f.y, f.w, f.h, dx, dy, dw, dh);
|
|
127
|
+
if (tint.r !== 1 || tint.g !== 1 || tint.b !== 1) {
|
|
128
|
+
this.ctx.globalCompositeOperation = 'multiply';
|
|
129
|
+
this.ctx.fillStyle = rgbaToCssString(tint);
|
|
130
|
+
this.ctx.fillRect(dx, dy, dw, dh);
|
|
131
|
+
}
|
|
132
|
+
this.ctx.restore();
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
this.ctx.drawImage(slot.desc.image, f.x, f.y, f.w, f.h, dx, dy, dw, dh);
|
|
136
|
+
}
|
|
137
|
+
this.drawCallCount++;
|
|
138
|
+
}
|
|
139
|
+
drawTile(tileX, tileY, atlas, frame) {
|
|
140
|
+
const slot = this.atlases[atlas];
|
|
141
|
+
if (!slot || slot.released)
|
|
142
|
+
return;
|
|
143
|
+
const f = slot.desc.frames[frame];
|
|
144
|
+
if (!f)
|
|
145
|
+
return;
|
|
146
|
+
const cam = this.camera;
|
|
147
|
+
if (!cam)
|
|
148
|
+
return;
|
|
149
|
+
// Tile -> iso (no Z; tiles sit on the ground plane).
|
|
150
|
+
const isoX = (tileX - tileY) * ISO_HALF_W;
|
|
151
|
+
const isoY = (tileX + tileY) * ISO_HALF_H;
|
|
152
|
+
worldToScreen(cam, isoX, isoY, SCRATCH_VEC2);
|
|
153
|
+
const screenX = SCRATCH_VEC2.x;
|
|
154
|
+
const screenY = SCRATCH_VEC2.y;
|
|
155
|
+
const dw = f.w * cam.zoom;
|
|
156
|
+
const dh = f.h * cam.zoom;
|
|
157
|
+
// Tile anchor: top of the diamond aligns with the iso point.
|
|
158
|
+
// Standard 2:1 dimetric. Tile draws centered horizontally on
|
|
159
|
+
// the projection point, top edge at the iso Y.
|
|
160
|
+
const dx = screenX - dw / 2;
|
|
161
|
+
const dy = screenY - dh / 2;
|
|
162
|
+
this.ctx.drawImage(slot.desc.image, f.x, f.y, f.w, f.h, dx, dy, dw, dh);
|
|
163
|
+
this.drawCallCount++;
|
|
164
|
+
}
|
|
165
|
+
drawText(worldX, worldY, text, style) {
|
|
166
|
+
const cam = this.camera;
|
|
167
|
+
if (!cam)
|
|
168
|
+
return;
|
|
169
|
+
// Text is treated as a 2D-overlay; no iso projection (text
|
|
170
|
+
// labels read left-to-right regardless of iso angle).
|
|
171
|
+
worldToScreen(cam, worldX, worldY, SCRATCH_VEC2);
|
|
172
|
+
const screenX = SCRATCH_VEC2.x;
|
|
173
|
+
const screenY = SCRATCH_VEC2.y;
|
|
174
|
+
this.ctx.save();
|
|
175
|
+
this.ctx.font = style.font;
|
|
176
|
+
this.ctx.fillStyle = rgbaToCssString(style.fill);
|
|
177
|
+
this.ctx.textAlign = style.align ?? 'left';
|
|
178
|
+
this.ctx.textBaseline = style.baseline ?? 'alphabetic';
|
|
179
|
+
this.ctx.fillText(text, screenX, screenY);
|
|
180
|
+
this.ctx.restore();
|
|
181
|
+
this.drawCallCount++;
|
|
182
|
+
}
|
|
183
|
+
drawParticle(worldX, worldY, worldZ, size, color, additive) {
|
|
184
|
+
const cam = this.camera;
|
|
185
|
+
if (!cam)
|
|
186
|
+
return;
|
|
187
|
+
if (size <= 0 || color.a <= 0)
|
|
188
|
+
return;
|
|
189
|
+
// Iso projection same as drawSprite: world (x,y,z) -> iso (sx,sy)
|
|
190
|
+
// -> screen via camera.
|
|
191
|
+
const isoX = (worldX - worldY) * ISO_HALF_W;
|
|
192
|
+
const isoY = (worldX + worldY) * ISO_HALF_H - worldZ * ISO_Z_SCALE;
|
|
193
|
+
worldToScreen(cam, isoX, isoY, SCRATCH_VEC2);
|
|
194
|
+
const sx = SCRATCH_VEC2.x;
|
|
195
|
+
const sy = SCRATCH_VEC2.y;
|
|
196
|
+
const r = (size / 2) * cam.zoom;
|
|
197
|
+
// Fast path for additive particles: pre-baked white soft-disc
|
|
198
|
+
// with globalAlpha. Under 'lighter' blend, white * alpha gives
|
|
199
|
+
// RGB brightness contributions and the underlying canvas color
|
|
200
|
+
// emerges, so additive is hue-correct enough for the demo +
|
|
201
|
+
// Phase 4 sparkle emitters. No per-call CanvasGradient
|
|
202
|
+
// allocation; drawImage hits the fast composite path.
|
|
203
|
+
if (additive) {
|
|
204
|
+
if (!this.particleDisc)
|
|
205
|
+
this.particleDisc = bakeParticleDisc();
|
|
206
|
+
if (this.particleDisc) {
|
|
207
|
+
this.ctx.save();
|
|
208
|
+
this.ctx.globalCompositeOperation = 'lighter';
|
|
209
|
+
// For colored additive particles, multiply by color via
|
|
210
|
+
// globalAlpha (intensity) AND by drawing tinted via a
|
|
211
|
+
// single fillStyle pre-tint pass. Simpler heuristic for
|
|
212
|
+
// demo: alpha = color.a * brightness factor. Hue comes
|
|
213
|
+
// from the underlying canvas + sequential additive layers.
|
|
214
|
+
// For correctness we'd want per-color discs; deferred until
|
|
215
|
+
// a profile shows colored particles dominate.
|
|
216
|
+
const dia = r * 2;
|
|
217
|
+
const intensity = color.a;
|
|
218
|
+
// RGB brightness folds into globalAlpha for additive blend
|
|
219
|
+
// - white * alpha = grey. For tinted additive (most common),
|
|
220
|
+
// we drawImage twice: once with the disc weighted by R, etc.
|
|
221
|
+
// Simpler: bake a single colored composite by chaining.
|
|
222
|
+
// Practical compromise: alpha = max(r, g, b) * a so colored
|
|
223
|
+
// particles aren't washed out.
|
|
224
|
+
const channelMax = Math.max(color.r, color.g, color.b);
|
|
225
|
+
this.ctx.globalAlpha = intensity * channelMax;
|
|
226
|
+
this.ctx.drawImage(this.particleDisc, sx - r, sy - r, dia, dia);
|
|
227
|
+
this.ctx.restore();
|
|
228
|
+
this.drawCallCount++;
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// Fallback: gradient path (correct color tinting for non-additive,
|
|
233
|
+
// or when the pre-baked disc isn't available e.g. headless / SSR).
|
|
234
|
+
this.ctx.save();
|
|
235
|
+
if (additive) {
|
|
236
|
+
this.ctx.globalCompositeOperation = 'lighter';
|
|
237
|
+
}
|
|
238
|
+
const grad = this.ctx.createRadialGradient(sx, sy, 0, sx, sy, r);
|
|
239
|
+
grad.addColorStop(0, rgbaToCssString(color));
|
|
240
|
+
grad.addColorStop(1, rgbaToCssString({ r: color.r, g: color.g, b: color.b, a: 0 }));
|
|
241
|
+
this.ctx.fillStyle = grad;
|
|
242
|
+
this.ctx.beginPath();
|
|
243
|
+
this.ctx.arc(sx, sy, r, 0, Math.PI * 2);
|
|
244
|
+
this.ctx.fill();
|
|
245
|
+
this.ctx.restore();
|
|
246
|
+
this.drawCallCount++;
|
|
247
|
+
}
|
|
248
|
+
getDrawCallCount() {
|
|
249
|
+
return this.drawCallCount;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
//# sourceMappingURL=canvas2d-device.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvas2d-device.js","sourceRoot":"","sources":["../../src/renderer/canvas2d-device.ts"],"names":[],"mappings":"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"}
|