1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558 |
- import * as THREE from 'three'
- import gsap from 'gsap'
- import { Howl, Howler } from 'howler'
- import LevelLoader from './library/levelloader.js'
- import { loadGltf } from './library/loadgltf.js'
- import { loadRgbeBackground } from './library/loadbackground.js'
- import { getForwardVector, getRightVector, drawDebugLine } from './library/mathhelpers.js'
- import Ingredient from './library/ingredient.js'
- import './library/arrayhelpers.js'
- // Shaders
- import firefliesVertex from './shaders/firefly/vertex.glsl'
- import firefliesFragment from './shaders/firefly/fragment.glsl'
- import cauldronVertex from './shaders/cauldron/vertex.glsl'
- import cauldronFragment from './shaders/cauldron/fragment.glsl'
- // UI
- import { brewTutorialPrompt, closeMainMenuUI, mainMenuUI, openMainMenuUI } from './ui/mainmenuui.js'
- import { brewUI, openBrewUI, closeBrewUI, updateIngredients } from './ui/gameui.js'
- import { closeShopUI, openShopUI, shopTutorialPrompt, shopUI } from './ui/shopui.js'
- import { navigationUI, hideNavigationUI, nextRoom, previousRoom } from './ui/navigationui.js'
- // Game Data
- import * as levelData from './data/level.json'
- import * as ingredientInfo from './data/ingredients.json'
- import * as potionInfo from './data/potions.json'
- import Shopper from './shopper.js'
- import { buildCanvasText } from './library/canvastext.js'
- import { hideGameStatusUI, updateGameStatusUI } from './ui/gamestatusui.js'
- import { gameOverUI, showGameOverUI } from './ui/gameoverui.js'
- import { closeMarketUI, marketUI, openMarketUI } from './ui/marketui.js'
- import { creditsUI, hideCreditsUI } from './ui/creditsui.js'
- import { hideOptionsUI, optionsUI } from './ui/optionsui.js'
- export const GAME_SAVE_KEY = "spookonomics-v1"
- const raycast = new THREE.Raycaster()
- let game, jackolantern, doorway, directionalLight, doorway2, lollipop, candle, candleLit, book
- const ingredients = []
- const soundEffects = {}
- const stageData = {}
- export const ROOM_SHOP = 1;
- export const ROOM_BREW = 2;
- export const ROOM_MARKET = 3;
- const shelfSlots = [
- { "position": new THREE.Vector3(-3.5, 1.5, -3.5), "motionPath": [{ x: -3, y: 1.3, z: -3.5 }, { x: -1.5, y: 2, z: 0 }, { x: -0.5, y: 2, z: 0 }, { x: 0, y: 0.5, z: 0 }] },
- { "position": new THREE.Vector3(-4.5, 1.5, -3.5), "motionPath": [{ x: -3, y: 1.3, z: -3.5 }, { x: -1.5, y: 2, z: 0 }, { x: -0.5, y: 2, z: 0 }, { x: 0, y: 0.5, z: 0 }] },
- { "position": new THREE.Vector3(-4.5, 0, -3.0), "motionPath": [{ x: -3, y: 1.3, z: -3.5 }, { x: -1.5, y: 2, z: 0 }, { x: -0.5, y: 2, z: 0 }, { x: 0, y: 0.5, z: 0 }] },
- { "position": new THREE.Vector3(-3.3, 3, -3.5), "motionPath": [{ x: -3, y: 1.3, z: -3.5 }, { x: -1.5, y: 2, z: 0 }, { x: -0.5, y: 2, z: 0 }, { x: 0, y: 0.5, z: 0 }] }
- ]
- const storeShelfSlots = [
- { "position": new THREE.Vector3(-12.5, 2.75, -3.25), "motionPath": [{ x: -11.5, y: 2.75, z: -3.25 }, { x: -12, y: 2, z: -2 }, { x: -13, y: 0.75, z: 2.5 }] },
- { "position": new THREE.Vector3(-11.5, 2.75, -3.25), "motionPath": [{ x: -11.5, y: 2.75, z: -3.25 }, { x: -12, y: 2, z: -2 }, { x: -13, y: 0.75, z: 2.5 }] },
- { "position": new THREE.Vector3(-8.5, 2.75, -3.25), "motionPath": [{ x: -11.5, y: 2.75, z: -3.25 }, { x: -12, y: 2, z: -2 }, { x: -13, y: 0.75, z: 2.5 }] },
- { "position": new THREE.Vector3(-7.5, 2.75, -3.25), "motionPath": [{ x: -11.5, y: 2.75, z: -3.25 }, { x: -12, y: 2, z: -2 }, { x: -13, y: 0.75, z: 2.5 }] },
- { "position": new THREE.Vector3(-8.5, 1.25, -3.25), "motionPath": [{ x: -11.5, y: 2.75, z: -3.25 }, { x: -12, y: 2, z: -2 }, { x: -13, y: 0.75, z: 2.5 }] },
- { "position": new THREE.Vector3(-7.5, 1.25, -3.25), "motionPath": [{ x: -11.5, y: 2.75, z: -3.25 }, { x: -12, y: 2, z: -2 }, { x: -13, y: 0.75, z: 2.5 }] },
- ]
- const customerSlots = [
- { "position": new THREE.Vector3(-12.9, 0.1, 3) },
- { "position": new THREE.Vector3(-13.1, 0.1, 4) },
- { "position": new THREE.Vector3(-12.7, 0.1, 5) },
- { "position": new THREE.Vector3(-13.1, 0.1, 6) },
- { "position": new THREE.Vector3(-12.9, 0.1, 7) },
- { "position": new THREE.Vector3(-13, 0.1, 8) },
- ]
- const bubblePositions = []
- const bubbleVelocity = []
- const bubbleLifespan = []
- const bubbleScale = []
- const bubbleCount = 15
- async function beginBrew(game, stageData) {
- if (!stageData.potionToBrew) {
- stageData.soundEffects['audio/witch_cackle1.ogg'].play()
- return
- }
- const newInventory = [...stageData.ingredientInventory]
- newInventory.remove(stageData.selectedIngredients[0])
- newInventory.remove(stageData.selectedIngredients[1])
-
- if(stageData.ingredientInventory.length - newInventory.length < 2) {
- stageData.soundEffects['audio/witch_cackle1.ogg'].play()
- return
- }
- stageData.cauldron.isBrewing = true
- closeBrewUI(game, stageData)
- stageData.brewWitch.stir = true
- stageData.brewWitch.bounce = true
- stageData.spoon.visible = true
- stageData.selectedIngredients.forEach(ingredientName => {
- ingredients.find((ingredient => ingredient.getName() == ingredientName)).beginBrew()
- })
- stageData.bottle1 = await loadGltf(game, stageData.potionToBrew.model)
- stageData.bottle1.position.y = -1.5
- game.scene.add(stageData.bottle1)
- stageData.soundEffects['audio/click1.ogg'].play()
- const nextColorData = stageData.potionToBrew.color
- stageData.cauldronUniforms.uNextPotionColor.value = new THREE.Color(nextColorData.r, nextColorData.g, nextColorData.b)
- stageData.cauldronUniforms.uBlendTime.value = 0.0
- setTimeout(() => {
-
- stageData.soundEffects['audio/bubbling.mp3'].play()
- stageData.bubbleParticles.visible = true
- gsap.to(stageData.cauldronUniforms.uBlendTime, {
- value: 1, duration: 4.5, onComplete: () => {
- stageData.cauldronUniforms.uPotionColor.value = stageData.cauldronUniforms.uNextPotionColor.value
- stageData.cauldronUniforms.uBlendTime.value = 0.0
- }
- })
- }, 900)
- setTimeout(() => {
- addValueToSave(stageData.potionInventory, 'potion-inventory', stageData.potionToBrew.name)
- removeValueFromSave(stageData.ingredientInventory, 'ingredient-inventory', stageData.selectedIngredients[0])
- removeValueFromSave(stageData.ingredientInventory, 'ingredient-inventory', stageData.selectedIngredients[1])
- stageData.potionToBrew = null
- stageData.selectedIngredients = []
- stageData.bottle1.position.set(0, -1.5, 0)
- stageData.soundEffects['audio/bubbling.mp3'].fade(1, 0, 300).on('fade', () => {
- stageData.soundEffects['audio/bubbling.mp3'].stop()
- stageData.soundEffects['audio/bubbling.mp3'].volume(0.5)
- })
- stageData.brewWitch.stir = false
- stageData.brewWitch.bounce = false
- stageData.spoon.visible = false
- stageData.bubbleParticles.visible = false
- updateIngredients(game, stageData)
- gsap.to(stageData.bottle1.position, {
- duration: 3, y: 1.5, ease: "elastic", onComplete: () => {
- setTimeout(() => {
- game.scene.remove(stageData.bottle1)
- //TODO play potion particle effect
- stageData.cauldron.isBrewing = false
- }, 1000)
- }
- })
- }, 5000)
- }
- async function customerBuilder(randomPotion, delay) {
- const shopperPosition = customerSlots[stageData.customers.length].position
- const randomIndex = Math.floor(Math.random() * 3)
- let shopper
- switch (randomIndex) {
- case 0:
- shopper = new Shopper(game, 'characters/assembled_character_1.gltf.glb', randomPotion, new THREE.Vector3(3.5, 3.5, 3.5), shopperPosition, Math.PI, function (elapsedTime) {
- this.object3d.position.y = 0.0625 * Math.sin(2 * elapsedTime) + 0.5
- })
- break;
- case 1:
- shopper = new Shopper(game, 'characters/character_skeleton_minion.gltf', randomPotion, new THREE.Vector3(1, 1, 1), shopperPosition, Math.PI, function (elapsedTime) {
- this.object3d.scale.x = 1 + (0.03125 * Math.sin(11.8 * elapsedTime))
- this.object3d.scale.y = 1 + (-0.03125 * Math.sin(11.8 * elapsedTime))
- this.object3d.scale.z = 1 + (0.03125 * Math.sin(11.8 * elapsedTime))
- })
- break;
- case 2:
- shopper = new Shopper(game, 'characters/ghost_1.gltf.glb', randomPotion, new THREE.Vector3(3.5, 3.5, 3.5), shopperPosition, Math.PI, function (elapsedTime) {
- this.object3d.position.y = 0.0625 * Math.sin(2.2 * elapsedTime) + 0.5
- })
- break;
- }
- await shopper.init(delay)
- return shopper
- }
- export function sellTutorialPrompt(stageData) {
- if(stageData.shopTutorial2.alreadySeen) {
- return
- }
- stageData.shopInactivityHandle = setTimeout(() => {
- gsap.to(stageData.shopTutorial2.material, {duration: 1.5, opacity: 1, onStart: () => {
- stageData.shopTutorial2.castShadow = true
- }})
- }, 5000)
- }
- async function beginSell(game, stageData) {
- if (stageData.potionStocked.length < 1) {
- stageData.soundEffects['audio/witch_cackle1.ogg'].play()
- return
- }
- stageData.soundEffects['audio/click1.ogg'].play()
- stageData.isSellingPotions = true
- sellTutorialPrompt(stageData)
- let randomPotions = []
- randomPotions.push(...stageData.potionStocked)
- randomPotions.push(...stageData.potionInventory)
- randomPotions.push(...stageData.potionInfo.map(info => info.name))
- randomPotions.shuffle()
- let numberOfCustomers = Math.min(randomPotions.length, Math.floor(Math.random() * 3) + 3)
- for (let i = 0; i < numberOfCustomers; i++) {
- const desiredPotion = randomPotions.shift()
- const potionInfo = stageData.potionInfo.find(info => info.name == desiredPotion)
- stageData.customers.push(await customerBuilder(potionInfo, i))
- }
- setTimeout(() => {
- stageData.soundEffects['audio/store-entrance-bell.ogg'].play()
- }, 500)
- stageData.customers[0].showDesire()
- closeShopUI(game, stageData)
- }
- export async function updatePotionShelfDisplay() {
- let storeShelfSpot = 0
- for (let i = 0; i < storeShelfSlots.length; i++) {
- if (stageData.displayedPotions[i]) {
- game.entities.remove(stageData.displayedPotions[i])
- game.scene.remove(stageData.displayedPotions[i])
- stageData.displayedPotions[i] = null
- }
- const currentPotionInfo = stageData.potionInfo.find(potion => stageData.potionStocked[i] == potion.name)
- if (!currentPotionInfo) {
- continue
- }
- const currentPotion = await loadGltf(game, currentPotionInfo.model)
- currentPotion.position.copy(storeShelfSlots[storeShelfSpot++].position)
- currentPotion.potionData = currentPotionInfo
- stageData.displayedPotions[i] = currentPotion
- game.entities.push(currentPotion)
- game.scene.add(currentPotion)
- }
- }
- export function addValueToSave(container, key, value) {
- let containerString = localStorage.getItem(`${GAME_SAVE_KEY}-${key}`)
- if (!containerString) {
- container = [value]
- } else {
- container.push(value)
- }
- localStorage.setItem(`${GAME_SAVE_KEY}-${key}`, JSON.stringify(container))
- }
- export function addAmountToSave(key, value) {
- let amount = parseInt(localStorage.getItem(`${GAME_SAVE_KEY}-${key}`)) ?? 0
- amount += value
- localStorage.setItem(`${GAME_SAVE_KEY}-${key}`, amount)
- return amount
- }
- export function removeValueFromSave(container, key, value) {
- let containerString = localStorage.getItem(`${GAME_SAVE_KEY}-${key}`)
- if (!containerString) {
- container = []
- } else {
- container.remove(value)
- }
- localStorage.setItem(`${GAME_SAVE_KEY}-${key}`, JSON.stringify(container))
- }
- export function clearSaveData(stageData) {
- localStorage.removeItem(`${GAME_SAVE_KEY}-potion-inventory`)
- localStorage.removeItem(`${GAME_SAVE_KEY}-potion-stocked`)
- stageData.potionInventory = []
- localStorage.setItem(`${GAME_SAVE_KEY}-potion-inventory`, JSON.stringify(stageData.potionInventory))
- stageData.potionStocked = []
- localStorage.setItem(`${GAME_SAVE_KEY}-potion-stocked`, JSON.stringify(stageData.potionStocked))
-
- stageData.currency = 100
- localStorage.setItem(`${GAME_SAVE_KEY}-currency`, stageData.currency)
- stageData.currentDay = 1
- localStorage.setItem(`${GAME_SAVE_KEY}-currentday`, stageData.currentDay)
- stageData.cartItems = []
- stageData.ingredientInventory = ["mushroom", "mushroom", "pumpkin", "tomato", "tomato", "lettuce", "lettuce"]
- localStorage.setItem(`${GAME_SAVE_KEY}-ingredient-inventory`, JSON.stringify(stageData.ingredientInventory))
- updateGameStatusUI(game, stageData)
-
- }
- export function loadSaveData(stageData) {
- let potionInventoryString = localStorage.getItem(`${GAME_SAVE_KEY}-potion-inventory`)
- if (!potionInventoryString) {
- localStorage.setItem(`${GAME_SAVE_KEY}-potion-inventory`, JSON.stringify([]))
- stageData.potionInventory = []
- } else {
- stageData.potionInventory = JSON.parse(potionInventoryString)
- stageData.potionInventory.sort()
- }
- let potionsStockedString = localStorage.getItem(`${GAME_SAVE_KEY}-potion-stocked`)
- if (!potionsStockedString) {
- localStorage.setItem(`${GAME_SAVE_KEY}-potion-stocked`, JSON.stringify([]))
- stageData.potionStocked = []
- } else {
- stageData.potionStocked = JSON.parse(potionsStockedString)
- stageData.potionStocked.sort()
- }
- stageData.currency = localStorage.getItem(`${GAME_SAVE_KEY}-currency`)
- if (!stageData.currency) {
- localStorage.setItem(`${GAME_SAVE_KEY}-currency`, 100)
- stageData.currency = 100
- }
- stageData.currentDay = localStorage.getItem(`${GAME_SAVE_KEY}-currentday`)
- if (!stageData.currentDay) {
- localStorage.setItem(`${GAME_SAVE_KEY}-currentday`, 1)
- stageData.currentDay = 1
- }
- let ingredientInventoryString = localStorage.getItem(`${GAME_SAVE_KEY}-ingredient-inventory`)
- if (!ingredientInventoryString) {
- localStorage.setItem(`${GAME_SAVE_KEY}-ingredient-inventory`, JSON.stringify([]))
- stageData.ingredientInventory = ["mushroom", "mushroom", "pumpkin", "tomato", "tomato", "lettuce", "lettuce"]
- } else {
- stageData.ingredientInventory = JSON.parse(ingredientInventoryString)
- stageData.ingredientInventory.sort()
- }
- }
- function buyIngredients(game, stageData) {
- let amountToDeduct = 0
- stageData.cartItems.forEach(item => {
- amountToDeduct += stageData.ingredientInfo[item].cost
- addValueToSave(stageData.ingredientInventory, "ingredient-inventory", item)
- })
-
- stageData.currency = addAmountToSave("currency", -amountToDeduct)
- if(amountToDeduct > 0) {
- stageData.soundEffects['audio/cash-register.ogg'].play()
- }
- updateGameStatusUI(game, stageData)
- closeMarketUI(game, stageData)
- }
- export async function init(inGame) {
- game = inGame
- loadSaveData(stageData)
- stageData.currentRoom = 2
- stageData.soundEffects = soundEffects
- stageData.selectedIngredients = []
- stageData.cartItems = []
- stageData.cameraPositions = [
- { "camera": new THREE.Vector3(-15, 5, 7), "focus": new THREE.Vector3(-13, 0.1, 0) },
- { "camera": new THREE.Vector3(1, 5, 7), "focus": new THREE.Vector3(0, 0.1, 0) },
- { "camera": new THREE.Vector3(12, 5, 7), "focus": new THREE.Vector3(18, 0.1, 0) },
- { "camera": new THREE.Vector3(1.37, 2.41, 3.77), "focus": new THREE.Vector3(-1.15, 0.79, -0.19) }
- ]
- const soundFilePaths = [
- 'audio/click1.ogg',
- 'audio/sinkWater1.ogg',
- 'audio/doorOpen_1.ogg',
- 'audio/doorClose_4.ogg',
- 'audio/drawKnife2.ogg',
- 'audio/witch_cackle1.ogg',
- 'audio/bubbling.mp3',
- 'audio/chest_close_creak.ogg',
- 'audio/chest_open_creak.ogg',
- 'audio/handleCoins.ogg',
- 'audio/impactGlass_medium_000.ogg',
- 'audio/impactGlass_medium_001.ogg',
- 'audio/impactGlass_medium_002.ogg',
- 'audio/impactGlass_medium_003.ogg',
- 'audio/impactGlass_medium_004.ogg',
- 'audio/impactWood_heavy_002.ogg',
- 'audio/impactWood_heavy_004.ogg',
- 'audio/impactSoft_medium_002.ogg',
- 'audio/impactSoft_medium_004.ogg',
- 'audio/cash-register.ogg',
- 'audio/store-entrance-bell.ogg'
- ]
- soundFilePaths.forEach(path => {
- soundEffects[path] = new Howl({
- src: [path],
- preload: true,
- })
- })
- stageData.musicLoop = new Howl({
- src: ['audio/Vampires LOOP (All Instruments).ogg'],
- preload: true,
- loop: true,
- volume: 0.5
- })
- soundEffects['audio/doorClose_4.ogg'].volume(0.5)
- soundEffects['audio/doorOpen_1.ogg'].volume(0.5)
- soundEffects['audio/bubbling.mp3'].volume(0.5)
- soundEffects['audio/store-entrance-bell.ogg'].volume(0.5)
- let levelLoader = new LevelLoader(game)
- game.camera.position.copy(stageData.cameraPositions[3].camera)
- game.lookAtFocus = stageData.cameraPositions[3].focus.clone()
- await loadRgbeBackground(game, 'kloppenheim_02_puresky_1k.hdr')
- game.scene.background = null
- game.scene.fog = new THREE.Fog(0x000000, 12, 20)
- let instancedMeshes = await levelLoader.load(levelData.default)
- instancedMeshes.forEach(instancedMesh => {
- game.scene.add(instancedMesh)
- })
- stageData.ingredientInfo = ingredientInfo.default
- stageData.potionInfo = potionInfo.default
- Object.values(stageData.ingredientInfo).forEach(info => {
- ingredients.push(new Ingredient(info, game))
- })
- ///////////////
- /// Brewing ///
- ///////////////
- let shelfSpot = 0
- ingredients.forEach(async ingredient => {
- await ingredient.spawn(shelfSlots[shelfSpot++])
- })
- stageData.candycorn = await loadGltf(game, 'models/candycorn.gltf')
- stageData.candycorn.position.x = -4.5
- stageData.candycorn.position.z = -3.5
- game.scene.add(stageData.candycorn)
- stageData.pumpkin2 = await loadGltf(game, 'models/pumpkin_orange.gltf')
- game.entities.push(stageData.pumpkin2)
- stageData.pumpkin2.position.x = -10
- stageData.pumpkin2.position.y = 1
- stageData.pumpkin2.position.z = 0.5
- game.scene.add(stageData.pumpkin2)
- jackolantern = await loadGltf(game, 'models/pumpkin_orange_jackolantern.gltf')
- game.entities.push(jackolantern)
- jackolantern.spin = 0
- jackolantern.position.set(3, 0.1, 1)
- jackolantern.rotateOnAxis(THREE.Object3D.DEFAULT_UP, -Math.PI / 4)
- game.scene.add(jackolantern)
- candle = await loadGltf(game, 'models/candle_thin.gltf.glb')
- game.entities.push(candle)
- candle.position.x = 0
- candle.position.y = 1
- candle.position.z = -2.5
- candle.isLit = false
- game.scene.add(candle)
- candleLit = await loadGltf(game, 'models/candle_thin_lit.gltf.glb')
- game.entities.push(candleLit)
- candleLit.position.x = 0
- candleLit.position.y = 1
- candleLit.position.z = -2.5
- candleLit.visible = false
- game.scene.add(candleLit)
- stageData.cauldronUniforms = THREE.UniformsUtils.merge([
- THREE.UniformsLib["common"],
- THREE.UniformsLib["lights"]
- ])
- stageData.cauldronUniforms.uTime = { value: 0 },
- stageData.cauldronUniforms.uPixelRatio = { value: Math.min(window.devicePixelRatio, 2) },
- stageData.cauldronUniforms.uBlendTime = { value: 0 },
- stageData.cauldronUniforms.uPotionColor = { value: new THREE.Vector3(0, 0.8, 0.2) },
- stageData.cauldronUniforms.uNextPotionColor = { value: new THREE.Vector3(0, 0.8, 0.2) },
- stageData.cauldronUniforms.uTransparency = { value: 0.8 }
- stageData.cauldron = await loadGltf(game, 'models/simple_cauldron.gltf.glb')
- const cauldronBell = stageData.cauldron.getObjectByName("Sphere015")
- cauldronBell.material.side = THREE.DoubleSide
- const cauldronTop = stageData.cauldron.getObjectByName("Sphere015_1")
- const cauldronTopMaterial = new THREE.ShaderMaterial({
- fragmentShader: cauldronFragment,
- vertexShader: cauldronVertex,
- uniforms: stageData.cauldronUniforms,
- transparent: true,
- depthWrite: false,
- side: THREE.DoubleSide,
- lights: true,
- })
- cauldronTop.material = cauldronTopMaterial
- game.entities.push(stageData.cauldron)
- stageData.cauldron.position.y = 0.1
- game.scene.add(stageData.cauldron)
- stageData.brewTutorial1 = buildCanvasText("Click to Brew", { font: "86px Alice" })
- stageData.brewTutorial1.position.z = 2.4
- stageData.brewTutorial1.position.y = 0.2
- stageData.brewTutorial1.rotateOnAxis(new THREE.Vector3(1, 0, 0), -Math.PI / 2)
- stageData.brewTutorial1.alreadySeen = false
- stageData.brewTutorial1.material.opacity = 0
- stageData.brewTutorial1.castShadow = false
- game.scene.add(stageData.brewTutorial1)
- stageData.brewWitch = await loadGltf(game, 'characters/character_witch.gltf')
- game.entities.push(stageData.brewWitch)
- stageData.brewWitch.position.x = 1
- stageData.brewWitch.position.y = 0.1
- stageData.brewWitch.position.z = -1
- stageData.brewWitch.stir = false
- stageData.brewWitch.bounce = false
- stageData.brewWitch.lookAt(stageData.cauldron.position)
- game.scene.add(stageData.brewWitch)
- stageData.shopWitch = await loadGltf(game, 'characters/character_witch.gltf')
- game.entities.push(stageData.shopWitch)
- stageData.shopWitch.position.x = -13
- stageData.shopWitch.position.y = 0.1
- stageData.shopWitch.position.z = -2
- game.scene.add(stageData.shopWitch)
- stageData.marketWitch = await loadGltf(game, 'characters/character_witch.gltf')
- game.entities.push(stageData.marketWitch)
- stageData.marketWitch.position.x = 20
- stageData.marketWitch.position.y = 0.1
- stageData.marketWitch.position.z = 2.5
- stageData.marketWitch.rotateOnAxis(THREE.Object3D.DEFAULT_UP, Math.PI)
- game.scene.add(stageData.marketWitch)
- stageData.spoon = await loadGltf(game, 'models/spoon.gltf')
- game.entities.push(stageData.spoon)
- stageData.spoon.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI)
- stageData.spoon.position.y = 1.25
- stageData.spoon.visible = false
- game.scene.add(stageData.spoon)
- book = await loadGltf(game, 'models/book_grey.gltf.glb')
- game.entities.push(book)
- book.rotateOnAxis(THREE.Object3D.DEFAULT_UP, Math.PI)
- book.position.x = 4.6
- book.position.y = 1.9
- book.position.z = -3.9
- game.scene.add(book)
- const bubbleGeometry = new THREE.SphereGeometry(0.1, 24, 24)
- const bubbleMaterial = cauldronTopMaterial
- //bubbleMaterial.uniforms.uTransparency.value = 0.3
- stageData.bubbleParticles = new THREE.InstancedMesh(bubbleGeometry, bubbleMaterial, bubbleCount)
- stageData.bubbleParticles.instanceMatrix.setUsage(THREE.DynamicDrawUsage)
- stageData.bubbleParticles.visible = false
- game.scene.add(stageData.bubbleParticles)
- const matrix = new THREE.Matrix4()
- for (let index = 0; index < bubbleCount; index++) {
- bubblePositions[index] = new THREE.Vector3(0.5 * (Math.random() - 0.5), 0.5, 0.5 * (Math.random() - 0.5))
- bubbleLifespan[index] = 7 - (index * 0.5)
- bubbleScale[index] = 0.2
- bubbleVelocity[index] = new THREE.Vector3(0, 0.01, 0)
- }
- const quaternion = new THREE.Quaternion()
- const scaleVector = new THREE.Vector3(1, 1, 1)
- for (let index = 0; index < bubbleCount; index++) {
- quaternion.setFromAxisAngle(THREE.Object3D.DEFAULT_UP, 0)
- const scale = scaleVector.set(bubbleScale[index], bubbleScale[index], bubbleScale[index])
- matrix.compose(bubblePositions[index], quaternion, scale)
- stageData.bubbleParticles.setMatrixAt(index, matrix)
- }
- doorway = await loadGltf(game, 'models/wall_doorway.glb')
- doorway.position.set(-6, 0, -2)
- doorway.castShadow = false
- doorway.traverse((child) => {
- if (child.isMesh) {
- child.castShadow = false
- }
- })
- doorway.rotateOnAxis(THREE.Object3D.DEFAULT_UP, Math.PI / 2)
- doorway.isOpen = false
- doorway.isMoving = false
- game.entities.push(doorway)
- game.scene.add(doorway)
- doorway2 = await loadGltf(game, 'models/wall_doorway.glb')
- doorway2.position.set(6, 0, 2)
- doorway2.castShadow = false
- doorway2.traverse((child) => {
- if (child.isMesh) {
- child.castShadow = false
- }
- })
- doorway2.rotateOnAxis(THREE.Object3D.DEFAULT_UP, Math.PI / 2)
- doorway2.isOpen = false
- doorway2.isMoving = false
- game.entities.push(doorway2)
- game.scene.add(doorway2)
- ////////////
- /// Shop ///
- ////////////
- // const debugSphereGeometry = new THREE.SphereGeometry(0.1)
- // const debugMaterial = new THREE.MeshBasicMaterial({color: "magenta"})
- // const debugSphere = new THREE.Mesh(debugSphereGeometry, debugMaterial)
- // customerSlots.forEach(slot => {
- // debugSphere.position.copy(slot.position)
- // game.scene.add(debugSphere.clone())
- // })
- stageData.displayedPotions = []
- updatePotionShelfDisplay()
- lollipop = await loadGltf(game, 'models/lollipop_green.gltf')
- lollipop.position.x = -11.5
- lollipop.position.y = 1.28
- lollipop.position.z = -3.4
- lollipop.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI / 2)
- lollipop.rotateOnAxis(new THREE.Vector3(0, 0, 1), Math.PI / 2 + -0.3)
- game.entities.push(lollipop)
- game.scene.add(lollipop)
- stageData.chest = await loadGltf(game, 'models/chest_large.glb')
- stageData.chest.scale.set(0.5, 0.5, 0.5)
- stageData.chest.position.x = -15.5
- stageData.chest.position.y = 1
- stageData.chest.position.z = 0
- stageData.chest.rotateOnAxis(THREE.Object3D.DEFAULT_UP, Math.PI / 4)
- game.entities.push(stageData.chest)
- game.scene.add(stageData.chest)
- stageData.shopTutorial1 = buildCanvasText("Click to Sell", { font: "100px Alice" })
- stageData.shopTutorial1.position.x = -15.5
- stageData.shopTutorial1.position.z = 0.2
- stageData.shopTutorial1.position.y = 1.8
- stageData.shopTutorial1.alreadySeen = false
- stageData.shopTutorial1.scale.set(0.6, 0.6, 0.6)
- stageData.shopTutorial1.material.opacity = 0
- stageData.shopTutorial1.castShadow = false
- game.scene.add(stageData.shopTutorial1)
- stageData.shopTutorial2 = buildCanvasText("Pick the correct potion", { font: "72px Alice" })
- stageData.shopTutorial2.position.x = -10
- stageData.shopTutorial2.position.z = -3.4
- stageData.shopTutorial2.position.y = 1.5
- stageData.shopTutorial2.alreadySeen = false
- stageData.shopTutorial2.scale.set(1, 1, 1)
- stageData.shopTutorial2.material.opacity = 0
- stageData.shopTutorial2.castShadow = false
- game.scene.add(stageData.shopTutorial2)
- stageData.coin = await loadGltf(game, 'models/coin.gltf.glb')
- stageData.coin.position.x = -12
- stageData.coin.position.y = 1.1
- stageData.coin.position.z = 0
- game.entities.push(stageData.coin)
- game.scene.add(stageData.coin)
- stageData.customers = []
- //////////////
- /// Market ///
- //////////////
- stageData.sellingSkeleton = await loadGltf(game, 'characters/character_skeleton_mage.gltf')
- stageData.sellingSkeleton.position.x = 20
- stageData.sellingSkeleton.position.y = 0.1
- stageData.sellingSkeleton.position.z = -1
- stageData.sellingSkeleton.rotateOnAxis(THREE.Object3D.DEFAULT_UP, -Math.PI / 4)
- game.entities.push(stageData.sellingSkeleton)
- game.scene.add(stageData.sellingSkeleton)
- stageData.sign = await loadGltf(game, 'models/sign_left.gltf')
- stageData.sign.position.x = 14
- stageData.sign.position.y = 0.1
- stageData.sign.position.z = 2
- game.entities.push(stageData.sign)
- game.scene.add(stageData.sign)
- stageData.mushroomCrate = await loadGltf(game, 'models/crate_mushrooms.gltf')
- stageData.mushroomCrate.scale.set(0.5, 0.5, 0.5)
- stageData.mushroomCrate.position.x = 19.5
- stageData.mushroomCrate.position.z = 0.5
- stageData.mushroomCrate.rotateOnAxis(THREE.Object3D.DEFAULT_UP, Math.PI / 4)
- game.entities.push(stageData.mushroomCrate)
- game.scene.add(stageData.mushroomCrate)
- stageData.tomatoCrate = await loadGltf(game, 'models/crate_tomatoes.gltf')
- stageData.tomatoCrate.scale.set(0.5, 0.5, 0.5)
- stageData.tomatoCrate.position.x = 21
- game.entities.push(stageData.tomatoCrate)
- game.scene.add(stageData.tomatoCrate)
- stageData.lettuceCrate = await loadGltf(game, 'models/crate_lettuce.gltf')
- stageData.lettuceCrate.scale.set(0.5, 0.5, 0.5)
- stageData.lettuceCrate.position.x = 18.5
- stageData.lettuceCrate.position.z = -0.5
- game.entities.push(stageData.lettuceCrate)
- game.scene.add(stageData.lettuceCrate)
- const coffin = await loadGltf(game, 'models/coffin_decorated.gltf')
- coffin.position.x = 23
- coffin.position.z = 6
- coffin.rotateOnAxis(THREE.Object3D.DEFAULT_UP, -Math.PI / 4)
- game.scene.add(coffin)
- const candy = await loadGltf(game, 'models/candy_orange_A.gltf')
- candy.position.x = 21
- candy.position.y = 0.25
- candy.position.z = 6
- candy.rotateOnAxis(THREE.Object3D.DEFAULT_UP, -Math.PI / 4)
- game.scene.add(candy)
- const candyBucket = await loadGltf(game, 'models/candy_bucket_B_decorated.gltf')
- candyBucket.position.x = 21.5
- candyBucket.position.y = 0.1
- candyBucket.position.z = 5.5
- candyBucket.rotateOnAxis(THREE.Object3D.DEFAULT_UP, -Math.PI / 2)
- game.scene.add(candyBucket)
- /**
- * Fireflies
- */
- stageData.firefliesUniforms = {
- uTime: { value: 0 },
- uPixelRatio: { value: Math.min(window.devicePixelRatio, 2) }
- }
- const firefliesMaterial = new THREE.ShaderMaterial({
- fragmentShader: firefliesFragment,
- vertexShader: firefliesVertex,
- uniforms: stageData.firefliesUniforms,
- transparent: true,
- depthWrite: false,
- blending: THREE.AdditiveBlending
- })
- const firefliesGeometry = new THREE.BufferGeometry()
- const firefliesCount = 50
- const positionArray = new Float32Array(firefliesCount * 3)
- const scaleArray = new Float32Array(firefliesCount)
- for (let i = 0; i < firefliesCount; i++) {
- positionArray[i * 3 + 0] = 24 * (Math.random() - 0.5) + 18
- positionArray[i * 3 + 1] = 4 * Math.random() + 2
- positionArray[i * 3 + 2] = 16 * (Math.random() - 0.5)
- scaleArray[i] = 0.8 * Math.random() + 1.2
- }
- firefliesGeometry.setAttribute('position', new THREE.BufferAttribute(positionArray, 3))
- firefliesGeometry.setAttribute('aScale', new THREE.BufferAttribute(scaleArray, 1))
- const fireflies = new THREE.Points(firefliesGeometry, firefliesMaterial)
- game.scene.add(fireflies)
- ////////////////
- /// Lighting ///
- ////////////////
- const ambientLight = new THREE.AmbientLight(0xFFFFFF, 1)
- game.scene.add(ambientLight)
- const shadowSize = 24
- directionalLight = new THREE.DirectionalLight(0xFFFFFF, 0.9)
- directionalLight.position.set(8, 10, 6)
- directionalLight.castShadow = true
- directionalLight.shadow.camera.left = -shadowSize
- directionalLight.shadow.camera.right = shadowSize
- directionalLight.shadow.camera.top = -shadowSize
- directionalLight.shadow.camera.bottom = shadowSize
- directionalLight.shadow.camera.far = 28
- directionalLight.shadow.mapSize.width = Math.min(game.renderer.capabilities.maxTextureSize, 2048)
- directionalLight.shadow.mapSize.height = Math.min(game.renderer.capabilities.maxTextureSize, 2048)
- directionalLight.shadow.bias = -0.005
- directionalLight.shadow.radius = 6
- directionalLight.cameraOffset = new THREE.Vector3()
- directionalLight.cameraOffset.copy(directionalLight.position)
- directionalLight.cameraOffset.sub(new THREE.Vector3(0, 0, 0))
- directionalLight.target = game.camera
- directionalLight.update = function () {
- const currentOffset = new THREE.Vector3()
- currentOffset.copy(directionalLight.cameraOffset).add(game.camera.position)
- directionalLight.position.set(currentOffset.x, currentOffset.y, currentOffset.z)
- if (directionalLight.shadow) {
- directionalLight.shadow.camera.position.set(currentOffset.x, currentOffset.y, currentOffset.z)
- }
- }
- game.scene.add(directionalLight)
- // const shadowHelper = new THREE.CameraHelper( directionalLight.shadow.camera )
- // game.scene.add(shadowHelper)
- mainMenuUI(game, stageData)
- optionsUI(game, stageData)
- creditsUI(game, stageData)
- navigationUI(game, stageData)
- brewUI(game, stageData)
- shopUI(game, stageData)
- marketUI(game, stageData)
- gameOverUI(game, stageData)
- updateGameStatusUI(game, stageData)
- stageData.beginBrew = () => { beginBrew(game, stageData) }
- stageData.beginSell = () => { beginSell(game, stageData) }
- stageData.buyIngredients = () => { buyIngredients(game, stageData)}
- stageData.adjustMasterVolume = (volume) => {
- Howler.volume(volume / 100)
- }
- stageData.adjustMusicVolume = (volume) => {
- stageData.musicLoop.volume(volume / 100)
- }
- ///////////
- // DEBUG //
- ///////////
- // moveToRoom(ROOM_SHOP)
- // closeMainMenuUI(game, stageData)
- // const planeGeometry = new THREE.PlaneGeometry(1,1)
- // const debugPlane = new THREE.Mesh(planeGeometry, textBubbleMaterial)
- // debugPlane.position.x = 2
- // debugPlane.position.y = 1
- // debugPlane.scale.set(2,2,2)
- // game.scene.add(debugPlane)
- }
- export function update(game) {
- let elapsedTime = game.clock.getElapsedTime()
- ingredients.forEach(ingredient => {
- ingredient.wobble()
- })
- stageData.candycorn.position.y = 0.125 * Math.sin(4.1 * elapsedTime) + 3.0
- stageData.candycorn.rotateOnAxis(THREE.Object3D.DEFAULT_UP, -0.02)
- directionalLight.update()
- raycast.setFromCamera(game.mousePosition, game.camera)
- Howler.pos(game.camera.position.x, game.camera.position.y, game.camera.position.z)
- Howler.orientation(game.camera.position.x, game.camera.position.y, game.camera.position.z, 0, 1, 0)
- const quaternion = new THREE.Quaternion()
- const scaleVector = new THREE.Vector3(1, 1, 1)
- const matrix = new THREE.Matrix4()
- for (let index = 0; index < bubbleCount; index++) {
- bubblePositions[index].add(bubbleVelocity[index])
- bubbleScale[index] = Math.min(bubbleScale[index] + 0.01, 1)
- bubbleLifespan[index] -= 0.1
- if (bubbleLifespan[index] <= 0) {
- bubbleLifespan[index] = 7
- bubblePositions[index].set(0.5 * (Math.random() - 0.5), 0.5, 0.5 * (Math.random() - 0.5))
- bubbleScale[index] = 0.2
- }
- //quaternion.setFromAxisAngle(THREE.Object3D.DEFAULT_UP, 0)
- const scale = scaleVector.set(bubbleScale[index], bubbleScale[index], bubbleScale[index])
- matrix.compose(bubblePositions[index], quaternion, scale)
- stageData.bubbleParticles.setMatrixAt(index, matrix)
- }
- stageData.bubbleParticles.instanceMatrix.needsUpdate = true
- if (stageData.brewWitch.stir) {
- stageData.spoon.position.x = 0.125 * Math.sin(4 * elapsedTime)
- stageData.spoon.position.z = 0.125 * Math.cos(4 * elapsedTime)
- }
- if (stageData.brewWitch.bounce) {
- stageData.brewWitch.scale.x = 1 + (0.03125 * Math.sin(12 * elapsedTime))
- stageData.brewWitch.scale.y = 1 + (-0.03125 * Math.sin(12 * elapsedTime))
- stageData.brewWitch.scale.z = 1 + (0.03125 * Math.sin(12 * elapsedTime))
- } else {
- stageData.brewWitch.scale.x = 1
- stageData.brewWitch.scale.y = 1
- stageData.brewWitch.scale.z = 1
- }
- if (jackolantern.spin > 0) {
- jackolantern.spin -= 0.05
- jackolantern.rotateOnAxis(THREE.Object3D.DEFAULT_UP, -0.1)
- if (jackolantern.spin < 0) {
- jackolantern.spin = 0
- }
- }
- stageData.sellingSkeleton.scale.x = 1 + (0.03125 * Math.sin(12 * elapsedTime))
- stageData.sellingSkeleton.scale.y = 1 + (-0.03125 * Math.sin(12 * elapsedTime))
- stageData.sellingSkeleton.scale.z = 1 + (0.03125 * Math.sin(12 * elapsedTime))
- stageData.firefliesUniforms.uTime.value = elapsedTime
- stageData.cauldronUniforms.uTime.value = elapsedTime
- stageData.customers.forEach(customer => {
- customer.update(elapsedTime)
- })
- candle.position.y = 0.625 * Math.sin(0.5 * elapsedTime) + 1.3
- candleLit.position.y = 0.625 * Math.sin(0.5 * elapsedTime) + 1.3
- let intersects = raycast.intersectObjects(game.entities.filter(entity => entity.visible))
- if (intersects.length > 0) {
- document.body.style.cursor = "pointer"
- } else {
- document.body.style.cursor = "default"
- }
- }
- function isAChildOf(parent, childToCheck) {
- if (parent == childToCheck) return true
- if (childToCheck.parent != null) {
- return isAChildOf(parent, childToCheck.parent)
- }
- return false
- }
- export function onClick() {
- //drawDebugLine(game, witch.position, getForwardVector(witch).multiplyScalar(10), 0xff0000, 1000)
- //drawDebugLine(game, witch.position, getRightVector(witch).multiplyScalar(10), 0x00ff00, 1000)
- let intersects = raycast.intersectObjects(game.entities.filter(entity => entity.visible))
- if (intersects.length > 0) {
- intersects.every(intersect => {
- switch (stageData.currentRoom) {
- case ROOM_SHOP:
- //SHOP CLICK ACTIONS HERE
- if (isAChildOf(stageData.chest, intersect.object)) {
- if (stageData.isSellingPotions) {
- return false
- }
- if (stageData.shopInactivityHandle) {
- clearTimeout(stageData.shopInactivityHandle)
- stageData.shopInactivityHandle = 0
- }
- if (stageData.chest.isStocking) {
- closeShopUI(game, stageData)
- } else {
- openShopUI(game, stageData)
- if (stageData.shopTutorial1.material.opacity > 0) {
- gsap.to(stageData.shopTutorial1.material, {
- duration: 1.5, opacity: 0, onComplete: () => {
- stageData.shopTutorial1.castShadow = false
- stageData.shopTutorial1.alreadySeen = true
- }
- })
- }
- }
- return false
- }
- if (isAChildOf(stageData.shopWitch, intersect.object)) {
- if (stageData.isSellingPotions) {
- return false
- }
- if (stageData.shopInactivityHandle) {
- clearTimeout(stageData.shopInactivityHandle)
- stageData.shopInactivityHandle = 0
- }
- if (stageData.chest.isStocking) {
- closeShopUI(game, stageData)
- } else {
- openShopUI(game, stageData)
- if (stageData.shopTutorial1.material.opacity > 0) {
- gsap.to(stageData.shopTutorial1.material, {
- duration: 1.5, opacity: 0, onComplete: () => {
- stageData.shopTutorial1.castShadow = false
- stageData.shopTutorial1.alreadySeen = true
- }
- })
- }
- }
- return false
- }
- if (isAChildOf(stageData.coin, intersect.object)) {
- let coinMotionPath = [{ x: -12, y: 1.1, z: 1 }, { x: -12, y: 2, z: 2 }, { x: -15.5, y: 2, z: 2 }, { x: -15.5, y: 1.2, z: 0 }]
- gsap.to(stageData.coin.position, {
- duration: 3.5, motionPath: coinMotionPath, onComplete: () => {
- stageData.soundEffects['audio/handleCoins.ogg'].play()
- closeChest(stageData.chest, () => {
- stageData.coin.scale.set(0, 0, 0)
- stageData.coin.position.set(-12, 1.1, 0)
- gsap.to(stageData.coin.scale, {
- ease: "elastic",
- duration: 0.7, x: 1, y: 1, z: 1,
- })
- })
- }
- })
- setTimeout(() => {
- openChest(stageData.chest)
- }, 700)
- return false
- }
- stageData.displayedPotions.forEach((potion) => {
- if (isAChildOf(potion, intersect.object)) {
- if (stageData.isSellingPotions) {
- if (stageData.sellInactivityHandle) {
- clearTimeout(stageData.sellInactivityHandle)
- stageData.sellInactivityHandle = 0
- }
- if (stageData.customers[0].isMatchingPotion(potion.potionData)) {
- if (stageData.shopTutorial2.material.opacity > 0) {
- gsap.to(stageData.shopTutorial2.material, {
- duration: 1.5, opacity: 0, onComplete: () => {
- stageData.shopTutorial2.castShadow = false
- stageData.shopTutorial2.alreadySeen = true
- }
- })
- }
- const firstCustomer = stageData.customers[0]
- firstCustomer.hideDesire()
- const potionMotionPath = [{ x: -11.5, y: 2.75, z: -3.25 }, { x: -12, y: 2, z: -2 }, { x: -13, y: 0.75, z: 2.5 }]
- gsap.to(potion.position, {
- duration: 2, motionPath: potionMotionPath, onComplete: () => {
- removeValueFromSave(stageData.potionStocked, 'potion-stocked', potion.potionData.name)
- updatePotionShelfDisplay()
- firstCustomer.acceptPotion()
- stageData.soundEffects['audio/cash-register.ogg'].play()
- stageData.currency = addAmountToSave("currency", potion.potionData.value)
- updateGameStatusUI(game, stageData)
- //move money from customer to chest
- //add money to save data
- //move customer out of room
- nextCustomer()
- //display next customer desire
- //if no more customers or no more valid potions, end selling
- }
- })
- } else {
- //TODO: potion does not match
- playBottleClink()
- gsap.fromTo(potion.rotation, { duration: 0.1, z: 0.2 }, { duration: 0.1, z: 0 })
- stageData.customers[0].rejectPotion()
- if (stageData.customers[0].rejectCount >= 3) {
- stageData.customers[0].showSad()
- nextCustomer()
- }
- }
- } else {
- playBottleClink()
- gsap.fromTo(potion.rotation, { duration: 0.1, z: 0.2 }, { duration: 0.1, z: 0 })
- }
- }
- })
- if (isAChildOf(stageData.pumpkin2, intersect.object)) {
- if (stageData.pumpkin2.isMoving) {
- return
- }
- stageData.pumpkin2.isMoving = true
- gsap.to(stageData.pumpkin2.position, {
- duration: 2, y: 3, onComplete: () => {
- setTimeout(() => {
- stageData.soundEffects['audio/impactWood_heavy_002.ogg'].play()
- }, 400)
- setTimeout(() => {
- stageData.soundEffects['audio/impactSoft_medium_002.ogg'].play()
- }, 800)
- gsap.to(stageData.pumpkin2.position, {
- duration: 1, y: 1, ease: "bounce", onComplete: () => {
- stageData.pumpkin2.isMoving = false
- }
- })
- }
- })
- return false
- }
- break;
- case ROOM_BREW:
- //BREW CLICK ACTIONS HERE
- if (isAChildOf(jackolantern, intersect.object)) {
- soundEffects['audio/drawKnife2.ogg'].play()
- jackolantern.spin += Math.PI
- return false
- }
- if (isAChildOf(book, intersect.object)) {
- //soundEffects['audio/drawKnife2.ogg'].play()
- //jackolantern.spin += Math.PI
- let bookMotionPath = [{ x: 4.6, y: 1.9, z: 0 }, { x: 4.6, y: 1.9, z: -3.9 }]
- gsap.to(book.position, {
- duration: 6, motionPath: bookMotionPath, onComplete: () => {
- }
- })
- return false
- }
- if (isAChildOf(stageData.brewWitch, intersect.object)) {
- if (stageData.cauldron.isBrewing) {
- return false
- }
- if (stageData.brewInactivityHandle) {
- clearTimeout(stageData.brewInactivityHandle)
- stageData.brewInactivityHandle = 0
- }
- if (stageData.cauldron.brewMenuOpen) {
- closeBrewUI(game, stageData)
- } else {
- openBrewUI(game, stageData)
- //cauldron.isBrewing = true
- if (stageData.brewTutorial1.material.opacity > 0) {
- gsap.to(stageData.brewTutorial1.material, {
- duration: 1.5, opacity: 0, onComplete: () => {
- stageData.brewTutorial1.castShadow = false
- stageData.brewTutorial1.alreadySeen = true
- }
- })
- }
- }
- return false
- }
- if (isAChildOf(stageData.cauldron, intersect.object)) {
- if (stageData.cauldron.isBrewing) {
- return false
- }
- if (stageData.brewInactivityHandle) {
- clearTimeout(stageData.brewInactivityHandle)
- stageData.brewInactivityHandle = 0
- }
- if (stageData.cauldron.brewMenuOpen) {
- closeBrewUI(game, stageData)
- } else {
- openBrewUI(game, stageData)
- if (stageData.brewTutorial1.material.opacity > 0) {
- gsap.to(stageData.brewTutorial1.material, {
- duration: 1.5, opacity: 0, onComplete: () => {
- stageData.brewTutorial1.castShadow = false
- stageData.brewTutorial1.alreadySeen = true
- }
- })
- }
- //cauldron.isBrewing = true
- }
- // let soundId = soundEffects['audio/sinkWater1.ogg'].play()
- // soundEffects['audio/sinkWater1.ogg'].once('play', () => {
- // soundEffects['audio/sinkWater1.ogg'].volume(0.5, soundId)
- // // soundEffects['audio/sinkWater1.ogg'].pos(cauldron.position.x, cauldron.position.y, cauldron.position.z, soundId)
- // // soundEffects['audio/sinkWater1.ogg'].pannerAttr({
- // // panningModel: 'HRTF',
- // // refDistance: 1.0,
- // // rolloffFactor: 0.8,
- // // distanceModel: 'exponential',
- // // }, soundId)
- // soundEffects['audio/sinkWater1.ogg'].stereo(1, soundId)
- // //console.log(soundEffects['audio/sinkWater1.ogg'])
- // }, soundId)
- return false
- }
- if (isAChildOf(candle, intersect.object)) {
- candle.isLit = true
- candleLit.visible = true
- candle.visible = false
- return false
- }
- if (isAChildOf(candleLit, intersect.object)) {
- candle.isLit = false
- candleLit.visible = false
- candle.visible = true
- return false
- }
- break;
- case ROOM_MARKET:
- //MARKET CLICK ACTIONS HERE
- if (isAChildOf(stageData.sign, intersect.object)) {
- previousRoom(game, stageData)
- return false
- }
- if(isAChildOf(stageData.mushroomCrate, intersect.object) ||
- isAChildOf(stageData.lettuceCrate, intersect.object) ||
- isAChildOf(stageData.tomatoCrate, intersect.object) ||
- isAChildOf(stageData.sellingSkeleton, intersect.object) ||
- isAChildOf(stageData.marketWitch, intersect.object)
- ) {
- if (stageData.sellingSkeleton.marketMenuOpen) {
- closeMarketUI(game, stageData)
- } else {
- openMarketUI(game, stageData)
- }
- return false
- }
- break;
- }
- if (isAChildOf(doorway, intersect.object)) {
- if (stageData.currentRoom == ROOM_BREW) {
- previousRoom(game, stageData)
- } else if (stageData.currentRoom == ROOM_SHOP) {
- nextRoom(game, stageData)
- }
- return false
- }
- if (isAChildOf(doorway2, intersect.object)) {
- if (stageData.currentRoom == ROOM_BREW) {
- nextRoom(game, stageData)
- } else if (stageData.currentRoom == ROOM_MARKET) {
- previousRoom(game, stageData)
- }
- return false
- }
- })
- }
- }
- function nextCustomer() {
- const firstCustomer = stageData.customers.shift()
- if (!firstCustomer) {
- return
- }
- const customerMotionPath = [{ x: -11, y: 0.1, z: 3 }, { x: -8, y: 0.1, z: 3 }, { x: -4, y: 0.1, z: 10 }]
- setTimeout(() => {
- stageData.soundEffects['audio/store-entrance-bell.ogg'].play()
- }, 3000)
- gsap.to(firstCustomer.object3d.position, {
- duration: 8, motionPath: customerMotionPath, onComplete: () => {
- game.scene.remove(firstCustomer.object3d)
- }
- })
- gsap.to(firstCustomer.object3d.rotation, {
- duration: 1, y: Math.PI, onComplete: () => {
- }, onUpdate: () => {
- firstCustomer.requestBillboard.lookAt(game.camera.position)
- }
- })
- //const shopperPosition = customerSlots[stageData.customers.length].position
- //move other customers forward
- for (let i = 0; i < stageData.customers.length; i++) {
- const customer = stageData.customers[i]
- const shopperPosition = customerSlots[i].position
- gsap.to(customer.object3d.position, { duration: 1.5, x: shopperPosition.x, y: shopperPosition.y, z: shopperPosition.z })
- }
- if (stageData.customers.length == 0) {
- stageData.isSellingPotions = false
- stageData.currentDay = addAmountToSave("currentday", 1)
- updateGameStatusUI(game, stageData)
- if(stageData.currency >= 1000) {
- showGameOverUI(game, stageData)
- }
- } else {
- stageData.customers[0].showDesire()
- if (stageData.potionStocked.length < 1) {
- stageData.customers[0].rejectPotion(3)
- stageData.customers[0].showSad()
- nextCustomer()
- }
- }
- }
- export function playBottleClink() {
- setTimeout(() => {
- let randomSound2 = Math.floor(5 * Math.random())
- stageData.soundEffects[`audio/impactGlass_medium_00${randomSound2}.ogg`].play()
- }, 50)
- let randomSound = Math.floor(5 * Math.random())
- stageData.soundEffects[`audio/impactGlass_medium_00${randomSound}.ogg`].play()
- }
- function openChest(chest) {
- let lid = chest.getObjectByName("chest_large_lid")
- if (chest.isOpen) {
- return
- }
- if (!chest.isMoving) {
- soundEffects['audio/chest_open_creak.ogg'].play()
- chest.isMoving = true
- gsap.to(lid.rotation, {
- duration: 2.5, x: -(Math.PI / 2) + (Math.PI / 8), ease: "elastic", onComplete: () => {
- chest.isOpen = true
- chest.isMoving = false
- }
- })
- }
- }
- function closeChest(chest, onComplete = () => { }) {
- let lid = chest.getObjectByName("chest_large_lid")
- if (!chest.isOpen) {
- return
- }
- if (!chest.isMoving) {
- soundEffects['audio/chest_close_creak.ogg'].play()
- chest.isMoving = true
- gsap.to(lid.rotation, {
- duration: 1.5, x: 0, ease: "bounce", onComplete: () => {
- chest.isOpen = false
- chest.isMoving = false
- onComplete()
- }
- })
- }
- }
- function openDoorway(doorway) {
- let door = doorway.getObjectByName("wall_doorway_door")
- if (doorway.isOpen) {
- return
- }
- if (!doorway.isMoving) {
- soundEffects['audio/doorOpen_1.ogg'].play()
- doorway.isMoving = true
- gsap.to(door.rotation, {
- duration: 2.5, y: -Math.PI / 2, ease: "elastic", onComplete: () => {
- doorway.isOpen = true
- doorway.isMoving = false
- }
- })
- }
- }
- function closeDoorway(doorway) {
- let door = doorway.getObjectByName("wall_doorway_door")
- if (!doorway.isOpen) {
- return
- }
- if (!doorway.isMoving) {
- soundEffects['audio/doorClose_4.ogg'].play()
- doorway.isMoving = true
- gsap.to(door.rotation, {
- duration: 2.5, y: 0, ease: "elastic", onComplete: () => {
- doorway.isOpen = false
- doorway.isMoving = false
- }
- })
- }
- }
- function moveToShop() {
- closeBrewUI(game, stageData)
- openDoorway(doorway)
- const camPosition = stageData.cameraPositions[0]
- gsap.to(game.camera.position, {
- duration: 2.5, x: camPosition.camera.x, y: camPosition.camera.y, z: camPosition.camera.z, onComplete: () => {
- closeDoorway(doorway)
- }
- })
- gsap.to(game.lookAtFocus, { duration: 2.5, x: camPosition.focus.x, y: camPosition.focus.y, z: camPosition.focus.z })
- stageData.currentRoom = ROOM_SHOP
- shopTutorialPrompt(stageData)
- }
- function moveToBrew() {
- let doorwayToOpen = doorway
- if (stageData.currentRoom == ROOM_MARKET) {
- doorwayToOpen = doorway2
- }
- openDoorway(doorwayToOpen)
- const camPosition = stageData.cameraPositions[1]
- gsap.to(game.camera.position, {
- duration: 2.5, x: camPosition.camera.x, y: camPosition.camera.y, z: camPosition.camera.z, onComplete: () => {
- closeDoorway(doorwayToOpen)
- }
- })
- gsap.to(game.lookAtFocus, { duration: 2.5, x: camPosition.focus.x, y: camPosition.focus.y, z: camPosition.focus.z })
- stageData.currentRoom = ROOM_BREW
- brewTutorialPrompt(stageData)
- }
- function moveToMarket() {
- closeBrewUI(game, stageData)
- openDoorway(doorway2)
- const camPosition = stageData.cameraPositions[2]
- gsap.to(game.camera.position, {
- duration: 2.5, x: camPosition.camera.x, y: camPosition.camera.y, z: camPosition.camera.z, onComplete: () => {
- closeDoorway(doorway2)
- }
- })
- gsap.to(game.lookAtFocus, { duration: 2.5, x: camPosition.focus.x, y: camPosition.focus.y, z: camPosition.focus.z })
- stageData.currentRoom = ROOM_MARKET
- }
- export function moveToRoom(roomId) {
- if (roomId == stageData.currentRoom) {
- return
- }
- switch (roomId) {
- case ROOM_SHOP:
- moveToShop()
- break;
- case ROOM_BREW:
- moveToBrew()
- break;
- case ROOM_MARKET:
- moveToMarket()
- break;
- }
- }
- let keyHistory = []
- export function onKeyPress(code) {
- if (game.keyboard['Digit1'] && stageData.currentRoom == ROOM_BREW) {
- moveToRoom(ROOM_SHOP)
- keyHistory = []
- }
- if (game.keyboard['Digit2'] && stageData.currentRoom != ROOM_BREW) {
- moveToRoom(ROOM_BREW)
- keyHistory = []
- }
- if (game.keyboard['Digit3'] && stageData.currentRoom == ROOM_BREW) {
- moveToRoom(ROOM_MARKET)
- keyHistory = []
- }
- if (game.keyboard['Digit9'] || game.keyboard['F9']) {
- game.orbitControls.enabled = !game.orbitControls.enabled
- if (!game.orbitControls.enabled) {
- console.log(`position:`, game.camera.position)
- console.log(`focus:`, game.orbitControls.target)
- moveToRoom(stageData.currentRoom)
- keyHistory = []
- }
- }
- if (game.keyboard['Escape']) {
- returnToMainMenu(game, stageData)
- keyHistory = []
- }
- keyHistory.push(code.replace("Key", ""))
- const cheatcode = keyHistory.join("").toLowerCase()
- if(cheatcode.includes("ghostbux")) {
- keyHistory = []
- stageData.currency += 1000
- addAmountToSave("currency", 1000)
- updateGameStatusUI(game, stageData)
- stageData.soundEffects['audio/witch_cackle1.ogg'].play()
- }
- if(cheatcode.includes("awesomesauce")) {
- keyHistory = []
- stageData.potionInventory.push(...stageData.potionInfo.map(info => info.name))
- addValueToSave(stageData.potionInventory, "potion-inventory", "spooksauce")
- stageData.soundEffects['audio/witch_cackle1.ogg'].play()
- }
- if(cheatcode.includes("yeschef")) {
- keyHistory = []
- //TODO: grant all ingredients
- stageData.soundEffects['audio/witch_cackle1.ogg'].play()
- }
- }
- export function returnToMainMenu(game, stageData) {
- Howler.stop()
- closeBrewUI(game, stageData)
- closeShopUI(game, stageData)
- hideNavigationUI(game, stageData)
- openMainMenuUI(game, stageData)
- hideGameStatusUI(game, stageData)
- hideOptionsUI(game, stageData)
- hideCreditsUI(game, stageData)
- stageData.currentRoom = ROOM_BREW
- game.camera.position.copy(stageData.cameraPositions[3].camera)
- game.lookAtFocus = stageData.cameraPositions[3].focus.clone()
- stageData.brewTutorial1.material.opacity = 0
- stageData.brewTutorial1.castShadow = false
- stageData.brewTutorial1.alreadySeen = false
- stageData.shopTutorial1.material.opacity = 0
- stageData.shopTutorial1.castShadow = false
- stageData.shopTutorial1.alreadySeen = false
- stageData.shopTutorial2.material.opacity = 0
- stageData.shopTutorial2.castShadow = false
- stageData.shopTutorial2.alreadySeen = false
- updatePotionShelfDisplay()
- updateGameStatusUI(game, stageData)
- }
|