valtech-components 4.0.420 → 4.0.422

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.
@@ -58,7 +58,7 @@ import fixWebmDuration from 'fix-webm-duration';
58
58
  * Current version of valtech-components.
59
59
  * This is automatically updated during the publish process.
60
60
  */
61
- const VERSION = '4.0.420';
61
+ const VERSION = '4.0.422';
62
62
 
63
63
  // Control de estado de refresco (singleton a nivel de módulo)
64
64
  let isRefreshing = false;
@@ -63203,6 +63203,9 @@ class RetroAudioService {
63203
63203
  async startGameMusic() {
63204
63204
  await this.startMusic('game');
63205
63205
  }
63206
+ async startBubbleBlitzMusic() {
63207
+ await this.startMusic('bubble');
63208
+ }
63206
63209
  async startMusic(mode = 'game') {
63207
63210
  if (!this.canPlayMusic())
63208
63211
  return;
@@ -63217,17 +63220,19 @@ class RetroAudioService {
63217
63220
  this.fadeMasterTo(1, 0.24);
63218
63221
  this.activeMelody = mode;
63219
63222
  this.noteIndex = 0;
63220
- const melody = mode === 'menu' ? this.menuMelody() : this.gameMelody();
63221
- const intervalMs = mode === 'menu' ? 640 : 190;
63222
- const wave = mode === 'menu' ? 'triangle' : 'square';
63223
- const gain = mode === 'menu' ? 0.018 : 0.025;
63223
+ const melody = mode === 'menu' ? this.menuMelody() : mode === 'bubble' ? this.bubbleMelody() : this.gameMelody();
63224
+ const intervalMs = mode === 'menu' ? 640 : mode === 'bubble' ? 760 : 190;
63225
+ const wave = mode === 'game' ? 'square' : 'triangle';
63226
+ const gain = mode === 'menu' ? 0.018 : mode === 'bubble' ? 0.014 : 0.025;
63224
63227
  this.musicTimer = window.setInterval(() => {
63225
63228
  const step = this.noteIndex;
63226
63229
  const [freq, duration] = melody[this.noteIndex % melody.length];
63227
63230
  this.noteIndex += 1;
63228
- this.tone(freq, duration, wave, gain);
63231
+ void this.musicTone(freq, duration, wave, gain);
63229
63232
  if (mode === 'menu')
63230
63233
  this.savannaPulse(step);
63234
+ if (mode === 'bubble')
63235
+ this.bubbleAmbientPulse(step);
63231
63236
  }, intervalMs);
63232
63237
  }
63233
63238
  stopMusic() {
@@ -63304,6 +63309,62 @@ class RetroAudioService {
63304
63309
  [146.83, 0.18],
63305
63310
  ]);
63306
63311
  }
63312
+ bubblePop(size = 2, combo = 1) {
63313
+ const safeSize = Math.max(2, Math.min(12, Math.round(size)));
63314
+ const safeCombo = Math.max(1, Math.min(9, Math.round(combo)));
63315
+ const base = 520 + safeSize * 18;
63316
+ void this.filteredNoise(0.065, 0.018 + safeSize * 0.0018, 'bandpass', 1200 + safeSize * 85);
63317
+ void this.sweep(base, base * 1.9, 0.09, 'sine', 0.026 + safeSize * 0.0014);
63318
+ window.setTimeout(() => void this.tone(base * 1.25, 0.055, 'triangle', 0.018), 38);
63319
+ if (safeSize >= 5 || safeCombo > 1) {
63320
+ window.setTimeout(() => void this.bubbleCombo(safeCombo), 82);
63321
+ }
63322
+ }
63323
+ bubbleCombo(combo = 1) {
63324
+ const safeCombo = Math.max(1, Math.min(9, Math.round(combo)));
63325
+ const root = 660 + safeCombo * 26;
63326
+ void this.sequence([
63327
+ [root, 0.045],
63328
+ [root * 1.25, 0.055],
63329
+ [root * 1.5, 0.07],
63330
+ ], 'triangle', 0.024 + safeCombo * 0.0016, 42);
63331
+ }
63332
+ bubbleInvalid() {
63333
+ void this.sequence([
63334
+ [246.94, 0.055],
63335
+ [196, 0.085],
63336
+ ], 'sine', 0.034, 54);
63337
+ }
63338
+ bubbleSettle(drop = 1) {
63339
+ const safeDrop = Math.max(1, Math.min(9, Math.round(drop)));
63340
+ void this.filteredNoise(0.055, 0.009 + safeDrop * 0.001, 'lowpass', 360 + safeDrop * 28);
63341
+ void this.tone(164.81 + safeDrop * 8, 0.045, 'triangle', 0.012);
63342
+ }
63343
+ bubbleShuffle() {
63344
+ void this.sequence([
63345
+ [392, 0.04],
63346
+ [523.25, 0.04],
63347
+ [659.25, 0.05],
63348
+ [783.99, 0.07],
63349
+ ], 'sine', 0.024, 38);
63350
+ window.setTimeout(() => void this.filteredNoise(0.16, 0.018, 'bandpass', 950), 28);
63351
+ }
63352
+ bubbleFever() {
63353
+ void this.sweep(392, 1567.98, 0.24, 'triangle', 0.034);
63354
+ window.setTimeout(() => void this.sequence([[987.77, 0.055], [1318.51, 0.085]], 'sine', 0.025, 48), 128);
63355
+ window.setTimeout(() => void this.filteredNoise(0.12, 0.014, 'highpass', 2400), 72);
63356
+ }
63357
+ bubbleFinish() {
63358
+ this.stopMusic();
63359
+ void this.sequence([
63360
+ [523.25, 0.07],
63361
+ [659.25, 0.07],
63362
+ [783.99, 0.08],
63363
+ [1046.5, 0.13],
63364
+ [1318.51, 0.18],
63365
+ ], 'triangle', 0.042, 62);
63366
+ window.setTimeout(() => void this.filteredNoise(0.18, 0.016, 'bandpass', 1800), 110);
63367
+ }
63307
63368
  chessimalsMove() {
63308
63369
  void this.sequence([
63309
63370
  [392, 0.045],
@@ -63639,6 +63700,33 @@ class RetroAudioService {
63639
63700
  [659.25, 0.18],
63640
63701
  ];
63641
63702
  }
63703
+ bubbleMelody() {
63704
+ return [
63705
+ [261.63, 0.72],
63706
+ [0, 0.18],
63707
+ [329.63, 0.66],
63708
+ [392, 0.78],
63709
+ [0, 0.24],
63710
+ [349.23, 0.64],
63711
+ [293.66, 0.72],
63712
+ [0, 0.28],
63713
+ [440, 0.84],
63714
+ [392, 0.7],
63715
+ [329.63, 0.78],
63716
+ [0, 0.32],
63717
+ ];
63718
+ }
63719
+ bubbleAmbientPulse(step) {
63720
+ if (step % 4 === 0) {
63721
+ window.setTimeout(() => void this.musicTone(130.81, 1.35, 'sine', 0.009), 24);
63722
+ }
63723
+ if (step % 6 === 2) {
63724
+ window.setTimeout(() => void this.musicTone(196, 0.95, 'triangle', 0.008), 120);
63725
+ }
63726
+ if (step % 8 === 5) {
63727
+ window.setTimeout(() => void this.musicTone(523.25, 0.42, 'sine', 0.006), 180);
63728
+ }
63729
+ }
63642
63730
  async sequence(notes, wave = 'square', gain = 0.055, intervalMs = 90) {
63643
63731
  if (!this.enabled())
63644
63732
  return;
@@ -63758,6 +63846,25 @@ class RetroAudioService {
63758
63846
  oscillator.start();
63759
63847
  oscillator.stop(ctx.currentTime + duration + 0.02);
63760
63848
  }
63849
+ async musicTone(freq, duration = 0.1, type = 'triangle', gainValue = 0.018) {
63850
+ if (!this.canPlayMusic())
63851
+ return;
63852
+ if (freq <= 0)
63853
+ return;
63854
+ const ctx = await this.wakeAudio();
63855
+ const oscillator = ctx.createOscillator();
63856
+ const gain = ctx.createGain();
63857
+ oscillator.type = type;
63858
+ oscillator.frequency.value = freq;
63859
+ gain.gain.setValueAtTime(0.0001, ctx.currentTime);
63860
+ gain.gain.exponentialRampToValueAtTime(gainValue * this.musicVolume(), ctx.currentTime + 0.08);
63861
+ gain.gain.setTargetAtTime(gainValue * this.musicVolume() * 0.74, ctx.currentTime + 0.12, 0.2);
63862
+ gain.gain.exponentialRampToValueAtTime(0.0001, ctx.currentTime + duration);
63863
+ oscillator.connect(gain);
63864
+ gain.connect(this.masterGain ?? ctx.destination);
63865
+ oscillator.start();
63866
+ oscillator.stop(ctx.currentTime + duration + 0.08);
63867
+ }
63761
63868
  async sweep(fromFreq, toFreq, duration = 0.16, type = 'triangle', gainValue = 0.03) {
63762
63869
  if (!this.canPlaySfx())
63763
63870
  return;
@@ -67711,7 +67818,7 @@ class BubbleBlitzGameComponent {
67711
67818
  this.selectedGroupKey.set('');
67712
67819
  this.animating.set(false);
67713
67820
  this.clearAnimationTimer();
67714
- void this.audio.startGameMusic().catch(() => undefined);
67821
+ void this.audio.startBubbleBlitzMusic().catch(() => undefined);
67715
67822
  this.startTimer();
67716
67823
  }
67717
67824
  resetToSetup() {
@@ -67731,7 +67838,7 @@ class BubbleBlitzGameComponent {
67731
67838
  this.selectedGroupKey.set(group.map((item) => item.id).sort().join('|'));
67732
67839
  if (group.length < 2) {
67733
67840
  this.combo.set(1);
67734
- this.audio.miss();
67841
+ this.audio.bubbleInvalid();
67735
67842
  return;
67736
67843
  }
67737
67844
  this.animating.set(true);
@@ -67741,22 +67848,27 @@ class BubbleBlitzGameComponent {
67741
67848
  const points = group.length * group.length * this.combo() * feverMultiplier;
67742
67849
  this.score.update((value) => value + points);
67743
67850
  this.popped.update((value) => value + group.length);
67744
- this.fever.set(this.feverReady() ? 0 : Math.min(this.config().feverTarget, this.fever() + group.length));
67851
+ const feverWasReady = this.feverReady();
67852
+ this.fever.set(feverWasReady ? 0 : Math.min(this.config().feverTarget, this.fever() + group.length));
67745
67853
  this.combo.update((value) => {
67746
67854
  const next = Math.min(9, group.length >= 5 ? value + 1 : value);
67747
67855
  this.maxCombo.update((best) => Math.max(best, next));
67748
67856
  return next;
67749
67857
  });
67750
- this.audio.match();
67858
+ this.audio.bubblePop(group.length, this.combo());
67859
+ if (!feverWasReady && this.feverReady()) {
67860
+ window.setTimeout(() => this.audio.bubbleFever(), 100);
67861
+ }
67751
67862
  this.clearAnimationTimer();
67752
67863
  this.animationTimer = window.setTimeout(() => {
67753
67864
  const settled = this.refillBoard(this.grid().filter((item) => !groupIds.has(item.id)));
67754
67865
  this.grid.set(settled);
67866
+ this.audio.bubbleSettle(Math.max(1, ...settled.map((item) => item.drop ?? 0)));
67755
67867
  this.animationTimer = window.setTimeout(() => {
67756
67868
  if (!this.hasPlayableGroup(this.grid())) {
67757
67869
  this.grid.set(this.createBoard().map((item) => ({ ...item, state: 'entering', drop: this.config().rows })));
67758
67870
  this.combo.set(1);
67759
- this.audio.flip();
67871
+ this.audio.bubbleShuffle();
67760
67872
  this.animationTimer = window.setTimeout(() => this.finishAnimation(), BUBBLE_SETTLE_MS);
67761
67873
  return;
67762
67874
  }
@@ -67849,7 +67961,7 @@ class BubbleBlitzGameComponent {
67849
67961
  this.stopTimer();
67850
67962
  this.phase.set('finished');
67851
67963
  this.saveScore();
67852
- this.audio.win();
67964
+ this.audio.bubbleFinish();
67853
67965
  }
67854
67966
  createBoard() {
67855
67967
  const cells = [];