reze-engine 0.3.12 → 0.3.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -66
- package/dist/bezier-interpolate.d.ts +15 -0
- package/dist/bezier-interpolate.d.ts.map +1 -0
- package/dist/bezier-interpolate.js +40 -0
- package/dist/engine.d.ts +3 -8
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +19 -30
- package/dist/engine_ts.d.ts +143 -0
- package/dist/engine_ts.d.ts.map +1 -0
- package/dist/engine_ts.js +1575 -0
- package/dist/ik.d.ts +32 -0
- package/dist/ik.d.ts.map +1 -0
- package/dist/ik.js +337 -0
- package/dist/player.d.ts +64 -0
- package/dist/player.d.ts.map +1 -0
- package/dist/player.js +220 -0
- package/dist/pool-scene.d.ts +52 -0
- package/dist/pool-scene.d.ts.map +1 -0
- package/dist/pool-scene.js +1122 -0
- package/dist/pool.d.ts +38 -0
- package/dist/pool.d.ts.map +1 -0
- package/dist/pool.js +422 -0
- package/dist/rzm-converter.d.ts +12 -0
- package/dist/rzm-converter.d.ts.map +1 -0
- package/dist/rzm-converter.js +40 -0
- package/dist/rzm-loader.d.ts +24 -0
- package/dist/rzm-loader.d.ts.map +1 -0
- package/dist/rzm-loader.js +488 -0
- package/dist/rzm-writer.d.ts +27 -0
- package/dist/rzm-writer.d.ts.map +1 -0
- package/dist/rzm-writer.js +701 -0
- package/package.json +1 -1
- package/src/camera.ts +358 -358
- package/src/engine.ts +28 -28
- package/src/ik-solver.ts +411 -411
- package/src/math.ts +584 -584
- package/src/physics.ts +742 -742
- package/src/vmd-loader.ts +276 -276
- package/dist/audio.d.ts +0 -29
- package/dist/audio.d.ts.map +0 -1
- package/dist/audio.js +0 -116
- package/dist/particles.d.ts +0 -67
- package/dist/particles.d.ts.map +0 -1
- package/dist/particles.js +0 -266
package/dist/audio.js
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Audio system for generating procedural explosion sounds
|
|
3
|
-
* Based on Web Audio API synthesis
|
|
4
|
-
*/
|
|
5
|
-
export class AudioSystem {
|
|
6
|
-
constructor() {
|
|
7
|
-
this.ctx = null;
|
|
8
|
-
this.enabled = false;
|
|
9
|
-
this.volume = 1;
|
|
10
|
-
this.limiter = null;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Initialize audio context and compressor
|
|
14
|
-
* Must be called after user interaction (browser requirement)
|
|
15
|
-
*/
|
|
16
|
-
init() {
|
|
17
|
-
if (!this.ctx) {
|
|
18
|
-
this.ctx = new (window.AudioContext || window.webkitAudioContext)();
|
|
19
|
-
this.limiter = this.ctx.createDynamicsCompressor();
|
|
20
|
-
this.limiter.threshold.value = -10;
|
|
21
|
-
this.limiter.knee.value = 40;
|
|
22
|
-
this.limiter.ratio.value = 12;
|
|
23
|
-
this.limiter.connect(this.ctx.destination);
|
|
24
|
-
this.enabled = true;
|
|
25
|
-
}
|
|
26
|
-
// Resume if suspended (iOS/Safari requirement)
|
|
27
|
-
if (this.ctx.state === "suspended") {
|
|
28
|
-
this.ctx.resume();
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Play deep bass explosion sound (firework burst)
|
|
33
|
-
* Combines sub-bass, noise, and crack sounds for realistic explosion
|
|
34
|
-
*/
|
|
35
|
-
playExplosion() {
|
|
36
|
-
if (!this.enabled || !this.ctx || !this.limiter)
|
|
37
|
-
return;
|
|
38
|
-
const t = this.ctx.currentTime;
|
|
39
|
-
// --- 1. SUB-BASS KICK (deep rumble) ---
|
|
40
|
-
const osc = this.ctx.createOscillator();
|
|
41
|
-
const oscGain = this.ctx.createGain();
|
|
42
|
-
osc.type = "sine";
|
|
43
|
-
osc.frequency.setValueAtTime(50, t); // Start at 50Hz
|
|
44
|
-
osc.frequency.exponentialRampToValueAtTime(20, t + 2.5); // Drop to 20Hz
|
|
45
|
-
oscGain.gain.setValueAtTime(this.volume * 1.5, t);
|
|
46
|
-
oscGain.gain.exponentialRampToValueAtTime(0.01, t + 5.0); // Long decay
|
|
47
|
-
osc.connect(oscGain);
|
|
48
|
-
oscGain.connect(this.limiter);
|
|
49
|
-
osc.start(t);
|
|
50
|
-
osc.stop(t + 5.0);
|
|
51
|
-
// --- 2. NOISE (crackling/rumble texture) ---
|
|
52
|
-
const bufferSize = this.ctx.sampleRate * 5.0;
|
|
53
|
-
const buffer = this.ctx.createBuffer(1, bufferSize, this.ctx.sampleRate);
|
|
54
|
-
const data = buffer.getChannelData(0);
|
|
55
|
-
for (let i = 0; i < bufferSize; i++) {
|
|
56
|
-
data[i] = Math.random() * 2 - 1;
|
|
57
|
-
}
|
|
58
|
-
const noise = this.ctx.createBufferSource();
|
|
59
|
-
noise.buffer = buffer;
|
|
60
|
-
const noiseFilter = this.ctx.createBiquadFilter();
|
|
61
|
-
noiseFilter.type = "lowpass";
|
|
62
|
-
noiseFilter.frequency.setValueAtTime(150, t);
|
|
63
|
-
noiseFilter.frequency.exponentialRampToValueAtTime(30, t + 4.0);
|
|
64
|
-
const noiseGain = this.ctx.createGain();
|
|
65
|
-
noiseGain.gain.setValueAtTime(this.volume * 1.0, t);
|
|
66
|
-
noiseGain.gain.exponentialRampToValueAtTime(0.01, t + 4.5);
|
|
67
|
-
noise.connect(noiseFilter);
|
|
68
|
-
noiseFilter.connect(noiseGain);
|
|
69
|
-
noiseGain.connect(this.limiter);
|
|
70
|
-
noise.start(t);
|
|
71
|
-
// --- 3. CRACK (sharp attack) ---
|
|
72
|
-
const crack = this.ctx.createOscillator();
|
|
73
|
-
crack.type = "triangle";
|
|
74
|
-
const crackGain = this.ctx.createGain();
|
|
75
|
-
crack.frequency.setValueAtTime(200, t);
|
|
76
|
-
crack.frequency.exponentialRampToValueAtTime(50, t + 0.1);
|
|
77
|
-
crackGain.gain.setValueAtTime(this.volume * 0.3, t);
|
|
78
|
-
crackGain.gain.exponentialRampToValueAtTime(0.01, t + 0.1);
|
|
79
|
-
crack.connect(crackGain);
|
|
80
|
-
crackGain.connect(this.limiter);
|
|
81
|
-
crack.start(t);
|
|
82
|
-
crack.stop(t + 0.1);
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Play lighter "whoosh" sound for rocket launch
|
|
86
|
-
*/
|
|
87
|
-
playRocketLaunch() {
|
|
88
|
-
if (!this.enabled || !this.ctx || !this.limiter)
|
|
89
|
-
return;
|
|
90
|
-
const t = this.ctx.currentTime;
|
|
91
|
-
// Rising tone
|
|
92
|
-
const osc = this.ctx.createOscillator();
|
|
93
|
-
const oscGain = this.ctx.createGain();
|
|
94
|
-
osc.type = "sawtooth";
|
|
95
|
-
osc.frequency.setValueAtTime(100, t);
|
|
96
|
-
osc.frequency.exponentialRampToValueAtTime(400, t + 0.5);
|
|
97
|
-
oscGain.gain.setValueAtTime(this.volume * 0.3, t);
|
|
98
|
-
oscGain.gain.exponentialRampToValueAtTime(0.01, t + 0.5);
|
|
99
|
-
osc.connect(oscGain);
|
|
100
|
-
oscGain.connect(this.limiter);
|
|
101
|
-
osc.start(t);
|
|
102
|
-
osc.stop(t + 0.5);
|
|
103
|
-
}
|
|
104
|
-
setVolume(volume) {
|
|
105
|
-
this.volume = Math.max(0, Math.min(1, volume));
|
|
106
|
-
}
|
|
107
|
-
getVolume() {
|
|
108
|
-
return this.volume;
|
|
109
|
-
}
|
|
110
|
-
isEnabled() {
|
|
111
|
-
return this.enabled;
|
|
112
|
-
}
|
|
113
|
-
setEnabled(enabled) {
|
|
114
|
-
this.enabled = enabled;
|
|
115
|
-
}
|
|
116
|
-
}
|
package/dist/particles.d.ts
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { Vec3 } from "./math";
|
|
2
|
-
export interface ParticleSystemConfig {
|
|
3
|
-
particleCount: number;
|
|
4
|
-
particleSize: number;
|
|
5
|
-
fadeSpeed: number;
|
|
6
|
-
explosionForce: number;
|
|
7
|
-
hoverDuration: number;
|
|
8
|
-
gravity: number;
|
|
9
|
-
friction: number;
|
|
10
|
-
rocketSpeed: number;
|
|
11
|
-
rocketSize: number;
|
|
12
|
-
}
|
|
13
|
-
export declare const DEFAULT_PARTICLE_CONFIG: ParticleSystemConfig;
|
|
14
|
-
interface ParticleData {
|
|
15
|
-
positions: Float32Array;
|
|
16
|
-
velocities: Float32Array;
|
|
17
|
-
colors: Float32Array;
|
|
18
|
-
lifetimes: Float32Array;
|
|
19
|
-
count: number;
|
|
20
|
-
}
|
|
21
|
-
export declare class Firework {
|
|
22
|
-
private startX;
|
|
23
|
-
private config;
|
|
24
|
-
private phase;
|
|
25
|
-
private timer;
|
|
26
|
-
private pos;
|
|
27
|
-
private vel;
|
|
28
|
-
private targetY;
|
|
29
|
-
private colors;
|
|
30
|
-
private rocketAlive;
|
|
31
|
-
private rocketLifetime;
|
|
32
|
-
private justExploded;
|
|
33
|
-
private particles;
|
|
34
|
-
private baseColors;
|
|
35
|
-
constructor(startX: number, config: ParticleSystemConfig);
|
|
36
|
-
private hslToRgb;
|
|
37
|
-
update(deltaTime: number): void;
|
|
38
|
-
private smoothstep;
|
|
39
|
-
private explode;
|
|
40
|
-
isDead(): boolean;
|
|
41
|
-
isRocket(): boolean;
|
|
42
|
-
getRocketPosition(): Vec3;
|
|
43
|
-
getRocketColor(): Vec3;
|
|
44
|
-
getParticleData(): ParticleData | null;
|
|
45
|
-
hasJustExploded(): boolean;
|
|
46
|
-
clearExplosionFlag(): void;
|
|
47
|
-
}
|
|
48
|
-
export declare class ParticleSystem {
|
|
49
|
-
private fireworks;
|
|
50
|
-
private config;
|
|
51
|
-
private maxConcurrentFireworks;
|
|
52
|
-
private autoLaunch;
|
|
53
|
-
private launchInterval;
|
|
54
|
-
private lastLaunchTime;
|
|
55
|
-
private nextLaunchDelay;
|
|
56
|
-
constructor(config?: Partial<ParticleSystemConfig>);
|
|
57
|
-
update(deltaTime: number): void;
|
|
58
|
-
spawnFirework(x?: number): void;
|
|
59
|
-
getFireworks(): Firework[];
|
|
60
|
-
setAutoLaunch(enabled: boolean): void;
|
|
61
|
-
setConfig(config: Partial<ParticleSystemConfig>): void;
|
|
62
|
-
getConfig(): ParticleSystemConfig;
|
|
63
|
-
setMaxConcurrentFireworks(max: number): void;
|
|
64
|
-
getMaxConcurrentFireworks(): number;
|
|
65
|
-
}
|
|
66
|
-
export {};
|
|
67
|
-
//# sourceMappingURL=particles.d.ts.map
|
package/dist/particles.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"particles.d.ts","sourceRoot":"","sources":["../src/particles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAI7B,MAAM,WAAW,oBAAoB;IACnC,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,cAAc,EAAE,MAAM,CAAA;IACtB,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,eAAO,MAAM,uBAAuB,EAAE,oBAUrC,CAAA;AAED,UAAU,YAAY;IACpB,SAAS,EAAE,YAAY,CAAA;IACvB,UAAU,EAAE,YAAY,CAAA;IACxB,MAAM,EAAE,YAAY,CAAA;IACpB,SAAS,EAAE,YAAY,CAAA;IACvB,KAAK,EAAE,MAAM,CAAA;CACd;AAED,qBAAa,QAAQ;IAeP,OAAO,CAAC,MAAM;IAAU,OAAO,CAAC,MAAM;IAdlD,OAAO,CAAC,KAAK,CAA0C;IACvD,OAAO,CAAC,KAAK,CAAY;IACzB,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,WAAW,CAAgB;IACnC,OAAO,CAAC,cAAc,CAAc;IACpC,OAAO,CAAC,YAAY,CAAiB;IAGrC,OAAO,CAAC,SAAS,CAA4B;IAC7C,OAAO,CAAC,UAAU,CAA4B;gBAE1B,MAAM,EAAE,MAAM,EAAU,MAAM,EAAE,oBAAoB;IA4BxE,OAAO,CAAC,QAAQ;IAsBhB,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAgE/B,OAAO,CAAC,UAAU;IAKlB,OAAO,CAAC,OAAO;IAwDf,MAAM,IAAI,OAAO;IAIjB,QAAQ,IAAI,OAAO;IAInB,iBAAiB,IAAI,IAAI;IAIzB,cAAc,IAAI,IAAI;IAItB,eAAe,IAAI,YAAY,GAAG,IAAI;IAItC,eAAe,IAAI,OAAO;IAI1B,kBAAkB,IAAI,IAAI;CAG3B;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,MAAM,CAAuD;IACrE,OAAO,CAAC,sBAAsB,CAAY;IAG1C,OAAO,CAAC,UAAU,CAAiB;IACnC,OAAO,CAAC,cAAc,CAAe;IACrC,OAAO,CAAC,cAAc,CAAY;IAClC,OAAO,CAAC,eAAe,CAAY;gBAEvB,MAAM,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC;IAMlD,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAoB/B,aAAa,CAAC,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAS/B,YAAY,IAAI,QAAQ,EAAE;IAI1B,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAQrC,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,oBAAoB,CAAC,GAAG,IAAI;IAItD,SAAS,IAAI,oBAAoB;IAIjC,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAI5C,yBAAyB,IAAI,MAAM;CAGpC"}
|
package/dist/particles.js
DELETED
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
import { Vec3 } from "./math";
|
|
2
|
-
const MAX_PARTICLES = 30000;
|
|
3
|
-
export const DEFAULT_PARTICLE_CONFIG = {
|
|
4
|
-
particleCount: 5000, // Reduced from 23000 for better performance
|
|
5
|
-
particleSize: 0.1, // Smaller particles
|
|
6
|
-
fadeSpeed: 0.008, // Faster fade = particles die sooner
|
|
7
|
-
explosionForce: 0.6, // Smaller explosion radius
|
|
8
|
-
hoverDuration: 1.2, // Shorter hover
|
|
9
|
-
gravity: 0.00265,
|
|
10
|
-
friction: 0.95494,
|
|
11
|
-
rocketSpeed: 1.0,
|
|
12
|
-
rocketSize: 0.4, // Smaller rocket trail
|
|
13
|
-
};
|
|
14
|
-
export class Firework {
|
|
15
|
-
constructor(startX, config) {
|
|
16
|
-
this.startX = startX;
|
|
17
|
-
this.config = config;
|
|
18
|
-
this.phase = "rocket";
|
|
19
|
-
this.timer = 0;
|
|
20
|
-
this.colors = [];
|
|
21
|
-
this.rocketAlive = true;
|
|
22
|
-
this.rocketLifetime = 1.0;
|
|
23
|
-
this.justExploded = false; // Flag for triggering audio
|
|
24
|
-
// Explosion particle data
|
|
25
|
-
this.particles = null;
|
|
26
|
-
this.baseColors = null;
|
|
27
|
-
// Generate color palette (mono, dual, or tri-color)
|
|
28
|
-
const rand = Math.random();
|
|
29
|
-
const baseHue = Math.random();
|
|
30
|
-
if (rand < 0.33) {
|
|
31
|
-
// MONO (1 Color)
|
|
32
|
-
this.colors.push(this.hslToRgb(baseHue, 1.0, 0.6));
|
|
33
|
-
}
|
|
34
|
-
else if (rand < 0.66) {
|
|
35
|
-
// DUAL (2 Colors - Complementary)
|
|
36
|
-
this.colors.push(this.hslToRgb(baseHue, 1.0, 0.6));
|
|
37
|
-
this.colors.push(this.hslToRgb((baseHue + 0.5) % 1.0, 1.0, 0.5));
|
|
38
|
-
}
|
|
39
|
-
else {
|
|
40
|
-
// TRI (3 Colors - Triad)
|
|
41
|
-
this.colors.push(this.hslToRgb(baseHue, 1.0, 0.6));
|
|
42
|
-
this.colors.push(this.hslToRgb((baseHue + 0.33) % 1.0, 1.0, 0.6));
|
|
43
|
-
this.colors.push(this.hslToRgb((baseHue + 0.66) % 1.0, 1.0, 0.6));
|
|
44
|
-
}
|
|
45
|
-
this.pos = new Vec3(startX, -50, (Math.random() - 0.5) * 30);
|
|
46
|
-
this.vel = new Vec3((Math.random() - 0.5) * 0.5, config.rocketSpeed * (0.9 + Math.random() * 0.3), (Math.random() - 0.5) * 0.5);
|
|
47
|
-
this.targetY = 10 + Math.random() * 20; // Higher explosion point
|
|
48
|
-
}
|
|
49
|
-
hslToRgb(h, s, l) {
|
|
50
|
-
let r, g, b;
|
|
51
|
-
if (s === 0) {
|
|
52
|
-
r = g = b = l;
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
const hue2rgb = (p, q, t) => {
|
|
56
|
-
if (t < 0)
|
|
57
|
-
t += 1;
|
|
58
|
-
if (t > 1)
|
|
59
|
-
t -= 1;
|
|
60
|
-
if (t < 1 / 6)
|
|
61
|
-
return p + (q - p) * 6 * t;
|
|
62
|
-
if (t < 1 / 2)
|
|
63
|
-
return q;
|
|
64
|
-
if (t < 2 / 3)
|
|
65
|
-
return p + (q - p) * (2 / 3 - t) * 6;
|
|
66
|
-
return p;
|
|
67
|
-
};
|
|
68
|
-
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
69
|
-
const p = 2 * l - q;
|
|
70
|
-
r = hue2rgb(p, q, h + 1 / 3);
|
|
71
|
-
g = hue2rgb(p, q, h);
|
|
72
|
-
b = hue2rgb(p, q, h - 1 / 3);
|
|
73
|
-
}
|
|
74
|
-
return new Vec3(r, g, b);
|
|
75
|
-
}
|
|
76
|
-
update(deltaTime) {
|
|
77
|
-
if (this.phase === "rocket") {
|
|
78
|
-
this.pos.x += this.vel.x;
|
|
79
|
-
this.pos.y += this.vel.y;
|
|
80
|
-
this.pos.z += this.vel.z;
|
|
81
|
-
this.vel.y *= 0.99;
|
|
82
|
-
this.rocketLifetime -= deltaTime * 0.5;
|
|
83
|
-
if (this.vel.y < 0.2 || this.pos.y >= this.targetY || this.rocketLifetime <= 0) {
|
|
84
|
-
this.explode();
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
else if (this.phase === "explode") {
|
|
88
|
-
this.timer += deltaTime;
|
|
89
|
-
if (!this.particles)
|
|
90
|
-
return;
|
|
91
|
-
const isHovering = this.timer < this.config.hoverDuration;
|
|
92
|
-
const gravityFactor = this.smoothstep(this.timer, this.config.hoverDuration, this.config.hoverDuration + 0.5);
|
|
93
|
-
let aliveCount = 0;
|
|
94
|
-
const positions = this.particles.positions;
|
|
95
|
-
const velocities = this.particles.velocities;
|
|
96
|
-
const colors = this.particles.colors;
|
|
97
|
-
const lifetimes = this.particles.lifetimes;
|
|
98
|
-
for (let i = 0; i < this.particles.count; i++) {
|
|
99
|
-
if (lifetimes[i] > 0) {
|
|
100
|
-
aliveCount++;
|
|
101
|
-
const i3 = i * 3;
|
|
102
|
-
// Update position
|
|
103
|
-
positions[i3] += velocities[i3];
|
|
104
|
-
positions[i3 + 1] += velocities[i3 + 1];
|
|
105
|
-
positions[i3 + 2] += velocities[i3 + 2];
|
|
106
|
-
if (isHovering) {
|
|
107
|
-
// Hovering phase - apply friction
|
|
108
|
-
velocities[i3] *= this.config.friction;
|
|
109
|
-
velocities[i3 + 1] *= this.config.friction;
|
|
110
|
-
velocities[i3 + 2] *= this.config.friction;
|
|
111
|
-
}
|
|
112
|
-
else {
|
|
113
|
-
// Falling phase - apply gravity and fade
|
|
114
|
-
velocities[i3 + 1] -= this.config.gravity * gravityFactor;
|
|
115
|
-
velocities[i3] *= 0.98;
|
|
116
|
-
velocities[i3 + 1] *= 0.98;
|
|
117
|
-
velocities[i3 + 2] *= 0.98;
|
|
118
|
-
lifetimes[i] -= this.config.fadeSpeed;
|
|
119
|
-
}
|
|
120
|
-
// Update color alpha
|
|
121
|
-
const alpha = Math.max(0, lifetimes[i]);
|
|
122
|
-
if (this.baseColors) {
|
|
123
|
-
colors[i3] = this.baseColors[i3] * alpha * 1.5;
|
|
124
|
-
colors[i3 + 1] = this.baseColors[i3 + 1] * alpha * 1.5;
|
|
125
|
-
colors[i3 + 2] = this.baseColors[i3 + 2] * alpha * 1.5;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
if (aliveCount === 0) {
|
|
130
|
-
this.phase = "dead";
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
smoothstep(x, edge0, edge1) {
|
|
135
|
-
const t = Math.max(0, Math.min(1, (x - edge0) / (edge1 - edge0)));
|
|
136
|
-
return t * t * (3 - 2 * t);
|
|
137
|
-
}
|
|
138
|
-
explode() {
|
|
139
|
-
this.phase = "explode";
|
|
140
|
-
this.timer = 0;
|
|
141
|
-
this.rocketAlive = false;
|
|
142
|
-
this.justExploded = true; // Mark for audio trigger
|
|
143
|
-
const particleCount = Math.min(this.config.particleCount, MAX_PARTICLES);
|
|
144
|
-
const positions = new Float32Array(particleCount * 3);
|
|
145
|
-
const velocities = new Float32Array(particleCount * 3);
|
|
146
|
-
const colors = new Float32Array(particleCount * 3);
|
|
147
|
-
const lifetimes = new Float32Array(particleCount);
|
|
148
|
-
this.baseColors = new Float32Array(particleCount * 3);
|
|
149
|
-
const baseSpeed = this.config.explosionForce * (0.8 + Math.random() * 0.4);
|
|
150
|
-
for (let i = 0; i < particleCount; i++) {
|
|
151
|
-
const i3 = i * 3;
|
|
152
|
-
// Set initial position to explosion center
|
|
153
|
-
positions[i3] = this.pos.x;
|
|
154
|
-
positions[i3 + 1] = this.pos.y;
|
|
155
|
-
positions[i3 + 2] = this.pos.z;
|
|
156
|
-
// Generate spherical velocity distribution
|
|
157
|
-
const theta = Math.random() * Math.PI * 2;
|
|
158
|
-
const phi = Math.acos(2 * Math.random() - 1);
|
|
159
|
-
const speed = baseSpeed * (0.8 + Math.random() * 0.4);
|
|
160
|
-
velocities[i3] = speed * Math.sin(phi) * Math.cos(theta);
|
|
161
|
-
velocities[i3 + 1] = speed * Math.sin(phi) * Math.sin(theta);
|
|
162
|
-
velocities[i3 + 2] = speed * Math.cos(phi);
|
|
163
|
-
// Pick color from palette and vary brightness
|
|
164
|
-
const targetColor = this.colors[Math.floor(Math.random() * this.colors.length)];
|
|
165
|
-
const brightness = 0.5 + Math.random() * 0.8;
|
|
166
|
-
this.baseColors[i3] = targetColor.x * brightness;
|
|
167
|
-
this.baseColors[i3 + 1] = targetColor.y * brightness;
|
|
168
|
-
this.baseColors[i3 + 2] = targetColor.z * brightness;
|
|
169
|
-
colors[i3] = this.baseColors[i3];
|
|
170
|
-
colors[i3 + 1] = this.baseColors[i3 + 1];
|
|
171
|
-
colors[i3 + 2] = this.baseColors[i3 + 2];
|
|
172
|
-
lifetimes[i] = 1.0;
|
|
173
|
-
}
|
|
174
|
-
this.particles = {
|
|
175
|
-
positions,
|
|
176
|
-
velocities,
|
|
177
|
-
colors,
|
|
178
|
-
lifetimes,
|
|
179
|
-
count: particleCount,
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
isDead() {
|
|
183
|
-
return this.phase === "dead";
|
|
184
|
-
}
|
|
185
|
-
isRocket() {
|
|
186
|
-
return this.phase === "rocket" && this.rocketAlive;
|
|
187
|
-
}
|
|
188
|
-
getRocketPosition() {
|
|
189
|
-
return this.pos;
|
|
190
|
-
}
|
|
191
|
-
getRocketColor() {
|
|
192
|
-
return this.colors[0];
|
|
193
|
-
}
|
|
194
|
-
getParticleData() {
|
|
195
|
-
return this.particles;
|
|
196
|
-
}
|
|
197
|
-
hasJustExploded() {
|
|
198
|
-
return this.justExploded;
|
|
199
|
-
}
|
|
200
|
-
clearExplosionFlag() {
|
|
201
|
-
this.justExploded = false;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
export class ParticleSystem {
|
|
205
|
-
constructor(config) {
|
|
206
|
-
this.fireworks = [];
|
|
207
|
-
this.config = { ...DEFAULT_PARTICLE_CONFIG };
|
|
208
|
-
this.maxConcurrentFireworks = 5; // Limit concurrent fireworks for performance
|
|
209
|
-
// Auto-launch settings
|
|
210
|
-
this.autoLaunch = false;
|
|
211
|
-
this.launchInterval = 3000; // ms
|
|
212
|
-
this.lastLaunchTime = 0;
|
|
213
|
-
this.nextLaunchDelay = 0;
|
|
214
|
-
if (config) {
|
|
215
|
-
this.config = { ...this.config, ...config };
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
update(deltaTime) {
|
|
219
|
-
// Update existing fireworks
|
|
220
|
-
for (let i = this.fireworks.length - 1; i >= 0; i--) {
|
|
221
|
-
this.fireworks[i].update(deltaTime);
|
|
222
|
-
if (this.fireworks[i].isDead()) {
|
|
223
|
-
this.fireworks.splice(i, 1);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
// Auto-launch
|
|
227
|
-
if (this.autoLaunch) {
|
|
228
|
-
const now = performance.now();
|
|
229
|
-
if (now - this.lastLaunchTime > this.nextLaunchDelay) {
|
|
230
|
-
this.lastLaunchTime = now;
|
|
231
|
-
this.nextLaunchDelay = this.launchInterval + Math.random() * 1000;
|
|
232
|
-
this.spawnFirework();
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
spawnFirework(x) {
|
|
237
|
-
// Don't spawn if we're at the limit (for performance)
|
|
238
|
-
if (this.fireworks.length >= this.maxConcurrentFireworks) {
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
241
|
-
const startX = x !== undefined ? x : (Math.random() - 0.5) * 5;
|
|
242
|
-
this.fireworks.push(new Firework(startX, this.config));
|
|
243
|
-
}
|
|
244
|
-
getFireworks() {
|
|
245
|
-
return this.fireworks;
|
|
246
|
-
}
|
|
247
|
-
setAutoLaunch(enabled) {
|
|
248
|
-
this.autoLaunch = enabled;
|
|
249
|
-
if (enabled) {
|
|
250
|
-
this.lastLaunchTime = performance.now();
|
|
251
|
-
this.nextLaunchDelay = 0;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
setConfig(config) {
|
|
255
|
-
this.config = { ...this.config, ...config };
|
|
256
|
-
}
|
|
257
|
-
getConfig() {
|
|
258
|
-
return { ...this.config };
|
|
259
|
-
}
|
|
260
|
-
setMaxConcurrentFireworks(max) {
|
|
261
|
-
this.maxConcurrentFireworks = Math.max(1, max);
|
|
262
|
-
}
|
|
263
|
-
getMaxConcurrentFireworks() {
|
|
264
|
-
return this.maxConcurrentFireworks;
|
|
265
|
-
}
|
|
266
|
-
}
|