@strixit/storyscript 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env +1 -0
- package/CodeSnippets/StoryScriptSnippets.code-snippets +573 -0
- package/LICENSE.txt +21 -0
- package/LiveTemplates/StoryScript.xml +271 -0
- package/README.md +9 -2
- package/baseViteConfig.js +112 -0
- package/clientFiles/currentGameName.js +1 -0
- package/clientFiles/gitignore +12 -0
- package/clientFiles/tsconfig.json +34 -0
- package/clientFiles/vite.config.ts +14 -0
- package/clientFiles/vitest.config.ts +29 -0
- package/dist/src/Engine/Actions/index.d.ts +2 -0
- package/dist/src/Engine/Actions/open.d.ts +6 -0
- package/dist/src/Engine/Actions/openWithKey.d.ts +7 -0
- package/dist/src/Engine/EntityCreatorFunctions.d.ts +18 -0
- package/dist/src/Engine/Interfaces/action.d.ts +37 -0
- package/dist/src/Engine/Interfaces/autoplayStep.d.ts +11 -0
- package/dist/src/Engine/Interfaces/barrier.d.ts +16 -0
- package/dist/src/Engine/Interfaces/barrierAction.d.ts +19 -0
- package/dist/src/Engine/Interfaces/character.d.ts +48 -0
- package/dist/src/Engine/Interfaces/combatSetup.d.ts +9 -0
- package/dist/src/Engine/Interfaces/combatTurn.d.ts +37 -0
- package/dist/src/Engine/Interfaces/combatWinnings.d.ts +6 -0
- package/dist/src/Engine/Interfaces/combinations/activeCombination.d.ts +14 -0
- package/dist/src/Engine/Interfaces/combinations/combinable.d.ts +18 -0
- package/dist/src/Engine/Interfaces/combinations/combinationAction.d.ts +41 -0
- package/dist/src/Engine/Interfaces/combinations/combinationMatchResult.d.ts +14 -0
- package/dist/src/Engine/Interfaces/combinations/combinations.d.ts +17 -0
- package/dist/src/Engine/Interfaces/combinations/combine.d.ts +22 -0
- package/dist/src/Engine/Interfaces/combinations/combineResult.d.ts +18 -0
- package/dist/src/Engine/Interfaces/combinations/gameCombinations.d.ts +40 -0
- package/dist/src/Engine/Interfaces/compiledLocation.d.ts +33 -0
- package/dist/src/Engine/Interfaces/conversations/conversation.d.ts +44 -0
- package/dist/src/Engine/Interfaces/conversations/conversationLogEntry.d.ts +13 -0
- package/dist/src/Engine/Interfaces/conversations/conversationNode.d.ts +22 -0
- package/dist/src/Engine/Interfaces/conversations/conversationReply.d.ts +49 -0
- package/dist/src/Engine/Interfaces/createCharacter/createCharacter.d.ts +20 -0
- package/dist/src/Engine/Interfaces/createCharacter/createCharacterAttribute.d.ts +18 -0
- package/dist/src/Engine/Interfaces/createCharacter/createCharacterAttributeEntry.d.ts +18 -0
- package/dist/src/Engine/Interfaces/createCharacter/createCharacterQuestion.d.ts +18 -0
- package/dist/src/Engine/Interfaces/createCharacter/createCharacterQuestionEntry.d.ts +22 -0
- package/dist/src/Engine/Interfaces/createCharacter/createCharacterStep.d.ts +40 -0
- package/dist/src/Engine/Interfaces/customCursor.d.ts +8 -0
- package/dist/src/Engine/Interfaces/defaultEquipment.d.ts +19 -0
- package/dist/src/Engine/Interfaces/definitions.d.ts +40 -0
- package/dist/src/Engine/Interfaces/destination.d.ts +31 -0
- package/dist/src/Engine/Interfaces/dynamicStyle.d.ts +10 -0
- package/dist/src/Engine/Interfaces/enemy.d.ts +46 -0
- package/dist/src/Engine/Interfaces/enumerations/actionStatus.d.ts +18 -0
- package/dist/src/Engine/Interfaces/enumerations/actionType.d.ts +9 -0
- package/dist/src/Engine/Interfaces/enumerations/equipmentType.d.ts +51 -0
- package/dist/src/Engine/Interfaces/enumerations/gameState.d.ts +11 -0
- package/dist/src/Engine/Interfaces/enumerations/playState.d.ts +10 -0
- package/dist/src/Engine/Interfaces/enumerations/targetType.d.ts +9 -0
- package/dist/src/Engine/Interfaces/equipment.d.ts +43 -0
- package/dist/src/Engine/Interfaces/feature.d.ts +37 -0
- package/dist/src/Engine/Interfaces/featureCollection.d.ts +7 -0
- package/dist/src/Engine/Interfaces/game.d.ts +148 -0
- package/dist/src/Engine/Interfaces/gameEvents.d.ts +7 -0
- package/dist/src/Engine/Interfaces/groupableItem.d.ts +32 -0
- package/dist/src/Engine/Interfaces/helpers.d.ts +50 -0
- package/dist/src/Engine/Interfaces/index.d.ts +78 -0
- package/dist/src/Engine/Interfaces/interfaceTexts.d.ts +140 -0
- package/dist/src/Engine/Interfaces/item.d.ts +96 -0
- package/dist/src/Engine/Interfaces/key.d.ts +16 -0
- package/dist/src/Engine/Interfaces/location.d.ts +78 -0
- package/dist/src/Engine/Interfaces/maps/map.d.ts +45 -0
- package/dist/src/Engine/Interfaces/maps/mapLocation.d.ts +23 -0
- package/dist/src/Engine/Interfaces/party.d.ts +33 -0
- package/dist/src/Engine/Interfaces/person.d.ts +25 -0
- package/dist/src/Engine/Interfaces/quest.d.ts +51 -0
- package/dist/src/Engine/Interfaces/rules/characterRules.d.ts +84 -0
- package/dist/src/Engine/Interfaces/rules/combatRules.d.ts +46 -0
- package/dist/src/Engine/Interfaces/rules/combinationRules.d.ts +15 -0
- package/dist/src/Engine/Interfaces/rules/demoMode.d.ts +15 -0
- package/dist/src/Engine/Interfaces/rules/encounterRules.d.ts +4 -0
- package/dist/src/Engine/Interfaces/rules/explorationRules.d.ts +42 -0
- package/dist/src/Engine/Interfaces/rules/generalRules.d.ts +62 -0
- package/dist/src/Engine/Interfaces/rules/rules.d.ts +31 -0
- package/dist/src/Engine/Interfaces/rules/setupRules.d.ts +56 -0
- package/dist/src/Engine/Interfaces/rules/titleScreen.d.ts +16 -0
- package/dist/src/Engine/Interfaces/saveGame.d.ts +33 -0
- package/dist/src/Engine/Interfaces/scoreEntry.d.ts +13 -0
- package/dist/src/Engine/Interfaces/services/autoplayService.d.ts +19 -0
- package/dist/src/Engine/Interfaces/services/characterService.d.ts +17 -0
- package/dist/src/Engine/Interfaces/services/combatService.d.ts +26 -0
- package/dist/src/Engine/Interfaces/services/combinationService.d.ts +9 -0
- package/dist/src/Engine/Interfaces/services/commandService.d.ts +64 -0
- package/dist/src/Engine/Interfaces/services/conversationService.d.ts +5 -0
- package/dist/src/Engine/Interfaces/services/dataSerializer.d.ts +4 -0
- package/dist/src/Engine/Interfaces/services/dataService.d.ts +33 -0
- package/dist/src/Engine/Interfaces/services/dataSynchronizer.d.ts +3 -0
- package/dist/src/Engine/Interfaces/services/gameService.d.ts +10 -0
- package/dist/src/Engine/Interfaces/services/index.d.ts +16 -0
- package/dist/src/Engine/Interfaces/services/itemService.d.ts +14 -0
- package/dist/src/Engine/Interfaces/services/localStorageService.d.ts +6 -0
- package/dist/src/Engine/Interfaces/services/locationService.d.ts +8 -0
- package/dist/src/Engine/Interfaces/services/soundService.d.ts +5 -0
- package/dist/src/Engine/Interfaces/services/tradeService.d.ts +9 -0
- package/dist/src/Engine/Interfaces/soundPlayer.d.ts +30 -0
- package/dist/src/Engine/Interfaces/sprite.d.ts +23 -0
- package/dist/src/Engine/Interfaces/stateList.d.ts +9 -0
- package/dist/src/Engine/Interfaces/statistics.d.ts +13 -0
- package/dist/src/Engine/Interfaces/stock.d.ts +39 -0
- package/dist/src/Engine/Interfaces/trade.d.ts +62 -0
- package/dist/src/Engine/ServiceFactory.d.ts +52 -0
- package/dist/src/Engine/Services/AutoplayService.d.ts +11 -0
- package/dist/src/Engine/Services/CharacterService.d.ts +21 -0
- package/dist/src/Engine/Services/CombatService.d.ts +16 -0
- package/dist/src/Engine/Services/CombinationService.d.ts +15 -0
- package/dist/src/Engine/Services/CommandService.d.ts +24 -0
- package/dist/src/Engine/Services/ConversationService.d.ts +26 -0
- package/dist/src/Engine/Services/DataSerializer.d.ts +17 -0
- package/dist/src/Engine/Services/DataService.d.ts +16 -0
- package/dist/src/Engine/Services/DataSynchronizer.d.ts +12 -0
- package/dist/src/Engine/Services/GameService.d.ts +28 -0
- package/dist/src/Engine/Services/HelperService.d.ts +18 -0
- package/dist/src/Engine/Services/ItemService.d.ts +21 -0
- package/dist/src/Engine/Services/LocalStorageService.d.ts +7 -0
- package/dist/src/Engine/Services/LocationService.d.ts +25 -0
- package/dist/src/Engine/Services/SoundService.d.ts +13 -0
- package/dist/src/Engine/Services/TradeService.d.ts +17 -0
- package/dist/src/Engine/Services/index.d.ts +17 -0
- package/dist/src/Engine/Services/serializationData.d.ts +8 -0
- package/dist/src/Engine/Services/sharedFunctions.d.ts +21 -0
- package/dist/src/Engine/arrayAndFunctionExtensions.d.ts +3 -0
- package/dist/src/Engine/defaultTexts.d.ts +7 -0
- package/dist/src/Engine/gameEvents.d.ts +10 -0
- package/dist/src/Engine/index.d.ts +8 -0
- package/dist/src/Engine/stateProperties.d.ts +6 -0
- package/dist/src/Engine/utilityFunctions.d.ts +25 -0
- package/dist/src/UI/Composables/CustomCursor.d.ts +2 -0
- package/dist/src/UI/Composables/LocationMap.d.ts +26 -0
- package/dist/src/UI/Composables/Sound.d.ts +7 -0
- package/dist/src/UI/Composables/TextFeatures.d.ts +8 -0
- package/dist/src/UI/Composables/VisualFeatures.d.ts +7 -0
- package/dist/src/UI/Composables/index.d.ts +7 -0
- package/dist/src/UI/Composables/playStateWatch.d.ts +2 -0
- package/dist/src/UI/StateStore.d.ts +14256 -0
- package/dist/src/UI/error.d.ts +6 -0
- package/dist/src/UI/helpers.d.ts +3 -0
- package/dist/src/UI/index.d.ts +6 -0
- package/dist/src/UI/main.d.ts +1 -0
- package/dist/src/UI/startup.d.ts +2 -0
- package/dist/src/constants.d.ts +17 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/storyscript.css +9566 -0
- package/dist/storyscript.js +9659 -0
- package/dist/storyscript.js.map +1 -0
- package/dist/storyscript.mjs +9569 -0
- package/dist/storyscript.mjs.map +1 -0
- package/electron.cjs +44 -0
- package/index.html +14 -0
- package/package.json +118 -4
- package/scripts/cli.js +268 -0
- package/scripts/createAsset.js +117 -0
- package/scripts/createGame.js +40 -0
- package/scripts/helpers.js +21 -0
- package/scripts/postbuild.js +179 -0
- package/scripts/setupTesting.js +17 -0
- package/shims-vue.d.ts +1 -0
- package/src/Engine/Actions/index.ts +2 -0
- package/src/Engine/Actions/open.ts +12 -0
- package/src/Engine/Actions/openWithKey.ts +23 -0
- package/src/Engine/EntityCreatorFunctions.ts +602 -0
- package/src/Engine/Interfaces/action.ts +43 -0
- package/src/Engine/Interfaces/autoplayStep.ts +13 -0
- package/src/Engine/Interfaces/barrier.ts +18 -0
- package/src/Engine/Interfaces/barrierAction.ts +21 -0
- package/src/Engine/Interfaces/character.ts +58 -0
- package/src/Engine/Interfaces/combatSetup.ts +10 -0
- package/src/Engine/Interfaces/combatTurn.ts +45 -0
- package/src/Engine/Interfaces/combatWinnings.ts +7 -0
- package/src/Engine/Interfaces/combinations/activeCombination.ts +16 -0
- package/src/Engine/Interfaces/combinations/combinable.ts +21 -0
- package/src/Engine/Interfaces/combinations/combinationAction.ts +45 -0
- package/src/Engine/Interfaces/combinations/combinationMatchResult.ts +16 -0
- package/src/Engine/Interfaces/combinations/combinations.ts +19 -0
- package/src/Engine/Interfaces/combinations/combine.ts +25 -0
- package/src/Engine/Interfaces/combinations/combineResult.ts +21 -0
- package/src/Engine/Interfaces/combinations/gameCombinations.ts +48 -0
- package/src/Engine/Interfaces/compiledLocation.ts +37 -0
- package/src/Engine/Interfaces/conversations/conversation.ts +47 -0
- package/src/Engine/Interfaces/conversations/conversationLogEntry.ts +14 -0
- package/src/Engine/Interfaces/conversations/conversationNode.ts +26 -0
- package/src/Engine/Interfaces/conversations/conversationReply.ts +59 -0
- package/src/Engine/Interfaces/createCharacter/createCharacter.ts +23 -0
- package/src/Engine/Interfaces/createCharacter/createCharacterAttribute.ts +21 -0
- package/src/Engine/Interfaces/createCharacter/createCharacterAttributeEntry.ts +21 -0
- package/src/Engine/Interfaces/createCharacter/createCharacterQuestion.ts +21 -0
- package/src/Engine/Interfaces/createCharacter/createCharacterQuestionEntry.ts +25 -0
- package/src/Engine/Interfaces/createCharacter/createCharacterStep.ts +47 -0
- package/src/Engine/Interfaces/customCursor.ts +5 -0
- package/src/Engine/Interfaces/defaultEquipment.ts +32 -0
- package/src/Engine/Interfaces/definitions.ts +48 -0
- package/src/Engine/Interfaces/destination.ts +37 -0
- package/src/Engine/Interfaces/dynamicStyle.ts +11 -0
- package/src/Engine/Interfaces/enemy.ts +54 -0
- package/src/Engine/Interfaces/enumerations/actionStatus.ts +20 -0
- package/src/Engine/Interfaces/enumerations/actionType.ts +9 -0
- package/src/Engine/Interfaces/enumerations/equipmentType.ts +61 -0
- package/src/Engine/Interfaces/enumerations/gameState.ts +11 -0
- package/src/Engine/Interfaces/enumerations/playState.ts +10 -0
- package/src/Engine/Interfaces/enumerations/targetType.ts +9 -0
- package/src/Engine/Interfaces/equipment.ts +44 -0
- package/src/Engine/Interfaces/feature.ts +43 -0
- package/src/Engine/Interfaces/featureCollection.ts +8 -0
- package/src/Engine/Interfaces/game.ts +201 -0
- package/src/Engine/Interfaces/gameEvents.ts +11 -0
- package/src/Engine/Interfaces/groupableItem.ts +37 -0
- package/src/Engine/Interfaces/helpers.ts +55 -0
- package/src/Engine/Interfaces/index.ts +158 -0
- package/src/Engine/Interfaces/interfaceTexts.ts +142 -0
- package/src/Engine/Interfaces/item.ts +111 -0
- package/src/Engine/Interfaces/key.ts +18 -0
- package/src/Engine/Interfaces/location.ts +92 -0
- package/src/Engine/Interfaces/maps/map.ts +55 -0
- package/src/Engine/Interfaces/maps/mapLocation.ts +28 -0
- package/src/Engine/Interfaces/party.ts +40 -0
- package/src/Engine/Interfaces/person.ts +29 -0
- package/src/Engine/Interfaces/quest.ts +60 -0
- package/src/Engine/Interfaces/rules/characterRules.ts +96 -0
- package/src/Engine/Interfaces/rules/combatRules.ts +52 -0
- package/src/Engine/Interfaces/rules/combinationRules.ts +17 -0
- package/src/Engine/Interfaces/rules/demoMode.ts +18 -0
- package/src/Engine/Interfaces/rules/encounterRules.ts +5 -0
- package/src/Engine/Interfaces/rules/explorationRules.ts +47 -0
- package/src/Engine/Interfaces/rules/generalRules.ts +80 -0
- package/src/Engine/Interfaces/rules/rules.ts +46 -0
- package/src/Engine/Interfaces/rules/setupRules.ts +65 -0
- package/src/Engine/Interfaces/rules/titleScreen.ts +19 -0
- package/src/Engine/Interfaces/saveGame.ts +39 -0
- package/src/Engine/Interfaces/scoreEntry.ts +14 -0
- package/src/Engine/Interfaces/services/autoplayService.ts +23 -0
- package/src/Engine/Interfaces/services/characterService.ts +35 -0
- package/src/Engine/Interfaces/services/combatService.ts +30 -0
- package/src/Engine/Interfaces/services/combinationService.ts +10 -0
- package/src/Engine/Interfaces/services/commandService.ts +75 -0
- package/src/Engine/Interfaces/services/conversationService.ts +7 -0
- package/src/Engine/Interfaces/services/dataSerializer.ts +4 -0
- package/src/Engine/Interfaces/services/dataService.ts +39 -0
- package/src/Engine/Interfaces/services/dataSynchronizer.ts +9 -0
- package/src/Engine/Interfaces/services/gameService.ts +17 -0
- package/src/Engine/Interfaces/services/index.ts +33 -0
- package/src/Engine/Interfaces/services/itemService.ts +25 -0
- package/src/Engine/Interfaces/services/localStorageService.ts +6 -0
- package/src/Engine/Interfaces/services/locationService.ts +13 -0
- package/src/Engine/Interfaces/services/soundService.ts +7 -0
- package/src/Engine/Interfaces/services/tradeService.ts +15 -0
- package/src/Engine/Interfaces/soundPlayer.ts +30 -0
- package/src/Engine/Interfaces/sprite.ts +27 -0
- package/src/Engine/Interfaces/stateList.ts +13 -0
- package/src/Engine/Interfaces/statistics.ts +14 -0
- package/src/Engine/Interfaces/stock.ts +46 -0
- package/src/Engine/Interfaces/trade.ts +72 -0
- package/src/Engine/ServiceFactory.ts +167 -0
- package/src/Engine/Services/AutoplayService.ts +59 -0
- package/src/Engine/Services/CharacterService.ts +291 -0
- package/src/Engine/Services/CombatService.ts +266 -0
- package/src/Engine/Services/CombinationService.ts +197 -0
- package/src/Engine/Services/CommandService.ts +197 -0
- package/src/Engine/Services/ConversationService.ts +406 -0
- package/src/Engine/Services/DataSerializer.ts +326 -0
- package/src/Engine/Services/DataService.ts +93 -0
- package/src/Engine/Services/DataSynchronizer.ts +216 -0
- package/src/Engine/Services/GameService.ts +554 -0
- package/src/Engine/Services/HelperService.ts +94 -0
- package/src/Engine/Services/ItemService.ts +294 -0
- package/src/Engine/Services/LocalStorageService.ts +24 -0
- package/src/Engine/Services/LocationService.ts +380 -0
- package/src/Engine/Services/SoundService.ts +54 -0
- package/src/Engine/Services/TradeService.ts +176 -0
- package/src/Engine/Services/index.ts +17 -0
- package/src/Engine/Services/serializationData.ts +8 -0
- package/src/Engine/Services/sharedFunctions.ts +296 -0
- package/src/Engine/arrayAndFunctionExtensions.ts +269 -0
- package/src/Engine/defaultTexts.ts +162 -0
- package/src/Engine/gameEvents.ts +51 -0
- package/src/Engine/index.ts +9 -0
- package/src/Engine/stateProperties.ts +9 -0
- package/src/Engine/utilityFunctions.ts +238 -0
- package/src/Games/_GameTemplate/character.ts +31 -0
- package/src/Games/_GameTemplate/customTexts.ts +8 -0
- package/src/Games/_GameTemplate/interfaces/action.ts +5 -0
- package/src/Games/_GameTemplate/interfaces/combatSetup.ts +7 -0
- package/src/Games/_GameTemplate/interfaces/combatTurn.ts +8 -0
- package/src/Games/_GameTemplate/interfaces/destination.ts +5 -0
- package/src/Games/_GameTemplate/interfaces/enemy.ts +11 -0
- package/src/Games/_GameTemplate/interfaces/equipment.ts +5 -0
- package/src/Games/_GameTemplate/interfaces/feature.ts +9 -0
- package/src/Games/_GameTemplate/interfaces/game.ts +22 -0
- package/src/Games/_GameTemplate/interfaces/interfaceTexts.ts +5 -0
- package/src/Games/_GameTemplate/interfaces/item.ts +18 -0
- package/src/Games/_GameTemplate/interfaces/key.ts +10 -0
- package/src/Games/_GameTemplate/interfaces/location.ts +22 -0
- package/src/Games/_GameTemplate/interfaces/map.ts +16 -0
- package/src/Games/_GameTemplate/interfaces/party.ts +8 -0
- package/src/Games/_GameTemplate/interfaces/person.ts +11 -0
- package/src/Games/_GameTemplate/interfaces/quest.ts +9 -0
- package/src/Games/_GameTemplate/locations/Start.html +5 -0
- package/src/Games/_GameTemplate/locations/start.ts +12 -0
- package/src/Games/_GameTemplate/rules.ts +60 -0
- package/src/Games/_GameTemplate/types.ts +55 -0
- package/src/Games/_GameTemplate/ui/styles/game.css +3 -0
- package/src/Games/_GameTemplate/webroot/favicon.ico +0 -0
- package/src/Games/_GameTemplate/webroot/gameinfo.json +10 -0
- package/src/Games/_GameTemplate/webroot/logo192.png +0 -0
- package/src/Games/_GameTemplate/webroot/logo512.png +0 -0
- package/src/Tests/Games/_GameTemplate/specs/rules.test.ts +9 -0
- package/src/UI/Components/AppShell.vue +43 -0
- package/src/UI/Components/Autoplay.vue +32 -0
- package/src/UI/Components/Combinations.vue +37 -0
- package/src/UI/Components/Common/Collapsible.vue +50 -0
- package/src/UI/Components/Common/ModalDialog.vue +54 -0
- package/src/UI/Components/Common/Sprite.vue +53 -0
- package/src/UI/Components/Description.vue +23 -0
- package/src/UI/Components/Encounter/Combat/Combat.vue +89 -0
- package/src/UI/Components/Encounter/Combat/CombatActions.vue +26 -0
- package/src/UI/Components/Encounter/Combat/CombatParticipant.vue +22 -0
- package/src/UI/Components/Encounter/Combat/CombatTargetSelect.vue +55 -0
- package/src/UI/Components/Encounter/Combat/CombatWin.vue +33 -0
- package/src/UI/Components/Encounter/Conversation.vue +66 -0
- package/src/UI/Components/Encounter/Encounter.vue +39 -0
- package/src/UI/Components/Encounter/Enemy.vue +30 -0
- package/src/UI/Components/Encounter/Trade/Stock.vue +58 -0
- package/src/UI/Components/Encounter/Trade/Trade.vue +24 -0
- package/src/UI/Components/Exploration/ActionLog.vue +17 -0
- package/src/UI/Components/Exploration/Destination.vue +66 -0
- package/src/UI/Components/Exploration/Exploration.vue +67 -0
- package/src/UI/Components/Exploration/Ground.vue +22 -0
- package/src/UI/Components/Exploration/LocationMap.vue +42 -0
- package/src/UI/Components/Exploration/LocationText.vue +39 -0
- package/src/UI/Components/Exploration/LocationVisual.vue +38 -0
- package/src/UI/Components/GameContainer.vue +49 -0
- package/src/UI/Components/GameMenu.vue +111 -0
- package/src/UI/Components/GameState/GameOver.vue +20 -0
- package/src/UI/Components/GameState/HighScores.vue +22 -0
- package/src/UI/Components/GameState/Intro.vue +17 -0
- package/src/UI/Components/GameState/TitleScreen.vue +42 -0
- package/src/UI/Components/GameState/Victory.vue +28 -0
- package/src/UI/Components/Navigation.vue +94 -0
- package/src/UI/Components/Party/Backpack.vue +68 -0
- package/src/UI/Components/Party/BuildCharacter.vue +64 -0
- package/src/UI/Components/Party/CharacterSheet.vue +57 -0
- package/src/UI/Components/Party/CreateCharacter.vue +46 -0
- package/src/UI/Components/Party/Equipment.vue +48 -0
- package/src/UI/Components/Party/EquipmentSlot.vue +31 -0
- package/src/UI/Components/Party/LevelUp.vue +27 -0
- package/src/UI/Components/Party/Party.vue +29 -0
- package/src/UI/Components/Party/Quests.vue +48 -0
- package/src/UI/Components/Sound.vue +42 -0
- package/src/UI/Composables/CustomCursor.ts +60 -0
- package/src/UI/Composables/LocationMap.ts +315 -0
- package/src/UI/Composables/Sound.ts +111 -0
- package/src/UI/Composables/TextFeatures.ts +148 -0
- package/src/UI/Composables/VisualFeatures.ts +220 -0
- package/src/UI/Composables/index.ts +15 -0
- package/src/UI/Composables/playStateWatch.ts +31 -0
- package/src/UI/StateStore.ts +237 -0
- package/src/UI/error.ts +6 -0
- package/src/UI/helpers.ts +62 -0
- package/src/UI/index.ts +13 -0
- package/src/UI/main.ts +62 -0
- package/src/UI/startup.ts +58 -0
- package/src/UI/styles/bootstrap-storyscript.scss +31 -0
- package/src/UI/styles/empty.css +3 -0
- package/src/UI/styles/lookinglass.png +0 -0
- package/src/UI/styles/storyscript.css +735 -0
- package/typeExtensions.d.ts +25 -0
- package/vite-env.d.ts +1 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import {IDataSerializer, IFeature, parseFunction, serializeFunction} from 'storyScript';
|
|
2
|
+
import {InitEntityCollection} from "src/Engine/EntityCreatorFunctions";
|
|
3
|
+
import {StateProperties} from "src/Engine/stateProperties";
|
|
4
|
+
import {SerializationData} from "./serializationData";
|
|
5
|
+
import {getKeyPropertyNames, getPlural, isDataRecord} from "src/Engine/utilityFunctions";
|
|
6
|
+
import {DescriptionProperty, IdProperty} from 'src/constants';
|
|
7
|
+
|
|
8
|
+
export class DataSerializer implements IDataSerializer {
|
|
9
|
+
private conversationPropertiesToSerialize = [
|
|
10
|
+
'actions',
|
|
11
|
+
'startNode',
|
|
12
|
+
'singleRepliesChosen'
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
constructor(private readonly _pristineEntities: Record<string, Record<string, any>>) {
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
createSerializableClone = (original: any): any => {
|
|
19
|
+
return this.buildStructureForSerialization(undefined, original, undefined);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
restoreObjects = (
|
|
23
|
+
loaded: any
|
|
24
|
+
): any => {
|
|
25
|
+
for (const key in loaded) {
|
|
26
|
+
if (!loaded.hasOwnProperty(key)) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const value = loaded[key];
|
|
31
|
+
|
|
32
|
+
if (this.restoreArray(value, loaded, key)) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (typeof value === 'object') {
|
|
37
|
+
this.restoreObjects(value);
|
|
38
|
+
} else if (typeof value === 'string' && value.indexOf('function') > -1) {
|
|
39
|
+
loaded[key] = parseFunction(value);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return loaded;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private readonly buildStructureForSerialization = (clone: any, original: any, pristine: any, parentKey?: string): any => {
|
|
47
|
+
clone ??= this.createClone(original);
|
|
48
|
+
|
|
49
|
+
if (clone == original) {
|
|
50
|
+
return clone;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
for (const key in original) {
|
|
54
|
+
if (!original.hasOwnProperty(key)) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let originalValue = original[key];
|
|
59
|
+
let pristineValue: any;
|
|
60
|
+
|
|
61
|
+
if (original.type && original.id) {
|
|
62
|
+
pristine = this._pristineEntities[getPlural(original.type)]?.[original.id];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
pristineValue = pristine?.[key];
|
|
66
|
+
|
|
67
|
+
// When we haven't found a pristine entity even though we checked the pristine collection,
|
|
68
|
+
// we may be dealing with an entity that is added as a feature, such as an item. These are
|
|
69
|
+
// not in the pristine features entities but in their own set. Try to find the pristine
|
|
70
|
+
// entity by checking the features of the pristine locations.
|
|
71
|
+
if (!pristineValue && original.type && original.id) {
|
|
72
|
+
let feature: IFeature;
|
|
73
|
+
|
|
74
|
+
for (const location in this._pristineEntities.locations) {
|
|
75
|
+
feature = this._pristineEntities.locations[location].features?.find(f => f.id === original.id);
|
|
76
|
+
|
|
77
|
+
if (feature) {
|
|
78
|
+
pristineValue = feature[key];
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Do add NULL values to the clone for equipment, as these can be used as placeholders.
|
|
85
|
+
// This is used by the engine to determine which equipment slots are available.
|
|
86
|
+
if (originalValue === null) {
|
|
87
|
+
if (parentKey === 'equipment') {
|
|
88
|
+
clone[key] = null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (this.skipProperty(original, originalValue, key, parentKey)) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
let newValue = this.processArrayDeletedValuesAndProperties(originalValue);
|
|
99
|
+
|
|
100
|
+
this.getClonedValue({
|
|
101
|
+
clone: clone,
|
|
102
|
+
key: key,
|
|
103
|
+
original: original,
|
|
104
|
+
originalValue: newValue,
|
|
105
|
+
pristine: pristine,
|
|
106
|
+
pristineValue: pristineValue
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return clone;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private processArrayDeletedValuesAndProperties(originalValue: any,): any {
|
|
114
|
+
let newValue: any = originalValue;
|
|
115
|
+
|
|
116
|
+
// Add deleted entries to the array. Create a new array for this to prevent changing
|
|
117
|
+
// the one used at runtime. Also copy over additional properties that may be there,
|
|
118
|
+
// except for the array of deleted entities. This is handled differently.
|
|
119
|
+
if (Array.isArray(originalValue)) {
|
|
120
|
+
const deletedEntries = originalValue.getDeleted();
|
|
121
|
+
newValue = [...originalValue];
|
|
122
|
+
deletedEntries.forEach(e => newValue.push(e));
|
|
123
|
+
const additionalProperties = Object.keys(originalValue).filter(k => k !== '_deleted' && !newValue[k]);
|
|
124
|
+
additionalProperties.forEach(p => {
|
|
125
|
+
newValue[p] = originalValue[p];
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return newValue;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private readonly restoreArray = (value: any, loaded: any, key: string): boolean => {
|
|
133
|
+
if (!Array.isArray(value)) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
InitEntityCollection(loaded, key);
|
|
138
|
+
this.restoreObjects(value);
|
|
139
|
+
const entriesToDelete = value.filter(e => e?.[StateProperties.Deleted]);
|
|
140
|
+
|
|
141
|
+
entriesToDelete.forEach(e => {
|
|
142
|
+
value.delete(e);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
const arrayPropertyKey = `${key}_arrProps`;
|
|
146
|
+
const additionalArrayProperties = loaded[arrayPropertyKey];
|
|
147
|
+
|
|
148
|
+
if (additionalArrayProperties) {
|
|
149
|
+
Object.keys(additionalArrayProperties).forEach(k => {
|
|
150
|
+
// Use a cast to any to suppress the warning that named properties should be used
|
|
151
|
+
// with objects and numeric indexes with arrays.
|
|
152
|
+
(<any>value)[k] = additionalArrayProperties[k];
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
delete loaded[arrayPropertyKey];
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
private readonly createClone = (values: any): any => {
|
|
162
|
+
let clone: any;
|
|
163
|
+
|
|
164
|
+
if (Array.isArray(values)) {
|
|
165
|
+
clone = [];
|
|
166
|
+
} else {
|
|
167
|
+
clone = typeof values === 'object' ? {} : values;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return clone;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private readonly getClonedValue = (data: SerializationData): void => {
|
|
174
|
+
if (this.processDataRecord(data)) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (this.processArray(data)) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (typeof data.originalValue === 'object') {
|
|
183
|
+
if (Array.isArray(data.clone)) {
|
|
184
|
+
data.clone.push({});
|
|
185
|
+
} else {
|
|
186
|
+
data.clone[data.key] = {};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const createdObject = data.clone[data.key];
|
|
190
|
+
this.buildStructureForSerialization(createdObject, data.originalValue, data.pristineValue, data.key);
|
|
191
|
+
|
|
192
|
+
// Do not include empty objects in the serialized data.
|
|
193
|
+
if (!Array.isArray(data.clone) && typeof (createdObject) === 'object' && Object.keys(createdObject).length === 0) {
|
|
194
|
+
delete data.clone[data.key];
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (typeof data.originalValue === 'function') {
|
|
201
|
+
if (!data.originalValue.isProxy && data.originalValue.toString() !== data.pristineValue?.toString()) {
|
|
202
|
+
data.clone[data.key] = serializeFunction(data.originalValue);
|
|
203
|
+
}
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Store only values that are different from the pristine value, values that are needed to create a
|
|
208
|
+
// traversable world structure, and the key values of deleted array records.
|
|
209
|
+
if (data.originalValue != data.pristineValue || this.isKeyProperty(data.pristine, data.key) || data.original[StateProperties.Deleted] === true) {
|
|
210
|
+
data.clone[data.key] = data.originalValue;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
private readonly processDataRecord = (data: SerializationData): boolean => {
|
|
215
|
+
if (!isDataRecord(data.originalValue)) {
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
data.clone[data.key] = [];
|
|
220
|
+
const record = data.clone[data.key];
|
|
221
|
+
const match = data.pristine?.find((p: any[]) => p[0] === data.originalValue[0]);
|
|
222
|
+
|
|
223
|
+
if (!match) {
|
|
224
|
+
const value = data.originalValue[1];
|
|
225
|
+
|
|
226
|
+
if (value[StateProperties.Deleted]) {
|
|
227
|
+
record[0] = data.pristineValue[0];
|
|
228
|
+
record[1] = {[StateProperties.Deleted]: true};
|
|
229
|
+
return true;
|
|
230
|
+
} else if (typeof value === 'function' && value[StateProperties.Added]) {
|
|
231
|
+
record[0] = data.originalValue[0];
|
|
232
|
+
record[1] = {'function': serializeFunction(value), [StateProperties.Added]: true};
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
record[0] = data.originalValue[0];
|
|
238
|
+
|
|
239
|
+
this.getClonedValue(<SerializationData>{
|
|
240
|
+
clone: data.clone[data.key],
|
|
241
|
+
key: '1',
|
|
242
|
+
original: data.originalValue,
|
|
243
|
+
originalValue: data.originalValue[1],
|
|
244
|
+
pristine: match,
|
|
245
|
+
pristineValue: match?.[1]
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// Delete empty objects from the record.
|
|
249
|
+
if (record[1] && !Object.keys(record[1]).length) {
|
|
250
|
+
record.splice(1, 1);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
private readonly processArray = (data: SerializationData): boolean => {
|
|
257
|
+
if (!Array.isArray(data.originalValue)) {
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Do not include empty arrays in the serialized data when we have a pristine value to compare
|
|
262
|
+
// to. Otherwise, keep empty arrays as they are needed to correctly reconstruct the object.
|
|
263
|
+
if (data.pristine && !data.originalValue.length) {
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
data.clone[data.key] = [];
|
|
268
|
+
const resultArray = data.clone[data.key];
|
|
269
|
+
this.buildStructureForSerialization(resultArray, data.originalValue, data.pristineValue);
|
|
270
|
+
|
|
271
|
+
// Delete empty objects from the array. Only remove objects that have no properties or
|
|
272
|
+
// only properties with no values.
|
|
273
|
+
const emptyItems = resultArray.filter(e => {
|
|
274
|
+
if (typeof e !== 'object') {
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
return !Object.values(e).filter(v => v !== undefined && v !== null && v !== '').length;
|
|
278
|
+
});
|
|
279
|
+
emptyItems.forEach(e => resultArray.splice(resultArray.indexOf(e), 1));
|
|
280
|
+
|
|
281
|
+
const additionalArrayProperties = Object.keys(data.originalValue).filter(v => {
|
|
282
|
+
let isAdditionalProperty = isNaN(parseInt(v));
|
|
283
|
+
|
|
284
|
+
if (isAdditionalProperty) {
|
|
285
|
+
// add and delete proxies are used for location destinations, these are added in the LocationService.
|
|
286
|
+
// Ignore these proxies.
|
|
287
|
+
if (v === 'add' || v === 'delete') {
|
|
288
|
+
isAdditionalProperty = false;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return isAdditionalProperty;
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
additionalArrayProperties.forEach(p => {
|
|
296
|
+
const arrayPropertyKey = `${data.key}_arrProps`;
|
|
297
|
+
data.clone[arrayPropertyKey] = data.clone[arrayPropertyKey] || {};
|
|
298
|
+
this.getClonedValue({
|
|
299
|
+
clone: data.clone[arrayPropertyKey],
|
|
300
|
+
key: p,
|
|
301
|
+
original: data.originalValue,
|
|
302
|
+
originalValue: data.originalValue[p],
|
|
303
|
+
pristine: data.pristine,
|
|
304
|
+
pristineValue: data.pristineValue
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
return true;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
private readonly skipProperty = (original: any, originalValue: any, key: string, parentKey: string): boolean => {
|
|
312
|
+
return (
|
|
313
|
+
originalValue === undefined
|
|
314
|
+
|| originalValue.isProxy
|
|
315
|
+
// Exclude descriptions and conversation nodes from the save data, these are not present on the pristine data.
|
|
316
|
+
// Use an additional property to identify them, as their names are quite generic.
|
|
317
|
+
|| (original[IdProperty] && (key === DescriptionProperty || key === 'descriptions'))
|
|
318
|
+
|| (parentKey === 'conversation' && !this.conversationPropertiesToSerialize.includes(key))
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
private readonly isKeyProperty = (item: any, propertyName: string): boolean => {
|
|
323
|
+
const keyProperties = getKeyPropertyNames(item);
|
|
324
|
+
return keyProperties.first === propertyName || keyProperties.second === propertyName;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import {isEmpty} from '../utilityFunctions';
|
|
2
|
+
import {
|
|
3
|
+
IDataSerializer,
|
|
4
|
+
IDataService,
|
|
5
|
+
IDataSynchronizer,
|
|
6
|
+
IGame,
|
|
7
|
+
ILocalStorageService,
|
|
8
|
+
IRules,
|
|
9
|
+
ISaveGame,
|
|
10
|
+
PlayState
|
|
11
|
+
}from 'storyScript';
|
|
12
|
+
import {GameStateSave, SaveGamePrefix} from "src/constants";
|
|
13
|
+
|
|
14
|
+
export class DataService implements IDataService {
|
|
15
|
+
|
|
16
|
+
constructor(
|
|
17
|
+
private _localStorageService: ILocalStorageService,
|
|
18
|
+
private serializer: IDataSerializer,
|
|
19
|
+
private synchronizer: IDataSynchronizer,
|
|
20
|
+
private _rules: IRules,
|
|
21
|
+
private _gameNameSpace: string) {
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
save = <T>(key: string, value: T): void => {
|
|
25
|
+
const clone = this.serializer.createSerializableClone(value);
|
|
26
|
+
this._localStorageService.set(this.getKey(key), JSON.stringify({data: clone}));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
load = <T>(key: string): T => {
|
|
30
|
+
try {
|
|
31
|
+
const jsonData = this._localStorageService.get(this.getKey(key));
|
|
32
|
+
|
|
33
|
+
if (jsonData) {
|
|
34
|
+
const data = JSON.parse(jsonData).data;
|
|
35
|
+
|
|
36
|
+
if (isEmpty(data)) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const result = this.serializer.restoreObjects(data);
|
|
41
|
+
this.synchronizer.synchronizeEntityData(result);
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return null;
|
|
46
|
+
} catch (exception: any) {
|
|
47
|
+
console.log('No data loaded for key ' + key + '. Error: ' + exception.message);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
remove = (key: string): void => {
|
|
54
|
+
this._localStorageService.remove(this.getKey(key));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
saveGame = (game: IGame, name?: string): void => {
|
|
58
|
+
if (!game.started) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
name = name ? SaveGamePrefix + name : GameStateSave;
|
|
63
|
+
this._rules.general?.beforeSave?.(game);
|
|
64
|
+
|
|
65
|
+
const saveGame = <ISaveGame>{
|
|
66
|
+
party: game.party,
|
|
67
|
+
world: game.locations,
|
|
68
|
+
maps: game.maps,
|
|
69
|
+
worldProperties: game.worldProperties,
|
|
70
|
+
statistics: game.statistics,
|
|
71
|
+
playedAudio: game.sounds.playedAudio
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
this.save(name, saveGame);
|
|
75
|
+
|
|
76
|
+
if (game.playState === PlayState.Menu) {
|
|
77
|
+
game.playState = null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
this._rules.general.afterSave?.(game);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
getSaveKeys = (): string[] => this._localStorageService.getKeys(this.getKey(SaveGamePrefix));
|
|
84
|
+
|
|
85
|
+
hasGameState = (): boolean => {
|
|
86
|
+
const state = this._localStorageService.get(this.getKey(GameStateSave));
|
|
87
|
+
return state !== null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private getKey = (key?: string): string => {
|
|
91
|
+
return key ? `${this._gameNameSpace}_${key}` : this._gameNameSpace;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import {IDataSynchronizer}from 'storyScript';
|
|
2
|
+
import {StateProperties} from "src/Engine/stateProperties";
|
|
3
|
+
import {getPlural, isDataRecord, propertyMatch} from "src/Engine/utilityFunctions";
|
|
4
|
+
import {InitEntityCollection} from "src/Engine/EntityCreatorFunctions";
|
|
5
|
+
|
|
6
|
+
export class DataSynchronizer implements IDataSynchronizer {
|
|
7
|
+
constructor(private _pristineEntities: Record<string, Record<string, any>>) {
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
synchronizeEntityData = (
|
|
11
|
+
entity: any,
|
|
12
|
+
pristineEntity?: any,
|
|
13
|
+
parentEntity?: any,
|
|
14
|
+
pristineParentEntity?: any,
|
|
15
|
+
parentProperty?: string
|
|
16
|
+
): void => {
|
|
17
|
+
if (this.updateDataRecord(entity, pristineEntity)) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (this.updateArray(entity, pristineEntity, parentEntity, pristineParentEntity, parentProperty)) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (this.isEntity(entity)) {
|
|
26
|
+
if (pristineEntity === undefined) {
|
|
27
|
+
pristineEntity = this._pristineEntities[getPlural(entity.type)][entity.id];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// We don't have pristine entities for the 'world' and 'maps' properties of saved games.
|
|
32
|
+
// Use the pristine collections for these. When location and map files are deleted from
|
|
33
|
+
// the game, we need to remove them from the stored data as well.
|
|
34
|
+
if (parentProperty === 'world') {
|
|
35
|
+
pristineEntity = this._pristineEntities['locations'];
|
|
36
|
+
this.deleteRemovedEntities(entity, pristineEntity);
|
|
37
|
+
} else if (parentProperty === 'maps') {
|
|
38
|
+
pristineEntity = this._pristineEntities['maps'];
|
|
39
|
+
this.deleteRemovedEntities(entity, pristineEntity);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Use the properties of both the entity and the pristine entity, but only
|
|
43
|
+
// process them once.
|
|
44
|
+
let propertyNames = [...new Set(
|
|
45
|
+
((pristineEntity && Object.keys(pristineEntity)) ?? [])
|
|
46
|
+
.concat((entity && Object.keys(entity)) ?? []))];
|
|
47
|
+
|
|
48
|
+
propertyNames.forEach(p => {
|
|
49
|
+
let currentProperty = entity[p];
|
|
50
|
+
|
|
51
|
+
// If there is no pristine entity, recurse down the object to find entities to synchronize.
|
|
52
|
+
if (pristineEntity === undefined) {
|
|
53
|
+
if (Array.isArray(currentProperty) || typeof currentProperty === 'object') {
|
|
54
|
+
this.synchronizeEntityData(currentProperty, undefined, entity, pristineEntity, p);
|
|
55
|
+
}
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let pristineProperty = pristineEntity[p];
|
|
60
|
+
|
|
61
|
+
if (currentProperty === undefined) {
|
|
62
|
+
if (Array.isArray(pristineProperty)) {
|
|
63
|
+
entity[p] = [];
|
|
64
|
+
} else if (typeof pristineProperty === 'object' && pristineProperty !== null) {
|
|
65
|
+
entity[p] = {};
|
|
66
|
+
} else {
|
|
67
|
+
// If there is no value for this property on the object yet,
|
|
68
|
+
// it wasn't changed during runtime. Set it now.
|
|
69
|
+
entity[p] = pristineProperty;
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
currentProperty = entity[p];
|
|
75
|
+
|
|
76
|
+
if (pristineProperty === undefined) {
|
|
77
|
+
if (Array.isArray(currentProperty)) {
|
|
78
|
+
pristineProperty = [];
|
|
79
|
+
} else if (typeof currentProperty === 'object') {
|
|
80
|
+
pristineProperty = {};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (Array.isArray(pristineProperty) || typeof pristineProperty === 'object') {
|
|
85
|
+
this.synchronizeEntityData(currentProperty, pristineProperty, entity, pristineEntity, p);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Either the property is the same on the entity and the pristine entity (a key value)
|
|
89
|
+
// or the value was changed at runtime. Either way, do nothing.
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private updateDataRecord = (entity: any, pristineEntity: any): boolean => {
|
|
94
|
+
if (!isDataRecord(pristineEntity?.[0]) && !isDataRecord(entity?.[0])) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
pristineEntity.forEach((p: any[]) => {
|
|
99
|
+
const match = entity.concat(entity.getDeleted()).find((e: any[]) => e[0] === p[0]);
|
|
100
|
+
|
|
101
|
+
if (!match) {
|
|
102
|
+
// If there is no match, it means the record was added during design-time.
|
|
103
|
+
// Add it to the entity as well.
|
|
104
|
+
entity.push(p);
|
|
105
|
+
} else if (match[StateProperties.Deleted]) {
|
|
106
|
+
return;
|
|
107
|
+
} else if (typeof (p[1]) === 'function') {
|
|
108
|
+
match[1] = p[1];
|
|
109
|
+
} else {
|
|
110
|
+
if (!match[1]) {
|
|
111
|
+
match.push(p?.[1] && Array.isArray(p[1]) ? [] : {});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
this.synchronizeEntityData(match[1], p[1], entity, pristineEntity, '1');
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const newRecords = entity.filter((e: any[]) => !pristineEntity.find(p => p[0] === e[0]));
|
|
119
|
+
const recordsToRemove = newRecords.filter((e: any[]) => !e[1]?.[StateProperties.Added]);
|
|
120
|
+
recordsToRemove.forEach(r => entity.splice(entity.indexOf(r), 1));
|
|
121
|
+
const recordsToKeep = newRecords.filter((e: any[]) => e[1]?.[StateProperties.Added]);
|
|
122
|
+
recordsToKeep.forEach(r => {
|
|
123
|
+
if (r[1].function) {
|
|
124
|
+
r[1] = r[1].function;
|
|
125
|
+
r[1][StateProperties.Added] = true;
|
|
126
|
+
}
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
this.markEntriesAsDeleted(entity);
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private updateArray(
|
|
134
|
+
entity: any[],
|
|
135
|
+
pristineEntity: any[],
|
|
136
|
+
parentEntity?: any,
|
|
137
|
+
pristineParentEntity?: any,
|
|
138
|
+
parentProperty?: string
|
|
139
|
+
): boolean {
|
|
140
|
+
if (!Array.isArray(entity) || !Array.isArray(pristineEntity)) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (entity.length == 0 && pristineEntity.length == 0) {
|
|
145
|
+
// Empty arrays weren't included in the serialized data. Call InitEntityCollection
|
|
146
|
+
// on them now to properly restore entities.
|
|
147
|
+
InitEntityCollection(parentEntity, parentProperty);
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const existingItems = entity.filter(e => e[StateProperties.Added] && !pristineEntity.find(p => propertyMatch(e, p)));
|
|
152
|
+
const matchedItems = pristineEntity.filter(e => entity.find(p => propertyMatch(e, p)));
|
|
153
|
+
const itemsToAdd = pristineEntity.filter(e => !entity.concat(entity.getDeleted()).find(p => propertyMatch(e, p)));
|
|
154
|
+
const finalItems = [];
|
|
155
|
+
|
|
156
|
+
// matched and existing items need to be recursively checked. Added items aren't in the stored data yet, so they
|
|
157
|
+
// can be added straight away. The order is very important! Matched items are in both and need to go first. Added
|
|
158
|
+
// items second, as these are now part of the design. Existing items have been added at runtime and should go last.
|
|
159
|
+
matchedItems.forEach(e => {
|
|
160
|
+
finalItems.push(this.updateArrayElement(e, entity, parentEntity, pristineParentEntity, parentProperty));
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
itemsToAdd.forEach(i => finalItems.push(i));
|
|
164
|
+
|
|
165
|
+
existingItems.forEach(e => {
|
|
166
|
+
finalItems.push(this.updateArrayElement(e, entity, parentEntity, pristineParentEntity, parentProperty));
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
entity.length = 0;
|
|
170
|
+
|
|
171
|
+
finalItems.forEach(i => {
|
|
172
|
+
entity.push(typeof i === 'object' ? {...i} : i);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
this.markEntriesAsDeleted(entity);
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private updateArrayElement = (element: any, entity: any, parentEntity, pristineParentEntity, parentProperty) => {
|
|
180
|
+
const currentValue = entity.filter(p => propertyMatch(element, p))[0];
|
|
181
|
+
|
|
182
|
+
// In case of an entity with the 'added' flag, 'i' is a skeleton value. Set the pristine entity to 'undefined'
|
|
183
|
+
// so it will be looked up again later on.
|
|
184
|
+
const pristineValue = element[StateProperties.Added] && this.isEntity(element) ? undefined : element;
|
|
185
|
+
|
|
186
|
+
if (!currentValue[StateProperties.Deleted]) {
|
|
187
|
+
this.synchronizeEntityData(currentValue, pristineValue, parentEntity, pristineParentEntity, parentProperty);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return currentValue;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
private isEntity = (entity: any): boolean => {
|
|
194
|
+
return entity?.type !== undefined && entity?.id !== undefined;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private markEntriesAsDeleted = (item: any[]) => {
|
|
198
|
+
const itemsToDelete = item.filter(i => i[StateProperties.Deleted]);
|
|
199
|
+
itemsToDelete.forEach(i => item.delete(i));
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private deleteRemovedEntities = (entity: any, pristineEntity: any): void => {
|
|
203
|
+
if (!pristineEntity) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const entityKeys = Object.keys(entity);
|
|
208
|
+
const pristineKeys = Object.keys(pristineEntity);
|
|
209
|
+
|
|
210
|
+
entityKeys.forEach(k => {
|
|
211
|
+
if (pristineKeys.indexOf(k) < 0) {
|
|
212
|
+
delete entity[k];
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|