fruta 0.0.3 → 0.1.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 +367 -26
- package/dist/animation/anim.d.ts +13 -0
- package/dist/animation/animate.d.ts +101 -0
- package/dist/audio/audio.d.ts +29 -0
- package/dist/fruta.d.ts +302 -0
- package/dist/fruta.js +19 -0
- package/dist/frutaGl.d.ts +277 -0
- package/dist/game/behaviors.d.ts +38 -0
- package/dist/game/charts.d.ts +127 -0
- package/dist/game/play.d.ts +4 -0
- package/dist/game/project.d.ts +54 -0
- package/dist/input/gamepad.d.ts +38 -0
- package/dist/math/math.d.ts +37 -0
- package/dist/math/pathfind.d.ts +7 -0
- package/dist/math/physics.d.ts +38 -0
- package/dist/math/pool.d.ts +11 -0
- package/dist/math/spatial.d.ts +10 -0
- package/dist/render/create/FontCreator.d.ts +16 -0
- package/dist/render/create/SaveRestore.d.ts +5 -0
- package/dist/render/create/ShapeCreator.d.ts +49 -0
- package/dist/render/create/canvasTarget.d.ts +5 -0
- package/dist/render/shaders.d.ts +29 -0
- package/dist/render/webgl.d.ts +67 -0
- package/dist/renderer.d.ts +42 -0
- package/dist/types.d.ts +9 -0
- package/dist/utils/logStyle.d.ts +1 -0
- package/dist/world/camera.d.ts +24 -0
- package/dist/world/entities.d.ts +45 -0
- package/dist/world/particles.d.ts +42 -0
- package/dist/world/tilemap.d.ts +44 -0
- package/dist/world/timers.d.ts +11 -0
- package/package.json +35 -35
- package/DOCUMENTATION.MD +0 -874
- package/dist/main.js +0 -1
- package/index.html +0 -9
- package/settings.json +0 -6
- package/src/core/create/_fontCreator.js +0 -11
- package/src/core/create/_saveOrRestore.js +0 -22
- package/src/core/create/_shapeCreator.js +0 -20
- package/src/core/create/fontCreatorMixin.js +0 -167
- package/src/core/create/shapeCreatorMixin.js +0 -656
- package/src/core/fruta.js +0 -22
- package/src/core/game/game.js +0 -30
- package/src/core/game/scene.js +0 -29
- package/src/core/game/tick.js +0 -42
- package/src/core/utils/logStyle.js +0 -8
- package/src/core/utils/utils.js +0 -0
- package/src/methods/constants.js +0 -0
- package/src/methods/creator/_scene.js +0 -0
- package/src/methods/creator/creator.js +0 -0
- package/webpack.config.js +0 -47
package/dist/fruta.d.ts
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import ShapeCreator from './render/create/ShapeCreator';
|
|
2
|
+
import FontCreator from './render/create/FontCreator';
|
|
3
|
+
import SaveRestore from './render/create/SaveRestore';
|
|
4
|
+
import { Tween, Timeline, type EaseFn, type EaseName } from './animation/animate';
|
|
5
|
+
import { GamepadController, type GamepadConfig } from './input/gamepad';
|
|
6
|
+
import * as M from './math/math';
|
|
7
|
+
import { World, type WorldOptions } from './math/physics';
|
|
8
|
+
import { type PlayOptions, type Sound, type BeepOptions } from './audio/audio';
|
|
9
|
+
import { Camera } from './world/camera';
|
|
10
|
+
import { Tilemap, type TilemapConfig } from './world/tilemap';
|
|
11
|
+
import { type BurstOptions, type Emitter, type EmitterOptions } from './world/particles';
|
|
12
|
+
import { Anim, type AnimState } from './animation/anim';
|
|
13
|
+
import { type Entity, type EntitySpec } from './world/entities';
|
|
14
|
+
import { type TimerHandle } from './world/timers';
|
|
15
|
+
import * as charts from './game/charts';
|
|
16
|
+
import { FrutaGL, type FrutaGLConfig } from './frutaGl';
|
|
17
|
+
import type { FrutaConfig } from './types';
|
|
18
|
+
export type { FrutaConfig } from './types';
|
|
19
|
+
export type { EaseName, EaseFn } from './animation/animate';
|
|
20
|
+
export { Tween, Timeline } from './animation/animate';
|
|
21
|
+
export { GamepadController } from './input/gamepad';
|
|
22
|
+
export type { GamepadConfig } from './input/gamepad';
|
|
23
|
+
export { lerp, clamp, map, dist, angle, rand, randInt, pick, hits, inside, resolve, cubicBezier, bezierPath, hexToPixel, pixelToHex, hexRound, hexNeighbors } from './math/math';
|
|
24
|
+
export type { Rect, Circle, Shape, Vec, Hex } from './math/math';
|
|
25
|
+
export { World } from './math/physics';
|
|
26
|
+
export type { Body as PhysicsBody, WorldOptions } from './math/physics';
|
|
27
|
+
export { findPath } from './math/pathfind';
|
|
28
|
+
export type { Cell } from './math/pathfind';
|
|
29
|
+
export { Pool } from './math/pool';
|
|
30
|
+
export { SpatialHash } from './math/spatial';
|
|
31
|
+
export interface SolveBody {
|
|
32
|
+
x: number;
|
|
33
|
+
y: number;
|
|
34
|
+
w: number;
|
|
35
|
+
h: number;
|
|
36
|
+
vx?: number;
|
|
37
|
+
vy?: number;
|
|
38
|
+
onGround?: boolean;
|
|
39
|
+
}
|
|
40
|
+
export type { PlayOptions, Sound, BeepOptions } from './audio/audio';
|
|
41
|
+
export { Camera } from './world/camera';
|
|
42
|
+
export { Tilemap } from './world/tilemap';
|
|
43
|
+
export type { TilemapConfig, TileDef, Body } from './world/tilemap';
|
|
44
|
+
export { Anim } from './animation/anim';
|
|
45
|
+
export type { AnimState } from './animation/anim';
|
|
46
|
+
export type { BurstOptions, Emitter, EmitterOptions } from './world/particles';
|
|
47
|
+
export type { Entity, EntitySpec } from './world/entities';
|
|
48
|
+
export type { TimerHandle } from './world/timers';
|
|
49
|
+
export type { ChartSurface, ChartDatum, Series, RadarSeries, BarOptions, LineChartOptions, PieOptions, RadarOptions, LegendOptions } from './game/charts';
|
|
50
|
+
export { playProject } from './game/play';
|
|
51
|
+
export { validateProject, saveProject, loadProject } from './game/project';
|
|
52
|
+
export type { GameProject, GameScene, GameEntity, BehaviorSpec } from './game/project';
|
|
53
|
+
export { BEHAVIORS, behaviorNames, resolveBehavior } from './game/behaviors';
|
|
54
|
+
export type { BehaviorFn, BehaviorFactory, PlayEntity, PlayContext } from './game/behaviors';
|
|
55
|
+
export { WebGLBatch, Shader, PostFX } from './render/webgl';
|
|
56
|
+
export type { QuadOptions, Uniforms } from './render/webgl';
|
|
57
|
+
export { Shaders } from './render/shaders';
|
|
58
|
+
export type { ShaderName } from './render/shaders';
|
|
59
|
+
export { FrutaGL, GLCamera } from './frutaGl';
|
|
60
|
+
export type { FrutaGLConfig, GLRect, GLCircle, GLEllipse, GLLine, GLText, GLSprite, GLTransform, GLPolygon, GLGradient, GLFill, GLScene, GLTweenOptions } from './frutaGl';
|
|
61
|
+
export interface Scene {
|
|
62
|
+
enter?(): void;
|
|
63
|
+
update?(dt: number, time: number): void;
|
|
64
|
+
leave?(): void;
|
|
65
|
+
}
|
|
66
|
+
export type Fill = string | CanvasGradient | CanvasPattern;
|
|
67
|
+
type Color = Fill;
|
|
68
|
+
export interface Point {
|
|
69
|
+
x: number;
|
|
70
|
+
y: number;
|
|
71
|
+
}
|
|
72
|
+
export type LoopFn = (dt: number, time: number) => void;
|
|
73
|
+
export interface RectOptions {
|
|
74
|
+
x: number;
|
|
75
|
+
y: number;
|
|
76
|
+
w: number;
|
|
77
|
+
h: number;
|
|
78
|
+
fill?: Color;
|
|
79
|
+
stroke?: Color;
|
|
80
|
+
strokeWidth?: number;
|
|
81
|
+
radius?: number;
|
|
82
|
+
rotation?: number;
|
|
83
|
+
}
|
|
84
|
+
export interface CircleOptions {
|
|
85
|
+
x: number;
|
|
86
|
+
y: number;
|
|
87
|
+
r: number;
|
|
88
|
+
fill?: Color;
|
|
89
|
+
stroke?: Color;
|
|
90
|
+
strokeWidth?: number;
|
|
91
|
+
}
|
|
92
|
+
export interface EllipseOptions {
|
|
93
|
+
x: number;
|
|
94
|
+
y: number;
|
|
95
|
+
rx: number;
|
|
96
|
+
ry: number;
|
|
97
|
+
rotation?: number;
|
|
98
|
+
fill?: Color;
|
|
99
|
+
stroke?: Color;
|
|
100
|
+
strokeWidth?: number;
|
|
101
|
+
}
|
|
102
|
+
export interface LineOptions {
|
|
103
|
+
x1: number;
|
|
104
|
+
y1: number;
|
|
105
|
+
x2: number;
|
|
106
|
+
y2: number;
|
|
107
|
+
stroke?: Color;
|
|
108
|
+
strokeWidth?: number;
|
|
109
|
+
}
|
|
110
|
+
export interface PolygonOptions {
|
|
111
|
+
points: Point[];
|
|
112
|
+
fill?: Color;
|
|
113
|
+
stroke?: Color;
|
|
114
|
+
strokeWidth?: number;
|
|
115
|
+
close?: boolean;
|
|
116
|
+
}
|
|
117
|
+
export interface TextOptions {
|
|
118
|
+
x: number;
|
|
119
|
+
y: number;
|
|
120
|
+
fill?: Color;
|
|
121
|
+
size?: number;
|
|
122
|
+
font?: string;
|
|
123
|
+
align?: CanvasTextAlign;
|
|
124
|
+
baseline?: CanvasTextBaseline;
|
|
125
|
+
stroke?: Color;
|
|
126
|
+
strokeWidth?: number;
|
|
127
|
+
}
|
|
128
|
+
export interface ImageOptions {
|
|
129
|
+
x: number;
|
|
130
|
+
y: number;
|
|
131
|
+
w?: number;
|
|
132
|
+
h?: number;
|
|
133
|
+
}
|
|
134
|
+
export interface SpriteOptions {
|
|
135
|
+
x: number;
|
|
136
|
+
y: number;
|
|
137
|
+
w?: number;
|
|
138
|
+
h?: number;
|
|
139
|
+
frame?: number;
|
|
140
|
+
frameW?: number;
|
|
141
|
+
frameH?: number;
|
|
142
|
+
cols?: number;
|
|
143
|
+
rotation?: number;
|
|
144
|
+
anchor?: 'topleft' | 'center';
|
|
145
|
+
flipX?: boolean;
|
|
146
|
+
}
|
|
147
|
+
export interface Transform {
|
|
148
|
+
x?: number;
|
|
149
|
+
y?: number;
|
|
150
|
+
rotate?: number;
|
|
151
|
+
scale?: number | {
|
|
152
|
+
x: number;
|
|
153
|
+
y: number;
|
|
154
|
+
};
|
|
155
|
+
alpha?: number;
|
|
156
|
+
}
|
|
157
|
+
export interface TweenOptions {
|
|
158
|
+
to: Record<string, number | string>;
|
|
159
|
+
duration: number;
|
|
160
|
+
ease?: EaseName | EaseFn;
|
|
161
|
+
delay?: number;
|
|
162
|
+
repeat?: number;
|
|
163
|
+
yoyo?: boolean;
|
|
164
|
+
onUpdate?: (target: Record<string, number>, progress: number) => void;
|
|
165
|
+
onComplete?: () => void;
|
|
166
|
+
}
|
|
167
|
+
export interface StaggerOptions extends TweenOptions {
|
|
168
|
+
each?: number;
|
|
169
|
+
}
|
|
170
|
+
export declare class FrutaApp {
|
|
171
|
+
readonly canvas: HTMLCanvasElement;
|
|
172
|
+
readonly context: CanvasRenderingContext2D;
|
|
173
|
+
readonly shapes: ShapeCreator;
|
|
174
|
+
readonly fonts: FontCreator;
|
|
175
|
+
readonly state: SaveRestore;
|
|
176
|
+
readonly camera: Camera;
|
|
177
|
+
private _mouse;
|
|
178
|
+
private _pointerDown;
|
|
179
|
+
private keys;
|
|
180
|
+
private readonly images;
|
|
181
|
+
private readonly assets;
|
|
182
|
+
private readonly audio;
|
|
183
|
+
private userLoop;
|
|
184
|
+
private anims;
|
|
185
|
+
private gamepads;
|
|
186
|
+
private readonly particles;
|
|
187
|
+
private entities;
|
|
188
|
+
private scenes;
|
|
189
|
+
private currentScene;
|
|
190
|
+
private currentSceneName;
|
|
191
|
+
private transition;
|
|
192
|
+
private readonly timers;
|
|
193
|
+
private readonly warned;
|
|
194
|
+
private debugOn;
|
|
195
|
+
private fps;
|
|
196
|
+
private readonly watched;
|
|
197
|
+
private rafId;
|
|
198
|
+
private lastTime;
|
|
199
|
+
private bg?;
|
|
200
|
+
private autoClear;
|
|
201
|
+
timeScale: number;
|
|
202
|
+
private keyHandlers;
|
|
203
|
+
private pressHandlers;
|
|
204
|
+
private releaseHandlers;
|
|
205
|
+
private moveHandlers;
|
|
206
|
+
private clickHandlers;
|
|
207
|
+
private readonly scrollKeys;
|
|
208
|
+
private readonly handleKeyDown;
|
|
209
|
+
private readonly handleKeyUp;
|
|
210
|
+
private readonly handlePointerMove;
|
|
211
|
+
private readonly handlePointerDown;
|
|
212
|
+
private readonly handlePointerUp;
|
|
213
|
+
private readonly handleClick;
|
|
214
|
+
constructor(config?: FrutaConfig);
|
|
215
|
+
mount(parent?: HTMLElement): this;
|
|
216
|
+
clear(): this;
|
|
217
|
+
background(color: Color): this;
|
|
218
|
+
rect(o: RectOptions): this;
|
|
219
|
+
circle(o: CircleOptions): this;
|
|
220
|
+
ellipse(o: EllipseOptions): this;
|
|
221
|
+
line(o: LineOptions): this;
|
|
222
|
+
polygon(o: PolygonOptions): this;
|
|
223
|
+
text(content: string, o: TextOptions): this;
|
|
224
|
+
image(src: string, o: ImageOptions): this;
|
|
225
|
+
linearGradient(x1: number, y1: number, x2: number, y2: number, stops: Array<[number, string]>): CanvasGradient;
|
|
226
|
+
radialGradient(x: number, y: number, r: number, stops: Array<[number, string]>, innerRadius?: number): CanvasGradient;
|
|
227
|
+
barChart(o: charts.BarOptions): this;
|
|
228
|
+
lineChart(o: charts.LineChartOptions): this;
|
|
229
|
+
pieChart(o: charts.PieOptions): this;
|
|
230
|
+
radar(o: charts.RadarOptions): this;
|
|
231
|
+
legend(o: charts.LegendOptions): this;
|
|
232
|
+
load(assets: Record<string, string>): Promise<void>;
|
|
233
|
+
addImage(name: string, source: CanvasImageSource): this;
|
|
234
|
+
sprite(name: string, o: SpriteOptions): this;
|
|
235
|
+
frameAt(time: number, fps: number, frameCount: number): number;
|
|
236
|
+
tilemap(config: TilemapConfig): Tilemap;
|
|
237
|
+
play(name: string, opts?: PlayOptions): Sound;
|
|
238
|
+
beep(opts?: BeepOptions): this;
|
|
239
|
+
volume(v: number): this;
|
|
240
|
+
mute(on?: boolean): this;
|
|
241
|
+
hits(a: M.Shape, b: M.Shape): boolean;
|
|
242
|
+
inside(p: M.Vec, s: M.Shape): boolean;
|
|
243
|
+
solve(a: SolveBody, b: M.Rect): 'x' | 'y' | null;
|
|
244
|
+
physics(opts?: WorldOptions): World;
|
|
245
|
+
overlap<A extends M.Shape, B extends M.Shape>(a: A | A[], b: B | B[], onHit?: (a: A, b: B) => void): this;
|
|
246
|
+
lerp(a: number, b: number, t: number): number;
|
|
247
|
+
clamp(v: number, min: number, max: number): number;
|
|
248
|
+
map(v: number, inMin: number, inMax: number, outMin: number, outMax: number): number;
|
|
249
|
+
dist(ax: number, ay: number, bx: number, by: number): number;
|
|
250
|
+
angle(ax: number, ay: number, bx: number, by: number): number;
|
|
251
|
+
rand(min: number, max: number): number;
|
|
252
|
+
randInt(min: number, max: number): number;
|
|
253
|
+
pick<T>(arr: T[]): T;
|
|
254
|
+
push(t?: Transform): this;
|
|
255
|
+
pop(): this;
|
|
256
|
+
loop(fn: LoopFn): this;
|
|
257
|
+
stop(): this;
|
|
258
|
+
scene(name: string, def: Scene): this;
|
|
259
|
+
start(name: string, fadeSeconds?: number): this;
|
|
260
|
+
private swapScene;
|
|
261
|
+
burst(opts: BurstOptions): this;
|
|
262
|
+
drawParticles(): this;
|
|
263
|
+
anim(states: Record<string, AnimState>): Anim;
|
|
264
|
+
emit(opts: EmitterOptions): Emitter;
|
|
265
|
+
after(seconds: number, fn: () => void): TimerHandle;
|
|
266
|
+
every(seconds: number, fn: () => void): TimerHandle;
|
|
267
|
+
store(key: string, value: unknown): this;
|
|
268
|
+
stored<T>(key: string, fallback: T): T;
|
|
269
|
+
add(spec: EntitySpec): Entity;
|
|
270
|
+
drawEntities(): this;
|
|
271
|
+
get all(): Entity[];
|
|
272
|
+
debug(on?: boolean): this;
|
|
273
|
+
watch(label: string, value: unknown): this;
|
|
274
|
+
private drawDebug;
|
|
275
|
+
tween<T extends object>(target: T, opts: TweenOptions): Tween;
|
|
276
|
+
stagger<T extends object>(targets: T[], opts: StaggerOptions): Tween[];
|
|
277
|
+
timeline(): Timeline;
|
|
278
|
+
private buildTween;
|
|
279
|
+
private startEngine;
|
|
280
|
+
get mouse(): Point;
|
|
281
|
+
get mouseDown(): boolean;
|
|
282
|
+
keyDown(key: string): boolean;
|
|
283
|
+
onKey(key: string, fn: () => void): this;
|
|
284
|
+
onPress(fn: (p: Point) => void): this;
|
|
285
|
+
onRelease(fn: (p: Point) => void): this;
|
|
286
|
+
onClick(fn: (p: Point) => void): this;
|
|
287
|
+
onMove(fn: (p: Point) => void): this;
|
|
288
|
+
gamepad(config?: GamepadConfig): GamepadController;
|
|
289
|
+
destroy(): void;
|
|
290
|
+
private updateMouse;
|
|
291
|
+
private normalizeColor;
|
|
292
|
+
private paint;
|
|
293
|
+
}
|
|
294
|
+
interface FrutaFactory {
|
|
295
|
+
(config: FrutaConfig & {
|
|
296
|
+
renderer: 'webgl';
|
|
297
|
+
}): FrutaGL;
|
|
298
|
+
(config?: FrutaConfig): FrutaApp;
|
|
299
|
+
gl(config?: FrutaGLConfig): FrutaGL;
|
|
300
|
+
}
|
|
301
|
+
declare const Fruta: FrutaFactory;
|
|
302
|
+
export default Fruta;
|
package/dist/fruta.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
class T{canvas;context;constructor($,J){this.canvas=$;this.context=J}}class c extends T{fillStyleShape($){return this.context.fillStyle=$,this}drawRectShape($=[0,0,0,0],J=[0,0,0,0],K=[0,0,0,0]){return this.context.clearRect(...$),this.context.fillRect(...J),this.context.strokeRect(...K),this}roundRectShape($,J,K,Z,V){return this.context.roundRect($,J,K,Z,V),this}startShape(){return this.context.beginPath(),this}strokeShape($){if($)this.context.stroke($);else this.context.stroke();return this}moveTo($,J){return this.context.moveTo($,J),this}lineTo($,J){return this.context.lineTo($,J),this}fillShape($){if($)this.context.fill($);else this.context.fill();return this}arcShape($,J,K,Z,V,Q){return this.context.arc($,J,K,Z,V,Q),this}quadraticCurveTo($,J,K,Z){return this.context.quadraticCurveTo($,J,K,Z),this}bezierCurveTo($,J,K,Z,V,Q){return this.context.bezierCurveTo($,J,K,Z,V,Q),this}arcTo($,J,K,Z,V){return this.context.arcTo($,J,K,Z,V),this}path2DShape($){return $?new Path2D($):new Path2D}domMatrixShape(){return new DOMMatrix}addPathShape($,J,K){return $.addPath(J,K),$}ellipseShape($,J,K,Z,V,Q,H,N){return this.context.ellipse($,J,K,Z,V,Q,H,N),this}createLinearGradient($,J,K,Z){return this.context.createLinearGradient($,J,K,Z)}createRadialGradient($,J,K,Z,V,Q){return this.context.createRadialGradient($,J,K,Z,V,Q)}addColorStop($,J){let K=Math.max(J.length-1,1);return J.forEach((Z,V)=>$.addColorStop(V/K,Z.color)),this}createPattern($,J="repeat"){return this.context.createPattern($,J)}lineCapShape($){return this.context.lineCap=$,this}lineJoinShape($){return this.context.lineJoin=$,this}lineWidthShape($){return this.context.lineWidth=$,this}miterLimitShape($){return this.context.miterLimit=$,this}shadowBlurOfShape($){return this.context.shadowBlur=$,this}shadowColorOfShape($){return this.context.shadowColor=$,this}shadowOffSetXY($,J){return this.context.shadowOffsetX=$,this.context.shadowOffsetY=J,this}strokeStyle($){return this.context.strokeStyle=$,this}scaleShape($,J){return this.context.scale($,J),this}rotateShape($){return this.context.rotate($),this}translateShape($,J){return this.context.translate($,J),this}transformShape($,J,K,Z,V,Q){return this.context.transform($,J,K,Z,V,Q),this}setTransformShape($,J,K,Z,V,Q){return this.context.setTransform($,J,K,Z,V,Q),this}globalAlpha($){return this.context.globalAlpha=$,this}globalCompositeOperation($){return this.context.globalCompositeOperation=$,this}clipShape(){return this.context.clip(),this}shadowColor($){return this.context.shadowColor=$,this}createImage($){let J=new Image,K=this.context.drawImage;return J.onload=()=>K(J,...$.img),J.src=$.src,this}createImageData($,J){return this.context.createImageData($,J)}getImgData($,J,K,Z){return this.context.getImageData($,J,K,Z)}putImageData($,J,K){return this.context.putImageData($,J,K),this}}class n extends T{loadFont($,J,K){return new FontFace($,J,K).load().then((V)=>document.fonts.add(V)),this}fontRenderingType($){return this.context.textRendering=$,this}fontFill($,J,K,Z){return this.context.fillText($,J,K,Z),this}fontStrokeText($,J,K,Z){return this.context.strokeText($,J,K,Z),this}fontStyle($){return this.context.font=$,this}fontAlign($){return this.context.textAlign=$,this}fontDirection($){return this.context.direction=$,this}fontBaseline($){return this.context.textBaseline=$,this}fontVariantCaps($){return this.context.fontVariantCaps=$,this}fontKerning($){return this.context.fontKerning=$,this}fontWordSpacing($){return this.context.wordSpacing=$,this}fontS($){return this.context.fontStretch=$,this}fontMeasure($){return this.context.measureText($)}}class s extends T{save(){this.context.save()}restore(){this.context.restore()}}var Y0=($)=>{console.log(`%c${$}`,"font-weight: bold; font-size: 50px;color: red; text-shadow: 3px 3px 0 rgb(217,31,38) , 6px 6px 0 rgb(226,91,14) , 9px 9px 0 rgb(245,221,8) , 12px 12px 0 rgb(5,148,68) , 15px 15px 0 rgb(2,135,206) , 18px 18px 0 rgb(4,77,145) , 21px 21px 0 rgb(42,21,113)")};var X0=2*Math.PI/3,j0=2*Math.PI/4.5,m=($)=>{if($<0.36363636363636365)return 7.5625*$*$;if($<0.7272727272727273)return 7.5625*($-=0.5454545454545454)*$+0.75;if($<0.9090909090909091)return 7.5625*($-=0.8181818181818182)*$+0.9375;return 7.5625*($-=0.9545454545454546)*$+0.984375},R0=($)=>$===0||$===1?$:Math.pow(2,-10*$)*Math.sin(($*10-0.75)*X0)+1,a={linear:($)=>$,easeIn:($)=>$*$,easeOut:($)=>1-(1-$)*(1-$),easeInOut:($)=>$<0.5?2*$*$:1-Math.pow(-2*$+2,2)/2,quadIn:($)=>$*$,quadOut:($)=>1-(1-$)*(1-$),quadInOut:($)=>$<0.5?2*$*$:1-Math.pow(-2*$+2,2)/2,cubicIn:($)=>$*$*$,cubicOut:($)=>1-Math.pow(1-$,3),cubicInOut:($)=>$<0.5?4*$*$*$:1-Math.pow(-2*$+2,3)/2,quartIn:($)=>$*$*$*$,quartOut:($)=>1-Math.pow(1-$,4),quartInOut:($)=>$<0.5?8*$*$*$*$:1-Math.pow(-2*$+2,4)/2,sineIn:($)=>1-Math.cos($*Math.PI/2),sineOut:($)=>Math.sin($*Math.PI/2),sineInOut:($)=>-(Math.cos(Math.PI*$)-1)/2,expoIn:($)=>$===0?0:Math.pow(2,10*$-10),expoOut:($)=>$===1?1:1-Math.pow(2,-10*$),expoInOut:($)=>$===0?0:$===1?1:$<0.5?Math.pow(2,20*$-10)/2:(2-Math.pow(2,-20*$+10))/2,circIn:($)=>1-Math.sqrt(1-Math.pow($,2)),circOut:($)=>Math.sqrt(1-Math.pow($-1,2)),circInOut:($)=>$<0.5?(1-Math.sqrt(1-Math.pow(2*$,2)))/2:(Math.sqrt(1-Math.pow(-2*$+2,2))+1)/2,backIn:($)=>2.70158*$*$*$-1.70158*$*$,backOut:($)=>1+2.70158*Math.pow($-1,3)+1.70158*Math.pow($-1,2),backInOut:($)=>$<0.5?Math.pow(2*$,2)*(7.189819*$-2.5949095)/2:(Math.pow(2*$-2,2)*(3.5949095*(2*$-2)+2.5949095)+2)/2,bounceIn:($)=>1-m(1-$),bounceOut:m,bounceInOut:($)=>$<0.5?(1-m(1-2*$))/2:(1+m(2*$-1))/2,elasticIn:($)=>$===0||$===1?$:-Math.pow(2,10*$-10)*Math.sin(($*10-10.75)*X0),elasticOut:R0,elasticInOut:($)=>$===0?0:$===1?1:$<0.5?-(Math.pow(2,20*$-10)*Math.sin((20*$-11.125)*j0))/2:Math.pow(2,-20*$+10)*Math.sin((20*$-11.125)*j0)/2+1,bounce:m,elastic:R0};function C($){if($=$.trim(),$[0]==="#"){let K=$.slice(1);if(K.length===3)K=K[0]+K[0]+K[1]+K[1]+K[2]+K[2];let Z=parseInt(K,16);return[Z>>16&255,Z>>8&255,Z&255,1]}let J=$.match(/rgba?\(([^)]+)\)/);if(J){let K=J[1].split(",").map((Z)=>parseFloat(Z));return[K[0]||0,K[1]||0,K[2]||0,K[3]??1]}return[0,0,0,1]}function e($,J,K){let Z=(Q)=>Math.max(0,Math.min(255,Math.round($[Q]+(J[Q]-$[Q])*K))),V=$[3]+(J[3]-$[3])*K;return`rgba(${Z(0)}, ${Z(1)}, ${Z(2)}, ${V.toFixed(3)})`}class k{target;spec;from={};captured=!1;elapsed=0;cancelled=!1;paused=!1;constructor($,J){this.target=$;this.spec=J}update($){if(this.cancelled)return!0;if(this.paused)return!1;if(this.elapsed+=$,this.elapsed<this.spec.delay)return!1;if(!this.captured){for(let B in this.spec.to)this.from[B]=this.target[B];this.captured=!0}let{duration:J,repeat:K,yoyo:Z,ease:V,to:Q,onUpdate:H,onComplete:N}=this.spec,G=this.elapsed-this.spec.delay,I=K+1,_=J>0?Math.floor(G/J):I,F=_>=I,W;if(F){let B=I-1;W=Z&&B%2===1?0:1}else{let B=J>0?G%J/J:1;W=Z&&_%2===1?1-B:B}let D=V(W);for(let B in Q)this.target[B]=this.from[B]+(Q[B]-this.from[B])*D;if(H?.(this.target,D),F)return N?.(),!0;return!1}cancel(){this.cancelled=!0}pause(){this.paused=!0}resume(){this.paused=!1}}class w{factory;children=[];cursor=0;finishedCb;constructor($){this.factory=$}to($,J,K={}){let V=(K.at??this.cursor)+(J.delay??0);return this.children.push({u:this.factory($,J,V),done:!1}),this.cursor=V+J.duration,this}call($,J={}){let K=J.at??this.cursor;return this.children.push({u:this.factory({},{to:{},duration:0,onComplete:$},K),done:!1}),this}then($){return this.finishedCb=$,this}update($){let J=!0;for(let K of this.children)if(!K.done){if(K.done=K.u.update($),!K.done)J=!1}if(J&&this.finishedCb)this.finishedCb(),this.finishedCb=void 0;return J}}var C0={A:0,B:1,X:2,Y:3,LB:4,RB:5,LT:6,RT:7,Back:8,Start:9,L3:10,R3:11,Up:12,Down:13,Left:14,Right:15,Home:16},c0={leftX:0,leftY:1,rightX:2,rightY:3},n0=Object.fromEntries(Object.entries(C0).map(([$,J])=>[J,$]));class v{config;prev=new Set;curr=new Set;pad=null;liveIndex=null;onConnect=($)=>{if(this.config.index===void 0||this.config.index===$.gamepad.index)this.liveIndex=$.gamepad.index};onDisconnect=($)=>{if(this.liveIndex===$.gamepad.index)this.liveIndex=null};constructor($){this.config=$;window.addEventListener("gamepadconnected",this.onConnect),window.addEventListener("gamepaddisconnected",this.onDisconnect)}update(){let $=Array.from(navigator.getGamepads?.()??[]),J=this.config.index??this.liveIndex??-1,K=J>=0&&$[J]?J:$.findIndex((Z)=>Z);if(this.pad=K>=0?$[K]??null:null,this.prev=this.curr,this.curr=new Set,this.pad)this.pad.buttons.forEach((Z,V)=>{if(Z.pressed)this.curr.add(V)})}dispose(){window.removeEventListener("gamepadconnected",this.onConnect),window.removeEventListener("gamepaddisconnected",this.onDisconnect)}indexOf($){let J=this.config.buttons?.[$];if(typeof J==="number")return J;return C0[J??$]??-1}get connected(){return this.pad!==null}get mapping(){return this.pad?.mapping??""}down($){return this.curr.has(this.indexOf($))}pressed($){let J=this.indexOf($);return this.curr.has(J)&&!this.prev.has(J)}released($){let J=this.indexOf($);return!this.curr.has(J)&&this.prev.has(J)}value($){return this.pad?.buttons[this.indexOf($)]?.value??0}axis($){if(!this.pad)return 0;let K=this.config.axes?.[$]??c0[$]??-1,Z=this.pad.axes[K]??0;return Math.abs(Z)<(this.config.deadzone??0.12)?0:Z}stick($){return{x:this.axis(`${$}X`),y:this.axis(`${$}Y`)}}allPressed(){return[...this.curr].sort(($,J)=>$-J).map(($)=>n0[$]??`Button ${$}`)}pressedIndices(){return[...this.curr].sort(($,J)=>$-J)}axes(){return this.pad?Array.from(this.pad.axes):[]}}var Q0=($,J,K)=>$+(J-$)*K,H0=($,J,K)=>Math.min(Math.max($,J),K),G0=($,J,K,Z,V)=>Z+(V-Z)*($-J)/(K-J),I0=($,J,K,Z)=>Math.hypot(K-$,Z-J),N0=($,J,K,Z)=>Math.atan2(Z-J,K-$),W0=($,J)=>$+Math.random()*(J-$),F0=($,J)=>Math.floor($+Math.random()*(J-$+1)),_0=($)=>$[Math.floor(Math.random()*$.length)],V0=($)=>("r"in $),z=($,J)=>{let K=V0($),Z=V0(J);if(K&&Z)return Math.hypot($.x-J.x,$.y-J.y)<$.r+J.r;if(!K&&!Z)return $.x<J.x+J.w&&$.x+$.w>J.x&&$.y<J.y+J.h&&$.y+$.h>J.y;let V=K?J:$,Q=K?$:J,H=Math.max(V.x,Math.min(Q.x,V.x+V.w)),N=Math.max(V.y,Math.min(Q.y,V.y+V.h));return Math.hypot(Q.x-H,Q.y-N)<Q.r},x=($,J)=>V0(J)?Math.hypot($.x-J.x,$.y-J.y)<J.r:$.x>=J.x&&$.x<=J.x+J.w&&$.y>=J.y&&$.y<=J.y+J.h,g=($,J)=>{let K=Math.min($.x+$.w,J.x+J.w)-Math.max($.x,J.x),Z=Math.min($.y+$.h,J.y+J.h)-Math.max($.y,J.y);if(K<=0||Z<=0)return null;if(K<Z)return{x:$.x+$.w/2<J.x+J.w/2?-K:K,y:0};return{x:0,y:$.y+$.h/2<J.y+J.h/2?-Z:Z}},T0=($,J,K,Z,V)=>{let Q=1-V,H=Q*Q*Q,N=3*Q*Q*V,G=3*Q*V*V,I=V*V*V;return{x:H*$.x+N*J.x+G*K.x+I*Z.x,y:H*$.y+N*J.y+G*K.y+I*Z.y}},s0=($,J,K,Z,V=24)=>{let Q=[];for(let H=0;H<=V;H++)Q.push(T0($,J,K,Z,H/V));return Q},a0=($,J,K)=>({x:K*Math.sqrt(3)*($+J/2),y:K*1.5*J}),z0=($,J)=>{let K=$,Z=J,V=-K-Z,Q=Math.round(K),H=Math.round(V),N=Math.round(Z),G=Math.abs(Q-K),I=Math.abs(H-V),_=Math.abs(N-Z);if(G>I&&G>_)Q=-H-N;else if(I>_)H=-Q-N;else N=-Q-H;return{q:Q,r:N}},e0=($,J,K)=>z0((Math.sqrt(3)/3*$-J/3)/K,0.6666666666666666*J/K),o0=($,J)=>[{q:$+1,r:J},{q:$+1,r:J-1},{q:$,r:J-1},{q:$-1,r:J},{q:$-1,r:J+1},{q:$,r:J+1}];var S0=($)=>$.r!==void 0;function t0($,J){let K=S0($),Z=S0(J);if(K&&Z)return J8($,J);if(!K&&!Z)return $8($,J);if(K){let V=k0($,J);return V&&{nx:-V.nx,ny:-V.ny,depth:V.depth}}return k0(J,$)}function $8($,J){let{w:K,h:Z}=$,V=J.w,Q=J.h,H=Math.min($.x+K,J.x+V)-Math.max($.x,J.x),N=Math.min($.y+Z,J.y+Q)-Math.max($.y,J.y);if(H<=0||N<=0)return null;if(H<N)return{nx:J.x+V/2<$.x+K/2?-1:1,ny:0,depth:H};return{nx:0,ny:J.y+Q/2<$.y+Z/2?-1:1,depth:N}}function J8($,J){let K=J.x-$.x,Z=J.y-$.y,V=$.r+J.r,Q=Math.hypot(K,Z);if(Q>=V)return null;if(Q===0)return{nx:1,ny:0,depth:V};return{nx:K/Q,ny:Z/Q,depth:V-Q}}function k0($,J){let K=$.r,Z=J.w,V=J.h,Q=Math.max(J.x,Math.min($.x,J.x+Z)),H=Math.max(J.y,Math.min($.y,J.y+V)),N=$.x-Q,G=$.y-H,I=Math.hypot(N,G);if(I>0.0001){if(I>=K)return null;return{nx:N/I,ny:G/I,depth:K-I}}let _=$.x-J.x,F=J.x+Z-$.x,W=$.y-J.y,D=J.y+V-$.y,B=Math.min(_,F,W,D);if(B===_)return{nx:-1,ny:0,depth:K+_};if(B===F)return{nx:1,ny:0,depth:K+F};if(B===W)return{nx:0,ny:-1,depth:K+W};return{nx:0,ny:1,depth:K+D}}class u{bodies=[];gravity;iterations;damping;constructor($={}){this.gravity=$.gravity??1200,this.iterations=$.iterations??8,this.damping=$.damping??0}add($,J={}){let K=$;return K.vx??=0,K.vy??=0,K.mass??=1,K.restitution??=0.1,K.friction??=0.4,K.onGround??=!1,Object.assign(K,J),this.bodies.push(K),K}box($,J,K,Z,V={}){return this.add({x:$,y:J,w:K,h:Z},V)}circle($,J,K,Z={}){return this.add({x:$,y:J,r:K},Z)}static($,J,K,Z,V={}){return this.add({x:$,y:J,w:K,h:Z},{mass:0,...V})}remove($){let J=this.bodies.indexOf($);if(J>=0)this.bodies.splice(J,1)}step($){let J=this.damping?1/(1+this.damping*$):1;for(let Z of this.bodies){if(Z.mass===0)continue;Z.vy+=this.gravity*$,Z.vx*=J,Z.vy*=J,Z.x+=Z.vx*$,Z.y+=Z.vy*$,Z.onGround=!1}let K=[];for(let Z=0;Z<this.bodies.length;Z++)for(let V=Z+1;V<this.bodies.length;V++){let Q=this.bodies[Z],H=this.bodies[V];if(Q.mass===0&&H.mass===0)continue;let N=t0(Q,H);if(N)K.push({a:Q,b:H,nx:N.nx,ny:N.ny,depth:N.depth})}for(let Z=0;Z<this.iterations;Z++)for(let V of K)this.solveVelocity(V);for(let Z of K)this.correct(Z)}solveVelocity($){let{a:J,b:K,nx:Z,ny:V}=$,Q=J.mass===0?0:1/J.mass,H=K.mass===0?0:1/K.mass,N=Q+H;if(N===0)return;let G=K.vx-J.vx,I=K.vy-J.vy,_=G*Z+I*V;if(_>=0)return;let W=-(1+Math.min(J.restitution,K.restitution))*_/N;J.vx-=W*Z*Q,J.vy-=W*V*Q,K.vx+=W*Z*H,K.vy+=W*V*H,G=K.vx-J.vx,I=K.vy-J.vy;let D=G*Z+I*V,B=G-D*Z,L=I-D*V,P=Math.hypot(B,L);if(P<0.00001)return;B/=P,L/=P;let j=Math.sqrt(J.friction*K.friction),q=-(G*B+I*L)/N;q=Math.max(-W*j,Math.min(q,W*j)),J.vx-=q*B*Q,J.vy-=q*L*Q,K.vx+=q*B*H,K.vy+=q*L*H}correct($){let{a:J,b:K,nx:Z,ny:V,depth:Q}=$,H=J.mass===0?0:1/J.mass,N=K.mass===0?0:1/K.mass,G=H+N;if(G===0)return;let I=0.05,_=0.6,F=Math.max(Q-I,0)/G*_;if(J.x-=Z*F*H,J.y-=V*F*H,K.x+=Z*F*N,K.y+=V*F*N,V>0.5)J.onGround=!0;else if(V<-0.5)K.onGround=!0}}var K8={stop(){},volume(){}};class p{ctx=null;master=null;buffers=new Map;muted=!1;level=1;ensure(){if(!this.ctx)this.ctx=new AudioContext,this.master=this.ctx.createGain(),this.master.gain.value=this.level,this.master.connect(this.ctx.destination);return this.ctx}resume(){if(this.ctx&&this.ctx.state==="suspended")this.ctx.resume()}async loadSound($,J){let K=this.ensure();try{let Z=await fetch(J),V=await K.decodeAudioData(await Z.arrayBuffer());this.buffers.set($,V)}catch{}}play($,J={}){let K=this.ensure(),Z=this.buffers.get($);if(!Z||!this.master)return K8;let V=K.createBufferSource();V.buffer=Z,V.loop=J.loop??!1,V.playbackRate.value=J.rate??1;let Q=K.createGain();return Q.gain.value=J.volume??1,V.connect(Q).connect(this.master),V.start(),{stop(){try{V.stop()}catch{}},volume(H){Q.gain.value=H}}}beep($={}){let J=this.ensure();if(!this.master)return;let K=J.createOscillator(),Z=J.createGain();K.type=$.type??"square",K.frequency.value=$.freq??440;let V=$.duration??0.12,Q=J.currentTime;Z.gain.setValueAtTime($.volume??0.3,Q),Z.gain.exponentialRampToValueAtTime(0.0001,Q+V),K.connect(Z).connect(this.master),K.start(Q),K.stop(Q+V)}volume($){if(this.level=$,this.master)this.master.gain.value=this.muted?0:$}mute($=!0){if(this.muted=$,this.master)this.master.gain.value=$?0:this.level}}class o{canvas;ctx;x=0;y=0;zoom=1;shakeAmt=0;shakeLeft=0;shakeDur=0;constructor($,J){this.canvas=$;this.ctx=J}shake($,J=0.3){this.shakeAmt=$,this.shakeDur=J,this.shakeLeft=J}tick($){if(this.shakeLeft>0)this.shakeLeft=Math.max(0,this.shakeLeft-$)}follow($,J=1){let K=this.canvas.width/this.zoom,Z=this.canvas.height/this.zoom;this.x+=($.x-K/2-this.x)*J,this.y+=($.y-Z/2-this.y)*J}clamp($,J,K,Z){let V=this.canvas.width/this.zoom,Q=this.canvas.height/this.zoom;this.x=Math.max($,Math.min(this.x,Math.max($,K-V))),this.y=Math.max(J,Math.min(this.y,Math.max(J,Z-Q)))}begin(){this.ctx.save(),this.ctx.scale(this.zoom,this.zoom);let $=0,J=0;if(this.shakeLeft>0){let K=this.shakeAmt*(this.shakeLeft/this.shakeDur);$=(Math.random()-0.5)*2*K,J=(Math.random()-0.5)*2*K}this.ctx.translate(-this.x+$,-this.y+J)}end(){this.ctx.restore()}screenToWorld($,J){return{x:$/this.zoom+this.x,y:J/this.zoom+this.y}}}function D0($,J,K,Z,V,Q={}){let H=Q.diagonal??!0,N=(q,U)=>q>=0&&U>=0&&q<K&&U<Z;if(!N($.x,$.y)||!N(J.x,J.y)||V(J.x,J.y))return null;let G=(q,U)=>U*K+q,I=G($.x,$.y),_=G(J.x,J.y);if(I===_)return[{x:$.x,y:$.y}];let F=(q,U)=>{let M=Math.abs(q-J.x),Y=Math.abs(U-J.y);return H?M+Y+(Math.SQRT2-2)*Math.min(M,Y):M+Y},W=new Set([I]),D=new Map,B=new Map([[I,0]]),L=new Map([[I,F($.x,$.y)]]),P=H?[[1,0],[-1,0],[0,1],[0,-1],[1,1],[1,-1],[-1,1],[-1,-1]]:[[1,0],[-1,0],[0,1],[0,-1]],j=K*Z*4;while(W.size&&j-- >0){let q=-1,U=1/0;for(let E of W){let O=L.get(E)??1/0;if(O<U)U=O,q=E}if(q===_){let E=[],O=q;while(!0){if(E.push({x:O%K,y:Math.floor(O/K)}),O===I)break;let R=D.get(O);if(R===void 0)break;O=R}return E.reverse()}W.delete(q);let M=q%K,Y=Math.floor(q/K);for(let[E,O]of P){let R=M+E,S=Y+O;if(!N(R,S)||V(R,S))continue;if(E!==0&&O!==0&&(V(M+E,Y)||V(M,Y+O)))continue;let y=G(R,S),Z0=B.get(q)+(E!==0&&O!==0?Math.SQRT2:1);if(Z0<(B.get(y)??1/0))D.set(y,q),B.set(y,Z0),L.set(y,Z0+F(R,S)),W.add(y)}}return null}class h{app;size;grid;defs;bevel;constructor($,J){this.app=J;this.size=$.size,this.grid=$.data.map((K)=>K.split("")),this.defs=$.tiles,this.bevel=$.bevel??!1}get cols(){return this.grid.reduce(($,J)=>Math.max($,J.length),0)}get rows(){return this.grid.length}get width(){return this.cols*this.size}get height(){return this.rows*this.size}at($,J){return this.grid[J]?.[$]??""}set($,J,K){if(this.grid[J]?.[$]!==void 0)this.grid[J][$]=K}isSolid($,J){let K=this.defs[this.at($,J)];return!!K&&!!K.solid}path($,J,K){return D0($,J,this.cols,this.rows,(Z,V)=>this.isSolid(Z,V),K)}draw(){let $=this.size;for(let J=0;J<this.rows;J++)for(let K=0;K<this.cols;K++){let Z=this.defs[this.grid[J][K]];if(!Z)continue;let V=K*$,Q=J*$;if(Z.sprite)this.app.sprite(Z.sprite,{x:V,y:Q,w:$,h:$,frame:Z.frame});else if(Z.color){if(this.app.rect({x:V,y:Q,w:$,h:$,fill:Z.color}),this.bevel&&Z.solid){let H=Math.max(2,$*0.14);if(!this.isSolid(K,J-1))this.app.rect({x:V,y:Q,w:$,h:H,fill:"rgba(255,255,255,0.16)"});if(!this.isSolid(K,J+1))this.app.rect({x:V,y:Q+$-H,w:$,h:H,fill:"rgba(0,0,0,0.30)"})}}}}move($,J){$.x+=$.vx*J,this.resolve($,"x"),$.y+=$.vy*J,$.onGround=!1,this.resolve($,"y")}resolve($,J){let K=this.size,Z=Math.floor($.x/K),V=Math.floor(($.x+$.w-0.001)/K),Q=Math.floor($.y/K),H=Math.floor(($.y+$.h-0.001)/K);for(let N=Q;N<=H;N++)for(let G=Z;G<=V;G++){if(!this.isSolid(G,N))continue;if(J==="x"){if($.vx>0)$.x=G*K-$.w;else if($.vx<0)$.x=G*K+K;$.vx=0}else{if($.vy>0)$.y=N*K-$.h,$.onGround=!0;else if($.vy<0)$.y=N*K+K;$.vy=0}}}}class d{list=[];emitters=[];get count(){return this.list.length}get busy(){return this.list.length>0||this.emitters.some(($)=>$.active)}emit($){let J={x:$.x,y:$.y,active:!0,opts:$,acc:0,stop(){this.active=!1}};return this.emitters.push(J),J}burst($){let J=$.count??20,K=($.direction??0)*Math.PI/180,Z=($.spread??360)*Math.PI/180,[V,Q]=Array.isArray($.speed)?$.speed:[$.speed??80,$.speed??80],H=$.life??1,N=$.size??3,G=$.gravity??0,I=$.fade??!0,_=Array.isArray($.color)?$.color:[$.color??"#ffffff"];for(let F=0;F<J;F++){let W=K+(Math.random()-0.5)*Z,D=V+Math.random()*(Q-V);this.list.push({x:$.x,y:$.y,vx:Math.cos(W)*D,vy:Math.sin(W)*D,life:H,max:H,size:N,color:_[Math.floor(Math.random()*_.length)],gravity:G,fade:I})}}update($){for(let J=this.emitters.length-1;J>=0;J--){let K=this.emitters[J];if(!K.active){this.emitters.splice(J,1);continue}K.acc+=(K.opts.rate??30)*$;while(K.acc>=1)this.burst({...K.opts,x:K.x,y:K.y,count:1}),K.acc-=1}for(let J=this.list.length-1;J>=0;J--){let K=this.list[J];if(K.vy+=K.gravity*$,K.x+=K.vx*$,K.y+=K.vy*$,K.life-=$,K.life<=0)this.list.splice(J,1)}}draw($){for(let J of this.list)$.globalAlpha=J.fade?Math.max(0,J.life/J.max):1,$.fillStyle=J.color,$.beginPath(),$.arc(J.x,J.y,J.size,0,Math.PI*2),$.fill();$.globalAlpha=1}drawWith($){for(let J of this.list)$.circle({x:J.x,y:J.y,r:J.size,fill:J.color,alpha:J.fade?Math.max(0,J.life/J.max):1})}clear(){this.list.length=0,this.emitters.length=0}}class f{states;current;startAt=0;constructor($){this.states=$;this.current=Object.keys($)[0]??""}play($,J){if($!==this.current&&this.states[$])this.current=$,this.startAt=J}frame($){let J=this.states[this.current];if(!J||J.frames.length===0)return 0;let K=Math.floor(Math.max(0,$-this.startAt)*J.fps)%J.frames.length;return J.frames[K]}get state(){return this.current}}class l{app;list=[];constructor($){this.app=$}get count(){return this.list.length}add($){let J={w:0,h:0,vx:0,vy:0,...$,alive:!0,remove(){this.alive=!1}};return this.list.push(J),J}update($,J,K=0,Z=0){for(let V=this.list.length-1;V>=0;V--){let Q=this.list[V];if(!Q.alive){this.list.splice(V,1);continue}if(Q.gravity)Q.vy+=Q.gravity*$;if(Q.x+=Q.vx*$,Q.y+=Q.vy*$,Q.bounds&&K&&Z)this.edges(Q,K,Z);Q.update?.(Q,$,J)}}edges($,J,K){let Z=$.shape==="circle"||$.r!=null,V=Z?$.r??8:$.w,Q=Z?$.r??8:$.h,H=Z?V:0,N=Z?J-V:J-V,G=Z?Q:0,I=Z?K-Q:K-Q;if($.bounds==="bounce"){if($.x<H)$.x=H,$.vx=Math.abs($.vx);else if($.x>N)$.x=N,$.vx=-Math.abs($.vx);if($.y<G)$.y=G,$.vy=Math.abs($.vy);else if($.y>I)$.y=I,$.vy=-Math.abs($.vy)}else{if($.x<-V)$.x=J+V;else if($.x>J+V)$.x=-V;if($.y<-Q)$.y=K+Q;else if($.y>K+Q)$.y=-Q}}draw(){for(let $ of this.list){if(!$.alive)continue;if($.sprite)this.app.sprite($.sprite,{x:$.x,y:$.y,w:$.w||void 0,h:$.h||void 0,frame:$.frame,frameW:$.frameW,frameH:$.frameH,cols:$.cols,rotation:$.rotation,flipX:$.flipX,anchor:$.anchor});else if($.shape==="circle")this.app.circle({x:$.x,y:$.y,r:$.r??($.w?$.w/2:8),fill:$.color});else this.app.rect({x:$.x,y:$.y,w:$.w,h:$.h,fill:$.color,radius:$.radius,rotation:$.rotation})}}all(){return this.list}clear(){this.list.length=0}}class i{list=[];get count(){return this.list.length}after($,J){let K={left:$,interval:0,fn:J,repeat:!1,cancelled:!1};return this.list.push(K),{cancel(){K.cancelled=!0}}}every($,J){let K={left:$,interval:Math.max($,0.0001),fn:J,repeat:!0,cancelled:!1};return this.list.push(K),{cancel(){K.cancelled=!0}}}update($){for(let J=this.list.length-1;J>=0;J--){let K=this.list[J];if(K.cancelled){this.list.splice(J,1);continue}if(K.left-=$,K.left>0)continue;if(K.repeat){while(K.left<=0&&!K.cancelled)K.fn(),K.left+=K.interval;if(K.cancelled)this.list.splice(J,1)}else K.fn(),this.list.splice(J,1)}}clear(){this.list.length=0}}var w0=["#6cf","#f87","#7c7","#fc6","#a8f","#4dd","#f9a","#9d6"],A=($,J,K)=>J??$?.[K]??w0[K%w0.length],v0=($)=>$.map((J)=>typeof J==="number"?{value:J}:J),u0=($,J,K=44)=>({x:J.x??K,y:J.y??K,w:J.w??$.canvas.width-K*2,h:J.h??$.canvas.height-K*2}),Z8=($,J)=>{if($[0]!=="#")return $;let K=$.slice(1);if(K.length===3)K=K[0]+K[0]+K[1]+K[1]+K[2]+K[2];let Z=parseInt(K,16);return`rgba(${Z>>16&255},${Z>>8&255},${Z&255},${J})`};function r($,J){let K=J.size??13;J.items.forEach((Z,V)=>{let Q=J.y+V*(K+8);$.rect({x:J.x,y:Q,w:K,h:K,radius:3,fill:Z.color}),$.text(Z.label,{x:J.x+K+7,y:Q+K-2,fill:"#ccd",size:K})})}function h0($,J){let K=v0(J.data),{x:Z,y:V,w:Q,h:H}=u0($,J),N=J.max??Math.max(...K.map((F)=>F.value),0.000001),G=J.progress??1,I=V+H,_=Q/K.length;$.line({x1:Z,y1:I,x2:Z+Q,y2:I,stroke:"#445",strokeWidth:2}),K.forEach((F,W)=>{let D=F.value/N*(H-26)*G,B=Z+W*_;if($.rect({x:B+_*0.12,y:I-D,w:_*0.76,h:D,radius:5,fill:A(J.colors,F.color,W)}),F.label)$.text(F.label,{x:B+_/2,y:I+16,fill:"#99a",size:12,align:"center"});if(J.showValues!==!1)$.text(`${Math.round(F.value)}`,{x:B+_/2,y:I-D-12,fill:"#ccd",size:12,align:"center"})})}function f0($,J){let K=J.series??[{data:J.data??[]}],{x:Z,y:V,w:Q,h:H}=u0($,J),N=K.flatMap((L)=>L.data),G=J.max??Math.max(...N,0.000001),I=J.min??Math.min(...N,0),_=Math.max(...K.map((L)=>L.data.length),2),F=(L)=>Z+Q*L/(_-1),W=(L)=>V+H*(1-(L-I)/(G-I||1)),D=J.progress??1;if(J.grid!==!1)for(let L=0;L<=4;L++){let P=V+H*L/4;$.line({x1:Z,y1:P,x2:Z+Q,y2:P,stroke:"#222838",strokeWidth:1}),$.text(`${Math.round(G-(G-I)*L/4)}`,{x:Z-8,y:P+4,fill:"#556",size:11,align:"right"})}let B=D*(_-1);if(K.forEach((L,P)=>{let j=A(J.colors,L.color,P);for(let q=0;q<L.data.length-1&&q<=B;q++){let U=Math.min(1,B-q);$.line({x1:F(q),y1:W(L.data[q]),x2:F(q)+(F(q+1)-F(q))*U,y2:W(L.data[q])+(W(L.data[q+1])-W(L.data[q]))*U,stroke:j,strokeWidth:3})}if(J.points){for(let q=0;q<L.data.length;q++)if(q<=B)$.circle({x:F(q),y:W(L.data[q]),r:3.5,fill:j})}}),J.legend&&K.some((L)=>L.name))r($,{x:Z,y:V-4,items:K.map((L,P)=>({label:L.name??`#${P+1}`,color:A(J.colors,L.color,P)}))})}function b0($,J){let K=v0(J.data),Z=K.reduce((_,F)=>_+F.value,0)||1,V=J.x??$.canvas.width/2-(J.legend===!1?0:70),Q=J.y??$.canvas.height/2,H=J.r??Math.min($.canvas.width,$.canvas.height)*0.32,N=(J.donut??0)*H,G=J.progress??1,I=-Math.PI/2;if(K.forEach((_,F)=>{let W=_.value/Z*Math.PI*2*G,D=Math.max(2,Math.ceil(W/0.12)),B=[];for(let L=0;L<=D;L++){let P=I+W*L/D;B.push({x:V+Math.cos(P)*H,y:Q+Math.sin(P)*H})}if(N>0)for(let L=D;L>=0;L--){let P=I+W*L/D;B.push({x:V+Math.cos(P)*N,y:Q+Math.sin(P)*N})}else B.push({x:V,y:Q});$.polygon({points:B,fill:A(J.colors,_.color,F)}),I+=W}),J.legend!==!1)r($,{x:V+H+24,y:Q-K.length*11,items:K.map((_,F)=>({label:`${_.label??`#${F+1}`} ${Math.round(_.value/Z*100)}%`,color:A(J.colors,_.color,F)}))})}function y0($,J){let K=J.series??[{values:J.values??[]}],Z=J.axes.length,V=J.x??$.canvas.width/2,Q=J.y??$.canvas.height/2,H=J.r??Math.min($.canvas.width,$.canvas.height)*0.34,N=J.max??Math.max(...K.flatMap((F)=>F.values),0.000001),G=J.rings??4,I=J.progress??1,_=(F,W)=>{let D=-Math.PI/2+F/Z*Math.PI*2;return{x:V+Math.cos(D)*W,y:Q+Math.sin(D)*W}};for(let F=1;F<=G;F++)$.polygon({points:Array.from({length:Z},(W,D)=>_(D,H*F/G)),stroke:"#222a3a",strokeWidth:1});for(let F=0;F<Z;F++){let W=_(F,H);$.line({x1:V,y1:Q,x2:W.x,y2:W.y,stroke:"#222a3a",strokeWidth:1});let D=_(F,H+20);$.text(J.axes[F],{x:D.x,y:D.y,fill:"#99a",size:12,align:"center",baseline:"middle"})}if(K.forEach((F,W)=>{let D=A(J.colors,F.color,W),B=F.values.map((L,P)=>_(P,H*Math.min(1,L/N)*I));$.polygon({points:B,fill:Z8(D,0.22),stroke:D,strokeWidth:2}),B.forEach((L)=>$.circle({x:L.x,y:L.y,r:3,fill:D}))}),J.legend&&K.some((F)=>F.name))r($,{x:16,y:16,items:K.map((F,W)=>({label:F.name??`#${W+1}`,color:A(J.colors,F.color,W)}))})}function m0($,J,K){let Z=$.createShader(J);if($.shaderSource(Z,K),$.compileShader(Z),!$.getShaderParameter(Z,$.COMPILE_STATUS))throw Error("Fruta WebGL shader error: "+$.getShaderInfoLog(Z));return Z}function L0($,J,K){let Z=$.createProgram();if($.attachShader(Z,m0($,$.VERTEX_SHADER,J)),$.attachShader(Z,m0($,$.FRAGMENT_SHADER,K)),$.linkProgram(Z),!$.getProgramParameter(Z,$.LINK_STATUS))throw Error("Fruta WebGL link error: "+$.getProgramInfoLog(Z));return Z}var Q8=`
|
|
2
|
+
attribute vec2 aPos; attribute vec2 aUV; attribute vec4 aColor;
|
|
3
|
+
uniform vec2 uResolution; varying vec2 vUV; varying vec4 vColor;
|
|
4
|
+
void main() {
|
|
5
|
+
vec2 clip = (aPos / uResolution) * 2.0 - 1.0;
|
|
6
|
+
gl_Position = vec4(clip.x, -clip.y, 0.0, 1.0);
|
|
7
|
+
vUV = aUV; vColor = aColor;
|
|
8
|
+
}`,H8=`
|
|
9
|
+
precision mediump float;
|
|
10
|
+
uniform sampler2D uTex; varying vec2 vUV; varying vec4 vColor;
|
|
11
|
+
void main() { gl_FragColor = texture2D(uTex, vUV) * vColor; }`,B0=8000,t=48;class $0{canvas;gl;prog;buf;data=new Float32Array(B0*t);count=0;textures=new Map;white;currentTex=null;loc;constructor($){this.canvas=$;let J=$.getContext("webgl",{alpha:!0,premultipliedAlpha:!1});if(!J)throw Error("Fruta: WebGL is not available in this browser.");this.gl=J,this.prog=L0(J,Q8,H8),this.buf=J.createBuffer(),J.bindBuffer(J.ARRAY_BUFFER,this.buf),J.bufferData(J.ARRAY_BUFFER,this.data.byteLength,J.DYNAMIC_DRAW),this.loc={pos:J.getAttribLocation(this.prog,"aPos"),uv:J.getAttribLocation(this.prog,"aUV"),color:J.getAttribLocation(this.prog,"aColor"),res:J.getUniformLocation(this.prog,"uResolution"),tex:J.getUniformLocation(this.prog,"uTex")},this.white=this.makeTexture(new Uint8Array([255,255,255,255]),1,1),J.enable(J.BLEND),J.blendFunc(J.SRC_ALPHA,J.ONE_MINUS_SRC_ALPHA)}makeTexture($,J=0,K=0){let Z=this.gl,V=Z.createTexture();if(Z.bindTexture(Z.TEXTURE_2D,V),$ instanceof Uint8Array)Z.texImage2D(Z.TEXTURE_2D,0,Z.RGBA,J,K,0,Z.RGBA,Z.UNSIGNED_BYTE,$);else Z.texImage2D(Z.TEXTURE_2D,0,Z.RGBA,Z.RGBA,Z.UNSIGNED_BYTE,$);return Z.texParameteri(Z.TEXTURE_2D,Z.TEXTURE_WRAP_S,Z.CLAMP_TO_EDGE),Z.texParameteri(Z.TEXTURE_2D,Z.TEXTURE_WRAP_T,Z.CLAMP_TO_EDGE),Z.texParameteri(Z.TEXTURE_2D,Z.TEXTURE_MIN_FILTER,Z.LINEAR),Z.texParameteri(Z.TEXTURE_2D,Z.TEXTURE_MAG_FILTER,Z.LINEAR),V}texture($,J){let K=J,Z=K.naturalWidth||K.width||1,V=K.naturalHeight||K.height||1;this.textures.set($,{tex:this.makeTexture(J),w:Z,h:V})}getTexture($){return this.textures.get($)??null}removeTexture($){let J=this.textures.get($);if(J)this.gl.deleteTexture(J.tex),this.textures.delete($)}get whiteTexture(){return this.white}pushQuad($,J,K,Z,V,Q,H){if($!==this.currentTex||this.count>=B0)this.flush(),this.currentTex=$;let N=this.data,G=this.count*t,I=(_,F,W,D)=>{N[G++]=_,N[G++]=F,N[G++]=W,N[G++]=D,N[G++]=Z,N[G++]=V,N[G++]=Q,N[G++]=H};I(J[0],J[1],K[0],K[1]),I(J[2],J[3],K[2],K[1]),I(J[4],J[5],K[2],K[3]),I(J[0],J[1],K[0],K[1]),I(J[4],J[5],K[2],K[3]),I(J[6],J[7],K[0],K[3]),this.count++}resize($,J){this.canvas.width=$,this.canvas.height=J}clear($=0,J=0,K=0,Z=0){let V=this.gl;V.viewport(0,0,this.canvas.width,this.canvas.height),V.clearColor($,J,K,Z),V.clear(V.COLOR_BUFFER_BIT)}rect($){this.quad(this.white,$,0,0,1,1)}sprite($,J){let K=this.textures.get($);if(!K)return;this.quad(K.tex,J,J.u0??0,J.v0??0,J.u1??1,J.v1??1)}quad($,J,K,Z,V,Q){if($!==this.currentTex||this.count>=B0)this.flush(),this.currentTex=$;let H=1,N=1,G=1,I=1;if(J.color)if(typeof J.color==="string"){let E=C(J.color);H=E[0]/255,N=E[1]/255,G=E[2]/255,I=E[3]}else H=J.color[0],N=J.color[1],G=J.color[2],I=J.color[3];if(J.alpha!==void 0)I=J.alpha;let _=J.x+J.w/2,F=J.y+J.h/2,W=J.w/2,D=J.h/2,B=(J.rotation??0)*(Math.PI/180),L=Math.cos(B),P=Math.sin(B),j=(E,O)=>_+E*L-O*P,q=(E,O)=>F+E*P+O*L,U=this.data,M=this.count*t,Y=(E,O,R,S)=>{U[M++]=E,U[M++]=O,U[M++]=R,U[M++]=S,U[M++]=H,U[M++]=N,U[M++]=G,U[M++]=I};Y(j(-W,-D),q(-W,-D),K,Z),Y(j(W,-D),q(W,-D),V,Z),Y(j(W,D),q(W,D),V,Q),Y(j(-W,-D),q(-W,-D),K,Z),Y(j(W,D),q(W,D),V,Q),Y(j(-W,D),q(-W,D),K,Q),this.count++}flush(){if(this.count===0)return;let $=this.gl;$.useProgram(this.prog),$.uniform2f(this.loc.res,this.canvas.width,this.canvas.height),$.bindBuffer($.ARRAY_BUFFER,this.buf),$.bufferSubData($.ARRAY_BUFFER,0,this.data.subarray(0,this.count*t));let J=32;$.enableVertexAttribArray(this.loc.pos),$.vertexAttribPointer(this.loc.pos,2,$.FLOAT,!1,J,0),$.enableVertexAttribArray(this.loc.uv),$.vertexAttribPointer(this.loc.uv,2,$.FLOAT,!1,J,8),$.enableVertexAttribArray(this.loc.color),$.vertexAttribPointer(this.loc.color,4,$.FLOAT,!1,J,16),$.activeTexture($.TEXTURE0),$.bindTexture($.TEXTURE_2D,this.currentTex??this.white),$.uniform1i(this.loc.tex,0),$.drawArrays($.TRIANGLES,0,this.count*6),this.count=0}}var G8="attribute vec2 aPos; varying vec2 vUV; void main(){ vUV = aPos * 0.5 + 0.5; gl_Position = vec4(aPos, 0.0, 1.0); }",I8=`precision mediump float;
|
|
12
|
+
uniform sampler2D uScene;
|
|
13
|
+
uniform vec2 uResolution;
|
|
14
|
+
uniform float uTime;
|
|
15
|
+
varying vec2 vUV;
|
|
16
|
+
`;class J0{canvas;gl;fbo;tex;quad;w=0;h=0;programs=new Map;current=null;constructor($){this.canvas=$;let J=$.getContext("webgl");if(!J)throw Error("Fruta: WebGL is not available in this browser.");this.gl=J,this.tex=J.createTexture(),this.fbo=J.createFramebuffer(),this.quad=J.createBuffer(),J.bindBuffer(J.ARRAY_BUFFER,this.quad),J.bufferData(J.ARRAY_BUFFER,new Float32Array([-1,-1,3,-1,-1,3]),J.STATIC_DRAW),this.resize()}resize(){let $=this.canvas.width,J=this.canvas.height;if($===this.w&&J===this.h)return;this.w=$,this.h=J;let K=this.gl;K.bindTexture(K.TEXTURE_2D,this.tex),K.texImage2D(K.TEXTURE_2D,0,K.RGBA,$,J,0,K.RGBA,K.UNSIGNED_BYTE,null),K.texParameteri(K.TEXTURE_2D,K.TEXTURE_MIN_FILTER,K.LINEAR),K.texParameteri(K.TEXTURE_2D,K.TEXTURE_MAG_FILTER,K.LINEAR),K.texParameteri(K.TEXTURE_2D,K.TEXTURE_WRAP_S,K.CLAMP_TO_EDGE),K.texParameteri(K.TEXTURE_2D,K.TEXTURE_WRAP_T,K.CLAMP_TO_EDGE),K.bindFramebuffer(K.FRAMEBUFFER,this.fbo),K.framebufferTexture2D(K.FRAMEBUFFER,K.COLOR_ATTACHMENT0,K.TEXTURE_2D,this.tex,0),K.bindFramebuffer(K.FRAMEBUFFER,null)}setEffect($){let J=this.programs.get($);if(!J)J=L0(this.gl,G8,I8+$),this.programs.set($,J);this.current=J}begin(){this.resize();let $=this.gl;$.bindFramebuffer($.FRAMEBUFFER,this.fbo),$.viewport(0,0,this.w,this.h)}end($,J){let K=this.gl;if(K.bindFramebuffer(K.FRAMEBUFFER,null),K.viewport(0,0,this.w,this.h),!this.current)return;K.useProgram(this.current);let Z=K.getAttribLocation(this.current,"aPos");K.bindBuffer(K.ARRAY_BUFFER,this.quad),K.enableVertexAttribArray(Z),K.vertexAttribPointer(Z,2,K.FLOAT,!1,0,0),K.activeTexture(K.TEXTURE0),K.bindTexture(K.TEXTURE_2D,this.tex),K.uniform1i(K.getUniformLocation(this.current,"uScene"),0),K.uniform2f(K.getUniformLocation(this.current,"uResolution"),this.w,this.h),K.uniform1f(K.getUniformLocation(this.current,"uTime"),J);for(let[V,Q]of Object.entries($)){let H=K.getUniformLocation(this.current,V);if(!H)continue;if(Array.isArray(Q))if(Q.length===2)K.uniform2f(H,Q[0],Q[1]);else K.uniform3f(H,Q[0],Q[1],Q[2]);else K.uniform1f(H,Q)}K.drawArrays(K.TRIANGLES,0,3)}}var N8="attribute vec2 aPos; void main() { gl_Position = vec4(aPos, 0.0, 1.0); }";class K0{canvas;gl;prog;buf;constructor($,J){this.canvas=$;let K=$.getContext("webgl");if(!K)throw Error("Fruta: WebGL is not available in this browser.");this.gl=K;let Z=`precision mediump float;
|
|
17
|
+
uniform vec2 uResolution;
|
|
18
|
+
uniform float uTime;
|
|
19
|
+
${J}`;this.prog=L0(K,N8,Z),this.buf=K.createBuffer(),K.bindBuffer(K.ARRAY_BUFFER,this.buf),K.bufferData(K.ARRAY_BUFFER,new Float32Array([-1,-1,3,-1,-1,3]),K.STATIC_DRAW)}draw($,J={}){let K=this.gl;K.viewport(0,0,this.canvas.width,this.canvas.height),K.useProgram(this.prog);let Z=K.getAttribLocation(this.prog,"aPos");K.bindBuffer(K.ARRAY_BUFFER,this.buf),K.enableVertexAttribArray(Z),K.vertexAttribPointer(Z,2,K.FLOAT,!1,0,0),K.uniform2f(K.getUniformLocation(this.prog,"uResolution"),this.canvas.width,this.canvas.height),K.uniform1f(K.getUniformLocation(this.prog,"uTime"),$);for(let[V,Q]of Object.entries(J)){let H=K.getUniformLocation(this.prog,V);if(Array.isArray(Q))K.uniform2f(H,Q[0],Q[1]);else K.uniform1f(H,Q)}K.drawArrays(K.TRIANGLES,0,3)}}var q0={grayscale:"uniform float amount;void main(){vec3 c=texture2D(uScene,vUV).rgb;float g=dot(c,vec3(0.299,0.587,0.114));gl_FragColor=vec4(mix(c,vec3(g),amount),1.0);}",invert:"void main(){gl_FragColor=vec4(1.0-texture2D(uScene,vUV).rgb,1.0);}",sepia:"void main(){vec3 c=texture2D(uScene,vUV).rgb;gl_FragColor=vec4(min(vec3(dot(c,vec3(0.393,0.769,0.189)),dot(c,vec3(0.349,0.686,0.168)),dot(c,vec3(0.272,0.534,0.131))),1.0),1.0);}",tint:"uniform vec3 color;uniform float amount;void main(){vec3 c=texture2D(uScene,vUV).rgb;gl_FragColor=vec4(mix(c,c*color,amount),1.0);}",brightness:"uniform float brightness;uniform float contrast;void main(){vec3 c=texture2D(uScene,vUV).rgb;c=(c-0.5)*contrast+0.5+brightness;gl_FragColor=vec4(c,1.0);}",posterize:"uniform float levels;void main(){vec3 c=texture2D(uScene,vUV).rgb;float n=max(levels,2.0);gl_FragColor=vec4(floor(c*n+0.5)/n,1.0);}",threshold:"uniform float level;void main(){float g=dot(texture2D(uScene,vUV).rgb,vec3(0.299,0.587,0.114));gl_FragColor=vec4(vec3(step(level,g)),1.0);}",scanlines:"uniform float intensity;void main(){vec3 c=texture2D(uScene,vUV).rgb;float s=0.5+0.5*sin(vUV.y*uResolution.y*3.14159);c*=1.0-intensity*(1.0-s);gl_FragColor=vec4(c,1.0);}",crt:"uniform float curve;void main(){vec2 uv=vUV*2.0-1.0;uv*=1.0+dot(uv,uv)*curve*0.22;vec2 s=uv*0.5+0.5;if(s.x<0.0||s.x>1.0||s.y<0.0||s.y>1.0){gl_FragColor=vec4(0.0,0.0,0.0,1.0);return;}vec3 c=texture2D(uScene,s).rgb;c*=0.92+0.08*sin(s.y*uResolution.y*3.14159);c*=1.0-0.25*dot(uv,uv);gl_FragColor=vec4(c,1.0);}",vhs:"uniform float amount;void main(){vec2 uv=vUV;uv.x+=sin(uv.y*8.0+uTime*2.0)*0.003*amount;float n=fract(sin(dot(vec2(floor(uv.y*220.0),floor(uTime*8.0)),vec2(12.9,78.2)))*43758.0);float r=texture2D(uScene,uv+vec2(0.004*amount,0.0)).r;float g=texture2D(uScene,uv).g;float b=texture2D(uScene,uv-vec2(0.004*amount,0.0)).b;gl_FragColor=vec4(vec3(r,g,b)*(0.9+0.1*n),1.0);}",pixelate:"uniform float size;void main(){vec2 d=max(size,1.0)/uResolution;vec2 uv=floor(vUV/d)*d+d*0.5;gl_FragColor=texture2D(uScene,uv);}",noise:"uniform float amount;void main(){vec3 c=texture2D(uScene,vUV).rgb;float n=fract(sin(dot(vUV+fract(uTime),vec2(12.9898,78.233)))*43758.5453)-0.5;gl_FragColor=vec4(c+n*amount,1.0);}",glitch:"uniform float amount;void main(){float l=floor(vUV.y*30.0);float r=fract(sin(l*13.7+floor(uTime*10.0)*7.3)*4375.5);float sh=(r-0.5)*amount*0.08*step(0.75,r);vec2 uv=vUV+vec2(sh,0.0);float cr=texture2D(uScene,uv+vec2(0.006*amount,0.0)).r;float cg=texture2D(uScene,uv).g;float cb=texture2D(uScene,uv-vec2(0.006*amount,0.0)).b;gl_FragColor=vec4(cr,cg,cb,1.0);}",chromatic:"uniform float amount;void main(){vec2 dir=(vUV-0.5);float o=amount*0.012;float r=texture2D(uScene,vUV+dir*o).r;float g=texture2D(uScene,vUV).g;float b=texture2D(uScene,vUV-dir*o).b;gl_FragColor=vec4(r,g,b,1.0);}",wave:"uniform float amount;void main(){vec2 uv=vUV;uv.x+=sin(uv.y*40.0+uTime*3.0)*0.005*amount;uv.y+=cos(uv.x*40.0+uTime*2.0)*0.005*amount;gl_FragColor=texture2D(uScene,uv);}",fisheye:"uniform float amount;void main(){vec2 c=vUV-0.5;float r2=dot(c,c);vec2 uv=vUV+c*r2*amount;gl_FragColor=texture2D(uScene,uv);}",mirror:"void main(){vec2 uv=vUV;if(uv.x>0.5)uv.x=1.0-uv.x;gl_FragColor=texture2D(uScene,uv);}",shockwave:"uniform float speed;void main(){float t=fract(uTime*speed);vec2 d=vUV-0.5;float dist=length(d);float w=(1.0-smoothstep(0.0,0.08,abs(dist-t*0.7)))*0.03*(1.0-t);vec2 uv=vUV-normalize(d+0.0001)*w;gl_FragColor=texture2D(uScene,uv);}",blur:"uniform float radius;void main(){vec3 s=vec3(0.0);for(int i=-2;i<=2;i++)for(int j=-2;j<=2;j++){s+=texture2D(uScene,vUV+vec2(float(i),float(j))*radius/uResolution).rgb;}gl_FragColor=vec4(s/25.0,1.0);}",sharpen:"uniform float amount;void main(){vec2 px=1.0/uResolution;vec3 c=texture2D(uScene,vUV).rgb;vec3 n=texture2D(uScene,vUV+vec2(px.x,0.0)).rgb+texture2D(uScene,vUV-vec2(px.x,0.0)).rgb+texture2D(uScene,vUV+vec2(0.0,px.y)).rgb+texture2D(uScene,vUV-vec2(0.0,px.y)).rgb;gl_FragColor=vec4(c+(c*4.0-n)*amount,1.0);}",bloom:"uniform float amount;void main(){vec3 c=texture2D(uScene,vUV).rgb;vec3 b=vec3(0.0);for(int i=-2;i<=2;i++)for(int j=-2;j<=2;j++){vec3 s=texture2D(uScene,vUV+vec2(float(i),float(j))*2.0/uResolution).rgb;b+=max(s-0.6,0.0);}gl_FragColor=vec4(c+b/12.0*amount,1.0);}",dream:"uniform float amount;void main(){vec3 c=texture2D(uScene,vUV).rgb;vec3 b=vec3(0.0);for(int i=-2;i<=2;i++)for(int j=-2;j<=2;j++){b+=texture2D(uScene,vUV+vec2(float(i),float(j))*3.0/uResolution).rgb;}b/=25.0;gl_FragColor=vec4(mix(c,max(c,b)*1.1,amount),1.0);}",edge:"uniform float amount;void main(){vec2 px=1.0/uResolution;float c=dot(texture2D(uScene,vUV).rgb,vec3(0.333));float r=dot(texture2D(uScene,vUV+vec2(px.x,0.0)).rgb,vec3(0.333));float d=dot(texture2D(uScene,vUV+vec2(0.0,px.y)).rgb,vec3(0.333));float e=clamp(1.0-(abs(c-r)+abs(c-d))*amount*8.0,0.0,1.0);gl_FragColor=vec4(texture2D(uScene,vUV).rgb*e,1.0);}",vignette:"uniform float intensity;void main(){vec3 c=texture2D(uScene,vUV).rgb;float d=distance(vUV,vec2(0.5));c*=1.0-smoothstep(0.35,0.78,d)*intensity;gl_FragColor=vec4(c,1.0);}",nightvision:"uniform float amount;void main(){vec3 c=texture2D(uScene,vUV).rgb;float g=dot(c,vec3(0.299,0.587,0.114));float n=fract(sin(dot(vUV+fract(uTime),vec2(12.9,78.2)))*43758.0)*0.15;vec3 nv=vec3(0.1,g+n,0.1)*1.5;float d=distance(vUV,vec2(0.5));nv*=1.0-smoothstep(0.4,0.85,d);gl_FragColor=vec4(mix(c,nv,amount),1.0);}"},x0={grayscale:{amount:1},tint:{color:[1,0.5,0.5],amount:0.5},brightness:{brightness:0.05,contrast:1.35},posterize:{levels:5},threshold:{level:0.5},scanlines:{intensity:0.5},crt:{curve:1},vhs:{amount:1},pixelate:{size:6},noise:{amount:0.12},glitch:{amount:1},chromatic:{amount:2},wave:{amount:1},fisheye:{amount:0.4},shockwave:{speed:0.5},blur:{radius:1.5},sharpen:{amount:0.6},bloom:{amount:1},dream:{amount:1},edge:{amount:1},vignette:{intensity:0.6},nightvision:{amount:1}};class E0{gl;x=0;y=0;zoom=1;constructor($){this.gl=$}follow($,J=1){let K=this.gl.canvas.width/this.zoom,Z=this.gl.canvas.height/this.zoom;this.x+=($.x-K/2-this.x)*J,this.y+=($.y-Z/2-this.y)*J}clamp($,J,K,Z){let V=this.gl.canvas.width/this.zoom,Q=this.gl.canvas.height/this.zoom;this.x=Math.max($,Math.min(this.x,Math.max($,K-V))),this.y=Math.max(J,Math.min(this.y,Math.max(J,Z-Q)))}begin(){this.gl.push({x:-this.zoom*this.x,y:-this.zoom*this.y,scale:this.zoom})}end(){this.gl.pop()}screenToWorld($,J){return{x:$/this.zoom+this.x,y:J/this.zoom+this.y}}}var g0=[1,0,0,1,0,0],P0=($,J)=>[$[0]*J[0]+$[2]*J[1],$[1]*J[0]+$[3]*J[1],$[0]*J[2]+$[2]*J[3],$[1]*J[2]+$[3]*J[3],$[0]*J[4]+$[2]*J[5]+$[4],$[1]*J[4]+$[3]*J[5]+$[5]],W8=($)=>{let J=$.canvas,K;if(J instanceof HTMLCanvasElement)K=J;else if(typeof J==="string"){let Z=document.getElementById(J)??document.querySelector(J);if(!(Z instanceof HTMLCanvasElement))throw Error(`FrutaGL: no <canvas> matched "${J}".`);K=Z}else K=document.createElement("canvas");if($.width!==void 0)K.width=$.width;if($.height!==void 0)K.height=$.height;return K};class b{canvas;batch;bg=[0,0,0,1];stack=[];m=g0;alpha=1;circleTex="fruta:circle";colorCache=new Map;normCtx;warned=new Set;camera;entities;particles=new d;timers=new i;audio=new p;anims=[];gamepads=[];scenes=new Map;currentScene=null;currentSceneName="";transition={active:!1,t:0,dur:0,swapped:!1,next:""};debugOn=!1;fps=60;watched=new Map;postFX=null;postSrc=null;postUniforms={};textCanvas=document.createElement("canvas");textCtx=this.textCanvas.getContext("2d");textCache=new Map;textSeq=0;static TEXT_CACHE_MAX=128;gradCanvas=document.createElement("canvas");gradCtx=this.gradCanvas.getContext("2d");gradCache=new Map;gradSeq=0;userLoop=null;rafId=null;lastTime=0;time=0;timeScale=1;_mouse={x:0,y:0};_down=!1;keys=new Set;keyHandlers=[];pressHandlers=[];clickHandlers=[];moveHandlers=[];scrollKeys=new Set([" ","ArrowUp","ArrowDown","ArrowLeft","ArrowRight","PageUp","PageDown","Home","End"]);onKeyDown=($)=>{this.audio.resume();let J=$.target;if(!(!!J&&(J.tagName==="INPUT"||J.tagName==="TEXTAREA"||J.tagName==="SELECT"||J.isContentEditable))&&this.scrollKeys.has($.key))$.preventDefault();this.keys.add($.key);for(let Z of this.keyHandlers)if(Z.key===$.key)Z.fn()};onKeyUp=($)=>this.keys.delete($.key);onPointerMove=($)=>{this.setMouse($);for(let J of this.moveHandlers)J(this.mouse)};onPointerDown=($)=>{this.audio.resume(),this.setMouse($),this._down=!0;for(let J of this.pressHandlers)J(this.mouse)};onPointerUp=()=>{this._down=!1};onClickEv=($)=>{this.setMouse($);for(let J of this.clickHandlers)J(this.mouse)};constructor($={}){this.canvas=W8($),this.batch=new $0(this.canvas);let J=document.createElement("canvas");J.width=J.height=64;let K=J.getContext("2d"),Z=K.createRadialGradient(32,32,0,32,32,32);if(Z.addColorStop(0,"#fff"),Z.addColorStop(0.7,"#fff"),Z.addColorStop(1,"rgba(255,255,255,0)"),K.fillStyle=Z,K.beginPath(),K.arc(32,32,32,0,Math.PI*2),K.fill(),this.batch.texture(this.circleTex,J),this.normCtx=document.createElement("canvas").getContext("2d"),this.camera=new E0(this),this.entities=new l(this),$.background)this.bg=this.parse($.background);if($.mount)(typeof $.mount==="string"?document.querySelector($.mount):$.mount)?.appendChild(this.canvas);this.canvas.style.touchAction="none",this.canvas.addEventListener("pointermove",this.onPointerMove),this.canvas.addEventListener("pointerdown",this.onPointerDown),this.canvas.addEventListener("click",this.onClickEv),window.addEventListener("pointerup",this.onPointerUp),window.addEventListener("keydown",this.onKeyDown),window.addEventListener("keyup",this.onKeyUp)}mount($=document.body){return $.appendChild(this.canvas),this}addImage($,J){return this.batch.texture($,J),this}load($){return Promise.all(Object.entries($).map(([J,K])=>new Promise((Z)=>{let V=new Image;V.onload=()=>{this.batch.texture(J,V),Z()},V.onerror=()=>Z(),V.src=K}))).then(()=>{return})}background($){if($)this.bg=this.parse($);return this.batch.clear(this.bg[0],this.bg[1],this.bg[2],this.bg[3]),this}clear(){return this.batch.clear(0,0,0,0),this}rect($){return this.fillShape($.fill,this.corners($.x,$.y,$.w,$.h,$.rotation??0),"rect",($.alpha??1)*this.alpha),this}circle($){return this.fillShape($.fill,this.corners($.x-$.r,$.y-$.r,$.r*2,$.r*2,0),"circle",($.alpha??1)*this.alpha),this}ellipse($){return this.fillShape($.fill,this.corners($.x-$.rx,$.y-$.ry,$.rx*2,$.ry*2,$.rotation??0),"circle",($.alpha??1)*this.alpha),this}polygon($){let J=$.points;if(J.length<3)return this;let K=($.alpha??1)*this.alpha,Z=J.map((I)=>this.tp(I.x,I.y)),V=$.fill&&typeof $.fill!=="string"?$.fill.stops[$.fill.stops.length-1][1]:$.fill,[Q,H,N,G]=V?this.parse(V):[0,0,0,1];for(let I=1;I<Z.length-1;I++){let _=Z[0],F=Z[I],W=Z[I+1];this.batch.pushQuad(this.batch.whiteTexture,[_[0],_[1],F[0],F[1],W[0],W[1],W[0],W[1]],[0,0,1,1],Q,H,N,G*K)}return this}linearGradient($,J=90){return this.makeGradient("linear",$,J)}radialGradient($){return this.makeGradient("radial",$,0)}makeGradient($,J,K){let Z=J.map((Q,H,N)=>Array.isArray(Q)?Q:[N.length===1?0:H/(N.length-1),Q]),V=`${K}|${Z.map(([Q,H])=>Q.toFixed(3)+H).join(",")}`;return{_grad:!0,kind:$,stops:Z,angle:K,key:V}}fillShape($,J,K,Z){if($&&typeof $!=="string")this.batch.pushQuad(this.gradientTexture($,K),J,[0,0,1,1],1,1,1,Z);else{let V=K==="circle"?this.batch.getTexture(this.circleTex).tex:this.batch.whiteTexture,[Q,H,N,G]=$?this.parse($):[0,0,0,1];this.batch.pushQuad(V,J,[0,0,1,1],Q,H,N,G*Z)}}gradientTexture($,J){let K=`${J}|${$.kind}|${$.key}`,Z=this.gradCache.get(K);if(!Z){this.gradCanvas.width=64,this.gradCanvas.height=64;let Q=this.gradCtx;Q.clearRect(0,0,64,64);let H;if($.kind==="linear"){let N=$.angle*Math.PI/180,G=Math.cos(N)*32,I=Math.sin(N)*32;H=Q.createLinearGradient(32-G,32-I,32+G,32+I)}else H=Q.createRadialGradient(32,32,0,32,32,32);for(let[N,G]of $.stops)H.addColorStop(Math.max(0,Math.min(1,N)),G);if(Q.fillStyle=H,J==="circle")Q.beginPath(),Q.arc(32,32,32,0,Math.PI*2),Q.fill();else Q.fillRect(0,0,64,64);Z=`fruta:grad:${this.gradSeq++}`,this.batch.texture(Z,this.gradCanvas),this.gradCache.set(K,Z)}return this.batch.getTexture(Z).tex}tp($,J){return[this.m[0]*$+this.m[2]*J+this.m[4],this.m[1]*$+this.m[3]*J+this.m[5]]}line($){let J=$.x2-$.x1,K=$.y2-$.y1,Z=Math.hypot(J,K),V=Math.atan2(K,J)*180/Math.PI,Q=$.strokeWidth??1,[H,N,G,I]=$.stroke?this.parse($.stroke):[0,0,0,1],_=($.x1+$.x2)/2,F=($.y1+$.y2)/2;return this.batch.pushQuad(this.batch.whiteTexture,this.corners(_-Z/2,F-Q/2,Z,Q,V),[0,0,1,1],H,N,G,I*($.alpha??1)*this.alpha),this}text($,J){let K=J.size??16,Z=`${J.weight?J.weight+" ":""}${K}px ${J.font??"sans-serif"}`,V=J.fill??"#000000",Q=`${$}|${Z}|${V}`,H=this.textCache.get(Q);if(!H){let G=this.textCtx;G.font=Z;let I=Math.max(1,Math.ceil(G.measureText($).width)+4),_=Math.ceil(K*1.4)+4;this.textCanvas.width=I,this.textCanvas.height=_,G.font=Z,G.textBaseline="top",G.fillStyle=V,G.clearRect(0,0,I,_),G.fillText($,2,2);let F=`fruta:text:${this.textSeq++}`;if(this.batch.texture(F,this.textCanvas),H={name:F,w:I,h:_},this.textCache.set(Q,H),this.textCache.size>b.TEXT_CACHE_MAX){let W=this.textCache.keys().next().value,D=this.textCache.get(W);if(D)this.batch.removeTexture(D.name),this.textCache.delete(W)}}else this.textCache.delete(Q),this.textCache.set(Q,H);let N=J.x;if(J.align==="center")N-=H.w/2;else if(J.align==="right")N-=H.w;return this.batch.pushQuad(this.batch.getTexture(H.name).tex,this.corners(N,J.y,H.w,H.h,0),[0,0,1,1],1,1,1,J.alpha??this.alpha),this}sprite($,J){let K=this.batch.getTexture($);if(!K){if(!this.warned.has($))this.warned.add($),console.warn(`FrutaGL: no texture "${$}" — addImage()/load() it first.`);return this}let Z=0,V=0,Q=1,H=1,N=K.w,G=K.h;if(J.frameW&&J.frameH){let F=J.cols??Math.max(1,Math.floor(K.w/J.frameW)),W=J.frame??0,D=W%F*J.frameW,B=Math.floor(W/F)*J.frameH;Z=D/K.w,V=B/K.h,Q=(D+J.frameW)/K.w,H=(B+J.frameH)/K.h,N=J.frameW,G=J.frameH}if(J.flipX){let F=Z;Z=Q,Q=F}let I=J.w??N,_=J.h??G;return this.batch.pushQuad(K.tex,this.corners(J.x,J.y,I,_,J.rotation??0),[Z,V,Q,H],1,1,1,J.alpha??this.alpha),this}push($={}){this.stack.push({m:this.m,alpha:this.alpha});let J=this.m;if($.x||$.y)J=P0(J,[1,0,0,1,$.x??0,$.y??0]);if($.rotate){let K=$.rotate*Math.PI/180,Z=Math.cos(K),V=Math.sin(K);J=P0(J,[Z,V,-V,Z,0,0])}if($.scale!==void 0){let K=typeof $.scale==="number"?$.scale:$.scale.x,Z=typeof $.scale==="number"?$.scale:$.scale.y;J=P0(J,[K,0,0,Z,0,0])}if(this.m=J,$.alpha!==void 0)this.alpha*=$.alpha;return this}pop(){let $=this.stack.pop();if($)this.m=$.m,this.alpha=$.alpha;return this}corners($,J,K,Z,V){let Q=$+K/2,H=J+Z/2,N=K/2,G=Z/2,I=V*Math.PI/180,_=Math.cos(I),F=Math.sin(I),W=(Y,E)=>{let O=Q+Y*_-E*F,R=H+Y*F+E*_;return[this.m[0]*O+this.m[2]*R+this.m[4],this.m[1]*O+this.m[3]*R+this.m[5]]},[D,B]=W(-N,-G),[L,P]=W(N,-G),[j,q]=W(N,G),[U,M]=W(-N,G);return[D,B,L,P,j,q,U,M]}loop($){return this.userLoop=$,this.startEngine(),this}startEngine(){if(this.rafId!==null)return;this.lastTime=performance.now();let $=(J)=>{let K=(J-this.lastTime)/1000*this.timeScale;this.lastTime=J,this.time=J/1000,this.m=g0,this.alpha=1,this.stack.length=0;try{let Z=this.postSrc!==null&&this.postFX!==null;if(Z)this.postFX.begin();for(let Q of this.gamepads)Q.update();if(this.entities.update(K,this.time,this.canvas.width,this.canvas.height),this.particles.update(K),this.timers.update(K),this.anims.length)this.anims=this.anims.filter((Q)=>!Q.update(K));let V=this.transition;if(V.active){if(V.t+=K,!V.swapped&&V.t>=V.dur/2)V.swapped=!0,this.swapScene(V.next);if(V.t>=V.dur)V.active=!1}if(this.currentScene?.update?.(K,this.time),this.userLoop?.(K,this.time),V.active){let Q=V.dur/2,H=V.t<Q?V.t/Q:1-(V.t-Q)/Q;this.rect({x:0,y:0,w:this.canvas.width,h:this.canvas.height,fill:"#000",alpha:Math.min(1,Math.max(0,H))})}if(this.debugOn)this.fps=this.fps*0.9+1/Math.max(K,0.0001)*0.1,this.drawDebug();if(this.batch.flush(),Z)this.postFX.end(this.postUniforms,this.time)}catch(Z){console.error("FrutaGL: error in frame (loop kept alive):",Z)}if(this.userLoop||this.currentScene||this.transition.active||this.anims.length||this.particles.busy||this.timers.count>0||this.entities.count>0||this.debugOn||this.gamepads.length)this.rafId=requestAnimationFrame($);else this.rafId=null};this.rafId=requestAnimationFrame($)}stop(){return this.userLoop=null,this}add($){let J=this.entities.add($);return this.startEngine(),J}drawEntities(){return this.entities.draw(),this}get all(){return this.entities.all()}tilemap($){return new h($,this)}gamepad($={}){let J=new v($);return this.gamepads.push(J),this.startEngine(),J}hits($,J){return z($,J)}inside($,J){return x($,J)}solve($,J){let K=g($,J);if(!K)return null;if($.x+=K.x,$.y+=K.y,K.x!==0)return $.vx=0,"x";if(K.y<0)$.onGround=!0;return $.vy=0,"y"}physics($){return new u($)}overlap($,J,K){let Z=Array.isArray($)?$:[$],V=Array.isArray(J)?J:[J];for(let Q of Z)for(let H of V){if(Q===H)continue;if(z(Q,H))K?.(Q,H)}return this}burst($){return this.particles.burst($),this.startEngine(),this}emit($){let J=this.particles.emit($);return this.startEngine(),J}drawParticles(){return this.particles.drawWith(this),this}after($,J){let K=this.timers.after($,J);return this.startEngine(),K}every($,J){let K=this.timers.every($,J);return this.startEngine(),K}store($,J){try{localStorage.setItem(`fruta:${$}`,JSON.stringify(J))}catch{}return this}stored($,J){try{let K=localStorage.getItem(`fruta:${$}`);return K!=null?JSON.parse(K):J}catch{return J}}play($,J){return this.audio.play($,J)}beep($){return this.audio.beep($),this}volume($){return this.audio.volume($),this}mute($=!0){return this.audio.mute($),this}loadAudio($){return Promise.all(Object.entries($).map(([J,K])=>this.audio.loadSound(J,K))).then(()=>{return})}scene($,J){return this.scenes.set($,J),this}start($,J=0){if(J>0)this.transition={active:!0,t:0,dur:J,swapped:!1,next:$};else this.swapScene($);return this.startEngine(),this}swapScene($){let J=this.scenes.get($);if(!J)return;this.currentScene?.leave?.(),this.currentScene=J,this.currentSceneName=$,J.enter?.()}tween($,J){let K=this.buildTween($,J);return this.anims.push(K),this.startEngine(),K}stagger($,J){let K=J.each??0.1;return $.map((Z,V)=>this.tween(Z,{...J,delay:(J.delay??0)+V*K}))}timeline(){let $=new w((J,K,Z)=>this.buildTween(J,K,Z));return this.anims.push($),this.startEngine(),$}anim($){return new f($)}buildTween($,J,K){let Z=J.to??{},V={},Q=[];for(let G in Z){let I=Z[G];if(typeof I==="number")V[G]=I;else Q.push({key:G,from:C(this.normalizeColor(String($[G]))),to:C(this.normalizeColor(I))})}let H=J.onUpdate,N=Q.length||H?(G,I)=>{for(let _ of Q)G[_.key]=e(_.from,_.to,I);H?.(G,I)}:void 0;return new k($,{to:V,duration:J.duration,ease:typeof J.ease==="function"?J.ease:a[J.ease??"easeInOut"],delay:K??J.delay??0,repeat:J.repeat??0,yoyo:J.yoyo??!1,onUpdate:N,onComplete:J.onComplete})}normalizeColor($){let J=this.normCtx,K=J.fillStyle;J.fillStyle="#000000",J.fillStyle=$;let Z=J.fillStyle;return J.fillStyle=K,typeof Z==="string"?Z:"#000000"}debug($=!0){return this.debugOn=$,this.startEngine(),this}watch($,J){return this.watched.set($,J),this}drawDebug(){let $=[`fps ${this.fps.toFixed(0)}`,`entities ${this.entities.count}`,`particles ${this.particles.count}`,`tweens ${this.anims.length}`];if(this.currentSceneName)$.push(`scene ${this.currentSceneName}`);for(let[Q,H]of this.watched)$.push(`${Q} ${String(H)}`);let J=170,K=$.length*16+14,Z=this.canvas.width-J-8,V=8;this.rect({x:Z,y:V,w:J,h:K,fill:"rgba(11,11,18,0.82)"}),$.forEach((Q,H)=>this.text(Q,{x:Z+8,y:V+8+H*16,fill:"#7cfca0",size:12,font:"monospace"}))}shader($){let J=new K0(this.canvas,$);return{draw:(K={})=>J.draw(this.time,K)}}effect($,J={}){if($===null)return this.postSrc=null,this;let K=q0[$],Z=K??$;if(!this.postFX)this.postFX=new J0(this.canvas);if(Z!==this.postSrc)this.postFX.setEffect(Z),this.postSrc=Z;return this.postUniforms=K?{...x0[$],...J}:J,this.startEngine(),this}get mouse(){return{...this._mouse}}get mouseDown(){return this._down}keyDown($){return this.keys.has($)}onKey($,J){return this.keyHandlers.push({key:$,fn:J}),this}onPress($){return this.pressHandlers.push($),this}onClick($){return this.clickHandlers.push($),this}onMove($){return this.moveHandlers.push($),this}destroy(){if(this.rafId!==null)cancelAnimationFrame(this.rafId),this.rafId=null;this.userLoop=null,this.currentScene=null,this.scenes.clear(),this.particles.clear(),this.entities.clear(),this.timers.clear(),this.anims=[];for(let $ of this.gamepads)$.dispose();this.gamepads=[],this.watched.clear(),this.canvas.removeEventListener("pointermove",this.onPointerMove),this.canvas.removeEventListener("pointerdown",this.onPointerDown),this.canvas.removeEventListener("click",this.onClickEv),window.removeEventListener("pointerup",this.onPointerUp),window.removeEventListener("keydown",this.onKeyDown),window.removeEventListener("keyup",this.onKeyUp)}setMouse($){let J=this.canvas.getBoundingClientRect();this._mouse={x:$.clientX-J.left,y:$.clientY-J.top}}parse($){let J=this.colorCache.get($);if(J)return J;this.normCtx.fillStyle="#000000",this.normCtx.fillStyle=$;let K=this.normCtx.fillStyle,Z=C(typeof K==="string"?K:"#000000");return J=[Z[0]/255,Z[1]/255,Z[2]/255,Z[3]],this.colorCache.set($,J),J}}class p0{factory;items;count=0;constructor($,J){this.factory=J;this.items=Array($);for(let K=0;K<$;K++)this.items[K]=J()}get capacity(){return this.items.length}spawn(){return this.count<this.items.length?this.items[this.count++]:null}kill($){let J=--this.count,K=this.items[$];this.items[$]=this.items[J],this.items[J]=K}clear(){this.count=0}grow($){for(let J=0;J<$;J++)this.items.push(this.factory())}}class d0{cellSize;cells=new Map;used=[];constructor($){this.cellSize=$}key($,J){return(J+50000)*1e5+($+50000)}clear(){for(let $ of this.used){let J=this.cells.get($);if(J)J.length=0}this.used.length=0}insert($,J,K){let Z=this.key(Math.floor($/this.cellSize),Math.floor(J/this.cellSize)),V=this.cells.get(Z);if(!V)V=[],this.cells.set(Z,V);if(V.length===0)this.used.push(Z);V.push(K)}query($,J,K,Z){Z.length=0;let V=this.cellSize,Q=Math.floor(($-K)/V),H=Math.floor(($+K)/V),N=Math.floor((J-K)/V),G=Math.floor((J+K)/V);for(let I=N;I<=G;I++)for(let _=Q;_<=H;_++){let F=this.cells.get(this.key(_,I));if(F)for(let W=0;W<F.length;W++)Z.push(F[W])}return Z}}var X=($,J)=>typeof $==="number"?$:J,F8=($,J)=>typeof $==="string"?$:J,_8=($,J,K)=>Math.min(Math.max($,J),K),D8=($)=>$.r??(Math.max(X($.w,0),X($.h,0))/2||8),U0={move:($)=>{let J=$.bounds;return(K,Z,V,Q)=>{K.x+=K.vx*Z,K.y+=K.vy*Z;let H=D8(K);if(J==="bounce"){if(K.x<H)K.x=H,K.vx=Math.abs(K.vx);else if(K.x>Q.width-H)K.x=Q.width-H,K.vx=-Math.abs(K.vx);if(K.y<H)K.y=H,K.vy=Math.abs(K.vy);else if(K.y>Q.height-H)K.y=Q.height-H,K.vy=-Math.abs(K.vy)}else if(J==="wrap"){if(K.x<-H)K.x=Q.width+H;else if(K.x>Q.width+H)K.x=-H;if(K.y<-H)K.y=Q.height+H;else if(K.y>Q.height+H)K.y=-H}}},gravity:($)=>{let J=X($.force,1500);return(K,Z)=>{K.vy+=J*Z}},platformer:($)=>{let J=X($.speed,230),K=X($.jump,560),Z=X($.accel,1200),V=X($.gravity,1500);return(Q,H,N,G)=>{let I=G.app,_=(I.keyDown("ArrowRight")||I.keyDown("d")?1:0)-(I.keyDown("ArrowLeft")||I.keyDown("a")?1:0);if(Q.vx+=_*Z*H,!_)Q.vx*=0.8;if(Q.vx=_8(Q.vx,-J,J),(I.keyDown("ArrowUp")||I.keyDown("w")||I.keyDown(" "))&&Q.onGround)Q.vy=-K;if(Q.vy+=V*H,G.map)G.map.move(Q,H);else Q.x+=Q.vx*H,Q.y+=Q.vy*H}},topdown:($)=>{let J=X($.speed,200);return(K,Z,V,Q)=>{let H=Q.app,N=(H.keyDown("ArrowRight")||H.keyDown("d")?1:0)-(H.keyDown("ArrowLeft")||H.keyDown("a")?1:0),G=(H.keyDown("ArrowDown")||H.keyDown("s")?1:0)-(H.keyDown("ArrowUp")||H.keyDown("w")?1:0),I=Math.hypot(N,G)||1;if(K.vx=N/I*J,K.vy=G/I*J,Q.map)Q.map.move(K,Z);else K.x+=K.vx*Z,K.y+=K.vy*Z}},chase:($)=>{let J=F8($.target,"player"),K=X($.speed,80);return(Z,V,Q,H)=>{let N=H.find(J);if(!N)return;let G=N.x-Z.x,I=N.y-Z.y,_=Math.hypot(G,I)||1;Z.x+=G/_*K*V,Z.y+=I/_*K*V}},followMouse:($)=>{let J=X($.smooth,8);return(K,Z,V,Q)=>{let H=Q.app.mouse;K.x+=(H.x-K.x)*Math.min(1,Z*J),K.y+=(H.y-K.y)*Math.min(1,Z*J)}},spin:($)=>{let J=X($.speed,90);return(K,Z)=>{K.rotation=X(K.rotation,0)+J*Z}}},O0=()=>Object.keys(U0);function M0($,J){let K=typeof $==="string"?$:$.type,Z=typeof $==="string"?{}:$,V=U0[K];if(V)return V(Z);if(J&&J[K])return J[K];return null}function B8($,J){let K=[];for(let Z of $.behaviors??[]){let V=M0(Z,J);if(V)K.push(V);else console.warn("fruta: unknown behavior",Z,"(add it to the behavior catalog or the scripts map)")}return{...$,vx:$.vx??0,vy:$.vy??0,onGround:!1,alive:!0,_behaviors:K,remove(){this.alive=!1}}}function L8($,J){if(J.sprite)$.sprite(J.sprite,{x:J.x,y:J.y,w:J.w,h:J.h,rotation:J.rotation});else if(J.shape==="circle")$.circle({x:J.x,y:J.y,r:J.r??8,fill:J.color});else $.rect({x:J.x,y:J.y,w:J.w??16,h:J.h??16,fill:J.color,rotation:J.rotation})}function q8($,J,K){let Z=l0({width:J.meta.width,height:J.meta.height,background:J.meta.background,mount:$});if(J.assets?.sprites)Z.load(J.assets.sprites);if(J.assets?.sounds)Z.load(J.assets.sounds);for(let V of J.scenes){let Q=[],H=null,N={app:Z,get map(){return H??void 0},width:J.meta.width,height:J.meta.height,find:(G)=>Q.find((I)=>I.alive&&(I.tag===G||I.id===G))};Z.scene(V.name,{enter(){H=V.tilemap?Z.tilemap(V.tilemap):null,Q=V.entities.map((G)=>B8(G,K))},update(G,I){if(V.background)Z.background(V.background);for(let W of Q){if(!W.alive)continue;for(let D of W._behaviors)D(W,G,I,N)}Q=Q.filter((W)=>W.alive);let _=V.camera,F=_?Q.find((W)=>W.tag===_.follow||W.id===_.follow):null;if(F){if(Z.camera.follow(F,0.14),H&&_.clamp!==!1)Z.camera.clamp(0,0,H.width,H.height);Z.camera.begin()}if(H)H.draw();for(let W of Q)L8(Z,W);if(Z.drawParticles(),F)Z.camera.end()}})}return Z.start(J.start??J.scenes[0]?.name),Z}var P8=($)=>JSON.stringify($),E8=($)=>JSON.parse($);function U8($,J=[]){let K=[];if(!$?.meta||!($.meta.width>0)||!($.meta.height>0))K.push("meta.width and meta.height must be > 0");if(!$?.scenes?.length)return K.push("project has no scenes"),K;let Z=new Set([...O0(),...J]),V=new Set(Object.keys($.assets?.sprites??{})),Q=new Set;for(let H of $.scenes){if(Q.has(H.name))K.push(`duplicate scene name "${H.name}"`);Q.add(H.name);let N=new Set((H.entities??[]).flatMap((G)=>[G.id,G.tag].filter(Boolean)));if(H.camera&&!N.has(H.camera.follow))K.push(`scene "${H.name}": camera.follow "${H.camera.follow}" matches no entity id/tag`);for(let G of H.entities??[]){if(G.sprite&&!V.has(G.sprite))K.push(`scene "${H.name}": entity sprite "${G.sprite}" is not in assets.sprites`);for(let I of G.behaviors??[]){let _=typeof I==="string"?I:I.type;if(!Z.has(_))K.push(`scene "${H.name}": unknown behavior "${_}"`)}}}if($.start&&!Q.has($.start))K.push(`start scene "${$.start}" does not exist`);return K}var O8=($)=>{let J=$.canvas,K,Z=!1;if(J instanceof HTMLCanvasElement)K=J;else if(typeof J==="string"){let Q=document.getElementById(J)??document.querySelector(J);if(!(Q instanceof HTMLCanvasElement))throw Error(`Fruta: no <canvas> matched "${J}".`);K=Q}else K=document.createElement("canvas"),Z=!0;if($.width!==void 0)K.width=$.width;if($.height!==void 0)K.height=$.height;let V=K.getContext("2d");if(!V)throw Error("Fruta: 2D context is unavailable on this canvas.");return{canvas:K,context:V,created:Z}};class i0{canvas;context;shapes;fonts;state;camera;_mouse={x:0,y:0};_pointerDown=!1;keys=new Set;images=new Map;assets=new Map;audio=new p;userLoop=null;anims=[];gamepads=[];particles=new d;entities;scenes=new Map;currentScene=null;currentSceneName="";transition={active:!1,t:0,dur:0,swapped:!1,next:""};timers=new i;warned=new Set;debugOn=!1;fps=60;watched=new Map;rafId=null;lastTime=0;bg;autoClear=!0;timeScale=1;keyHandlers=[];pressHandlers=[];releaseHandlers=[];moveHandlers=[];clickHandlers=[];scrollKeys=new Set([" ","ArrowUp","ArrowDown","ArrowLeft","ArrowRight","PageUp","PageDown","Home","End"]);handleKeyDown=($)=>{this.audio.resume();let J=$.target;if(!(!!J&&(J.tagName==="INPUT"||J.tagName==="TEXTAREA"||J.tagName==="SELECT"||J.isContentEditable))&&this.scrollKeys.has($.key))$.preventDefault();this.keys.add($.key);for(let Z of this.keyHandlers)if(Z.key===$.key)Z.fn()};handleKeyUp=($)=>{this.keys.delete($.key)};handlePointerMove=($)=>{this.updateMouse($);for(let J of this.moveHandlers)J(this.mouse)};handlePointerDown=($)=>{this.audio.resume(),this.updateMouse($),this._pointerDown=!0;for(let J of this.pressHandlers)J(this.mouse)};handlePointerUp=($)=>{this.updateMouse($),this._pointerDown=!1;for(let J of this.releaseHandlers)J(this.mouse)};handleClick=($)=>{this.updateMouse($);for(let J of this.clickHandlers)J(this.mouse)};constructor($={}){let{canvas:J,context:K,created:Z}=O8($);if(this.canvas=J,this.context=K,this.shapes=new c(J,K),this.fonts=new n(J,K),this.state=new s(J,K),this.camera=new o(J,K),this.entities=new l(this),this.bg=$.background,this.autoClear=$.clear??!0,$.background)this.background($.background);if($.mount)(typeof $.mount==="string"?document.querySelector($.mount):$.mount)?.appendChild(this.canvas);if(Z)Y0("LET'S DO IT BABY!");J.style.touchAction="none",J.addEventListener("pointermove",this.handlePointerMove),J.addEventListener("pointerdown",this.handlePointerDown),J.addEventListener("click",this.handleClick),window.addEventListener("pointerup",this.handlePointerUp),window.addEventListener("keydown",this.handleKeyDown),window.addEventListener("keyup",this.handleKeyUp)}mount($=document.body){return $.appendChild(this.canvas),this}clear(){return this.context.clearRect(0,0,this.canvas.width,this.canvas.height),this}background($){return this.context.fillStyle=$,this.context.fillRect(0,0,this.canvas.width,this.canvas.height),this}rect($){let J=this.context;if(J.save(),$.rotation){let K=$.x+$.w/2,Z=$.y+$.h/2;J.translate(K,Z),J.rotate($.rotation*Math.PI/180),J.translate(-K,-Z)}if(J.beginPath(),$.radius)J.roundRect($.x,$.y,$.w,$.h,$.radius);else J.rect($.x,$.y,$.w,$.h);return this.paint($.fill,$.stroke,$.strokeWidth),J.restore(),this}circle($){let J=this.context;return J.beginPath(),J.arc($.x,$.y,$.r,0,Math.PI*2),this.paint($.fill,$.stroke,$.strokeWidth),this}ellipse($){let J=this.context;return J.beginPath(),J.ellipse($.x,$.y,$.rx,$.ry,($.rotation??0)*Math.PI/180,0,Math.PI*2),this.paint($.fill,$.stroke,$.strokeWidth),this}line($){let J=this.context;return J.beginPath(),J.moveTo($.x1,$.y1),J.lineTo($.x2,$.y2),J.strokeStyle=$.stroke??"black",J.lineWidth=$.strokeWidth??1,J.stroke(),this}polygon($){if($.points.length<2)return this;let J=this.context;J.beginPath(),J.moveTo($.points[0].x,$.points[0].y);for(let K=1;K<$.points.length;K++)J.lineTo($.points[K].x,$.points[K].y);if($.close!==!1)J.closePath();return this.paint($.fill,$.stroke,$.strokeWidth),this}text($,J){let K=this.context;if(K.font=`${J.size??16}px ${J.font??"sans-serif"}`,K.textAlign=J.align??"left",K.textBaseline=J.baseline??"alphabetic",J.stroke)K.strokeStyle=J.stroke,K.lineWidth=J.strokeWidth??1,K.strokeText($,J.x,J.y);if(J.fill||!J.stroke)K.fillStyle=J.fill??"black",K.fillText($,J.x,J.y);return this}image($,J){let K=this.images.get($);if(!K)K=new Image,K.src=$,this.images.set($,K);if(K.complete&&K.naturalWidth>0)if(J.w!==void 0&&J.h!==void 0)this.context.drawImage(K,J.x,J.y,J.w,J.h);else this.context.drawImage(K,J.x,J.y);return this}linearGradient($,J,K,Z,V){let Q=this.context.createLinearGradient($,J,K,Z);for(let[H,N]of V)Q.addColorStop(H,N);return Q}radialGradient($,J,K,Z,V=0){let Q=this.context.createRadialGradient($,J,V,$,J,K);for(let[H,N]of Z)Q.addColorStop(H,N);return Q}barChart($){return h0(this,$),this}lineChart($){return f0(this,$),this}pieChart($){return b0(this,$),this}radar($){return y0(this,$),this}legend($){return r(this,$),this}load($){return Promise.all(Object.entries($).map(([J,K])=>{if(/\.(mp3|wav|ogg|m4a|aac|flac)(\?|$)/i.test(K))return this.audio.loadSound(J,K);return new Promise((Z)=>{let V=new Image;V.onload=()=>{this.assets.set(J,V),Z()},V.onerror=()=>Z(),V.src=K})})).then(()=>{return})}addImage($,J){return this.assets.set($,J),this}sprite($,J){let K=this.assets.get($);if(!K){if(!this.warned.has($))this.warned.add($),console.warn(`Fruta: no asset "${$}" to draw — did you load() or addImage() it?`);return this}let Z=K.naturalWidth||K.width||0,V=K.naturalHeight||K.height||0,Q=0,H=0,N=Z,G=V;if(J.frameW!==void 0&&J.frameH!==void 0){let D=J.cols??Math.max(1,Math.floor(Z/J.frameW)),B=J.frame??0;Q=B%D*J.frameW,H=Math.floor(B/D)*J.frameH,N=J.frameW,G=J.frameH}let I=J.w??N,_=J.h??G,F=this.context,W=J.anchor==="center";if(J.rotation||J.flipX||W){let D=W?J.x:J.x+I/2,B=W?J.y:J.y+_/2;if(F.save(),F.translate(D,B),J.rotation)F.rotate(J.rotation*Math.PI/180);if(J.flipX)F.scale(-1,1);F.drawImage(K,Q,H,N,G,-I/2,-_/2,I,_),F.restore()}else F.drawImage(K,Q,H,N,G,J.x,J.y,I,_);return this}frameAt($,J,K){return Math.floor($*J)%K}tilemap($){return new h($,this)}play($,J){return this.audio.play($,J)}beep($){return this.audio.beep($),this}volume($){return this.audio.volume($),this}mute($=!0){return this.audio.mute($),this}hits($,J){return z($,J)}inside($,J){return x($,J)}solve($,J){let K=g($,J);if(!K)return null;if($.x+=K.x,$.y+=K.y,K.x!==0)return $.vx=0,"x";if(K.y<0)$.onGround=!0;return $.vy=0,"y"}physics($){return new u($)}overlap($,J,K){let Z=Array.isArray($)?$:[$],V=Array.isArray(J)?J:[J];for(let Q of Z)for(let H of V){if(Q===H)continue;if(z(Q,H))K?.(Q,H)}return this}lerp($,J,K){return Q0($,J,K)}clamp($,J,K){return H0($,J,K)}map($,J,K,Z,V){return G0($,J,K,Z,V)}dist($,J,K,Z){return I0($,J,K,Z)}angle($,J,K,Z){return N0($,J,K,Z)}rand($,J){return W0($,J)}randInt($,J){return F0($,J)}pick($){return _0($)}push($={}){let J=this.context;if(J.save(),$.x||$.y)J.translate($.x??0,$.y??0);if($.rotate)J.rotate($.rotate*Math.PI/180);if($.scale!==void 0)if(typeof $.scale==="number")J.scale($.scale,$.scale);else J.scale($.scale.x,$.scale.y);if($.alpha!==void 0)J.globalAlpha*=$.alpha;return this}pop(){return this.context.restore(),this}loop($){return this.userLoop=$,this.startEngine(),this}stop(){return this.userLoop=null,this}scene($,J){return this.scenes.set($,J),this}start($,J=0){if(J>0)this.transition={active:!0,t:0,dur:J,swapped:!1,next:$};else this.swapScene($);return this.startEngine(),this}swapScene($){let J=this.scenes.get($);if(!J)return;this.currentScene?.leave?.(),this.currentScene=J,this.currentSceneName=$,J.enter?.()}burst($){return this.particles.burst($),this.startEngine(),this}drawParticles(){return this.particles.draw(this.context),this}anim($){return new f($)}emit($){let J=this.particles.emit($);return this.startEngine(),J}after($,J){let K=this.timers.after($,J);return this.startEngine(),K}every($,J){let K=this.timers.every($,J);return this.startEngine(),K}store($,J){try{localStorage.setItem(`fruta:${$}`,JSON.stringify(J))}catch{}return this}stored($,J){try{let K=localStorage.getItem(`fruta:${$}`);return K!=null?JSON.parse(K):J}catch{return J}}add($){let J=this.entities.add($);return this.startEngine(),J}drawEntities(){return this.entities.draw(),this}get all(){return this.entities.all()}debug($=!0){return this.debugOn=$,this.startEngine(),this}watch($,J){return this.watched.set($,J),this}drawDebug(){let $=this.context,J=[`fps ${this.fps.toFixed(0)}`,`entities ${this.entities.count}`,`particles ${this.particles.count}`,`tweens ${this.anims.length}`,`mouse ${Math.round(this._mouse.x)}, ${Math.round(this._mouse.y)}`];if(this.currentSceneName)J.push(`scene ${this.currentSceneName}`);for(let[G,I]of this.watched)J.push(`${G} ${String(I)}`);let K=8,Z=15,V=168,Q=J.length*Z+K*2,H=this.canvas.width-V-8,N=8;$.save(),$.globalAlpha=0.82,$.fillStyle="#0b0b12",$.fillRect(H,N,V,Q),$.globalAlpha=1,$.fillStyle="#7cfca0",$.font="12px monospace",$.textAlign="left",$.textBaseline="top",J.forEach((G,I)=>$.fillText(G,H+K,N+K+I*Z)),$.restore()}tween($,J){let K=this.buildTween($,J);return this.anims.push(K),this.startEngine(),K}stagger($,J){let K=J.each??0.1;return $.map((Z,V)=>this.tween(Z,{...J,delay:(J.delay??0)+V*K}))}timeline(){let $=new w((J,K,Z)=>this.buildTween(J,K,Z));return this.anims.push($),this.startEngine(),$}buildTween($,J,K){let Z=J.to??{},V={},Q=[];for(let G in Z){let I=Z[G];if(typeof I==="number")V[G]=I;else Q.push({key:G,from:C(this.normalizeColor(String($[G]))),to:C(this.normalizeColor(I))})}let H=J.onUpdate,N=Q.length||H?(G,I)=>{for(let _ of Q)G[_.key]=e(_.from,_.to,I);H?.(G,I)}:void 0;return new k($,{to:V,duration:J.duration,ease:typeof J.ease==="function"?J.ease:a[J.ease??"easeInOut"],delay:K??J.delay??0,repeat:J.repeat??0,yoyo:J.yoyo??!1,onUpdate:N,onComplete:J.onComplete})}startEngine(){if(this.rafId!==null)return;this.lastTime=performance.now();let $=(J)=>{let K=(J-this.lastTime)/1000*this.timeScale;this.lastTime=J;try{for(let V of this.gamepads)V.update();if(this.entities.update(K,J/1000,this.canvas.width,this.canvas.height),this.particles.update(K),this.timers.update(K),this.camera.tick(K),this.anims.length)this.anims=this.anims.filter((V)=>!V.update(K));let Z=this.transition;if(Z.active){if(Z.t+=K,!Z.swapped&&Z.t>=Z.dur/2)Z.swapped=!0,this.swapScene(Z.next);if(Z.t>=Z.dur)Z.active=!1}if(this.bg&&this.autoClear)this.background(this.bg);if(this.currentScene?.update?.(K,J/1000),this.userLoop)this.userLoop(K,J/1000);if(Z.active){let V=Z.dur/2,Q=Z.t<V?Z.t/V:1-(Z.t-V)/V,H=this.context;H.save(),H.globalAlpha=Math.min(1,Math.max(0,Q)),H.fillStyle="#000",H.fillRect(0,0,this.canvas.width,this.canvas.height),H.restore()}if(this.debugOn)this.fps=this.fps*0.9+1/Math.max(K,0.0001)*0.1,this.drawDebug()}catch(Z){console.error("Fruta: error in frame (loop kept alive):",Z)}if(this.userLoop||this.anims.length||this.gamepads.length||this.currentScene||this.particles.busy||this.transition.active||this.entities.count>0||this.debugOn||this.timers.count>0)this.rafId=requestAnimationFrame($);else this.rafId=null};this.rafId=requestAnimationFrame($)}get mouse(){return{...this._mouse}}get mouseDown(){return this._pointerDown}keyDown($){return this.keys.has($)}onKey($,J){return this.keyHandlers.push({key:$,fn:J}),this}onPress($){return this.pressHandlers.push($),this}onRelease($){return this.releaseHandlers.push($),this}onClick($){return this.clickHandlers.push($),this}onMove($){return this.moveHandlers.push($),this}gamepad($={}){let J=new v($);return this.gamepads.push(J),this.startEngine(),J}destroy(){if(this.rafId!==null)cancelAnimationFrame(this.rafId),this.rafId=null;this.userLoop=null,this.anims=[],this.currentScene=null,this.scenes.clear(),this.particles.clear(),this.entities.clear(),this.timers.clear(),this.watched.clear(),this.transition.active=!1;for(let $ of this.gamepads)$.dispose();this.gamepads=[],this.canvas.removeEventListener("pointermove",this.handlePointerMove),this.canvas.removeEventListener("pointerdown",this.handlePointerDown),this.canvas.removeEventListener("click",this.handleClick),window.removeEventListener("pointerup",this.handlePointerUp),window.removeEventListener("keydown",this.handleKeyDown),window.removeEventListener("keyup",this.handleKeyUp)}updateMouse($){let J=this.canvas,K=J.getBoundingClientRect();if(!K.width||!K.height){this._mouse={x:0,y:0};return}let Z=Math.min(K.width/J.width,K.height/J.height),V=(K.width-J.width*Z)/2,Q=(K.height-J.height*Z)/2;this._mouse={x:($.clientX-K.left-V)/Z,y:($.clientY-K.top-Q)/Z}}normalizeColor($){let J=this.context,K=J.fillStyle;J.fillStyle="#000000",J.fillStyle=$;let Z=J.fillStyle;return J.fillStyle=K,typeof Z==="string"?Z:"#000000"}paint($,J,K=1){let Z=this.context;if($===void 0&&J===void 0)$="black";if($)Z.fillStyle=$,Z.fill();if(J)Z.strokeStyle=J,Z.lineWidth=K,Z.stroke()}}var r0=($={})=>$.renderer==="webgl"?new b($):new i0($);r0.gl=($={})=>new b($);var l0=r0;export{U8 as validateProject,P8 as saveProject,M0 as resolveBehavior,g as resolve,F0 as randInt,W0 as rand,q8 as playProject,e0 as pixelToHex,_0 as pick,G0 as map,E8 as loadProject,Q0 as lerp,x as inside,z as hits,a0 as hexToPixel,z0 as hexRound,o0 as hexNeighbors,D0 as findPath,I0 as dist,l0 as default,T0 as cubicBezier,H0 as clamp,s0 as bezierPath,O0 as behaviorNames,N0 as angle,u as World,$0 as WebGLBatch,k as Tween,w as Timeline,h as Tilemap,d0 as SpatialHash,q0 as Shaders,K0 as Shader,J0 as PostFX,p0 as Pool,v as GamepadController,E0 as GLCamera,b as FrutaGL,i0 as FrutaApp,o as Camera,U0 as BEHAVIORS,f as Anim};
|