diff --git a/src/art/sounds/sleep.mp3 b/src/art/sounds/sleep.mp3 index a5831a7..8010841 100644 Binary files a/src/art/sounds/sleep.mp3 and b/src/art/sounds/sleep.mp3 differ diff --git a/src/checkmodal.ts b/src/checkmodal.ts index 8d7a174..6844df3 100644 --- a/src/checkmodal.ts +++ b/src/checkmodal.ts @@ -7,6 +7,7 @@ import { BG_INSET, FG_BOLD } from "./colors.ts"; import { addButton } from "./button.ts"; import { getSkills } from "./skills.ts"; import { getPlayerProgress } from "./playerprogress.ts"; +import { sndRecruit } from "./sounds.ts"; export class CheckModal { #drawpile: DrawPile; diff --git a/src/hotbar.ts b/src/hotbar.ts index e396d7c..eda0826 100644 --- a/src/hotbar.ts +++ b/src/hotbar.ts @@ -6,6 +6,7 @@ import { addButton } from "./button.ts"; import { getPlayerProgress } from "./playerprogress.ts"; import { getStateManager } from "./statemanager.ts"; import { getCheckModal } from "./checkmodal.ts"; +import { sndRecruit, sndSleep } from "./sounds.ts"; //import { LadderPickup } from "./pickups.ts"; // import { generateMap } from "./mapgen.ts"; diff --git a/src/huntmode.ts b/src/huntmode.ts index 6907834..b9ad338 100644 --- a/src/huntmode.ts +++ b/src/huntmode.ts @@ -18,6 +18,7 @@ import { CARDINAL_DIRECTIONS } from "./mapgen.ts"; import { Block3D, Floor3D, World3D } from "./world3d.ts"; import { Floater } from "./floater.ts"; import { displace } from "./physics.ts"; +import { sndRecruit } from "./sounds.ts"; export class HuntMode { map: LoadedNewMap; diff --git a/src/pickups.ts b/src/pickups.ts index d115d3b..db4f620 100644 --- a/src/pickups.ts +++ b/src/pickups.ts @@ -19,6 +19,14 @@ import { FG_BOLD, FG_TEXT, SWATCH_EXP, SWATCH_STAT } from "./colors.ts"; import { Block3D } from "./world3d.ts"; import { DrawPile } from "./drawpile.ts"; import { Floater } from "./floater.ts"; +import { + sndBite, + sndDeath, + sndDig, + sndRecruit, + sndRewardFor, + sndRewardHuge, +} from "./sounds.ts"; export type Pickup = | LockPickup @@ -73,7 +81,10 @@ export class LockPickup { update() {} onClick(cell: CellView): boolean { - getCheckModal().show(this.check, () => (cell.pickup = null)); + getCheckModal().show(this.check, () => { + cell.pickup = null; + sndRecruit.play(); + }); return true; } @@ -146,6 +157,7 @@ export class BreakableBlockPickup { cellData.pickup = null; let n = choose([1, 1, 1, 1, 1, 2, 3]); + sndRewardFor(n).play(); for (let i = 0; i < n; i++) { let floater = new Floater( cellData.xy.offset(new Point(0.5, 0.5)), @@ -171,6 +183,9 @@ export class BreakableBlockPickup { } onSqueeze(_cellData: CellView) { + if (this.breakProgress == 0) { + sndDig.play({ volume: 0.5 }); + } this.breakProgress = Math.min( this.breakProgress + 0.02 + RECOVERY_PER_TICK, 1.0, @@ -358,6 +373,7 @@ export class ThrallPickup { onClick(cell: CellView): boolean { let data = getThralls().get(this.thrall); getCheckModal().show(data.initialCheck, () => { + sndRecruit.play(); getPlayerProgress().unlockThrall(this.thrall); cell.pickup = null; }); @@ -520,6 +536,13 @@ export class ThrallRecruitedPickup { 100, ); getPlayerProgress().damageThrall(this.thrall, choose([0.9])); + + let newLifeStage = getPlayerProgress().getThrallLifeStage(this.thrall); + if (lifeStage != LifeStage.Dead && newLifeStage == LifeStage.Dead) { + sndDeath.play({ volume: 1.0 }); + } else { + sndBite.play({ volume: 1.0 }); + } }, ); return true; @@ -651,6 +674,7 @@ export class ThrallCollectionPlatePickup { null, ); data.rewardCallback((what) => this.spawn(cell.xy, what)); + sndRewardHuge.play(); } } diff --git a/src/sound.ts b/src/sound.ts new file mode 100644 index 0000000..70cbf17 --- /dev/null +++ b/src/sound.ts @@ -0,0 +1,39 @@ +class SoundShared { + readonly context: AudioContext; + + constructor() { + this.context = new AudioContext(); + } +} +const shared = new SoundShared(); + +export class Sound { + #link: string; + #audioBufferPromise: Promise; + + constructor(link: string) { + this.#link = link; + this.#audioBufferPromise = this.#unsafeGetAudioBuffer(); + } + async #unsafeGetAudioBuffer(): Promise { + let resp = await fetch(this.#link); + let buf = await resp.arrayBuffer(); + return await shared.context.decodeAudioData(buf); + } + + async #getAudioBuffer(): Promise { + return await this.#audioBufferPromise; + } + + play(options?: { volume: number }) { + this.#getAudioBuffer().then((adata) => { + let source = shared.context.createBufferSource(); + source.buffer = adata; + let gain = shared.context.createGain(); + gain.gain.value = options?.volume ?? 1.0; + source.connect(gain); + gain.connect(shared.context.destination); + source.start(); + }); + } +} diff --git a/src/sounds.ts b/src/sounds.ts new file mode 100644 index 0000000..156de61 --- /dev/null +++ b/src/sounds.ts @@ -0,0 +1,33 @@ +import audBite from "./art/sounds/bite.mp3"; +import audDeath from "./art/sounds/death.mp3"; +import audDig from "./art/sounds/dig.mp3"; +import audRecruit from "./art/sounds/recruit.mp3"; +import audRewardBig from "./art/sounds/reward_big.mp3"; +import audRewardHuge from "./art/sounds/reward_huge.mp3"; +import audRewardMedium from "./art/sounds/reward_medium.mp3"; +import audRewardSmall from "./art/sounds/reward_small.mp3"; +import audSleep from "./art/sounds/sleep.mp3"; +import { Sound } from "./sound.ts"; + +export let sndBite = new Sound(audBite); +export let sndDeath = new Sound(audDeath); +export let sndDig = new Sound(audDig); +export let sndRecruit = new Sound(audRecruit); +export let sndRewardBig = new Sound(audRewardBig); +export let sndRewardHuge = new Sound(audRewardHuge); +export let sndRewardMedium = new Sound(audRewardMedium); +export let sndRewardSmall = new Sound(audRewardSmall); +export let sndSleep = new Sound(audSleep); + +export function sndRewardFor(amount: number) { + if (amount <= 1) { + return sndRewardSmall; + } + if (amount <= 2) { + return sndRewardMedium; + } + if (amount <= 3) { + return sndRewardBig; + } + return sndRewardHuge; +} diff --git a/src/statemanager.ts b/src/statemanager.ts index d03c724..05d6eea 100644 --- a/src/statemanager.ts +++ b/src/statemanager.ts @@ -5,6 +5,7 @@ import { getScorer } from "./scorer.ts"; import { getEndgameModal } from "./endgamemodal.ts"; import { SuccessorOption, Wish } from "./datatypes.ts"; import { generateManor } from "./manormap.ts"; +import { sndSleep } from "./sounds.ts"; const N_TURNS: number = 9; @@ -23,6 +24,7 @@ export class StateManager { this.#turn = 1; initPlayerProgress(asSuccessor, withWish); initHuntMode(new HuntMode(1, generateManor())); + sndSleep.play(); } advance() { @@ -31,6 +33,7 @@ export class StateManager { getPlayerProgress().applyEndOfTurn(); getPlayerProgress().refill(); initHuntMode(new HuntMode(getHuntMode().depth, generateManor())); + sndSleep.play(); } else { // TODO: Play a specific scene let ending = getScorer().pickEnding();