diff --git a/.env.example b/.env.example index e9c6cbbcd9..ecc741ea7b 100644 --- a/.env.example +++ b/.env.example @@ -2,6 +2,7 @@ REACT_APP_DEPLOYMENT_NAME=Source Academy REACT_APP_BACKEND_URL=http://localhost:4000 REACT_APP_USE_BACKEND=TRUE +REACT_APP_USE_EMPTY_ASSET_PREFIX=FALSE REACT_APP_PLAYGROUND_ONLY=FALSE REACT_APP_SHOW_RESEARCH_PROMPT=FALSE diff --git a/package.json b/package.json index 1f17980214..d7b3ddb6cf 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-hast": "^13.0.0", "normalize.css": "^8.0.1", - "phaser": "^3.55.2", + "phaser": "~3.87.0", "query-string": "^9.0.0", "re-resizable": "^6.9.9", "react": "^18.3.1", @@ -148,7 +148,9 @@ "husky": "^9.0.0", "npm-run-all2": "^7.0.0", "os-browserify": "^0.3.0", + "path": "^0.12.7", "path-browserify": "^1.0.1", + "phaser3spectorjs": "^0.0.8", "prettier": "^3.3.3", "process": "^0.11.10", "react-error-overlay": "^6.0.11", diff --git a/public/assets/mockChapter0.txt b/public/assets/mockChapter0.txt index 7a985d9fb0..b22cc9febf 100644 --- a/public/assets/mockChapter0.txt +++ b/public/assets/mockChapter0.txt @@ -113,7 +113,7 @@ dialogues Hmmm update_assessment_status*() Let me check - goto 2 if !userstate.assessments.173 else 3 + goto 1 if !userstate.assessments.173 else 3 what, What should I do now, Scottie? @you diff --git a/src/commons/utils/Constants.ts b/src/commons/utils/Constants.ts index 0c0137fa4e..28a5555c3a 100644 --- a/src/commons/utils/Constants.ts +++ b/src/commons/utils/Constants.ts @@ -21,6 +21,7 @@ const storiesBackendUrl = process.env.REACT_APP_STORIES_BACKEND_URL; const cadetLoggerUrl = isTest ? undefined : process.env.REACT_APP_CADET_LOGGER; const cadetLoggerInterval = parseInt(process.env.REACT_APP_CADET_LOGGER_INTERVAL || '10000', 10); const useBackend = !isTest && isTrue(process.env.REACT_APP_USE_BACKEND); +const useEmptyAssetPrefix = isTrue(process.env.REACT_APP_USE_EMPTY_ASSET_PREFIX); const defaultSourceChapter = Chapter.SOURCE_4; const defaultSourceVariant = Variant.DEFAULT; const defaultQuestionId = 0; @@ -154,6 +155,7 @@ const Constants = { storiesBackendUrl, cadetLoggerUrl, useBackend, + useEmptyAssetPrefix, defaultSourceChapter, defaultSourceVariant, defaultQuestionId, diff --git a/src/features/game/commons/CommonConstants.ts b/src/features/game/commons/CommonConstants.ts index 64ab1b6ee5..613d33fbcf 100644 --- a/src/features/game/commons/CommonConstants.ts +++ b/src/features/game/commons/CommonConstants.ts @@ -1,9 +1,10 @@ -import { Links } from 'src/commons/utils/Constants'; +import UtilConstants, { Links } from 'src/commons/utils/Constants'; import FontAssets from '../assets/FontAssets'; export const Constants = { assetsFolder: Links.sourceAcademyAssets, + useEmptyAssetPrefix: UtilConstants.useEmptyAssetPrefix, fadeDuration: 600, nullFunction: () => {}, nullInteractionId: '', diff --git a/src/features/game/input/GameInputManager.ts b/src/features/game/input/GameInputManager.ts index 2ab78d792a..d1909a447c 100644 --- a/src/features/game/input/GameInputManager.ts +++ b/src/features/game/input/GameInputManager.ts @@ -32,7 +32,7 @@ class GameInputManager { * @param active if true, mouse input is enabled. Else, mouse input is disabled. */ public enableMouseInput(active: boolean) { - this.scene.input.mouse.enabled = active; + if (this.scene.input.mouse) this.scene.input.mouse.enabled = active; } /** @@ -41,7 +41,7 @@ class GameInputManager { * @param active if true, keyboard input is enabled. Else, keyboard input is disabled. */ public enableKeyboardInput(active: boolean) { - this.scene.input.keyboard.enabled = active; + if (this.scene.input.keyboard) this.scene.input.keyboard.enabled = active; } /** @@ -57,6 +57,8 @@ class GameInputManager { event: string | symbol, callback: Function ) { + if (!this.scene.input.keyboard) return; + const keyObj = this.scene.input.keyboard.addKey(key); const keyboardListener = keyObj.addListener(event, callback); this.keyboardListeners.push(keyboardListener); diff --git a/src/features/game/parser/ParserValidator.ts b/src/features/game/parser/ParserValidator.ts index 52bd8af204..b29ac8e78c 100644 --- a/src/features/game/parser/ParserValidator.ts +++ b/src/features/game/parser/ParserValidator.ts @@ -1,3 +1,5 @@ +import { SoundAsset } from '../assets/AssetsTypes'; +import SoundAssets from '../assets/SoundAssets'; import { ItemId } from '../commons/CommonTypes'; import { GameItemType } from '../location/GameMapTypes'; import { GameSoundType } from '../sound/GameSoundTypes'; @@ -203,26 +205,40 @@ export default class ParserValidator { break; case GameEntityType.bgms: + // Count BGMs matching itemId const numberOfBgm = Parser.checkpoint.map .getSoundAssets() - .filter( - sound => sound.soundType === GameSoundType.BGM && sound.key === itemId - ).length; + .reduce((acc: number, sound: SoundAsset): number => { + return ( + acc + (sound.soundType === GameSoundType.BGM && sound.key === itemId ? 1 : 0) + ); + }, 0); if (numberOfBgm === 0) { - throw new Error(`Cannot find bgm key "${itemId}"`); + // Check if itemId is a default BGM + const isDefaultAsset = Object.values(SoundAssets).some( + sound => sound.soundType === GameSoundType.BGM && sound.key === itemId + ); + if (!isDefaultAsset) throw new Error(`Cannot find bgm key "${itemId}"`); } else if (numberOfBgm > 1) { throw new Error(`More than 1 bgm key "${itemId}"`); } break; case GameEntityType.sfxs: + // Count SFXs matching itemId const numberOfSfx = Parser.checkpoint.map .getSoundAssets() - .filter( - sound => sound.soundType === GameSoundType.SFX && sound.key === itemId - ).length; + .reduce((acc: number, sound: SoundAsset): number => { + return ( + acc + (sound.soundType === GameSoundType.SFX && sound.key === itemId ? 1 : 0) + ); + }, 0); if (numberOfSfx === 0) { - throw new Error(`Cannot find sfx key "${itemId}"`); + // Check if itemId is a default SFX + const isDefaultAsset = Object.values(SoundAssets).some( + sound => sound.soundType === GameSoundType.SFX && sound.key === itemId + ); + if (!isDefaultAsset) throw new Error(`Cannot find sfx key "${itemId}"`); } else if (numberOfSfx > 1) { throw new Error(`More than 1 sfx key "${itemId}"`); } diff --git a/src/features/game/sound/GameSoundManager.ts b/src/features/game/sound/GameSoundManager.ts index 8ae17c78d8..9997944518 100644 --- a/src/features/game/sound/GameSoundManager.ts +++ b/src/features/game/sound/GameSoundManager.ts @@ -161,14 +161,18 @@ class GameSoundManager { const soundAsset = mandatory(this.getSoundAsset(soundKey)); const bgmVol = soundAsset.config.volume !== undefined ? soundAsset.config.volume : 1; - this.currBgMusic = this.getBaseSoundManager().add(soundAsset.key, { - ...soundAsset.config, - volume: bgmVol * this.bgmVol - }) as Phaser.Sound.WebAudioSound; - this.currBgMusicKey = soundAsset.key; - - // Finally, play it - this.currBgMusic.play(); + try { + this.currBgMusic = this.getBaseSoundManager().add(soundAsset.key, { + ...soundAsset.config, + volume: bgmVol * this.bgmVol + }) as Phaser.Sound.WebAudioSound; + this.currBgMusicKey = soundAsset.key; + + // Finally, play it + this.currBgMusic.play(); + } catch (err) { + console.error(err); + } } /** diff --git a/src/features/game/utils/GameUtils.ts b/src/features/game/utils/GameUtils.ts index 76852077ad..3d8833485a 100644 --- a/src/features/game/utils/GameUtils.ts +++ b/src/features/game/utils/GameUtils.ts @@ -58,9 +58,12 @@ export function limitNumber(value: number, min: number, max: number) { */ export function toS3Path(fileName: string, courseCoded = false) { if (fileName.startsWith('/')) { - fileName = fileName.substr(1); + fileName = fileName.substring(1); } - return Constants.assetsFolder + (courseCoded ? assetsPrefix() + fileName : fileName); + return ( + Constants.assetsFolder + + (courseCoded && !Constants.useEmptyAssetPrefix ? assetsPrefix() + fileName : fileName) + ); } /** diff --git a/yarn.lock b/yarn.lock index 9b0f5fc791..29cf8c6a4b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9063,13 +9063,20 @@ __metadata: languageName: node linkType: hard -"eventemitter3@npm:^4.0.0, eventemitter3@npm:^4.0.1, eventemitter3@npm:^4.0.7": +"eventemitter3@npm:^4.0.0, eventemitter3@npm:^4.0.1": version: 4.0.7 resolution: "eventemitter3@npm:4.0.7" checksum: 10c0/5f6d97cbcbac47be798e6355e3a7639a84ee1f7d9b199a07017f1d2f1e2fe236004d14fa5dfaeba661f94ea57805385e326236a6debbc7145c8877fbc0297c6b languageName: node linkType: hard +"eventemitter3@npm:^5.0.1": + version: 5.0.1 + resolution: "eventemitter3@npm:5.0.1" + checksum: 10c0/4ba5c00c506e6c786b4d6262cfbce90ddc14c10d4667e5c83ae993c9de88aa856033994dd2b35b83e8dc1170e224e66a319fa80adc4c32adcd2379bbc75da814 + languageName: node + linkType: hard + "events@npm:^3.2.0": version: 3.3.0 resolution: "events@npm:3.3.0" @@ -9671,8 +9678,10 @@ __metadata: normalize.css: "npm:^8.0.1" npm-run-all2: "npm:^7.0.0" os-browserify: "npm:^0.3.0" + path: "npm:^0.12.7" path-browserify: "npm:^1.0.1" - phaser: "npm:^3.55.2" + phaser: "npm:~3.87.0" + phaser3spectorjs: "npm:^0.0.8" prettier: "npm:^3.3.3" process: "npm:^0.11.10" query-string: "npm:^9.0.0" @@ -14263,13 +14272,19 @@ __metadata: languageName: node linkType: hard -"phaser@npm:^3.55.2": - version: 3.55.2 - resolution: "phaser@npm:3.55.2" +"phaser3spectorjs@npm:^0.0.8": + version: 0.0.8 + resolution: "phaser3spectorjs@npm:0.0.8" + checksum: 10c0/84933cc3e314279d2bd2c4b3356f2c93e0fe6918dcf6d7ab88d0df4d3457df05703e895012a27ba50d8dd3d9bc61183b58a7b8dde9ec81fc3d887e0fd77184dd + languageName: node + linkType: hard + +"phaser@npm:~3.87.0": + version: 3.87.0 + resolution: "phaser@npm:3.87.0" dependencies: - eventemitter3: "npm:^4.0.7" - path: "npm:^0.12.7" - checksum: 10c0/aed4c37a22174e843344328c6b0cc0a6160a49fe12d37a6fa0aab82d37d6bfc00a3c76fa792621fa61b02148e340c39dae5be4f0f5b7cb95df7c774b826455b3 + eventemitter3: "npm:^5.0.1" + checksum: 10c0/39f5baef2cbd3c30ae65efb7f1b4a034f5fd87f51f74547574602a6bada014999a900561cb0efa17e086a6a06f886525d0832b1d465af382db8674c1ed42ef67 languageName: node linkType: hard