@datamoc/mw_games 0.7.6 → 0.7.7
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/CHANGELOG.md +270 -0
- package/dist/actors/Inventory.d.ts +7 -1
- package/dist/actors/Inventory.d.ts.map +1 -1
- package/dist/actors/Inventory.js.map +1 -1
- package/dist/actors/craft.d.ts +35 -14
- package/dist/actors/craft.d.ts.map +1 -1
- package/dist/actors/craft.js +56 -24
- package/dist/actors/craft.js.map +1 -1
- package/dist/actors/index.d.ts +1 -1
- package/dist/actors/index.d.ts.map +1 -1
- package/dist/ai/Heuristics.d.ts +232 -0
- package/dist/ai/Heuristics.d.ts.map +1 -0
- package/dist/ai/Heuristics.js +298 -0
- package/dist/ai/Heuristics.js.map +1 -0
- package/dist/ai/index.d.ts +4 -0
- package/dist/ai/index.d.ts.map +1 -1
- package/dist/ai/index.js +2 -0
- package/dist/ai/index.js.map +1 -1
- package/dist/ai/score.d.ts +126 -0
- package/dist/ai/score.d.ts.map +1 -0
- package/dist/ai/score.js +121 -0
- package/dist/ai/score.js.map +1 -0
- package/dist/assets/loader.d.ts +18 -4
- package/dist/assets/loader.d.ts.map +1 -1
- package/dist/assets/loader.js +40 -11
- package/dist/assets/loader.js.map +1 -1
- package/dist/audio/Positional.d.ts +125 -0
- package/dist/audio/Positional.d.ts.map +1 -0
- package/dist/audio/Positional.js +135 -0
- package/dist/audio/Positional.js.map +1 -0
- package/dist/audio/Sound.d.ts +7 -1
- package/dist/audio/Sound.d.ts.map +1 -1
- package/dist/audio/Sound.js +8 -2
- package/dist/audio/Sound.js.map +1 -1
- package/dist/audio/index.d.ts +2 -0
- package/dist/audio/index.d.ts.map +1 -1
- package/dist/audio/index.js +1 -0
- package/dist/audio/index.js.map +1 -1
- package/dist/battle/AttackDialog.d.ts +75 -0
- package/dist/battle/AttackDialog.d.ts.map +1 -0
- package/dist/battle/AttackDialog.js +115 -0
- package/dist/battle/AttackDialog.js.map +1 -0
- package/dist/battle/AttackPreview.d.ts +70 -0
- package/dist/battle/AttackPreview.d.ts.map +1 -0
- package/dist/battle/AttackPreview.js +78 -0
- package/dist/battle/AttackPreview.js.map +1 -0
- package/dist/battle/BattleStats.d.ts +45 -0
- package/dist/battle/BattleStats.d.ts.map +1 -0
- package/dist/battle/BattleStats.js +63 -0
- package/dist/battle/BattleStats.js.map +1 -0
- package/dist/battle/UnitSelector.d.ts +72 -0
- package/dist/battle/UnitSelector.d.ts.map +1 -0
- package/dist/battle/UnitSelector.js +117 -0
- package/dist/battle/UnitSelector.js.map +1 -0
- package/dist/battle/Whiteboard.d.ts +50 -0
- package/dist/battle/Whiteboard.d.ts.map +1 -0
- package/dist/battle/Whiteboard.js +94 -0
- package/dist/battle/Whiteboard.js.map +1 -0
- package/dist/battle/index.d.ts +10 -0
- package/dist/battle/index.d.ts.map +1 -1
- package/dist/battle/index.js +5 -0
- package/dist/battle/index.js.map +1 -1
- package/dist/board/FogOfWar.d.ts +32 -2
- package/dist/board/FogOfWar.d.ts.map +1 -1
- package/dist/board/FogOfWar.js +70 -6
- package/dist/board/FogOfWar.js.map +1 -1
- package/dist/core/Achievements.d.ts +40 -7
- package/dist/core/Achievements.d.ts.map +1 -1
- package/dist/core/Achievements.js +51 -7
- package/dist/core/Achievements.js.map +1 -1
- package/dist/core/Hex.d.ts +26 -6
- package/dist/core/Hex.d.ts.map +1 -1
- package/dist/core/Hex.js +40 -13
- package/dist/core/Hex.js.map +1 -1
- package/dist/core/Input.d.ts +36 -0
- package/dist/core/Input.d.ts.map +1 -1
- package/dist/core/Input.js +63 -1
- package/dist/core/Input.js.map +1 -1
- package/dist/core/Random.d.ts +93 -0
- package/dist/core/Random.d.ts.map +1 -1
- package/dist/core/Random.js +177 -0
- package/dist/core/Random.js.map +1 -1
- package/dist/core/SyncGuard.d.ts +57 -0
- package/dist/core/SyncGuard.d.ts.map +1 -0
- package/dist/core/SyncGuard.js +116 -0
- package/dist/core/SyncGuard.js.map +1 -0
- package/dist/core/index.d.ts +3 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +2 -1
- package/dist/core/index.js.map +1 -1
- package/dist/i18n/Po.d.ts +51 -0
- package/dist/i18n/Po.d.ts.map +1 -0
- package/dist/i18n/Po.js +176 -0
- package/dist/i18n/Po.js.map +1 -0
- package/dist/i18n/index.d.ts +2 -0
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/i18n/index.js +1 -0
- package/dist/i18n/index.js.map +1 -1
- package/dist/mw_games.global.js +104 -98
- package/dist/mwl/MapFile.d.ts +41 -0
- package/dist/mwl/MapFile.d.ts.map +1 -0
- package/dist/mwl/MapFile.js +43 -0
- package/dist/mwl/MapFile.js.map +1 -0
- package/dist/mwl/campaign.d.ts +51 -0
- package/dist/mwl/campaign.d.ts.map +1 -0
- package/dist/mwl/campaign.js +58 -0
- package/dist/mwl/campaign.js.map +1 -0
- package/dist/mwl/carryover.d.ts +79 -0
- package/dist/mwl/carryover.d.ts.map +1 -0
- package/dist/mwl/carryover.js +63 -0
- package/dist/mwl/carryover.js.map +1 -0
- package/dist/mwl/content.d.ts +10 -0
- package/dist/mwl/content.d.ts.map +1 -1
- package/dist/mwl/content.js +8 -0
- package/dist/mwl/content.js.map +1 -1
- package/dist/mwl/hooks.d.ts +6 -5
- package/dist/mwl/hooks.d.ts.map +1 -1
- package/dist/mwl/hooks.js.map +1 -1
- package/dist/mwl/index.d.ts +8 -2
- package/dist/mwl/index.d.ts.map +1 -1
- package/dist/mwl/index.js +4 -1
- package/dist/mwl/index.js.map +1 -1
- package/dist/mwl/runtime.d.ts +140 -7
- package/dist/mwl/runtime.d.ts.map +1 -1
- package/dist/mwl/runtime.js +427 -33
- package/dist/mwl/runtime.js.map +1 -1
- package/dist/mwl/schema.d.ts.map +1 -1
- package/dist/mwl/schema.js +97 -18
- package/dist/mwl/schema.js.map +1 -1
- package/dist/roguelike/MultiTurnBeam.d.ts +50 -12
- package/dist/roguelike/MultiTurnBeam.d.ts.map +1 -1
- package/dist/roguelike/MultiTurnBeam.js +100 -53
- package/dist/roguelike/MultiTurnBeam.js.map +1 -1
- package/dist/roguelike/Targeting.d.ts +94 -0
- package/dist/roguelike/Targeting.d.ts.map +1 -1
- package/dist/roguelike/Targeting.js +123 -0
- package/dist/roguelike/Targeting.js.map +1 -1
- package/dist/roguelike/index.d.ts +3 -1
- package/dist/roguelike/index.d.ts.map +1 -1
- package/dist/roguelike/index.js +1 -0
- package/dist/roguelike/index.js.map +1 -1
- package/dist/simulation/CampaignSave.d.ts +68 -0
- package/dist/simulation/CampaignSave.d.ts.map +1 -0
- package/dist/simulation/CampaignSave.js +54 -0
- package/dist/simulation/CampaignSave.js.map +1 -0
- package/dist/simulation/EventPresentation.d.ts +111 -0
- package/dist/simulation/EventPresentation.d.ts.map +1 -0
- package/dist/simulation/EventPresentation.js +120 -0
- package/dist/simulation/EventPresentation.js.map +1 -0
- package/dist/simulation/index.d.ts +4 -0
- package/dist/simulation/index.d.ts.map +1 -1
- package/dist/simulation/index.js +2 -0
- package/dist/simulation/index.js.map +1 -1
- package/dist/two-d/pixi-interop.d.ts +18 -6
- package/dist/two-d/pixi-interop.d.ts.map +1 -1
- package/dist/two-d/pixi-interop.js +18 -6
- package/dist/two-d/pixi-interop.js.map +1 -1
- package/dist/two-d/render/AnimatedSprite.d.ts +74 -7
- package/dist/two-d/render/AnimatedSprite.d.ts.map +1 -1
- package/dist/two-d/render/AnimatedSprite.js +88 -26
- package/dist/two-d/render/AnimatedSprite.js.map +1 -1
- package/dist/two-d/render/Camera.d.ts +64 -4
- package/dist/two-d/render/Camera.d.ts.map +1 -1
- package/dist/two-d/render/Camera.js +132 -11
- package/dist/two-d/render/Camera.js.map +1 -1
- package/dist/two-d/render/Halo.d.ts +58 -0
- package/dist/two-d/render/Halo.d.ts.map +1 -0
- package/dist/two-d/render/Halo.js +48 -0
- package/dist/two-d/render/Halo.js.map +1 -0
- package/dist/two-d/render/ImageModifiers.d.ts +174 -0
- package/dist/two-d/render/ImageModifiers.d.ts.map +1 -1
- package/dist/two-d/render/ImageModifiers.js +438 -1
- package/dist/two-d/render/ImageModifiers.js.map +1 -1
- package/dist/two-d/render/LightningArc.d.ts +57 -0
- package/dist/two-d/render/LightningArc.d.ts.map +1 -0
- package/dist/two-d/render/LightningArc.js +91 -0
- package/dist/two-d/render/LightningArc.js.map +1 -0
- package/dist/two-d/render/PaletteRemap.d.ts +135 -0
- package/dist/two-d/render/PaletteRemap.d.ts.map +1 -0
- package/dist/two-d/render/PaletteRemap.js +181 -0
- package/dist/two-d/render/PaletteRemap.js.map +1 -0
- package/dist/two-d/render/Particles.d.ts +26 -0
- package/dist/two-d/render/Particles.d.ts.map +1 -1
- package/dist/two-d/render/Particles.js +23 -2
- package/dist/two-d/render/Particles.js.map +1 -1
- package/dist/two-d/render/SpriteAttachment.d.ts +55 -0
- package/dist/two-d/render/SpriteAttachment.d.ts.map +1 -0
- package/dist/two-d/render/SpriteAttachment.js +60 -0
- package/dist/two-d/render/SpriteAttachment.js.map +1 -0
- package/dist/two-d/render/TerrainGraphics.d.ts +142 -0
- package/dist/two-d/render/TerrainGraphics.d.ts.map +1 -0
- package/dist/two-d/render/TerrainGraphics.js +161 -0
- package/dist/two-d/render/TerrainGraphics.js.map +1 -0
- package/dist/two-d/render/index.d.ts +13 -3
- package/dist/two-d/render/index.d.ts.map +1 -1
- package/dist/two-d/render/index.js +6 -1
- package/dist/two-d/render/index.js.map +1 -1
- package/dist/two-d/stage/StoryScreen.d.ts +110 -0
- package/dist/two-d/stage/StoryScreen.d.ts.map +1 -0
- package/dist/two-d/stage/StoryScreen.js +178 -0
- package/dist/two-d/stage/StoryScreen.js.map +1 -0
- package/dist/two-d/stage/index.d.ts +2 -0
- package/dist/two-d/stage/index.d.ts.map +1 -1
- package/dist/two-d/stage/index.js +1 -0
- package/dist/two-d/stage/index.js.map +1 -1
- package/dist/two-d/ui/Checkbox.d.ts +44 -0
- package/dist/two-d/ui/Checkbox.d.ts.map +1 -0
- package/dist/two-d/ui/Checkbox.js +89 -0
- package/dist/two-d/ui/Checkbox.js.map +1 -0
- package/dist/two-d/ui/DataTable.d.ts +79 -0
- package/dist/two-d/ui/DataTable.d.ts.map +1 -0
- package/dist/two-d/ui/DataTable.js +164 -0
- package/dist/two-d/ui/DataTable.js.map +1 -0
- package/dist/two-d/ui/Dropdown.d.ts +72 -0
- package/dist/two-d/ui/Dropdown.d.ts.map +1 -0
- package/dist/two-d/ui/Dropdown.js +126 -0
- package/dist/two-d/ui/Dropdown.js.map +1 -0
- package/dist/two-d/ui/FloatingText.d.ts +43 -4
- package/dist/two-d/ui/FloatingText.d.ts.map +1 -1
- package/dist/two-d/ui/FloatingText.js +52 -5
- package/dist/two-d/ui/FloatingText.js.map +1 -1
- package/dist/two-d/ui/FloatingTextStack.d.ts +86 -13
- package/dist/two-d/ui/FloatingTextStack.d.ts.map +1 -1
- package/dist/two-d/ui/FloatingTextStack.js +89 -18
- package/dist/two-d/ui/FloatingTextStack.js.map +1 -1
- package/dist/two-d/ui/Layout.d.ts +105 -0
- package/dist/two-d/ui/Layout.d.ts.map +1 -0
- package/dist/two-d/ui/Layout.js +174 -0
- package/dist/two-d/ui/Layout.js.map +1 -0
- package/dist/two-d/ui/ScrollBox.d.ts +67 -0
- package/dist/two-d/ui/ScrollBox.d.ts.map +1 -0
- package/dist/two-d/ui/ScrollBox.js +127 -0
- package/dist/two-d/ui/ScrollBox.js.map +1 -0
- package/dist/two-d/ui/Skins.d.ts +59 -0
- package/dist/two-d/ui/Skins.d.ts.map +1 -0
- package/dist/two-d/ui/Skins.js +77 -0
- package/dist/two-d/ui/Skins.js.map +1 -0
- package/dist/two-d/ui/Slider.d.ts +91 -0
- package/dist/two-d/ui/Slider.d.ts.map +1 -0
- package/dist/two-d/ui/Slider.js +170 -0
- package/dist/two-d/ui/Slider.js.map +1 -0
- package/dist/two-d/ui/Spinner.d.ts +67 -0
- package/dist/two-d/ui/Spinner.d.ts.map +1 -0
- package/dist/two-d/ui/Spinner.js +137 -0
- package/dist/two-d/ui/Spinner.js.map +1 -0
- package/dist/two-d/ui/TabbedList.d.ts +118 -0
- package/dist/two-d/ui/TabbedList.d.ts.map +1 -0
- package/dist/two-d/ui/TabbedList.js +221 -0
- package/dist/two-d/ui/TabbedList.js.map +1 -0
- package/dist/two-d/ui/TextModel.d.ts +65 -0
- package/dist/two-d/ui/TextModel.d.ts.map +1 -0
- package/dist/two-d/ui/TextModel.js +126 -0
- package/dist/two-d/ui/TextModel.js.map +1 -0
- package/dist/two-d/ui/TreeView.d.ts +68 -0
- package/dist/two-d/ui/TreeView.d.ts.map +1 -0
- package/dist/two-d/ui/TreeView.js +133 -0
- package/dist/two-d/ui/TreeView.js.map +1 -0
- package/dist/two-d/ui/Window.d.ts +13 -0
- package/dist/two-d/ui/Window.d.ts.map +1 -1
- package/dist/two-d/ui/Window.js +23 -0
- package/dist/two-d/ui/Window.js.map +1 -1
- package/dist/two-d/ui/WindowStack.d.ts.map +1 -1
- package/dist/two-d/ui/WindowStack.js +2 -0
- package/dist/two-d/ui/WindowStack.js.map +1 -1
- package/dist/two-d/ui/index.d.ts +27 -3
- package/dist/two-d/ui/index.d.ts.map +1 -1
- package/dist/two-d/ui/index.js +14 -2
- package/dist/two-d/ui/index.js.map +1 -1
- package/dist/two-d/ui/markup.d.ts +127 -0
- package/dist/two-d/ui/markup.d.ts.map +1 -0
- package/dist/two-d/ui/markup.js +322 -0
- package/dist/two-d/ui/markup.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/world/SideTurns.d.ts +115 -0
- package/dist/world/SideTurns.d.ts.map +1 -0
- package/dist/world/SideTurns.js +138 -0
- package/dist/world/SideTurns.js.map +1 -0
- package/dist/world/index.d.ts +2 -0
- package/dist/world/index.d.ts.map +1 -1
- package/dist/world/index.js +1 -0
- package/dist/world/index.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The position data for a jagged line between two points - a lightning bolt, a tether, a chain
|
|
3
|
+
* lightning link - the temporised two-point visual `two-d/render` had no helper for. Owns
|
|
4
|
+
* geometry only, the same division `Projectile` draws for a single moving point: this decides
|
|
5
|
+
* where the line's points are, a caller draws them (through `Shape2D`'s `Graphics`, typically,
|
|
6
|
+
* one `lineTo` per point) and decides colour, width and blend mode.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { LightningArc } from '@datamoc/mw_games/two-d/render';
|
|
11
|
+
*
|
|
12
|
+
* const bolt = new LightningArc({ x: 0, y: 0 }, { x: 100, y: 0 }, { duration: 0.2, segments: 4 });
|
|
13
|
+
* console.log(bolt.points.length); // 6 - both endpoints plus 4 interior points
|
|
14
|
+
*
|
|
15
|
+
* const expired = bolt.update(0.25);
|
|
16
|
+
* console.log(expired); // true - the instant it outlives its duration
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export class LightningArc {
|
|
20
|
+
constructor(from, to, options = {}) {
|
|
21
|
+
this.elapsed = 0;
|
|
22
|
+
this.sinceFlicker = 0;
|
|
23
|
+
this.expired = false;
|
|
24
|
+
this.current = [];
|
|
25
|
+
this.from = { ...from };
|
|
26
|
+
this.to = { ...to };
|
|
27
|
+
this.segments = Math.max(0, Math.floor(options.segments ?? 6));
|
|
28
|
+
this.jitter = options.jitter ?? 12;
|
|
29
|
+
this.flickerInterval = options.flickerInterval;
|
|
30
|
+
this.random = options.random ?? Math.random;
|
|
31
|
+
this.duration = options.duration;
|
|
32
|
+
this.reroll();
|
|
33
|
+
}
|
|
34
|
+
/** the endpoints plus every jittered interior point, in order from `from` to `to` */
|
|
35
|
+
get points() {
|
|
36
|
+
return this.current;
|
|
37
|
+
}
|
|
38
|
+
get done() {
|
|
39
|
+
return this.expired;
|
|
40
|
+
}
|
|
41
|
+
/** moves either endpoint, for a tether following two units that are themselves moving */
|
|
42
|
+
retarget(from, to) {
|
|
43
|
+
if (from)
|
|
44
|
+
this.from = { ...from };
|
|
45
|
+
if (to)
|
|
46
|
+
this.to = { ...to };
|
|
47
|
+
if (this.flickerInterval === undefined)
|
|
48
|
+
this.reroll();
|
|
49
|
+
}
|
|
50
|
+
/** @returns true the instant `duration` elapses, so a caller can remove the arc exactly once */
|
|
51
|
+
update(dt) {
|
|
52
|
+
if (this.expired)
|
|
53
|
+
return false;
|
|
54
|
+
this.elapsed += dt;
|
|
55
|
+
if (this.flickerInterval !== undefined) {
|
|
56
|
+
this.sinceFlicker += dt;
|
|
57
|
+
if (this.sinceFlicker >= this.flickerInterval) {
|
|
58
|
+
this.sinceFlicker = 0;
|
|
59
|
+
this.reroll();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (this.duration !== undefined && this.elapsed >= this.duration) {
|
|
63
|
+
this.expired = true;
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
reroll() {
|
|
69
|
+
const dx = this.to.x - this.from.x;
|
|
70
|
+
const dy = this.to.y - this.from.y;
|
|
71
|
+
const length = Math.hypot(dx, dy);
|
|
72
|
+
//a perpendicular unit vector; a zero-length arc (from === to) has none, so jitter is 0
|
|
73
|
+
const nx = length > 0 ? -dy / length : 0;
|
|
74
|
+
const ny = length > 0 ? dx / length : 0;
|
|
75
|
+
const points = [{ ...this.from }];
|
|
76
|
+
const steps = this.segments + 1;
|
|
77
|
+
for (let i = 1; i < steps; i += 1) {
|
|
78
|
+
const t = i / steps;
|
|
79
|
+
//tapers to 0 at both ends (t=0 and t=1), so a bolt does not visibly detach from its target
|
|
80
|
+
const taper = Math.sin(Math.PI * t);
|
|
81
|
+
const offset = (this.random() * 2 - 1) * this.jitter * taper;
|
|
82
|
+
points.push({
|
|
83
|
+
x: this.from.x + dx * t + nx * offset,
|
|
84
|
+
y: this.from.y + dy * t + ny * offset,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
points.push({ ...this.to });
|
|
88
|
+
this.current = points;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=LightningArc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LightningArc.js","sourceRoot":"","sources":["../../../src/two-d/render/LightningArc.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { Texture } from 'pixi.js';
|
|
2
|
+
/** A source palette colour and the colour it should become, both `0xRRGGBB`. */
|
|
3
|
+
export interface PaletteMapping {
|
|
4
|
+
readonly from: readonly number[];
|
|
5
|
+
readonly to: readonly number[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* How a pixel that is not an exact `from` colour is treated. `'exact'` (the default for
|
|
9
|
+
* `remapPixels`/`recolorTexture`) leaves it untouched - the correct behaviour for a discrete
|
|
10
|
+
* swap like `~RC`/`~PAL`, where `from` is a short, specific list rather than a covering of the
|
|
11
|
+
* whole image; `'nearest'` repaints every opaque pixel with whichever `from` entry it is
|
|
12
|
+
* closest to, which is what a `paletteRangeMapping` gradient over the *whole* palette a team's
|
|
13
|
+
* reference art uses (Wesnoth's magenta `TC` convention, or any equivalent) wants instead. Using
|
|
14
|
+
* `'nearest'` with a sparse `from` list (an `~RC` pair or two) would repaint the entire image
|
|
15
|
+
* toward whichever pair happens to be closest, which is the bug this mode exists to prevent.
|
|
16
|
+
*/
|
|
17
|
+
export type PaletteRemapMode = 'exact' | 'nearest';
|
|
18
|
+
/** The three-stop gradient a team colour (or any palette range) is built from. */
|
|
19
|
+
export interface PaletteRange {
|
|
20
|
+
readonly min: number;
|
|
21
|
+
readonly mid: number;
|
|
22
|
+
readonly max: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Builds a `PaletteMapping` that replaces every colour of `reference` with the matching stop
|
|
26
|
+
* of a three-colour gradient (`range.min` through `range.mid` to `range.max`), the shape a
|
|
27
|
+
* team colour or any other `[color_range]`-style remap is defined by. `reference` is assumed
|
|
28
|
+
* ordered from lightest to darkest; each entry's position in that order, not its own value,
|
|
29
|
+
* decides where along `min -> mid -> max` it lands, so recolouring never depends on knowing
|
|
30
|
+
* what the reference colours actually are.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* import { paletteRangeMapping, recolorTexture } from '@datamoc/mw_games/two-d/render';
|
|
35
|
+
* import type { Texture2D } from '@datamoc/mw_games/two-d/render';
|
|
36
|
+
*
|
|
37
|
+
* declare const texture: Texture2D;
|
|
38
|
+
* declare const magentaReference: readonly number[]; // a game's own reference palette, lightest first
|
|
39
|
+
*
|
|
40
|
+
* const redTeam = paletteRangeMapping(magentaReference, { min: 0xffe0e0, mid: 0xc00000, max: 0x400000 });
|
|
41
|
+
* const recolored = recolorTexture(texture, redTeam);
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export declare function paletteRangeMapping(reference: readonly number[], range: PaletteRange): PaletteMapping;
|
|
45
|
+
/**
|
|
46
|
+
* The renderer-free core of a palette remap. In `'exact'` mode (the default) a pixel is
|
|
47
|
+
* repainted only when it exactly matches one of `mapping.from`; every other pixel, including
|
|
48
|
+
* one close but not identical to a `from` colour, passes through unchanged. In `'nearest'` mode
|
|
49
|
+
* every opaque pixel is repainted with the `to` colour of whichever `from` entry it is nearest
|
|
50
|
+
* to in RGB space. Alpha and fully transparent pixels are always left untouched. Exported on
|
|
51
|
+
* its own so the remap logic is unit-testable without a canvas or a texture.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* import { remapPixels } from '@datamoc/mw_games/two-d/render';
|
|
56
|
+
*
|
|
57
|
+
* const pixels = new Uint8ClampedArray([255, 0, 255, 255, 10, 10, 10, 255]); // magenta, then dark grey
|
|
58
|
+
* const out = remapPixels(pixels, { from: [0xff00ff], to: [0xff0000] }); // 'exact' by default
|
|
59
|
+
* console.log([...out.slice(0, 4)]); // [255, 0, 0, 255] - the exact match repainted
|
|
60
|
+
* console.log([...out.slice(4, 8)]); // [10, 10, 10, 255] - not a match, left alone
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export declare function remapPixels(pixels: Uint8ClampedArray, mapping: PaletteMapping, mode?: PaletteRemapMode): Uint8ClampedArray;
|
|
64
|
+
/** The subset of `CanvasRenderingContext2D` a palette remap needs, so a test can fake it. */
|
|
65
|
+
export interface RemapCanvasContext {
|
|
66
|
+
drawImage(image: unknown, dx: number, dy: number): void;
|
|
67
|
+
getImageData(sx: number, sy: number, sw: number, sh: number): {
|
|
68
|
+
data: Uint8ClampedArray;
|
|
69
|
+
};
|
|
70
|
+
putImageData(imageData: {
|
|
71
|
+
data: Uint8ClampedArray;
|
|
72
|
+
width: number;
|
|
73
|
+
height: number;
|
|
74
|
+
}, dx: number, dy: number): void;
|
|
75
|
+
}
|
|
76
|
+
export interface RemapCanvas {
|
|
77
|
+
width: number;
|
|
78
|
+
height: number;
|
|
79
|
+
getContext(kind: '2d'): RemapCanvasContext | null;
|
|
80
|
+
}
|
|
81
|
+
export interface RecolorProbe {
|
|
82
|
+
createCanvas?(width: number, height: number): RemapCanvas | null;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Opens a canvas the size of `texture`, draws it in, and hands the 2D context to `paint` to
|
|
86
|
+
* make whatever further edits it needs (a palette remap, a composited overlay, a rewritten
|
|
87
|
+
* alpha channel); the result becomes the returned `Texture`. Shared by every texture-level
|
|
88
|
+
* image modifier (`recolorTexture` here, `~BLIT`/`~MASK`/`~BLEND` in `ImageModifiers.ts`) so
|
|
89
|
+
* each one only has to write its own pixel or compositing logic, not the canvas bookkeeping.
|
|
90
|
+
* Returns `texture` unchanged if there is no usable 2D canvas.
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```ts
|
|
94
|
+
* import { withTextureCanvas } from '@datamoc/mw_games/two-d/render';
|
|
95
|
+
* import type { Texture2D } from '@datamoc/mw_games/two-d/render';
|
|
96
|
+
*
|
|
97
|
+
* declare const texture: Texture2D;
|
|
98
|
+
*
|
|
99
|
+
* // a minimal custom modifier: invert every pixel's RGB, alpha untouched
|
|
100
|
+
* const inverted = withTextureCanvas(texture, {}, (context, width, height) => {
|
|
101
|
+
* const { data } = context.getImageData(0, 0, width, height);
|
|
102
|
+
* for (let i = 0; i < data.length; i += 4) {
|
|
103
|
+
* data[i] = 255 - data[i];
|
|
104
|
+
* data[i + 1] = 255 - data[i + 1];
|
|
105
|
+
* data[i + 2] = 255 - data[i + 2];
|
|
106
|
+
* }
|
|
107
|
+
* context.putImageData({ data, width, height }, 0, 0);
|
|
108
|
+
* });
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
export declare function withTextureCanvas(texture: Texture, probe: RecolorProbe, paint: (context: RemapCanvasContext, width: number, height: number) => void): Texture;
|
|
112
|
+
/**
|
|
113
|
+
* Recolours a texture by palette remap: draws it to a canvas, runs `remapPixels` over the raw
|
|
114
|
+
* pixel data, and returns a new `Texture` wrapping the result. The source texture is never
|
|
115
|
+
* mutated, so the same base art recolours per team without extra copies kept around by the
|
|
116
|
+
* caller. Needs a real 2D canvas context (unlike `remapPixels` itself), so pass `probe` in a
|
|
117
|
+
* test or host integration with no DOM; omitting it uses `document.createElement('canvas')`.
|
|
118
|
+
*
|
|
119
|
+
* `mode` defaults to `'exact'`, correct for a discrete swap (`~RC`/`~PAL`'s short, specific
|
|
120
|
+
* `from` list); a `paletteRangeMapping` gradient, meant to cover the whole reference palette a
|
|
121
|
+
* team's art is drawn against, wants `'nearest'` instead - see `PaletteRemapMode`'s own doc for
|
|
122
|
+
* why the wrong choice silently recolours far more of the image than intended.
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```ts
|
|
126
|
+
* import { paletteRangeMapping, recolorTexture } from '@datamoc/mw_games/two-d/render';
|
|
127
|
+
* import type { Texture2D } from '@datamoc/mw_games/two-d/render';
|
|
128
|
+
*
|
|
129
|
+
* declare const texture: Texture2D;
|
|
130
|
+
* const mapping = paletteRangeMapping([0xffffff, 0x808080, 0x000000], { min: 0xffe0e0, mid: 0xc00000, max: 0x400000 });
|
|
131
|
+
* const redTeam = recolorTexture(texture, mapping, {}, 'nearest');
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
export declare function recolorTexture(texture: Texture, mapping: PaletteMapping, probe?: RecolorProbe, mode?: PaletteRemapMode): Texture;
|
|
135
|
+
//# sourceMappingURL=PaletteRemap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PaletteRemap.d.ts","sourceRoot":"","sources":["../../../src/two-d/render/PaletteRemap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,gFAAgF;AAChF,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,EAAE,EAAE,SAAS,MAAM,EAAE,CAAC;CAC/B;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,SAAS,CAAC;AAEnD,kFAAkF;AAClF,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACrB;AAoBD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,EAAE,KAAK,EAAE,YAAY,GAAG,cAAc,CAOrG;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,WAAW,CAC1B,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,cAAc,EACvB,IAAI,GAAE,gBAA0B,GAC9B,iBAAiB,CA4CnB;AAED,6FAA6F;AAC7F,MAAM,WAAW,kBAAkB;IAClC,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IACxD,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,iBAAiB,CAAA;KAAE,CAAC;IAC1F,YAAY,CAAC,SAAS,EAAE;QAAE,IAAI,EAAE,iBAAiB,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;CAClH;AAED,MAAM,WAAW,WAAW;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,kBAAkB,GAAG,IAAI,CAAC;CAClD;AAED,MAAM,WAAW,YAAY;IAC5B,YAAY,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAAC;CACjE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,iBAAiB,CAChC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,YAAY,EACnB,KAAK,EAAE,CAAC,OAAO,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,GACzE,OAAO,CAiBT;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,cAAc,CAC7B,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,cAAc,EACvB,KAAK,GAAE,YAAiB,EACxB,IAAI,GAAE,gBAA0B,GAC9B,OAAO,CAMT"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { Texture } from 'pixi.js';
|
|
2
|
+
function hexToRgb(hex) {
|
|
3
|
+
return [(hex >> 16) & 0xff, (hex >> 8) & 0xff, hex & 0xff];
|
|
4
|
+
}
|
|
5
|
+
function rgbToHex(r, g, b) {
|
|
6
|
+
return (Math.round(r) << 16) | (Math.round(g) << 8) | Math.round(b);
|
|
7
|
+
}
|
|
8
|
+
function lerp(a, b, t) {
|
|
9
|
+
return a + (b - a) * t;
|
|
10
|
+
}
|
|
11
|
+
function lerpColor(from, to, t) {
|
|
12
|
+
const [r1, g1, b1] = hexToRgb(from);
|
|
13
|
+
const [r2, g2, b2] = hexToRgb(to);
|
|
14
|
+
return rgbToHex(lerp(r1, r2, t), lerp(g1, g2, t), lerp(b1, b2, t));
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Builds a `PaletteMapping` that replaces every colour of `reference` with the matching stop
|
|
18
|
+
* of a three-colour gradient (`range.min` through `range.mid` to `range.max`), the shape a
|
|
19
|
+
* team colour or any other `[color_range]`-style remap is defined by. `reference` is assumed
|
|
20
|
+
* ordered from lightest to darkest; each entry's position in that order, not its own value,
|
|
21
|
+
* decides where along `min -> mid -> max` it lands, so recolouring never depends on knowing
|
|
22
|
+
* what the reference colours actually are.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* import { paletteRangeMapping, recolorTexture } from '@datamoc/mw_games/two-d/render';
|
|
27
|
+
* import type { Texture2D } from '@datamoc/mw_games/two-d/render';
|
|
28
|
+
*
|
|
29
|
+
* declare const texture: Texture2D;
|
|
30
|
+
* declare const magentaReference: readonly number[]; // a game's own reference palette, lightest first
|
|
31
|
+
*
|
|
32
|
+
* const redTeam = paletteRangeMapping(magentaReference, { min: 0xffe0e0, mid: 0xc00000, max: 0x400000 });
|
|
33
|
+
* const recolored = recolorTexture(texture, redTeam);
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export function paletteRangeMapping(reference, range) {
|
|
37
|
+
const to = reference.map((_, index) => {
|
|
38
|
+
if (reference.length <= 1)
|
|
39
|
+
return range.mid;
|
|
40
|
+
const t = index / (reference.length - 1);
|
|
41
|
+
return t <= 0.5 ? lerpColor(range.min, range.mid, t * 2) : lerpColor(range.mid, range.max, (t - 0.5) * 2);
|
|
42
|
+
});
|
|
43
|
+
return { from: reference, to };
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The renderer-free core of a palette remap. In `'exact'` mode (the default) a pixel is
|
|
47
|
+
* repainted only when it exactly matches one of `mapping.from`; every other pixel, including
|
|
48
|
+
* one close but not identical to a `from` colour, passes through unchanged. In `'nearest'` mode
|
|
49
|
+
* every opaque pixel is repainted with the `to` colour of whichever `from` entry it is nearest
|
|
50
|
+
* to in RGB space. Alpha and fully transparent pixels are always left untouched. Exported on
|
|
51
|
+
* its own so the remap logic is unit-testable without a canvas or a texture.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* import { remapPixels } from '@datamoc/mw_games/two-d/render';
|
|
56
|
+
*
|
|
57
|
+
* const pixels = new Uint8ClampedArray([255, 0, 255, 255, 10, 10, 10, 255]); // magenta, then dark grey
|
|
58
|
+
* const out = remapPixels(pixels, { from: [0xff00ff], to: [0xff0000] }); // 'exact' by default
|
|
59
|
+
* console.log([...out.slice(0, 4)]); // [255, 0, 0, 255] - the exact match repainted
|
|
60
|
+
* console.log([...out.slice(4, 8)]); // [10, 10, 10, 255] - not a match, left alone
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export function remapPixels(pixels, mapping, mode = 'exact') {
|
|
64
|
+
const from = mapping.from.map(hexToRgb);
|
|
65
|
+
const out = new Uint8ClampedArray(pixels.length);
|
|
66
|
+
for (let index = 0; index < pixels.length; index += 4) {
|
|
67
|
+
const alpha = pixels[index + 3];
|
|
68
|
+
const r = pixels[index];
|
|
69
|
+
const g = pixels[index + 1];
|
|
70
|
+
const b = pixels[index + 2];
|
|
71
|
+
out[index] = r;
|
|
72
|
+
out[index + 1] = g;
|
|
73
|
+
out[index + 2] = b;
|
|
74
|
+
out[index + 3] = alpha;
|
|
75
|
+
if (alpha === 0 || from.length === 0)
|
|
76
|
+
continue;
|
|
77
|
+
if (mode === 'exact') {
|
|
78
|
+
const match = from.findIndex(([fr, fg, fb]) => fr === r && fg === g && fb === b);
|
|
79
|
+
if (match < 0)
|
|
80
|
+
continue;
|
|
81
|
+
const [tr, tg, tb] = hexToRgb(mapping.to[match] ?? mapping.from[match]);
|
|
82
|
+
out[index] = tr;
|
|
83
|
+
out[index + 1] = tg;
|
|
84
|
+
out[index + 2] = tb;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
let nearest = 0;
|
|
88
|
+
let nearestDistance = Infinity;
|
|
89
|
+
for (let paletteIndex = 0; paletteIndex < from.length; paletteIndex += 1) {
|
|
90
|
+
const [fr, fg, fb] = from[paletteIndex];
|
|
91
|
+
const dr = r - fr;
|
|
92
|
+
const dg = g - fg;
|
|
93
|
+
const db = b - fb;
|
|
94
|
+
const distance = dr * dr + dg * dg + db * db;
|
|
95
|
+
if (distance < nearestDistance) {
|
|
96
|
+
nearestDistance = distance;
|
|
97
|
+
nearest = paletteIndex;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const [tr, tg, tb] = hexToRgb(mapping.to[nearest] ?? mapping.from[nearest]);
|
|
101
|
+
out[index] = tr;
|
|
102
|
+
out[index + 1] = tg;
|
|
103
|
+
out[index + 2] = tb;
|
|
104
|
+
}
|
|
105
|
+
return out;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Opens a canvas the size of `texture`, draws it in, and hands the 2D context to `paint` to
|
|
109
|
+
* make whatever further edits it needs (a palette remap, a composited overlay, a rewritten
|
|
110
|
+
* alpha channel); the result becomes the returned `Texture`. Shared by every texture-level
|
|
111
|
+
* image modifier (`recolorTexture` here, `~BLIT`/`~MASK`/`~BLEND` in `ImageModifiers.ts`) so
|
|
112
|
+
* each one only has to write its own pixel or compositing logic, not the canvas bookkeeping.
|
|
113
|
+
* Returns `texture` unchanged if there is no usable 2D canvas.
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```ts
|
|
117
|
+
* import { withTextureCanvas } from '@datamoc/mw_games/two-d/render';
|
|
118
|
+
* import type { Texture2D } from '@datamoc/mw_games/two-d/render';
|
|
119
|
+
*
|
|
120
|
+
* declare const texture: Texture2D;
|
|
121
|
+
*
|
|
122
|
+
* // a minimal custom modifier: invert every pixel's RGB, alpha untouched
|
|
123
|
+
* const inverted = withTextureCanvas(texture, {}, (context, width, height) => {
|
|
124
|
+
* const { data } = context.getImageData(0, 0, width, height);
|
|
125
|
+
* for (let i = 0; i < data.length; i += 4) {
|
|
126
|
+
* data[i] = 255 - data[i];
|
|
127
|
+
* data[i + 1] = 255 - data[i + 1];
|
|
128
|
+
* data[i + 2] = 255 - data[i + 2];
|
|
129
|
+
* }
|
|
130
|
+
* context.putImageData({ data, width, height }, 0, 0);
|
|
131
|
+
* });
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
export function withTextureCanvas(texture, probe, paint) {
|
|
135
|
+
const width = texture.width;
|
|
136
|
+
const height = texture.height;
|
|
137
|
+
if (width <= 0 || height <= 0 || !texture.source)
|
|
138
|
+
return texture;
|
|
139
|
+
const canvas = probe.createCanvas?.(width, height) ??
|
|
140
|
+
(typeof document === 'undefined' ? null : document.createElement('canvas'));
|
|
141
|
+
if (!canvas)
|
|
142
|
+
return texture;
|
|
143
|
+
canvas.width = width;
|
|
144
|
+
canvas.height = height;
|
|
145
|
+
const context = canvas.getContext('2d');
|
|
146
|
+
if (!context)
|
|
147
|
+
return texture;
|
|
148
|
+
context.drawImage(texture.source.resource, 0, 0);
|
|
149
|
+
paint(context, width, height);
|
|
150
|
+
return Texture.from(canvas);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Recolours a texture by palette remap: draws it to a canvas, runs `remapPixels` over the raw
|
|
154
|
+
* pixel data, and returns a new `Texture` wrapping the result. The source texture is never
|
|
155
|
+
* mutated, so the same base art recolours per team without extra copies kept around by the
|
|
156
|
+
* caller. Needs a real 2D canvas context (unlike `remapPixels` itself), so pass `probe` in a
|
|
157
|
+
* test or host integration with no DOM; omitting it uses `document.createElement('canvas')`.
|
|
158
|
+
*
|
|
159
|
+
* `mode` defaults to `'exact'`, correct for a discrete swap (`~RC`/`~PAL`'s short, specific
|
|
160
|
+
* `from` list); a `paletteRangeMapping` gradient, meant to cover the whole reference palette a
|
|
161
|
+
* team's art is drawn against, wants `'nearest'` instead - see `PaletteRemapMode`'s own doc for
|
|
162
|
+
* why the wrong choice silently recolours far more of the image than intended.
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```ts
|
|
166
|
+
* import { paletteRangeMapping, recolorTexture } from '@datamoc/mw_games/two-d/render';
|
|
167
|
+
* import type { Texture2D } from '@datamoc/mw_games/two-d/render';
|
|
168
|
+
*
|
|
169
|
+
* declare const texture: Texture2D;
|
|
170
|
+
* const mapping = paletteRangeMapping([0xffffff, 0x808080, 0x000000], { min: 0xffe0e0, mid: 0xc00000, max: 0x400000 });
|
|
171
|
+
* const redTeam = recolorTexture(texture, mapping, {}, 'nearest');
|
|
172
|
+
* ```
|
|
173
|
+
*/
|
|
174
|
+
export function recolorTexture(texture, mapping, probe = {}, mode = 'exact') {
|
|
175
|
+
return withTextureCanvas(texture, probe, (context, width, height) => {
|
|
176
|
+
const imageData = context.getImageData(0, 0, width, height);
|
|
177
|
+
const remapped = remapPixels(imageData.data, mapping, mode);
|
|
178
|
+
context.putImageData({ data: remapped, width, height }, 0, 0);
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=PaletteRemap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PaletteRemap.js","sourceRoot":"","sources":["../../../src/two-d/render/PaletteRemap.ts"],"names":[],"mappings":"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"}
|
|
@@ -33,6 +33,22 @@ export interface Particle {
|
|
|
33
33
|
}
|
|
34
34
|
/** a `[min, max]` range picked per particle; a bare number means that value exactly */
|
|
35
35
|
export type ParticleRange = number | readonly [number, number];
|
|
36
|
+
/**
|
|
37
|
+
* Where in the emitter's local space a particle is born. Without one every particle starts at
|
|
38
|
+
* the emitter's origin, which is right for a hit spark and wrong for anything with an extent:
|
|
39
|
+
* the width of a forge, the mouth of a flame column, rain across a room.
|
|
40
|
+
*
|
|
41
|
+
* A `rect` spreads births uniformly across `width` x `height`; an `ellipse` spreads them
|
|
42
|
+
* uniformly inside it (the square-root of the radius, so they do not bunch at the centre).
|
|
43
|
+
* Both are centred on the emitter and move with it, since this is local space.
|
|
44
|
+
*/
|
|
45
|
+
export interface ParticleSpawnArea {
|
|
46
|
+
shape: 'rect' | 'ellipse';
|
|
47
|
+
/** full width of the area, in world units */
|
|
48
|
+
width: number;
|
|
49
|
+
/** full height; defaults to `width`, so a square rectangle or a circle */
|
|
50
|
+
height?: number;
|
|
51
|
+
}
|
|
36
52
|
export interface ParticleEmitterOptions {
|
|
37
53
|
/** drawn per particle; omit for a pure simulation that renders nothing (tests, headless) */
|
|
38
54
|
texture?: Texture2D;
|
|
@@ -64,6 +80,8 @@ export interface ParticleEmitterOptions {
|
|
|
64
80
|
alpha?: readonly [number, number];
|
|
65
81
|
/** rotation change in radians per second */
|
|
66
82
|
spin?: ParticleRange;
|
|
83
|
+
/** where in local space particles are born; without one they all start at the origin */
|
|
84
|
+
spawn?: ParticleSpawnArea;
|
|
67
85
|
/** multiplied into every particle sprite; ignored without a `texture` */
|
|
68
86
|
tint?: number;
|
|
69
87
|
}
|
|
@@ -118,6 +136,8 @@ export declare class ParticleEmitter extends Container {
|
|
|
118
136
|
private readonly spin;
|
|
119
137
|
/** the frame sequence to walk, if one was given - `texture` is ignored in its favour */
|
|
120
138
|
private readonly frames?;
|
|
139
|
+
/** where in local space births happen, or null for a single point at the origin */
|
|
140
|
+
private readonly spawnArea;
|
|
121
141
|
private emitting;
|
|
122
142
|
/** where the next pool search starts, so reuse stays O(1) amortised rather than O(pool) */
|
|
123
143
|
private poolCursor;
|
|
@@ -140,6 +160,12 @@ export declare class ParticleEmitter extends Container {
|
|
|
140
160
|
*/
|
|
141
161
|
burst(count: number): number;
|
|
142
162
|
private spawn;
|
|
163
|
+
/**
|
|
164
|
+
* One seeded draw for where in local space this birth happens. A point emitter draws
|
|
165
|
+
* nothing here, which is what keeps an emitter without a spawn area on exactly the random
|
|
166
|
+
* sequence it had before, so an existing seeded replay does not shift.
|
|
167
|
+
*/
|
|
168
|
+
private spawnOffset;
|
|
143
169
|
update(dt: number): void;
|
|
144
170
|
/** mirrors the pool onto the sprites; a no-op for a textureless emitter */
|
|
145
171
|
private draw;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Particles.d.ts","sourceRoot":"","sources":["../../../src/two-d/render/Particles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAU,MAAM,SAAS,CAAC;AAG5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C;;;;;;GAMG;AACH,MAAM,WAAW,QAAQ;IACxB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IAEX,0BAA0B;IAC1B,GAAG,EAAE,MAAM,CAAC;IAEZ,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;IAEb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IAEb,mFAAmF;IACnF,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IAEd;;;;;OAKG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd,uDAAuD;IACvD,MAAM,EAAE,OAAO,CAAC;CAChB;AAED,uFAAuF;AACvF,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE/D,MAAM,WAAW,sBAAsB;IACtC,4FAA4F;IAC5F,OAAO,CAAC,EAAE,SAAS,CAAC;IAEpB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,SAAS,SAAS,EAAE,CAAC;IAE9B,mGAAmG;IACnG,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,qFAAqF;IACrF,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,+BAA+B;IAC/B,IAAI,CAAC,EAAE,aAAa,CAAC;IAErB,4CAA4C;IAC5C,KAAK,CAAC,EAAE,aAAa,CAAC;IAEtB,2EAA2E;IAC3E,KAAK,CAAC,EAAE,aAAa,CAAC;IAEtB,wFAAwF;IACxF,OAAO,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAEnC,yEAAyE;IACzE,KAAK,CAAC,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAElC,kCAAkC;IAClC,KAAK,CAAC,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAElC,4CAA4C;IAC5C,IAAI,CAAC,EAAE,aAAa,CAAC;IAErB,yEAAyE;IACzE,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,qBAAa,eAAgB,SAAQ,SAAS;IAC7C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAkB;IACvC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IAExC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgB;IACrC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgB;IACtC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAgB;IAC3C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA4B;IACvD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA4B;IACvD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgB;IAErC,wFAAwF;IACxF,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAuB;IAE/C,OAAO,CAAC,QAAQ,CAAS;IAEzB,2FAA2F;IAC3F,OAAO,CAAC,UAAU,CAAK;IAEvB,2FAA2F;IAC3F,OAAO,CAAC,IAAI,CAAK;IAEjB,YAAY,OAAO,GAAE,sBAA2B,
|
|
1
|
+
{"version":3,"file":"Particles.d.ts","sourceRoot":"","sources":["../../../src/two-d/render/Particles.ts"],"names":[],"mappings":"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"}
|
|
@@ -62,6 +62,7 @@ export class ParticleEmitter extends Container {
|
|
|
62
62
|
this.alphaRange = options.alpha ?? [1, 0];
|
|
63
63
|
this.spin = options.spin ?? 0;
|
|
64
64
|
this.frames = options.frames;
|
|
65
|
+
this.spawnArea = options.spawn ?? null;
|
|
65
66
|
for (let i = 0; i < max; i++) {
|
|
66
67
|
this.pool.push({
|
|
67
68
|
x: 0,
|
|
@@ -145,8 +146,9 @@ export class ParticleEmitter extends Container {
|
|
|
145
146
|
const speed = pick(this.speed);
|
|
146
147
|
//reduced motion keeps the puff and its fade but drops the travel and spin
|
|
147
148
|
const motion = reducedMotion() ? 0 : 1;
|
|
148
|
-
|
|
149
|
-
particle.
|
|
149
|
+
const offset = this.spawnOffset();
|
|
150
|
+
particle.x = this.x + offset.x;
|
|
151
|
+
particle.y = this.y + offset.y;
|
|
150
152
|
particle.vx = Math.cos(angle) * speed * motion;
|
|
151
153
|
particle.vy = Math.sin(angle) * speed * motion;
|
|
152
154
|
particle.age = 0;
|
|
@@ -159,6 +161,25 @@ export class ParticleEmitter extends Container {
|
|
|
159
161
|
particle.active = true;
|
|
160
162
|
return true;
|
|
161
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* One seeded draw for where in local space this birth happens. A point emitter draws
|
|
166
|
+
* nothing here, which is what keeps an emitter without a spawn area on exactly the random
|
|
167
|
+
* sequence it had before, so an existing seeded replay does not shift.
|
|
168
|
+
*/
|
|
169
|
+
spawnOffset() {
|
|
170
|
+
const area = this.spawnArea;
|
|
171
|
+
if (!area)
|
|
172
|
+
return { x: 0, y: 0 };
|
|
173
|
+
const height = area.height ?? area.width;
|
|
174
|
+
if (area.shape === 'rect') {
|
|
175
|
+
return { x: (Random.float() - 0.5) * area.width, y: (Random.float() - 0.5) * height };
|
|
176
|
+
}
|
|
177
|
+
//the square root of the radius spreads points uniformly over the ellipse's area
|
|
178
|
+
//instead of clustering them at the centre, the same correction a uniform disc needs
|
|
179
|
+
const radius = Math.sqrt(Random.float());
|
|
180
|
+
const angle = Random.float() * Math.PI * 2;
|
|
181
|
+
return { x: Math.cos(angle) * radius * (area.width / 2), y: Math.sin(angle) * radius * (height / 2) };
|
|
182
|
+
}
|
|
162
183
|
update(dt) {
|
|
163
184
|
if (this.emitting && this.rate > 0) {
|
|
164
185
|
this.debt += this.rate * dt;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Particles.js","sourceRoot":"","sources":["../../../src/two-d/render/Particles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,MAAM,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"Particles.js","sourceRoot":"","sources":["../../../src/two-d/render/Particles.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export interface AttachmentPoint {
|
|
2
|
+
x: number;
|
|
3
|
+
y: number;
|
|
4
|
+
}
|
|
5
|
+
export interface SpriteAttachmentOptions {
|
|
6
|
+
/** where the attachment sits relative to its owner, in pixels */
|
|
7
|
+
offsetX?: number;
|
|
8
|
+
offsetY?: number;
|
|
9
|
+
/** seconds this attachment lives before `done`; omit for one that lives as long as its owner does */
|
|
10
|
+
duration?: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Ties a second sprite's position to a first one's, with its own offset and its own optional
|
|
14
|
+
* lifetime - a shadow flat beneath a unit, a status icon floating above it, a shield glyph at a
|
|
15
|
+
* corner. `StatusVisuals` only tints a sprite that already exists; this is for a sprite that is
|
|
16
|
+
* its own thing, positioned relative to an owner rather than drawn as part of it.
|
|
17
|
+
*
|
|
18
|
+
* Renderer-neutral by the same design `Projectile` and `LightningArc` use: it takes any
|
|
19
|
+
* `{ x, y }` point (a real Pixi `Sprite`, a plain object, a test double) and only ever writes
|
|
20
|
+
* to it, so it needs no renderer to construct or test. Z-order and parenting stay the caller's,
|
|
21
|
+
* for the same reason `Halo` leaves them: whether a shadow is added before or after its unit is
|
|
22
|
+
* a per-game drawing decision, not something a framework should guess.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* import { SpriteAttachment } from '@datamoc/mw_games/two-d/render';
|
|
27
|
+
*
|
|
28
|
+
* declare const shadowSprite: { x: number; y: number };
|
|
29
|
+
* declare const statusIcon: { x: number; y: number };
|
|
30
|
+
*
|
|
31
|
+
* const shadow = new SpriteAttachment(shadowSprite, { offsetY: 4 });
|
|
32
|
+
* const poisoned = new SpriteAttachment(statusIcon, { offsetY: -20, duration: 3 });
|
|
33
|
+
*
|
|
34
|
+
* shadow.follow(100, 200);
|
|
35
|
+
* poisoned.follow(100, 200);
|
|
36
|
+
*
|
|
37
|
+
* const expired = poisoned.update(1 / 60);
|
|
38
|
+
* console.log(expired); // false - not yet 3 seconds in
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare class SpriteAttachment {
|
|
42
|
+
private readonly child;
|
|
43
|
+
private readonly offsetX;
|
|
44
|
+
private readonly offsetY;
|
|
45
|
+
private readonly duration?;
|
|
46
|
+
private elapsed;
|
|
47
|
+
private expired;
|
|
48
|
+
constructor(child: AttachmentPoint, options?: SpriteAttachmentOptions);
|
|
49
|
+
get done(): boolean;
|
|
50
|
+
/** puts the attachment where its owner stands, with whatever offset it was built with */
|
|
51
|
+
follow(ownerX: number, ownerY: number): this;
|
|
52
|
+
/** @returns true the instant `duration` elapses, so a caller can remove it exactly once; always false with no `duration` */
|
|
53
|
+
update(dt: number): boolean;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=SpriteAttachment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpriteAttachment.d.ts","sourceRoot":"","sources":["../../../src/two-d/render/SpriteAttachment.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC/B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACV;AAED,MAAM,WAAW,uBAAuB;IACvC,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,qGAAqG;IACrG,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBAAa,gBAAgB;IAC5B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkB;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAS;IACnC,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,OAAO,CAAS;IAExB,YAAY,KAAK,EAAE,eAAe,EAAE,OAAO,GAAE,uBAA4B,EAKxE;IAED,IAAI,IAAI,IAAI,OAAO,CAElB;IAED,yFAAyF;IACzF,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAI3C;IAED,4HAA4H;IAC5H,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAQ1B;CACD"}
|