diff --git a/src/audio/audioManager.js b/src/audio/audioManager.js index 9733192..758df6e 100644 --- a/src/audio/audioManager.js +++ b/src/audio/audioManager.js @@ -1,6 +1,7 @@ import sounds from "./sound.js"; import music from "./music.js"; import settings from "../data/settings.js"; +import { on } from "../core/gameEvents.js"; let activeMusic = null; @@ -37,8 +38,6 @@ export function playMusic(name) { activeMusic.loop = true; activeMusic.play().catch(() => {}); - - console.log("Music Playing!"); } export function stopMusic() { @@ -55,13 +54,13 @@ export function stopMusic() { let currentPhase = "low"; -export function updateGameplayMusic(heat) { - if (heat >= 2 && currentPhase === "low") { +on("heatUp", (heat) => { + if (heat >= 4 && currentPhase === "low") { currentPhase = "high"; playMusic("high"); } -} +}); export function isGameplayMusicPlaying() { return activeMusicName === "low" || activeMusicName === "high"; diff --git a/src/data/gameData.js b/src/data/gameData.js index 61844f4..e7d89a4 100644 --- a/src/data/gameData.js +++ b/src/data/gameData.js @@ -1,49 +1,117 @@ -import { - resetGameplayMusic, - updateGameplayMusic, -} from "../audio/audioManager.js"; -import { emit } from "../core/gameEvents.js"; +import { resetGameplayMusic } from "../audio/audioManager.js"; +import { on, emit } from "../core/gameEvents.js"; +import { getRandomRecipe } from "./items.js"; const gameData = { essence: 0, heat: 1, }; -let heatListeners = []; -function attachHeatListener(observer) { - if (heatListeners.includes(observer)) return false; +const difficultyLevels = [ + { + requiredEssence: 0, + }, + { + requiredEssence: 100, + }, + { + requiredEssence: 300, + }, + { + requiredEssence: 600, + }, + { + requiredEssence: 900, + }, + { + requiredEssence: 1200, + }, +]; - heatListeners.push(observer); - return true; -} -function detachHeatListener(observer) { - const index = heatListeners.indexOf(observer); - if (index === -1) return false; +const dynamicDifficulty = { + scale: 1, + performance: 0, +}; - heatListeners.splice(index, 1); - return true; -} +let lastDelivery = performance.now(); +function adjustDynamicDifficulty(orderIndex, ratio) { + if (currentState.ordersFullfilled === 1) { + lastDelivery = performance.now(); + activeOrders.splice(orderIndex, 1); + return; + } + const now = performance.now(); + const sinceLastRatio = + (now - lastDelivery) / + (currentState.baseTime * currentState.timeToNextOrder); + lastDelivery = now; + + let performanceDelta = 0; + + // consistency + if (sinceLastRatio < 0.4) performanceDelta += 14; + else if (sinceLastRatio < 0.8) performanceDelta += 8; + else if (sinceLastRatio < 1.2) performanceDelta += 2; + else if (sinceLastRatio < 1.6) performanceDelta -= 5; + else performanceDelta -= 12; + + // delivery quality + if (ratio > 0.7) performanceDelta += 10; + else if (ratio > 0.45) performanceDelta += 5; + else if (ratio > 0.3) performanceDelta += 0; + else if (ratio > 0.1) performanceDelta -= 6; + else performanceDelta -= 12; + + if (activeOrders.length <= 2) performanceDelta *= 1.1; + else if (activeOrders.length <= 1) performanceDelta *= 1.2; -function callHeatListeners() { - heatListeners.forEach((observer) => observer(gameData.heat)); + dynamicDifficulty.performance *= 0.9; + dynamicDifficulty.performance += performanceDelta; + dynamicDifficulty.performance = Math.max( + -100, + Math.min(100, dynamicDifficulty.performance) + ); + dynamicDifficulty.scale = 1 + dynamicDifficulty.performance * 0.0025; + dynamicDifficulty.scale = Math.max( + 0.85, + Math.min(1.25, dynamicDifficulty.scale) + ); } function increaseHeat() { - if (gameData.heat >= defaultState.maxHeat) return; + if (gameData.heat >= difficultyLevels.length) return; gameData.heat++; - updateGameplayMusic(gameData.heat); - callHeatListeners(); + emit("heatUp", gameData.heat); } const activeOrders = []; -function makeDelivery() { - gameData.essence += 25; +on("delivered", (orderIndex) => { currentState.ordersFullfilled++; - if (currentState.ordersFullfilled >= currentState.nextHeat) { - increaseHeat(); + + const ratio = + activeOrders[orderIndex].timeRemaining / activeOrders[orderIndex].maxTime; + if (ratio >= 0.48) { + gameData.essence += 15 + Math.ceil(100 * ratio); + } else { + gameData.essence += 25 + Math.ceil(50 * ratio); } -} + + if (gameData.essence >= currentState.nextHeat) increaseHeat(); + + adjustDynamicDifficulty(orderIndex, ratio); + activeOrders.splice(orderIndex, 1); + for (const order of activeOrders) { + order.timeRemaining = Math.min( + order.maxTime, + order.timeRemaining + (ratio / 3) * order.maxTime + ); + } + + if (activeOrders.length === 0 && ratio > 0.6) { + addOrder(getRandomRecipe()); + } +}); function addOrder(recipe) { if (activeOrders.length > 2) return; @@ -51,18 +119,44 @@ function addOrder(recipe) { activeOrders.push({ recipe: recipe.recipe, timeRemaining: - currentState.baseTime * defaultState.orderDurationRatio * recipe.count, + (currentState.baseTime * defaultState.orderDurationRatio * recipe.count) / + dynamicDifficulty.scale, maxTime: - currentState.baseTime * defaultState.orderDurationRatio * recipe.count, + (currentState.baseTime * defaultState.orderDurationRatio * recipe.count) / + dynamicDifficulty.scale, }); emit("new_order"); + + currentState.timeToNextOrder = + (defaultState.orderAdditionRatio * currentState.baseTime) / + dynamicDifficulty.scale; +} + +function progressOrders(timeStep) { + let timedOutPots = 0; + for (let i = activeOrders.length - 1; i >= 0; i--) { + const order = activeOrders[i]; + + order.timeRemaining -= timeStep; + if (order.timeRemaining <= 0) { + timedOutPots++; + dynamicDifficulty.performance -= 10; + activeOrders.splice(i, 1); + } + } + + dynamicDifficulty.scale = 1 + dynamicDifficulty.performance * 0.0025; + dynamicDifficulty.scale = Math.max( + 0.85, + Math.min(1.25, dynamicDifficulty.scale) + ); + return timedOutPots; } const defaultState = Object.freeze({ - baseTime: 15000, // in ms - maxHeat: 6, - orderAdditionRatio: 12, - orderDurationRatio: 6, + baseTime: 9000, // in ms + orderAdditionRatio: 10, + orderDurationRatio: 8, cookRatio: 3, stationRatio: 2 / 3, }); @@ -76,30 +170,33 @@ const currentState = { }; function resetState() { + lastDelivery = performance.now(); + gameData.essence = 0; gameData.heat = 1; + currentState.ordersFullfilled = 0; - resetGameplayMusic(); + dynamicDifficulty.scale = 1; + dynamicDifficulty.performance = 0; - heatListeners.length = 0; - attachHeatListener((heatLevel) => { - currentState.nextHeat += heatLevel + 1; - currentState.baseTime = defaultState.baseTime / (2 + heatLevel); - currentState.timeToNextOrder = - defaultState.orderAdditionRatio * currentState.baseTime; - }); - callHeatListeners(); + resetGameplayMusic(); + emit("heatUp", 1); } +on("heatUp", (heatLevel) => { + currentState.nextHeat = difficultyLevels[heatLevel].requiredEssence; + currentState.baseTime = defaultState.baseTime / (1 + heatLevel * 0.55); + currentState.timeToNextOrder = + (defaultState.orderAdditionRatio * currentState.baseTime) / + dynamicDifficulty.scale; +}); + export { gameData, activeOrders, addOrder, - makeDelivery, + progressOrders, defaultState, currentState, resetState, - attachHeatListener, - detachHeatListener, - increaseHeat, }; diff --git a/src/data/settings.js b/src/data/settings.js index 2a279e9..77ae4c6 100644 --- a/src/data/settings.js +++ b/src/data/settings.js @@ -1,5 +1,5 @@ const settings = { - musicVolume: 0.4, + musicVolume: 0.2, sfxVolume: 0.5, }; diff --git a/src/entities/station.js b/src/entities/station.js index 568a0d3..b9e12fd 100644 --- a/src/entities/station.js +++ b/src/entities/station.js @@ -3,16 +3,9 @@ import { VARIATION_COUNT, BREW, OVERBREWED, - getRandomRecipe, hasRecipe, } from "../data/items.js"; -import { - gameData, - defaultState, - activeOrders, - addOrder, - makeDelivery, -} from "../data/gameData.js"; +import { gameData, defaultState, activeOrders } from "../data/gameData.js"; import { emit } from "../core/gameEvents.js"; import { CUT } from "../data/items.js"; @@ -103,16 +96,10 @@ class DeliveryStation { ); if (orderIndex !== -1) { - emit("delivered"); - activeOrders.splice(orderIndex, 1); - makeDelivery(); + emit("delivered", orderIndex); } else { gameData.essence++; } - if (activeOrders.length === 0) { - addOrder(getRandomRecipe()); - } - playerInv.glass = 0; }; diff --git a/src/logic/update.js b/src/logic/update.js index b7b4c5f..1862a82 100644 --- a/src/logic/update.js +++ b/src/logic/update.js @@ -14,7 +14,7 @@ import { takePlace, work } from "./actions.js"; import { GAME_STATES, setGameState } from "../core/gameState.js"; import { activeOrders, - defaultState, + progressOrders, currentState, resetState, addOrder, @@ -120,22 +120,6 @@ function calculatePosition(timeStep) { } } -function progressOrders(timeStep) { - for (let i = activeOrders.length - 1; i >= 0; i--) { - const order = activeOrders[i]; - - order.timeRemaining -= timeStep; - if (order.timeRemaining <= 0) { - activeOrders.splice(i, 1); - - player.lives--; - if (player.lives <= 0) { - setGameState(GAME_STATES.GAME_OVER); - } - } - } -} - function update(timeStep) { // UPDATE STATIONS for (let st of stations) { @@ -143,12 +127,13 @@ function update(timeStep) { } // UPDATE ORDERS - progressOrders(timeStep); + player.lives -= progressOrders(timeStep); + if (player.lives <= 0) { + setGameState(GAME_STATES.GAME_OVER); + } currentState.timeToNextOrder -= timeStep; if (currentState.timeToNextOrder < 0) { - currentState.timeToNextOrder = - currentState.baseTime * defaultState.orderAdditionRatio; addOrder(getRandomRecipe()); } @@ -166,6 +151,10 @@ function update(timeStep) { if (justPressed["f"]) { const active = getActiveInteractable(stations); if (active) currentInteraction = work(active, currentState.baseTime); + if (currentInteraction) { + player.velocityX = 0; + player.velocityY = 0; + } } } else { if (!currentInteraction._work_lock) currentInteraction = null;