diff --git a/src/assets/audio/SFX/bell.mp3 b/src/assets/audio/SFX/bell.mp3 new file mode 100644 index 0000000..6c4a9da Binary files /dev/null and b/src/assets/audio/SFX/bell.mp3 differ diff --git a/src/assets/audio/SFX/cutting.mp3 b/src/assets/audio/SFX/cutting.mp3 index 6921fd3..069acb7 100644 Binary files a/src/assets/audio/SFX/cutting.mp3 and b/src/assets/audio/SFX/cutting.mp3 differ diff --git a/src/assets/audio/SFX/fire-out.mp3 b/src/assets/audio/SFX/fire-out.mp3 new file mode 100644 index 0000000..79a8046 Binary files /dev/null and b/src/assets/audio/SFX/fire-out.mp3 differ diff --git a/src/assets/audio/SFX/fire-start.mp3 b/src/assets/audio/SFX/fire-start.mp3 new file mode 100644 index 0000000..4dd6f44 Binary files /dev/null and b/src/assets/audio/SFX/fire-start.mp3 differ diff --git a/src/assets/audio/SFX/fire.mp3 b/src/assets/audio/SFX/fire.mp3 new file mode 100644 index 0000000..960fbdf Binary files /dev/null and b/src/assets/audio/SFX/fire.mp3 differ diff --git a/src/assets/audio/SFX/ingredient.mp3 b/src/assets/audio/SFX/ingredient.mp3 index 094ce75..8215736 100644 Binary files a/src/assets/audio/SFX/ingredient.mp3 and b/src/assets/audio/SFX/ingredient.mp3 differ diff --git a/src/assets/audio/SFX/put-down.mp3 b/src/assets/audio/SFX/put-down.mp3 new file mode 100644 index 0000000..2acd1c8 Binary files /dev/null and b/src/assets/audio/SFX/put-down.mp3 differ diff --git a/src/assets/audio/soundtrack/high-heat.mp3 b/src/assets/audio/soundtrack/high-heat.mp3 new file mode 100644 index 0000000..c6648c1 Binary files /dev/null and b/src/assets/audio/soundtrack/high-heat.mp3 differ diff --git a/src/assets/audio/soundtrack/low-heat.mp3 b/src/assets/audio/soundtrack/low-heat.mp3 new file mode 100644 index 0000000..38fec98 Binary files /dev/null and b/src/assets/audio/soundtrack/low-heat.mp3 differ diff --git a/src/audio/audioListener.js b/src/audio/audioListener.js new file mode 100644 index 0000000..0194ae2 --- /dev/null +++ b/src/audio/audioListener.js @@ -0,0 +1,63 @@ +import { on } from "../core/gameEvents.js"; +import { playSound } from "./audioManager.js"; +import { INGREDIENTS } from "../data/items.js"; + +on("cut_complete", () => { + playSound("cutting"); +}); + +on("crush_complete", () => { + playSound("crushing"); +}); + +on("cauldron_start", () => { + playSound("fireOn"); +}); + +on("cauldron_done", () => { + playSound("fireOff"); +}); + +on("delivered", () => { + playSound("bell"); +}); + +on("trash", () => { + playSound("trash"); +}); + +on("ingredient_pickup", (ing) => { + switch (ing) { + case INGREDIENTS.Vinum: + case INGREDIENTS.Oleum: + case INGREDIENTS.Aqua: + playSound("base"); + break; + + default: + playSound("ingredient"); + break; + } +}); + +on("place", () => { + playSound("place"); +}); + +on("glass_pickup", () => { + playSound("glass"); +}); + +on("new_order", () => { + playSound("newOrder"); +}); + +on("bottle_potion", (result) => { + const overbrewed = result & ((1 << 28) | (1 << 30)); + + if (overbrewed) { + playSound("overbrewedFx"); + } else { + playSound("bottling"); + } +}); diff --git a/src/audio/audioManager.js b/src/audio/audioManager.js index f3bbe81..9733192 100644 --- a/src/audio/audioManager.js +++ b/src/audio/audioManager.js @@ -16,14 +16,14 @@ export function playSound(name) { sound.play().catch(() => {}); } +let activeMusicName = null; + export function playMusic(name) { + if (activeMusicName === name && activeMusicName !== "menu") return; const newMusic = music[name]; if (!newMusic) return; - // already playing - if (activeMusic === newMusic) return; - // stop previous if (activeMusic) { activeMusic.pause(); @@ -31,11 +31,14 @@ export function playMusic(name) { } activeMusic = newMusic; + activeMusicName = name; activeMusic.volume = settings.musicVolume; activeMusic.loop = true; activeMusic.play().catch(() => {}); + + console.log("Music Playing!"); } export function stopMusic() { @@ -45,6 +48,27 @@ export function stopMusic() { activeMusic.currentTime = 0; activeMusic = null; + activeMusicName = null; + + currentPhase = "low"; +} + +let currentPhase = "low"; + +export function updateGameplayMusic(heat) { + if (heat >= 2 && currentPhase === "low") { + currentPhase = "high"; + + playMusic("high"); + } +} + +export function isGameplayMusicPlaying() { + return activeMusicName === "low" || activeMusicName === "high"; +} + +export function resetGameplayMusic() { + currentPhase = "low"; } export function updateMusicVolume() { diff --git a/src/audio/music.js b/src/audio/music.js index 277f778..3df0a7c 100644 --- a/src/audio/music.js +++ b/src/audio/music.js @@ -1,15 +1,20 @@ import menuMusicFile from "../assets/audio/soundtrack/3_main-menu.mp3"; -import gameMusicFile from "../assets/audio/soundtrack/3_gameplay.mp3"; +import lowHeatMusicFile from "../assets/audio/soundtrack/low-heat.mp3"; +import highHeatMusicFile from "../assets/audio/soundtrack/high-heat.mp3"; const menuMusic = new Audio(menuMusicFile); menuMusic.loop = true; -const gameMusic = new Audio(gameMusicFile); -gameMusic.loop = true; +const lowHeat = new Audio(lowHeatMusicFile); +lowHeat.loop = true; + +const highHeat = new Audio(highHeatMusicFile); +highHeat.loop = true; const music = { menu: menuMusic, - game: gameMusic, + low: lowHeat, + high: highHeat, }; export default music; diff --git a/src/audio/sound.js b/src/audio/sound.js index 75b5e0d..9cfa029 100644 --- a/src/audio/sound.js +++ b/src/audio/sound.js @@ -2,12 +2,15 @@ import baseFx from "../assets/audio/SFX/base.wav"; import bottlingFx from "../assets/audio/SFX/bottling.wav"; import overbrewedFx from "../assets/audio/SFX/bottling-overbrewed.wav"; import crushingFx from "../assets/audio/SFX/crushing.wav"; -import cuttingFx from "../assets/audio/SFX/cutting.wav"; +import cuttingFx from "../assets/audio/SFX/cutting.mp3"; import glassFx from "../assets/audio/SFX/glass.wav"; -import ingredientFx from "../assets/audio/SFX/ingredient.wav"; +import ingredientFx from "../assets/audio/SFX/ingredient.mp3"; import newOrderFx from "../assets/audio/SFX/new-order.wav"; -import placeFx from "../assets/audio/SFX/place.wav"; +import placeFx from "../assets/audio/SFX/put-down.mp3"; import trashFx from "../assets/audio/SFX/trash.wav"; +import fireStart from "../assets/audio/SFX/fire-start.mp3"; +import fireOut from "../assets/audio/SFX/fire-out.mp3"; +import bellFx from "../assets/audio/SFX/bell.mp3"; const sounds = { base: new Audio(baseFx), @@ -20,6 +23,9 @@ const sounds = { newOrder: new Audio(newOrderFx), place: new Audio(placeFx), trash: new Audio(trashFx), + fireOn: new Audio(fireStart), + fireOff: new Audio(fireOut), + bell: new Audio(bellFx), }; export default sounds; diff --git a/src/core/gameEvents.js b/src/core/gameEvents.js new file mode 100644 index 0000000..e14fbd9 --- /dev/null +++ b/src/core/gameEvents.js @@ -0,0 +1,17 @@ +const listeners = {}; + +export function on(event, callback) { + if (!listeners[event]) { + listeners[event] = []; + } + + listeners[event].push(callback); +} + +export function emit(event, data = null) { + if (!listeners[event]) return; + + listeners[event].forEach((callback) => { + callback(data); + }); +} diff --git a/src/core/index.js b/src/core/index.js index 6bded44..8186512 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -44,7 +44,13 @@ import { import { drawGameOver } from "../rendering/gameOverRenderer.js"; import { drawHUD } from "../rendering/hudRendering.js"; import { drawRecipesMenu } from "../rendering/recipesRenderer.js"; -import { playMusic, updateMusicVolume } from "../audio/audioManager.js"; +import { + isGameplayMusicPlaying, + playMusic, + updateMusicVolume, + stopMusic, +} from "../audio/audioManager.js"; +import "../audio/audioListener.js"; const { canvas, ctx } = Canvas(); @@ -196,8 +202,15 @@ function loop() { case GAME_STATES.MENU: playMusic("menu"); break; + case GAME_STATES.PLAYING: - playMusic("game"); + if (!isGameplayMusicPlaying()) { + playMusic("low"); + } + break; + + case GAME_STATES.GAME_OVER: + stopMusic(); break; } } diff --git a/src/data/gameData.js b/src/data/gameData.js index 2efb193..61844f4 100644 --- a/src/data/gameData.js +++ b/src/data/gameData.js @@ -1,3 +1,9 @@ +import { + resetGameplayMusic, + updateGameplayMusic, +} from "../audio/audioManager.js"; +import { emit } from "../core/gameEvents.js"; + const gameData = { essence: 0, heat: 1, @@ -25,6 +31,7 @@ function callHeatListeners() { function increaseHeat() { if (gameData.heat >= defaultState.maxHeat) return; gameData.heat++; + updateGameplayMusic(gameData.heat); callHeatListeners(); } @@ -40,6 +47,7 @@ function makeDelivery() { function addOrder(recipe) { if (activeOrders.length > 2) return; + activeOrders.push({ recipe: recipe.recipe, timeRemaining: @@ -47,6 +55,7 @@ function addOrder(recipe) { maxTime: currentState.baseTime * defaultState.orderDurationRatio * recipe.count, }); + emit("new_order"); } const defaultState = Object.freeze({ @@ -70,6 +79,7 @@ function resetState() { gameData.essence = 0; gameData.heat = 1; currentState.ordersFullfilled = 0; + resetGameplayMusic(); heatListeners.length = 0; attachHeatListener((heatLevel) => { diff --git a/src/data/settings.js b/src/data/settings.js index 00c89ea..2a279e9 100644 --- a/src/data/settings.js +++ b/src/data/settings.js @@ -1,5 +1,5 @@ const settings = { - musicVolume: 0.0, + musicVolume: 0.4, sfxVolume: 0.5, }; diff --git a/src/entities/station.js b/src/entities/station.js index 34dc532..568a0d3 100644 --- a/src/entities/station.js +++ b/src/entities/station.js @@ -13,6 +13,8 @@ import { addOrder, makeDelivery, } from "../data/gameData.js"; +import { emit } from "../core/gameEvents.js"; +import { CUT } from "../data/items.js"; // Is this a valid ingredient for the task const canAction = (ing) => { @@ -42,6 +44,8 @@ class Station { this.startWorking = (baseTime) => { this._work_lock = true; this.duration = Math.ceil(baseTime * defaultState.stationRatio); + //Event + emit(action === CUT ? "cut_complete" : "crush_complete"); }; this.doWork = (timeStep) => { @@ -62,6 +66,7 @@ class Station { this.inventory === 0; this.place = (playerInv) => { + emit("place"); this.inventory = playerInv.ingredient; playerInv.ingredient = 0; }; @@ -73,6 +78,7 @@ class Station { !playerInv.hasGlass(); this.take = (playerInv) => { + emit("ingredient_pickup", playerInv.ingredient); playerInv.ingredient = this.inventory; this.inventory = 0; this.duration = 0; @@ -97,6 +103,7 @@ class DeliveryStation { ); if (orderIndex !== -1) { + emit("delivered"); activeOrders.splice(orderIndex, 1); makeDelivery(); } else { @@ -116,11 +123,16 @@ class Ingredient { constructor(ing) { this.inventory = ing; this.canPlace = (playerInv) => playerInv.ingredient === this.inventory; - this.place = (playerInv) => (playerInv.ingredient = 0); + this.place = (playerInv) => { + playerInv.ingredient = 0; + emit("place"); + }; this.canTake = (playerInv) => playerInv.empty(); this.take = (playerInv) => { playerInv.ingredient = this.inventory; + + emit("ingredient_pickup", playerInv.ingredient); }; } } @@ -133,6 +145,7 @@ class Glass { this.canTake = (playerInv) => playerInv.empty(); this.take = (playerInv) => { + emit("glass_pickup"); playerInv.glass = this.inventory; }; } @@ -177,6 +190,8 @@ class Cauldron { this.inventory |= playerInv.ingredient; playerInv.ingredient = 0; this.itemCount++; + + emit("place"); }; this.canTake = (playerInv) => @@ -190,12 +205,15 @@ class Cauldron { this.inventory = 0; this.progress = this.duration = 0; this.itemCount = 0; + emit("cauldron_done"); + emit("bottle_potion", playerInv.glass); }; this.canWork = () => this.duration === 0 && this._get_base(this.inventory) !== 0; this.startWorking = (baseTime) => { + emit("cauldron_start"); this.duration = baseTime * defaultState.cookRatio; this._first_brew = true; }; @@ -249,6 +267,7 @@ class TrashCan { this.canPlace = () => true; this.place = (playerInv) => { + emit("trash"); playerInv.ingredient = 0; playerInv.glass = 0; };