diff --git a/README.md b/README.md index f0a4aae42..54cd962d9 100644 --- a/README.md +++ b/README.md @@ -2,20 +2,5 @@ Valkyrie GM for Fantasy Flight Board Games -Inlcudes editor for user built scenarios/quests. - -Download here: -https://github.com/NPBruce/valkyrie/releases - -Currently supports: -* Descent: Journeys in the Dark (Second Edition) which requires Descent: Road to Legend to be installed. One quest included. -* Mansions of Madness Second Edition which requires the Mansions of Madness app to be installed. - -FFG apps can be downloaded from steam: - -* __Descent: Road to Legend:__ http://store.steampowered.com/app/477200/ -* __Mansions of Madness:__ http://store.steampowered.com/app/478980/ - -# NOTE -This is being made obsolete, in future please use this link: +Information: https://github.com/NPBruce/valkyrie/wiki diff --git a/Release Checklist.txt b/Release Checklist.txt deleted file mode 100644 index 79ee2341f..000000000 --- a/Release Checklist.txt +++ /dev/null @@ -1,32 +0,0 @@ -PreRelease: -1: Update version number -2: Update questdata version (if required) -3: build1.bat -4: Build (unity) -5: build2.bat -6: Test -7: Tag -9: Write changelog -10: Release - -Stable Release: -1: Merge to master from PreRelease tag -2: Update version number -3: build1.bat -4: Build (unity) -5: build2.bat -6: Tag -7: Write changelog -8: Release - -Bugfix Release: -1: Merge to master from fix branch -2: Update questdata version (if required) -2: Update version number -3: build1.bat -4: Build (unity) -5: build2.bat -6: Test -7: Tag -8: Write changelog -9: Release diff --git a/build.bat b/build.bat index e2424416b..87a0d9aae 100644 --- a/build.bat +++ b/build.bat @@ -47,16 +47,6 @@ rem duplicate licence to macos/linux xcopy /I /E /Y build\batch build\batchMac\Valkyrie.app xcopy /I /E /Y build\batch build\batchLinux\valkyrie-linux-%version% -rem copy content from source to win release -xcopy /E /Y content build\batch\valkyrie_Data\content -rem remove imported content -rmdir /s /q build\batch\valkyrie_Data\content\D2E\ffg -rmdir /s /q build\batch\valkyrie_Data\content\MoM\ffg -rem copy content from win to macos -xcopy /I /E /Y build\batch\valkyrie_Data build\batchMac\Valkyrie.app\Contents -rem copy content from win to linux -xcopy /I /E /Y build\batch\valkyrie_Data build\batchLinux\valkyrie-linux-%version%\valkyrie_Data - rem copy over windows build xcopy /E /Y build\win build\batch rem copy over macos build @@ -65,15 +55,12 @@ rem copy over linux build xcopy /E /Y build\linux build\batchLinux\valkyrie-linux-%version% rem delete previous build -del build\valkyrie-win-%version%.zip +del build\valkyrie-windows-%version%.exe del build\valkyrie-macos-%version%.zip del build\valkyrie-linux-%version%.tar.gz -rem create windows zip -cd build\batch -"C:\Program Files\7-Zip\7z.exe" a ..\valkyrie-win-%version%.zip * -r rem create macos zip -cd ..\batchMac +cd build\batchMac "C:\Program Files\7-Zip\7z.exe" a ..\valkyrie-macos-%version%.zip * -r rem create linux tar ball cd ..\batchLinux @@ -82,3 +69,12 @@ cd .. "C:\Program Files\7-Zip\7z.exe" a valkyrie-linux-%version%.tar.gz valkyrie-linux-%version%.tar del valkyrie-linux-%version%.tar cd .. + +set /a num=%version:~-1% 2>nul + +if "%num%"=="%version:~-1%" ( + "C:\Program Files (x86)\NSIS\makensis" /DVERSION=%version% valkyrie.nsi + goto :EOF +) + +"C:\Program Files (x86)\NSIS\makensis" /DVERSION=%version% /DPRERELEASE valkyrie.nsi diff --git a/content/MoM/base/dunwich_data.ini b/content/MoM/base/dunwich_data.ini deleted file mode 100644 index bd9ac7e12..000000000 --- a/content/MoM/base/dunwich_data.ini +++ /dev/null @@ -1,224 +0,0 @@ -[HorrorDunwichHorror01] -monster=DunwichHorror -text={ffg:DUNWICH_HORROR_01} - -[HorrorDunwichHorror02] -monster=DunwichHorror -text={ffg:DUNWICH_HORROR_02} - -[HorrorDunwichHorror03] -monster=DunwichHorror -text={ffg:DUNWICH_HORROR_03} - -[HorrorDunwichHorror04] -monster=DunwichHorror -text={ffg:DUNWICH_HORROR_04} - -[HorrorDunwichHorror05] -monster=DunwichHorror -text={ffg:DUNWICH_HORROR_05} - -[HorrorDunwichHorror06] -monster=DunwichHorror -text={ffg:DUNWICH_HORROR_06} - -[HorrorDunwichHorror07] -monster=DunwichHorror -text={ffg:DUNWICH_HORROR_07} - -[HorrorDunwichHorror08] -monster=DunwichHorror -text={ffg:DUNWICH_HORROR_08} - -[HorrorDunwichHorror09] -monster=DunwichHorror -text={ffg:DUNWICH_HORROR_09} - -[HorrorDunwichHorror10] -monster=DunwichHorror -text={ffg:DUNWICH_HORROR_10} - -[HorrorDunwichHorror11] -monster=DunwichHorror -text={ffg:DUNWICH_HORROR_11} - -[HorrorDunwichHorror12] -monster=DunwichHorror -text={ffg:DUNWICH_HORROR_12} - -[EvadeDunwichHorror01] -monster=DunwichHorror -text={ffg:DUNWICH_EVADE_01} - -[EvadeDunwichHorror02] -monster=DunwichHorror -text={ffg:DUNWICH_EVADE_02} - -[EvadeDunwichHorror03] -monster=DunwichHorror -text={ffg:DUNWICH_EVADE_03} - -[EvadeDunwichHorror04] -monster=DunwichHorror -text={ffg:DUNWICH_EVADE_04} - -[EvadeDunwichHorror05] -monster=DunwichHorror -text={ffg:DUNWICH_EVADE_05} - -[EvadeDunwichHorror06] -monster=DunwichHorror -text={ffg:DUNWICH_EVADE_06} - -[EvadeDunwichHorror07] -monster=DunwichHorror -text={ffg:DUNWICH_EVADE_07} - -[EvadeDunwichHorror08] -monster=DunwichHorror -text={ffg:DUNWICH_EVADE_08} - -[EvadeDunwichHorror09] -monster=DunwichHorror -text={ffg:DUNWICH_EVADE_09} - -[EvadeDunwichHorror10] -monster=DunwichHorror -text={ffg:DUNWICH_EVADE_10} - -[EvadeDunwichHorror11] -monster=DunwichHorror -text={ffg:DUNWICH_EVADE_11} - -[EvadeDunwichHorror12] -monster=DunwichHorror -text={ffg:DUNWICH_EVADE_12} - -[MonsterActivationDunwichHorror01] -ability={ffg:DUNWICH_MOVE_01} -master={ffg:DUNWICH_ATTACK_01} -movebutton={ffg:UI_NOT_IN_SPACE} - -[MonsterActivationDunwichHorror02] -ability={ffg:DUNWICH_MOVE_02} -master={ffg:DUNWICH_ATTACK_02} -movebutton={ffg:UI_NOT_WITHIN_1} - -[MonsterActivationDunwichHorror03] -ability={ffg:DUNWICH_MOVE_03} -master={ffg:DUNWICH_ATTACK_03} -movebutton={ffg:UI_NOT_WITHIN_MOVE} -move={ffg:MONSTER_COMMON_MOVE_02} - -[MonsterActivationDunwichHorror04] -ability={ffg:DUNWICH_MOVE_04} -master={ffg:DUNWICH_ATTACK_04} -movebutton={ffg:UI_NOT_WITHIN_MOVE} -move={ffg:MONSTER_COMMON_MOVE_02} - -[MonsterActivationDunwichHorror05] -ability={ffg:DUNWICH_MOVE_05} -master={ffg:DUNWICH_ATTACK_05} -movebutton={ffg:UI_NOT_WITHIN_MOVE} -move={ffg:MONSTER_COMMON_MOVE_02} - -[MonsterActivationDunwichHorror06] -ability={ffg:DUNWICH_MOVE_06} -master={ffg:DUNWICH_ATTACK_06} -movebutton={ffg:UI_NOT_WITHIN_MOVE} -move={ffg:MONSTER_COMMON_MOVE_02} - -[MonsterActivationDunwichHorror07] -ability={ffg:DUNWICH_MOVE_07} -master={ffg:DUNWICH_ATTACK_07} -movebutton={ffg:UI_NOT_WITHIN_MOVE} -move={ffg:MONSTER_COMMON_MOVE_02} - -[MonsterActivationDunwichHorror08] -ability={ffg:DUNWICH_MOVE_08} -master={ffg:DUNWICH_ATTACK_08} -movebutton={ffg:UI_NOT_WITHIN_1} - -[MonsterActivationDunwichHorror09] -ability={ffg:DUNWICH_MOVE_09} -master={ffg:DUNWICH_ATTACK_09} -movebutton={ffg:UI_NOT_WITHIN_1} - -[MonsterActivationDunwichHorror10] -ability={ffg:DUNWICH_MOVE_10} -master={ffg:DUNWICH_ATTACK_10} -movebutton={ffg:UI_NOT_WITHIN_1} - -[MonsterActivationDunwichHorror11] -ability={ffg:DUNWICH_MOVE_11} -master={ffg:DUNWICH_ATTACK_11} -movebutton={ffg:UI_NOT_WITHIN_1} - -[MonsterActivationDunwichHorror12] -ability={ffg:DUNWICH_MOVE_12} -master={ffg:DUNWICH_ATTACK_12} -movebutton={ffg:UI_NOT_WITHIN_1} - -[MonsterActivationDunwichHorror13] -ability={ffg:DUNWICH_MOVE_13} -master={ffg:DUNWICH_ATTACK_13} -movebutton={ffg:UI_NOT_IN_SPACE} - -[MonsterActivationDunwichHorror14] -ability={ffg:DUNWICH_MOVE_14} -master={ffg:DUNWICH_ATTACK_14} -movebutton={ffg:UI_NOT_IN_SPACE} - -[MonsterActivationDunwichHorror15] -ability={ffg:DUNWICH_MOVE_15} -master={ffg:DUNWICH_ATTACK_15} -movebutton={ffg:UI_NOT_WITHIN_1} - -[MonsterActivationDunwichHorror16] -ability={ffg:DUNWICH_MOVE_16} -master={ffg:DUNWICH_ATTACK_16} -movebutton={ffg:UI_NOT_WITHIN_1} - -[MonsterActivationDunwichHorror17] -ability={ffg:DUNWICH_MOVE_17} -master={ffg:DUNWICH_ATTACK_17} -movebutton={ffg:UI_NOT_WITHIN_1} - -[MonsterActivationDunwichHorror18] -ability={ffg:DUNWICH_MOVE_18} -master={ffg:DUNWICH_ATTACK_18} -movebutton={ffg:UI_NOT_WITHIN_1} - -[MonsterActivationDunwichHorror19] -ability={ffg:DUNWICH_MOVE_19} -master={ffg:DUNWICH_ATTACK_19} -movebutton={ffg:UI_NOT_WITHIN_1} - -[MonsterActivationDunwichHorror20] -ability={ffg:DUNWICH_MOVE_20} -master={ffg:DUNWICH_ATTACK_20} -movebutton={ffg:UI_NOT_IN_SPACE} - -[MonsterActivationDunwichHorror21] -ability={ffg:DUNWICH_MOVE_21} -master={ffg:DUNWICH_ATTACK_21} -movebutton={ffg:UI_NOT_WITHIN_MOVE} -move={ffg:MONSTER_COMMON_MOVE_02} - -[MonsterActivationDunwichHorror22] -ability={ffg:DUNWICH_MOVE_22} -master={ffg:DUNWICH_ATTACK_22} -movebutton={ffg:UI_NOT_WITHIN_MOVE} -move={ffg:MONSTER_COMMON_MOVE_02} - -[MonsterActivationDunwichHorror23] -ability={ffg:DUNWICH_MOVE_23} -master={ffg:DUNWICH_ATTACK_23} -movebutton={ffg:UI_NOT_WITHIN_MOVE} -move={ffg:MONSTER_COMMON_MOVE_02} - -[MonsterActivationDunwichHorror24] -ability={ffg:DUNWICH_MOVE_24} -master={ffg:DUNWICH_ATTACK_24} -movebutton={ffg:UI_NOT_WITHIN_MOVE} -move={ffg:MONSTER_COMMON_MOVE_02} diff --git a/content/MoM/base/investigators1E.ini b/content/MoM/base/investigators1E.ini deleted file mode 100644 index 46024f3b6..000000000 --- a/content/MoM/base/investigators1E.ini +++ /dev/null @@ -1,41 +0,0 @@ -[HeroMichaelMcGlen] -name={ffg:INVESTIGATOR_MICHAEL_MCGLEN} -image="{import}/img/Circle_Investigator_Michael_McGlen.dds" -traits=male - -[HeroAshcanPete] -name={ffg:INVESTIGATOR_ASHCAN_PETE} -image="{import}/img/Circle_Investigator_Ashcan_Pete.dds" -traits=male -item=ItemUniqueDukeTheDog - -[HeroGloriaGoldberg] -name={ffg:INVESTIGATOR_GLORIA_GOLDBERG} -image="{import}/img/Circle_Investigator_Gloria_Goldberg.dds" -traits=female - -[HeroHarveyWalters] -name={ffg:INVESTIGATOR_HARVEY_WALTERS} -image="{import}/img/Circle_Investigator_Harvey_Walters.dds" -traits=male - -[HeroJennyBarnes] -name={ffg:INVESTIGATOR_JENNY_BARNES} -image="{import}/img/Circle_Investigator_Jenny_Barnes.dds" -traits=female - -[HeroJoeDiamond] -name={ffg:INVESTIGATOR_JOE_DIAMOND} -image="{import}/img/Circle_Investigator_Joe_Diamond.dds" -traits=male - -[HeroKateWinthrop] -name={ffg:INVESTIGATOR_KATE_WINTHROP} -image="{import}/img/Circle_Investigator_Kate_Winthrop.dds" -traits=female -item=ItemUniqueFluxStabilizer - -[HeroSisterMary] -name={ffg:INVESTIGATOR_SISTER_MARY} -image="{import}/img/Circle_Investigator_Sister_Mary.dds" -traits=female diff --git a/content/MoM/base/investigatorsCotW.ini b/content/MoM/base/investigatorsCotW.ini deleted file mode 100644 index 844bfe26f..000000000 --- a/content/MoM/base/investigatorsCotW.ini +++ /dev/null @@ -1,19 +0,0 @@ -[HeroAmandaSharpe] -name={ffg:INVESTIGATOR_AMANDA_SHARPE} -image="{import}/img/Circle_Investigator_Amanda_Sharpe.dds" -traits=female - -[HeroBobJenkins] -name={ffg:INVESTIGATOR_BOB_JENKINS} -image="{import}/img/Circle_Investigator_Bob_Jenkins.dds" -traits=male - -[HeroMandyThompson] -name={ffg:INVESTIGATOR_MANDY_THOMPSON} -image="{import}/img/Circle_Investigator_Mandy_Thompson.dds" -traits=female - -[HeroMontereyJack] -name={ffg:INVESTIGATOR_MONTEREY_JACK} -image="{import}/img/Circle_Investigator_Monterey_Jack.dds" -traits=male diff --git a/content/MoM/base/investigatorsFA.ini b/content/MoM/base/investigatorsFA.ini deleted file mode 100644 index 8173abbce..000000000 --- a/content/MoM/base/investigatorsFA.ini +++ /dev/null @@ -1,19 +0,0 @@ -[HeroCarolynFern] -name={ffg:INVESTIGATOR_CAROLYN_FERN} -image="{import}/img/Circle_Investigator_Carolyn_Fern.dds" -traits=female - -[HeroDarrellSimmons] -name={ffg:INVESTIGATOR_DARRELL_SIMMONS} -image="{import}/img/Circle_Investigator_Darrell_Simmons.dds" -traits=male - -[HeroDexterDrake] -name={ffg:INVESTIGATOR_DEXTER_DRAKE} -image="{import}/img/Circle_Investigator_Dexter_Drake.dds" -traits=male - -[HeroVincentLee] -name={ffg:INVESTIGATOR_VINCENT_LEE} -image="{import}/img/Circle_Investigator_Vincent_Lee.dds" -traits=male diff --git a/content/MoM/base/monsters1E.ini b/content/MoM/base/monsters1E.ini deleted file mode 100644 index 468f68a28..000000000 --- a/content/MoM/base/monsters1E.ini +++ /dev/null @@ -1,104 +0,0 @@ -[MonsterZombie] -; Optional name for display (if spaces or other formatting required) -name={ffg:MONSTER_ZOMBIE} -; image to display -image="{import}/img/Monster_Zombie.dds" -; This will probably need some work to standardise -traits=undead small humanoid -; optional priority, allows content packs to duplicate content and replace items -; priority=0 -; activation= -; Optional health modifier from number of players -health=2 -healthperhero=1 - -[MonsterCthonian] -; Optional name for display (if spaces or other formatting required) -name={ffg:MONSTER_CTHONIAN} -; image to display -image="{import}/img/Monster_Cthonian.dds" -; optional priority, allows content packs to duplicate content and replace items -; priority=0 -; activation= -; Optional health modifier from number of players -health=3 -healthperhero=2 -traits=beast - -[MonsterHoundOfTindalos] -; Optional name for display (if spaces or other formatting required) -name={ffg:MONSTER_HOUND_OF_TINDALOS} -; image to display -image="{import}/img/Monster_Hound_Of_Tindalos.dds" -; optional priority, allows content packs to duplicate content and replace items -; priority=0 -; activation= -; Optional health modifier from number of players -health=1 -healthperhero=2 -traits=beast - -[MonsterManiac] -; Optional name for display (if spaces or other formatting required) -name={ffg:MONSTER_MANIAC} -; image to display -image="{import}/img/Monster_Maniac.dds" -; optional priority, allows content packs to duplicate content and replace items -; priority=0 -; activation= -; Optional health modifier from number of players -health=2 -healthperhero=1 -traits=humanoid - -[MonsterMiGo] -; Optional name for display (if spaces or other formatting required) -name={ffg:MONSTER_MI_GO} -; image to display -image="{import}/img/Monster_Mi_Go.dds" -; optional priority, allows content packs to duplicate content and replace items -; priority=0 -; activation= -; Optional health modifier from number of players -health=3 -healthperhero=1 -traits=beast - -[MonsterShoggoth] -; Optional name for display (if spaces or other formatting required) -name={ffg:MONSTER_SHOGGOTH} -; image to display -image="{import}/img/Monster_Shoggoth.dds" -; optional priority, allows content packs to duplicate content and replace items -; priority=0 -; activation= -; Optional health modifier from number of players -health=4 -healthperhero=2 -traits=beast - -[MonsterWitch] -; Optional name for display (if spaces or other formatting required) -name={ffg:MONSTER_WITCH} -; image to display -image="{import}/img/Monster_Witch.dds" -; optional priority, allows content packs to duplicate content and replace items -; priority=0 -; activation= -; Optional health modifier from number of players -health=2 -healthperhero=1 -traits=humanoid - -[MonsterCultLeader] -; Optional name for display (if spaces or other formatting required) -name={ffg:UNIQUE_MONSTER_CULT_LEADER} -; image to display -image="{import}/img/Monster_Cult_Leader.dds" -; optional priority, allows content packs to duplicate content and replace items -; priority=0 -; activation= -; Optional health modifier from number of players -health=8 -healthperhero=4 -traits=humanoid diff --git a/content/MoM/base/monstersCotW.ini b/content/MoM/base/monstersCotW.ini deleted file mode 100644 index 79e6adf1d..000000000 --- a/content/MoM/base/monstersCotW.ini +++ /dev/null @@ -1,90 +0,0 @@ -[MonsterChildOfTheGoat] -; Optional name for display (if spaces or other formatting required) -name={ffg:MONSTER_CHILD_OF_THE_GOAT} -; image to display -image="{import}/img/Monster_Child_Of_The_Goat.dds" -; optional priority, allows content packs to duplicate content and replace items -; priority=0 -; activation= -; Optional health modifier from number of players -health=1 -healthperhero=1 -traits=humanoid - -[MonsterDarkDruid] -; Optional name for display (if spaces or other formatting required) -name={ffg:MONSTER_DARK_DRUID} -; image to display -image="{import}/img/Monster_Dark_Druid.dds" -; optional priority, allows content packs to duplicate content and replace items -; priority=0 -; activation= -; Optional health modifier from number of players -health=2 -healthperhero=1 -traits=humanoid - -[MonsterDarkYoung] -; Optional name for display (if spaces or other formatting required) -name={ffg:MONSTER_DARK_YOUNG} -; image to display -image="{import}/img/Monster_Dark_Young.dds" -; optional priority, allows content packs to duplicate content and replace items -; priority=0 -; activation= -; Optional health modifier from number of players -health=4 -healthperhero=2 -traits=beast - -[MonsterGoatSpawn] -; Optional name for display (if spaces or other formatting required) -name={ffg:MONSTER_GOAT_SPAWN} -; image to display -image="{import}/img/Monster_Goat_Spawn.dds" -; optional priority, allows content packs to duplicate content and replace items -; priority=0 -; activation= -; Optional health modifier from number of players -health=1 -healthperhero=2 -traits=humanoid - -[MonsterNightgaunt] -; Optional name for display (if spaces or other formatting required) -name={ffg:MONSTER_NIGHTGAUNT} -; image to display -image="{import}/img/Monster_Nightgaunt.dds" -; optional priority, allows content packs to duplicate content and replace items -; priority=0 -; activation= -; Optional health modifier from number of players -health=1 -healthperhero=2 -traits=beast - -[MonsterDunwichHorror] -; Optional name for display (if spaces or other formatting required) -name={ffg:UNIQUE_MONSTER_DUNWICH_HORROR} -; image to display -image="{import}/img/Monster_Dunwich_Horror.dds" -; optional priority, allows content packs to duplicate content and replace items -; priority=0 -; activation= -; Optional health modifier from number of players -health=2 -healthperhero=3 -traits=horror - -[MonsterWizard] -; Optional name for display (if spaces or other formatting required) -name={ffg:UNIQUE_MONSTER_WIZARD} -; image to display -image="{import}/img/Monster_Wizard.dds" -; optional priority, allows content packs to duplicate content and replace items -; priority=0 -; activation= -; Optional health modifier from number of players -health=2 -healthperhero=1 -traits=humanoid diff --git a/content/MoM/base/monstersFA.ini b/content/MoM/base/monstersFA.ini deleted file mode 100644 index 4a9983ca5..000000000 --- a/content/MoM/base/monstersFA.ini +++ /dev/null @@ -1,25 +0,0 @@ -[MonsterByakhee] -; Optional name for display (if spaces or other formatting required) -name={ffg:MONSTER_BYAKHEE} -; image to display -image="{import}/img/Monster_Byakhee.dds" -; optional priority, allows content packs to duplicate content and replace items -; priority=0 -; activation= -; Optional health modifier from number of players -health=1 -healthperhero=2 -traits=beast - -[MonsterCrawlingOne] -; Optional name for display (if spaces or other formatting required) -name={ffg:MONSTER_CRAWLING_ONE} -; image to display -image="{import}/img/Monster_Crawling_One.dds" -; optional priority, allows content packs to duplicate content and replace items -; priority=0 -; activation= -; Optional health modifier from number of players -health=3 -healthperhero=1 -traits=beast diff --git a/libraries/FFGAppImport/AssetImport/FetchContent.cs b/libraries/FFGAppImport/AssetImport/FetchContent.cs index 3c37c5fa0..8a6dadf34 100644 --- a/libraries/FFGAppImport/AssetImport/FetchContent.cs +++ b/libraries/FFGAppImport/AssetImport/FetchContent.cs @@ -150,6 +150,10 @@ public void Import() { Directory.Delete(Path.GetTempPath() + "Valkyrie/Obb", true); } + + // Find any streaming asset files + string[] streamFiles = Directory.GetFiles(finder.location + "/StreamingAssets", "*", SearchOption.AllDirectories); + ImportStreamAssets(streamFiles); } // Import one assets file @@ -443,13 +447,39 @@ private void ExportText(Unity_Studio.AssetPreloadData asset) writer.Close(); } - // Run monster data extration tool if in dev - if (importData.editor && asset.Text.Equals("Localization")) + // Need to trim new lines from old D2E format + if (asset.Text.Equals("Localization")) { - if (finder is MoMFinder) + string[] locFix = File.ReadAllLines(fileName); + List locOut = new List(); + string currentLine = ""; + for (int i = 0; i < locFix.Length; i++) { - ExtractDataTool.MoM(m_TextAsset.Deobfuscate(finder.ObfuscateKey()), contentPath); + if (locFix[i].Split('\"').Length % 2 == 0) + { + if (currentLine.Length == 0) + { + currentLine = locFix[i]; + } + else + { + locOut.Add(currentLine + "\\n" + locFix[i]); + currentLine = ""; + } + } + else + { + if (currentLine.Length == 0) + { + locOut.Add(locFix[i]); + } + else + { + currentLine += "\\n" + locFix[i]; + } + } } + File.WriteAllLines(fileName, locOut.ToArray()); } } @@ -534,5 +564,39 @@ public static bool VersionNewer(string oldVersion, string newVersion) } return false; } + + /// + /// Find asset bundles and create a list of them in a file. Invalid files ignored. + /// + /// + protected void ImportStreamAssets(string[] streamFiles) + { + List bundles = new List(); + + foreach (string file in streamFiles) + { + try + { + using (FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read)) + { + byte[] buffer = new byte[8]; + fs.Read(buffer, 0, buffer.Length); + string header = System.Text.Encoding.Default.GetString(buffer); + fs.Close(); + if (header.IndexOf("UnityFS") == 0) + { + bundles.Add(file); + } + } + } + catch (System.Exception) + { + continue; + } + } + + // We can't extract these here because this isn't the main thread and unity doesn't handle that + File.WriteAllLines(contentPath + "/bundles.txt", bundles.ToArray()); + } } } \ No newline at end of file diff --git a/libraries/FFGAppImport/AssetImport/MoMFinder.cs b/libraries/FFGAppImport/AssetImport/MoMFinder.cs index 15aa59fdc..617e45b0d 100644 --- a/libraries/FFGAppImport/AssetImport/MoMFinder.cs +++ b/libraries/FFGAppImport/AssetImport/MoMFinder.cs @@ -13,7 +13,7 @@ public MoMFinder(Platform p) : base(p) // If the installed app isn't this or higher don't import override public string RequiredFFGVersion() { - return "1.3.0"; + return "1.3.6"; } // Steam app ID override public string AppId() diff --git a/libraries/FFGAppImport/FFGAppImport.cs b/libraries/FFGAppImport/FFGAppImport.cs index 28555585e..e18f54b4f 100644 --- a/libraries/FFGAppImport/FFGAppImport.cs +++ b/libraries/FFGAppImport/FFGAppImport.cs @@ -7,7 +7,7 @@ namespace FFGAppImport { public class FFGImport { - public static int version = 1; + public static int version = 2; public GameType type; public Platform platform; public string path; diff --git a/libraries/FFGAppImport/FFGAppImport.csproj b/libraries/FFGAppImport/FFGAppImport.csproj index ee6fd24e3..05cf83222 100644 --- a/libraries/FFGAppImport/FFGAppImport.csproj +++ b/libraries/FFGAppImport/FFGAppImport.csproj @@ -42,6 +42,10 @@ + + False + ..\..\..\..\..\..\..\Program Files\Unity\Editor\Data\Managed\UnityEngine.dll + diff --git a/libraries/MoMInjection/Class1.cs b/libraries/MoMInjection/Class1.cs new file mode 100644 index 000000000..f451127ac --- /dev/null +++ b/libraries/MoMInjection/Class1.cs @@ -0,0 +1,30 @@ +using UnityEngine; +using System.Collections.Generic; +using System.IO; + +namespace FFG.MoM +{ + public class Class1 + { + public static string Get(string key) + { + List outText = new List(); + outText.Add("Set: " + Localization.localizationHasBeenSet); + if (Localization.loadFunction == null) + { + outText.Add("Function Null"); + } + TextAsset textAsset = Resources.Load("Localization"); + if (textAsset != null) + { + outText.Add("Size: " + textAsset.bytes.Length); + } + + File.WriteAllLines("C:\\Users\\Bruce\\Desktop\\inject.txt", outText.ToArray()); + + return ""; + //return Localization.Get(key); + } + + } +} diff --git a/libraries/MoMInjection/MoMInjection.csproj b/libraries/MoMInjection/MoMInjection.csproj new file mode 100644 index 000000000..2d675dea2 --- /dev/null +++ b/libraries/MoMInjection/MoMInjection.csproj @@ -0,0 +1,58 @@ + + + + + Debug + AnyCPU + {96DE0CA8-7F84-4184-9F89-8B1ED6B13165} + Library + Properties + MoMInjection + MoMInjection + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\..\..\..\..\..\..\Program Files %28x86%29\Steam\steamapps\common\Mansions of Madness\Mansions of Madness_Data\Managed\ + TRACE + prompt + 4 + + + + ..\..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Mansions of Madness\Mansions of Madness_Data\Managed\Assembly-CSharp.dll + + + + + + + + + ..\..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Mansions of Madness\Mansions of Madness_Data\Managed\UnityEngine.dll + + + + + + + + + \ No newline at end of file diff --git a/libraries/MoMInjection/Properties/AssemblyInfo.cs b/libraries/MoMInjection/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..11a435891 --- /dev/null +++ b/libraries/MoMInjection/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MoMInjection")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MoMInjection")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("96de0ca8-7f84-4184-9f89-8b1ed6b13165")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/libraries/libraries.sln b/libraries/libraries.sln index 3c0985607..3a58a965f 100644 --- a/libraries/libraries.sln +++ b/libraries/libraries.sln @@ -13,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Injection", "Injection\Inje EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ObbExtract", "ObbExtract\ObbExtract.csproj", "{8B00BEA9-38B0-4C4F-AD79-1B5A1EFCEED3}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MoMInjection", "MoMInjection\MoMInjection.csproj", "{96DE0CA8-7F84-4184-9F89-8B1ED6B13165}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,6 +41,10 @@ Global {8B00BEA9-38B0-4C4F-AD79-1B5A1EFCEED3}.Debug|Any CPU.Build.0 = Debug|Any CPU {8B00BEA9-38B0-4C4F-AD79-1B5A1EFCEED3}.Release|Any CPU.ActiveCfg = Release|Any CPU {8B00BEA9-38B0-4C4F-AD79-1B5A1EFCEED3}.Release|Any CPU.Build.0 = Release|Any CPU + {96DE0CA8-7F84-4184-9F89-8B1ED6B13165}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {96DE0CA8-7F84-4184-9F89-8B1ED6B13165}.Debug|Any CPU.Build.0 = Debug|Any CPU + {96DE0CA8-7F84-4184-9F89-8B1ED6B13165}.Release|Any CPU.ActiveCfg = Release|Any CPU + {96DE0CA8-7F84-4184-9F89-8B1ED6B13165}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/unity/Assets/Plugins/FFGAppImport.dll b/unity/Assets/Plugins/FFGAppImport.dll index 321eed122..b4eab3196 100644 Binary files a/unity/Assets/Plugins/FFGAppImport.dll and b/unity/Assets/Plugins/FFGAppImport.dll differ diff --git a/unity/Assets/Plugins/FFGAppImport.dll.mdb b/unity/Assets/Plugins/FFGAppImport.dll.mdb index 0d6006fbb..79d638dd3 100644 Binary files a/unity/Assets/Plugins/FFGAppImport.dll.mdb and b/unity/Assets/Plugins/FFGAppImport.dll.mdb differ diff --git a/unity/Assets/Plugins/FFGAppImport.dll.mdb.meta b/unity/Assets/Plugins/FFGAppImport.dll.mdb.meta index 18a55b9bd..825cb6daa 100644 --- a/unity/Assets/Plugins/FFGAppImport.dll.mdb.meta +++ b/unity/Assets/Plugins/FFGAppImport.dll.mdb.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 guid: a703acfcb210cbc4290009864eae67f8 -timeCreated: 1498298825 +timeCreated: 1500113523 licenseType: Free DefaultImporter: userData: diff --git a/unity/Assets/Plugins/FFGAppImport.pdb b/unity/Assets/Plugins/FFGAppImport.pdb index 6134a1570..06713f68f 100644 Binary files a/unity/Assets/Plugins/FFGAppImport.pdb and b/unity/Assets/Plugins/FFGAppImport.pdb differ diff --git a/unity/Assets/Plugins/ValkyrieTools.dll b/unity/Assets/Plugins/ValkyrieTools.dll index bbc40bdd7..48eaaafd4 100644 Binary files a/unity/Assets/Plugins/ValkyrieTools.dll and b/unity/Assets/Plugins/ValkyrieTools.dll differ diff --git a/unity/Assets/Plugins/ValkyrieTools.dll.mdb b/unity/Assets/Plugins/ValkyrieTools.dll.mdb index 848a2f51b..997ba97ff 100644 Binary files a/unity/Assets/Plugins/ValkyrieTools.dll.mdb and b/unity/Assets/Plugins/ValkyrieTools.dll.mdb differ diff --git a/unity/Assets/Plugins/ValkyrieTools.dll.mdb.meta b/unity/Assets/Plugins/ValkyrieTools.dll.mdb.meta index 9b5b49a19..07e92527e 100644 --- a/unity/Assets/Plugins/ValkyrieTools.dll.mdb.meta +++ b/unity/Assets/Plugins/ValkyrieTools.dll.mdb.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 guid: 1978bddd73396a84195904b93efb08c3 -timeCreated: 1498298825 +timeCreated: 1500113214 licenseType: Free DefaultImporter: userData: diff --git a/unity/Assets/Plugins/ValkyrieTools.pdb b/unity/Assets/Plugins/ValkyrieTools.pdb index c0ea68ecc..1293698f1 100644 Binary files a/unity/Assets/Plugins/ValkyrieTools.pdb and b/unity/Assets/Plugins/ValkyrieTools.pdb differ diff --git a/unity/Assets/Resources/Text/Localization.txt b/unity/Assets/Resources/Text/Localization.txt deleted file mode 100644 index 2735d780a..000000000 --- a/unity/Assets/Resources/Text/Localization.txt +++ /dev/null @@ -1,428 +0,0 @@ -.,English,Spanish,French,German,Italian,Portuguese,Polish,Japanese,Chinese -// ICONS -ICON_SKILL_STRENGTH,,,,,,,,, -ICON_SKILL_AGILITY,,,,,,,,, -ICON_SKILL_OBSERVATION,,,,,,,,, -ICON_SKILL_LORE,,,,,,,,, -ICON_SKILL_INFLUENCE,,,,,,,,, -ICON_SKILL_WILL,,,,,,,,, -ICON_ACTION,,,,,,,,, -ICON_SUCCESS_RESULT,,,,,,,,, -ICON_INVESTIGATION_RESULT,,,,,,,,, -ICON_PRODUCT_MAD00 -ICON_PRODUCT_MAD01,,,,,,,,, -ICON_PRODUCT_MAD06,,,,,,,,, -ICON_PRODUCT_MAD09,,,,,,,,, -ICON_PRODUCT_MAD20,,,,,,,,, -ICON_PRODUCT_MAD21,,,,,,,,, -ICON_PRODUCT_MAD22,,,,,,,,, -ICON_PRODUCT_MAD23,,,,,,,,, - -// TEXTS -ABILITY,Ability,Capacidad,Capacité,Fähigkeit,Abilità,Habilidade,Umiejętność,, -ABOUT,About,Acerca de,A propos,Über Valkyrie,Informazioni su,Sobre Valkyrie, O Valkyrie,, -ABOUT_FFG,"Valkyrie is a game master helper tool inspired by Fantasy Flight Games' Descent: Road to Legend. Most images used are imported from FFG applications are are copyright FFG and other rights holders.","Valkyrie es una App de ayuda de director de juego inspirada por Descent: Camino a Leyenda, de Fantasy Flight Games. La mayoría de las imágenes utilizadas se importan de la aplicación de FFG y son propiedad de FFG con todos los derechos reservados.","Valkyrie est une application d'aide de jeu inspiré par Descent : Road to Legend de Fantasy Flight Games. La plupart des images utilisées sont issues des applications FFG et sont la propriété de FFG. Tous droits réservés.","Valkyrie ist ein durch Fantasy Flight Games' Descent: Road to Legend inspiriertes Gamemaster-Tool. Die meisten Bildressources werden aus FFG-Anwendungen importiert und unterliegen daher dem Copyright von FGG und Anderen.","Valkyrie è uno strumento di supporto ispirato da Descent: La Via per la Leggenda della Fantasy Flight Games. Molte delle immagini utilizzate sono importate dalle applicazioni FFG che sono soggette a copyright FFG e diritti di terzi.","Valkyrie é um App de ajuda de criação de aventuras inspirado por Fantasy Flight Games' Descent: Road to Legend. A maioria das imagens utilizadas no App são da FFG e são de propriedade da FFG com todos os direitos reservados.","Valkyrie to narzędzie pomocnicze mistrza gry inspirowane grą Descent: Droga do legendy wydaną przez Fantasy Flight Games. Większość używanych grafik została zaimportowana z aplikacji FFG i są objęte ochroną praw autorskich należących do FFG i innych podmiotów posiadających prawa.",, -ABOUT_LIBS,"Valkyrie uses DotNetZip-For-Unity and has code derived from Unity Studio and .NET Ogg Vorbis Encoder.","Valkyrie utiliza DotNetZip-For-Unity y posee código derivado de Unity Studio y .NET Ogg Vorbis Encoder.","Valkyrie utilise DotNetZip-For-Unity et contient du code dérivé de Unity Studio et .NET Ogg Vorbis Encoder.","Valkyrie benutzt DotNetZip-For-Unity und enthält Code der auf Unity Studio und dem .NET Vorbis Encoder aufbaut.","Valkyrie utilizza DotNetZip-For-Unity e codici derivanti da Unity Studio e .NET Ogg Vorbis Encoder","Valkyrie utilizza DotNetZip-For-Unity e codici derivanti da Unity Studio e .NET Ogg Vorbis Encoder","Valkyrie usa DotNetZip-For-Unity e tem código derivado da Unity Studio e .NET Ogg Vorbis Encoder.","W Valkyrie używana jest biblioteka DotNetZip-For-Unity, a także kod pochodzący z Unity Studio oraz .NET Ogg Vorbis Encoder.",, -ACTIONS,Actions,Acciones,Actions,Aktionen,Azioni,Ações,Akcje,, -ACTIVATED,Activated,Activado,Activé,Aktiviert,Attivo,Ativado,Aktywowano,, -ACTIVATION,Activation,Activación,Activation,Aktivierung,Attivazione,Ativação,Aktywacja,, -ACTIVATIONS,Activations,Activaciones,Activations,Aktivierungen,Attivazioni,Ativações,Aktywacje,, -ADD_COMPONENTS,Add Components:,Añadir Componentes:,Ajouter éléments :,Komponente hinzufügen:,Aggiungi Componente,Adicionar Componentes,Dodaj Komponent:,, -COMPONENTS,Components,Componentes,,Komponenten,,,Komponenty,, -RENAME,Rename,Renombrar,,Umbenennen,,,Zmień Nazwę,, -TEST,Test,,,,,,Test,, -SOURCE,Source,Origen,,Quelle,,,Źródło,, -COMMENT,Comment,Comentar,,Kommentar,,,Komentarz,, -TRUE,True,Verdadero,,,,,Prawda,, -FALSE,False,Falso,,,,,Fałsz,, -SNAP,Snap,Ajustado,,Auto,,,Zdjęcie,, -FREE,Free,Libre,,Frei,,,Darmowy,, -CONFIRM,Confirm,Confirmar,,OK,,,Potwierdź,, -INSPECT,Inspect,Inspeccionar,,Prüfen,,,Sprawdź,, -DURATION,Duration,Duración,,Dauer,,,Czas Trwania,, -DESCRIPTION,Description,,,Beschreibung,,,Opis,, -AUTHORS,Authors,,,Autoren,,,Autorzy,, -DIFFICULTY,Difficulty,Dificultad,,Schwierigkeit,,,Poziom Trudności,, -VALIDATE_SCENARIO,Validate,Validar,,Validieren,,,Zatwierdź Scenariusz,, -FILE,File,Fichero,,Datei,,,Plik,, -OPTIMIZE_LOCALIZATION,Optimize Localisation,Optimizar Localización,,Übersetzungen optimieren,,,Optymalizuj tłumaczenie,, -REORDER_COMPONENTS,Reorder Components,Reordenar Componentes,,Komponenten neu ordnen,,,Zmiana Kolejności Komponentów,, -POSITION_SNAP,╳ {val:SNAP},,,,,,,, -POSITION_FREE,〜 {val:FREE},,,,,,,, -ASSIGN,Assign,Asignar,Assigner,Hinzu,Assegna,,Przydziel,, -ATTACK,Attack,Atacar,Attaquer,Angriff,Attacca,Ataque,Atak,, -ATTACK_MESSAGE,Attack Message:,Mensaje Ataca:,Message en cas d'attaque :,Angriffsmeldung:,Messaggio d'attacco,Mensagem de Ataque,Komunikat Ataku:,, -AUDIO,Audio,Audio,Audio,Audio,Audio,Audio,Audio,, -BACK,Back,Volver,Retour,Zurück,Indietro,voltar,Powrót,, -BASE,Base,Base,Base,Basis,Base,Base,Podstawa,, -BUTTON,Button,Botón,Bouton,Schaltfläche,Pulsante,Botão,Przycisk,, -BUTTONS,Buttons,Botones,Boutons,Schaltflächen,Pulsanti,Botões,Przyciski,, -CAMERA,Camera,Cámara,Camera,Kamera,Camera,Camera,Kamera,, -CANCEL,Cancel,Cancelar,Annuler,Abbrechen,Annulla,Cancelar,Anuluj,, -CHOOSE_LANG,Choose Language,Elige Idioma,Choix de la langue,Sprache wählen,Lingua,Escolher Idioma,Wybór Języka,, -CLEAR_FIRE,Clear Fire,Quitar fuego,Eteindre le feu,Feuer gelöscht,Incendio Estinto,Apagar Incêndio,Usuń Ogień,, -COLOR,Colour,Color,Couleur,Farbe,Colore,Cor,Kolor,, -COMPONENT_NAME,Component Name:,Nombre del Componente:,Nom de l'élément :,Name der Erweiterung,Importa,Nome do Componente,Nazwa Komponentu,, -COMPONENT_TO_DELETE,"Component to Delete:","Componente a Eliminar:","Elément à supprimer","Zu löschende Erweiterung:","Contenuto da Eliminare",Deletar Componente,"Komponent do Usunięcia:",, -CONTENT_IMPORT,Import Content,Importar Contenido,Importer contenu,Importiere Inhalt,Contenuto Importato,Importar Componente,Import Komponentu,, -CONTENT_IMPORTING,Importing...,Importando...,Import...,Importiere...,In aggiornamento...,Importando...,Importowanie...,, -CONTENT_REIMPORT,Reimport Content,Reimportar Contenido,Réimporter contenu,Reimportiere Inhalt,Reimporta Contenuto,Reimportar Componente,Zaimportuj Ponownie Komponent,, -CONTINUE,Continue,Continuar,Continuer,Weiter,Continua,Continuar,Kontynuuj,続ける,繼續 -COPY,Copy,Copiar,Copier,Kopieren,Copia,Copiar,Kopiuj,, -CUSTOMMONSTER,CustomMonster,Monstruo Personalizado,Monstre personnalisé,Spezial-Monster,Mostro personalizzato,Monstro Personalizado,Personalizacja Potwora,, -D2E_APP_NOT_FOUND,"Unable to locate Road to Legend, install via Steam","Imposible localizar ""Road to Legend"", instalar desde Steam","Impossible de localiser l'installation de Road to Legend via Steam","""Road to Legend""-Installation konnte nicht gefunden werden, bitte über Steam installieren","Impossibile trovare "Road of Legend", installalo con Steam","Impossivel encontrar "Road of Legend", instalar via Steam","Nie można zlokalizować Road to Legend, zainstaluj za pośrednictwem Steam",, -D2E_HEROES_NAME,Heroes,Héroes,Héros,Helden,Eroi,Heróis,Bohaterowie,, -D2E_HERO_NAME,Hero,Héroe,Héro,Held,Eroe,Herói,Bohater,, -D2E_NAME,Descent: Journeys in the Dark Second Edition,Descent: Viaje a las Profundidades Segunda Edición,Descent : Voyages dans les Ténèbres (Seconde Edition),Descent 2. Edition: Die Reise ins Dunkel,Descent: Viaggi nelle Tenebre - Seconda Edizione,Descent: Journeys in the Dark Second Edition,Descent 2. edycja: Wędrówki w Mroku,, -D2E_QUEST_NAME,Quest,Aventura,Scenario,Szenario,Scenario,Aventura,Przygoda,, -DEFEATED,Defeated,Derrotado,Eliminé,Besiegt,Sconfitto,Derrotado,Pokonany,, -DELETE,Delete,Eliminar,Supprimer,Löschen,Cancella,Deletar,Usuń,, -DIALOG,Dialog,Diálogo,Dialogue,Dialog,Dialogo,Diálogo,Dialog,, -DOOR,Door,Puerta,Porte,Tür,Porta,Porta,Drzwi,, -DOWNLOAD,Download,Descargar,Télécharger,Download,Scarica,Download,Pobierz,, -E,E,E,E,E,E,E,E,, -EFFECTS,Effects,Efectos,Effets,Effekte,Effetti,Efeitos,Efekty,, -EMPTY,,,,,,,,, -END_TURN,End Turn,Fin del Turno,Fin du tour,Letzter Zug,Fine Turno,Fim do Turno,Koniec Tury,, -ROUND,Round {0},,,,,,,{0},, -EVADE,Evade,Esquivar,Evasion,Ausweichen,Evadi,Evasão,Unik,, -EVENT,Event,Evento,Evénement,Ereignis,Evento,Evento,Wydarzenie,, -EXIT,Exit,Salir,Sortir,Verlassen,Esci,Sair,Wyjście,, -FINISHED,Finished,Terminado,Terminé,Fertig,Continua,Terminado,Skończony,, -FIRST,First,Primero,Premier,Erster,Primo,Primeiro,Pierwszy,, -FORCE_ACTIVATE,Force Activate,Forzar Activación,Forcer l'activation,Erzwinge Aktivierung,Attivazione forzata,Ativação Forçada,Wymuś Aktywację,, - -HEALTH,Health,Salud,Santé,Ausdauer,Salute,Vida,Zdrowie,, -HEALTH_HERO,Per Hero,Por héroe,Per Hero,Pro Held,Per eroe,Por Herói,Na jednego Bohatera,, -HIGHLIGHT,Highlight,Destacar,Mettre en valeur,Markieren,In evidenza,Destaque,Zaznacz,, -HORROR_CHECK,Horror Check,Horror,Test d'horreur,Horrortest,Prova di Orrore,Teste de Horror,Sprawdź,, -INDENT," {0}"," {0}"," {0}"," {0}"," {0}"," {0}"," {0}",, -INFO,Info,Información,Info,Info,info,Info,Info,, -INFORMATION,Information,Información,Information,Information,Informazioni,Informação,Informacja,, -INITIAL_MESSAGE,Initial Message:,Mensaje Inicial:,Message de départ :,Eingangstext:,Messaggio iniziale,Mensagem Inicial,Wiadomość Początkowa:,, -ITEM,Item,Objeto,Objet,Gegenstand,Oggetto,Objeto,Przedmiot,, -QITEM,QItem,Objeto de Escenario,,,,,Przedmiot Fabularny,, -KO,KO,KO,KO,KO,,,KO,, -LOAD_QUEST,Load {0},Cargar {0},Charger {0},Lade {0},Carica {0},,Ładowanie {0},, -LOG,Log,Log,Log,Log,Log,,Dziennik,, -SKILLS,Skills,Habilidades,,Fähigkeiten,,,Umiejętności,, -ITEMS,Items,Objetos,,Inventar,,,Przedmioty,, -GOLD,Gold,Oro,,Gold,,,Złoto,, -MAIN_MENU,Main Menu,Menú Principal,Menu principal,Hauptmenü,Menu Principale,,Menu Główne,, -MAX,Max,Max,Max,Max,Max,,Max,, -MAX_X,Max {0},Max {0},Max {0},Max {0},Max {0},,Max {0},, -MAX_CAM,Max Cam,Cam Max,Max Cam,Kam Max,Cam Max,,Max Kam,, -MENU,Menu,Menú,Menu,Menü,Menu,Menu,, -MIN,Min,Min,Min,Min,Min,,Min,, -MIN_X,Min {0},Min {0},Min {0},Min {0},Min {0},,Min {0},, -MIN_CAM,Min Cam,Cam Min,Min Cam,Kam Min,Cam Min,,Min Kam,, -MOM_APP_NOT_FOUND,"Unable to locate Mansions of Madness, install via Steam","Imposible localizar ""Mansions of Madness"", instalar desde Steam,"Impossible de localiser l'installation des Demeures de l'Epouvante via Steam","""Villen des Wahnsinns""-Installation konnte nicht gefunden werden, bitte über Steam installieren","Impossibile trovare "Le Case della Follia", installalo con Steam",,"Nie można zlokalizować Posiadłości Szaleństwa, zainstaluj za pośrednictwem Steam",, -MOM_HEROES_NAME,Investigators,Investigadores,Investigateurs,Ermittler,Investigatori,,Badacze,, -MOM_HERO_NAME,Investigator,Investigador,Investigateur,Ermittler,Investigatore,,Badacz,, -MOM_NAME,Mansions of Madness Second Edition,Las Mansiones de la Locura: Segunda Edición,Les Demeures de l'Epouvante Seconde Edition,Villen des Wahnsinns: 2. Edition,Le Case della Follia Seconda Edizione,,Posiadłość Szaleństwa 2. edycja,, -MOM_QUEST_NAME,Scenario,Escenario,Scenario,Szenario,Scenario,,Scenariusz,, -MONSTER,Monster,Monstruo,Monstre,Monster,Mostro,,Potwór,, -MONSTER_ATTACKS,The monster attacks.,El monstruo ataca.,Le monstre attaque.,Das Monster greift an.,Il mostro attacca,,Potwór Atakuje,, -MONSTER_MASTER,Master,Maestro,Maître,Elite,Maggiore,,Elitarny,, -MONSTER_MASTER_X,Master {0},Maestro {0},Maître {0},Elite {0},Maggiore {0},,Elitarny {0},, -MONSTER_MINION,Minion,Esbirro,Serviteur,Minion,Minore,,Sługus,, -MONSTER_NORMAL,Normal,Normal,Normal,Normal,Normale,,Normalny,, - -MONSTER_UNIQUE,Unique,Único,Unique,Unique,Unico,,Niepowtarzalny,, -MOVES,Moves,Movimientos,Mouvements,Züge,Movimenti,,Ruchy,, -MPLACE,MPlace,Posición,Position,MPosition,Posizione,,PPozycja,, -MUSIC,Music,Música,Musique,Musik,Musica,,Muzyka,, -NAME,Name,Nombre,Nom,Name,Nome,,Imie,, -NEW,New,Nuevo,Nouveau,Neu,Nuovo,,Nowy,, -NEW_X,{New {0}},{Nuevo {0}},{Nouveau {0}},{Neu {0}},{Nuovo {0}},,{Nowy {0}},, -NONE,{None},{Ninguno},{Ninguno},{Keines},,,{Żaden},, - -NEXT_EVENTS,Next Events,Siguientes Eventos,Evénements suivants,Nächstes Ereignis,Evento Successivo,,Kolejne Wydarzenie,, -NOT_FIRST,Not First,No Primero,Pas en premier,Nicht zuerst,Non Primo,,Nie Pierwszy,, -NO_ATTACK_MESSAGE,No Attack Message:,Mensaje no Ataca:,"Message en absence d'attaque :",Kein-Angriff-Meldung:,Messaggio,,Wiadomość Poza Atakiem:,, - -NUMBER,Number,Número,Numéro,Nummer,Numero,,Numer,, -NUMBER_HEROS,{0} Heros:,{0} Héroes:,{0} Héros:,{0} Helden:,{0} Eroi:,,{0} Bohater:,, -OK,Ok,Ok,Ok,Ok,Ok,,Ok,, -OP,Op,Operador,Opérateur,Op,Opera,,Operator,, -OPTIONS,Options,Opciones,Options,Optionen,Opzioni,,Opcje,, -PLACEMENT,Placement,Colocación,Positionnement,Platzierung,Posizione,,Rozmieszczenie,, - -PLACE_IMG,Place Img:,Poner Img:,Placer Img :,Platz. Bild:,Posiziona Img:,,Umieść Obraz,, -POOL_TRAITS,Pool Traits:,Atributos Posibles:,Traits possibles :,Poolmerkmale:,Tratti Pubblici,,Pula Atrybutów:,, -POSITION,Position,Posición,Position,Position,Posizione,,Pozycja,, -POSITION_TYPE_HIGHLIGHT,Highlight,Destacado,Mise en valeur,Markieren,Evidenzia,,Podświetl,, -POSITION_TYPE_UNUSED,Unused,No Usado,Non utilisé,Unbenutzt,Inutilizzato,,Nieużywany,, -PUZZLE,Puzzle,Puzzle,Puzzle,Puzzle,Puzzle,,Zagadka,, -PUZZLE_ALT_LEVEL,Alt Level,Dificultad Alt.,Difficulté,Schwierigkeit,Difficoltà,,Alternatywny Poziom Trudności,, -PUZZLE_CLASS,Class,Tipo,Classe,Klasse,Tipo,,Kategoria,, -PUZZLE_CLASS_SELECT,Select Class,Escoger Tipo,Choix de la classe,Klasse wählen,Seleziona Tipo,,Wybór Kategorii,, -IMAGE,Image,Imagen,Image,Bild,Immagine,,Obraz,, - -PUZZLE_LEVEL,Level,Dificultad,Niveau,Level,Difficoltà,,Poziom Trudności,, -PUZZLE_SELECT_SKILL,Select Skill,Escoger Habilidad,Choisir compétence,Fähigkeit wählen,Seleziona Abilità,,Wybierz Umiejętność,, -PUZZLE_IMAGE_CLASS,image,mosaico,image,bild,mosaico,,obraz,, -PUZZLE_CODE_CLASS,code,código,code,code,codice,,kod,, -PUZZLE_SLIDE_CLASS,slide,mecanismo,mécanisme,schieberaetsel,meccanismo,,przesuń,, -QUEST,Quest,Escenario,Scenario,Szenario,Scenario,,Scenariusz,, -QUEST_NAME_DOWNLOAD,Download {0},Descargar {0},Télécharger {0},Download {0},Scarica {0},,Pobieranie {0},, -QUEST_NAME_EDITOR,{0} Editor,Editar {0},Editeur {0},{0}-Editor,{0} Editor,,Edytor {0},, -EDITOR,Editor,Editar,Editeur,Editor,Editor,,Edytor,, -QUEST_NAME_UPDATE," [Update] {0}"," [Actualizar] {0}"," [Mise à jour] {0}"," [Update] {0}"," [Aggiorna] {0}",," [Aktualizacja] {0}",, -QUOTA,Quota,Cuota,Quota,Quote,Quota,,Cytat,, -RECOVER,Recover,Recupear,Récupérer,Erholen,Recupera,,Wyzdrowieć,, - -RELOAD,Reload,Recargar,Recharger,Aktualisieren,Ricarica,,Przeładuj,, -REMOVE_COMPONENTS,"Remove Components:","Eliminar Componentes:","Retirer des éléments","Komponente entfernen:","Rimuovi Componente",,"Usuń Komponent:",, -REQUIRED_EXPANSIONS,Required Expansions:,Expansiones Requeridas:,Extensions requises :,Erforderliche Inhalte:,Espansioni Richieste:,,Wymagane Rozszerzenia:,, -REQUIRES_EXPANSION," Requires: {0}"," Requiere: {0}"," Requiert : {0}"," Benötigt: {0}"," Richieste: {0}",," Wymaga: {0}",, - -REQ_TRAITS,Req. Traits:,Atributos Req.:,"Attributs requis :",Ben. Fähigk.:,Tratti Richiesti:,,Wymagane Atrybuty:,, -RESET,Reset,Restablecer,Reset,Zurücksetzen,Reset,,Reset,, -ROTATE_TO,"Rotate: {0}","Rotar: {0}","Tourner : {0},"Rotiere: {0}","Rotazione: {0}",,,"Obrót: {0}",, -ROTATION,"Rotation","Rotación","Rotation","Rotation","Rotazione",,"Obrót",, -SAVE,Save,Guardar,Sauver,Speichern,Salva,,Zapisz,, -AUTOSAVE,Auto Save,Guardado Automático,Sauvegarde auto.,Autom. Speichern,,,Autozapis,, -SELECT_SAVE,Select Save,Sel. Partida Grabada,Choisir sauvegarde,Speicherst. wählen,,,Wybierz Zapis,, -SELECT,Select {0},Selección de {0},Choisir {0},{0} wählen,Seleziona {0},,Wybierz {0},, -SELECT_CLASS,Select Class,Selección de Clase,Choisir classe,Klasse wählen,,,Wybierz Klasę,, -SELECTION,Selection,Selección,Sélection,Auswahl,Selezione,,Wybór,, -SELECT_CONTENT,Select Content,Mi Colección,Choisir du contenu,Inhalt wählen,Seleziona Contenuto,,Wybierz Zawartość,, -SELECT_EXPANSION,Select Expansion Content,Elige Colección de Expansiones,Choix des extensions,Erweiterungsinhalt wählen,Seleziona Espansione,,Wybierz Zawartość Rozszerzenia,, -SELECT_IMAGE,Select Image,Escoger Imagen,Choisir Image,Bild wählen,Seleziona Immagine,,Wybierz Obraz,, -SELECT_ITEM,Select Item,Escoger Elemento,Choix des objets,Gegenstand wählen,Seleziona Oggetto,,Wybierz Przedmiot,, -SELECT_PACK,Select Pack,Selección de Paquetes,Choix des paquets,Paket wählen,Seleziona Pacchetto,,Wybierz Pakiet,, -SELECT_TO_COPY,Select {0} To Copy,Selección de {0} a Copiar,Choisir {0} à copier,{0} zum Kopieren ausgewählt,Selezione {0} da Copiare,,Wybierz {0} do Skopiowania,, -SELECT_TO_DELETE,Select {0} To Delete,Selección de {0} a Eliminar,Sélectionner {0} pour le supprimmer,{0} zur Löschung ausgewählt,Selezione {0} da Cancellare,,Wybierz {0} do Usunięcia,,, -HIDDEN,Hidden,Oculto,Caché,Versteckt,,,Ukryty,, -ACTIVE,Active,Activo,Actif,Aktiv,,,Aktywny,, -SET,Set,Cambiar,Appliquer,Setze,Set,,Ustaw,, -SKILL,Skill,Atributo,Compétence,Fähigkeit,Abilità,,Umiejętność,, -SELECT_SKILLS,Select Skills,Selección de Habilidades,Choisir compétences,Fähigkeiten wählen,,,Wybierz Umiejętności,, -SPAWN,Spawn,Generar,Générer,Spawn,Generazione,,Przywołaj,, -STARTING_ITEM,Starting Item,Objeto Inicial,Objets de départ,Startgegenstand,,,Przedmiot Startowy,, -STARTING_ITEMS,Starting Items,Equipo Inicial,Objets de départ,Startgegenstände,Oggetti Iniziali,,Przedmioty Startowe,, -START_QUEST,Start {0},Iniciar {0},Démarrer {0},Starte {0},Inizio {0},,Początek {0},, -START,Start,Iniciar,Démarrer,Starte,Inizio,,Start,, - -TESTS,Tests,Tests,Tests,Tests,Prove,,Testy,, -TILE,Tile,Tablero,Tuile,Teil,Tessera,,Pole,, - -TOKEN,Token,Marcador,Marqueur,Marker,Segnalino,,Znacznik,, -TOOLS,Tools,Utilidades,,Werkzeuge,,,Narzędzia,, -TOTAL_MOVES,Total Moves,Total Movims.,Déplacements totaux,Züge (insgesamt),Movimento Totale,,Ruchy (łącznie),, -TRAITS,Traits,Atributos,Traits,Fertigkeiten,Tratti,,Atrybuty,, -TRIGGER,Trigger,Activador,Déclencheur,Auslöser,Attivatore,,Aktywacja,, -TYPE,Type,Tipo,Type,Typ,Tipo,,Typ,, -TYPES,Types,Tipos,Types,Typen,Tipi,,Typy,, -UNABLE_BUTTON,Unable Button:,Botón No Puede:,Bouton impossible,Unmöglich-Button:,Pulsante Disabilitato:,,Dezaktywacja:,, - -UI,UI,Interfaz,UI,UI,,Interface,Interfejs,, -UNITS,Units,Unidades,Unités,Einheiten,,Unidades,Jednostki,, -HORIZONTAL,Horizontal,Horizontal,Horizontal,Horizontal,,Horizontal,Poziomy,, -VERTICAL,Vertical,Vertical,Vertical,Vertikal,,Vertical,Pionowy,, -ALIGN,Align,Alinear,Aligner,Ausrichten,,Alinhamento,Wyrównaj,, -POSITION,Position,Posición,Position,Position,,Posição,Pozycja,, -SIZE,Size,Tamaño,Taille,Größe,,Tamanho,Rozmiar,, -TEXT_SIZE,Text Size,Tamaño del Texto,Taille du texte,Textgröße,,Texto,Rozmiar Tekstu,, -ASPECT,Aspect,Aspecto,Aspect,Aspekt,,Aspecto,Stosunek,, -BORDER,Border,Borde,Bordure,Umrandung,,Borda,Brzeg,, -NO_BORDER,No Border,Sin Borde,Sans bordure,Keine Umrandung,,Sem Borda,Brak Brzegów,, -UNDO,Undo,Deshacer,Défaire,Rückgängig,Indietro,Desfazer,Cofnij,, -UNIQUE_DEFEATED,Unique\nDefeated,Único\nDerrotado,Unique\nBattu,Unique\nBesiegt,Unico\nSconfitto,Única\nDerrota,Unikatowy\nPokonany,, -UNIQUE_INFO,Unique Information,Información de Único,Info unique,Unique-Information,Unica Informazione,Informação Única,Unikatowa Informacja,, -UNIQUE_MONSTER,Unique Monster,Monstruo Único,Monstre unique,Unique-Monster,Mostro Unico,Monstro Único,Unikatowy Potwór,, -UNIQUE_TITLE,Unique Title,Título de Único,Titre unique,Unique-Titel,Titolo Unico,Titulo Único,Unikatowy Tytuł,, -UNUSED,Unused,No Usado,Non utilisé,Unbenutzt,Non Usato,Não Usado,Nieużywany,, -VALUE,Value,Valor,Valeur,Wert,Valore,Valor,Wartość,, -VAR,Var,Variable,Variable,Var,Var,Variável,Zmienna,, -VARS,Vars,Vars,Variables,Vars,Vars,Variáveis,Zmienne,, -VAR_NAME,Var Name:,Nombre Variable:,Nom variable :,Var-Name:,Var Nome:,,Nazwa Zmiennej:,, -X_ACTIVATED,{0} Activated,{0} Activado,{0} Activé,{0} aktiviert,Attivo,{0},,{0} Aktywowany,, - -BUY,Buy,Comprar,Acheter,Kaufen,,,Kup,, -SELL,Sell,Vender,Vendre,Verkaufen,,,Sprzedaj,, -CLASS,Class,Clase,Classe,Klasse,,,Klasa,, -ACT_1,Act I,Acto I,Acte 1,Akt I,,,Akt I,, -ACT_2,Act II,Acto II,Acte 2,Akt II,,,Akt II,, - -X_COLON,{0}:,{0}:,{0}:,{0}:,{0}:,{0}:,{0}:,{0}:,{0}: - -//TRAITS -// regex from ab to ab,Ab,,,,,,, -// ^(([a-z])([a-z]*))\r\n replace with \1,\U\2\E\3,,,,,,,,\r\n - -//Packs -BtT,BtT ,BtT ,BtT ,BtT ,BtT ,BtT ,BtT ,BtT ,BtT  -CotW,CotW ,CotW ,CotW ,CotW ,CotW ,CotW ,CotW ,CotW ,CotW  -FA,FA ,FA ,FA ,FA ,FA ,FA ,FA ,FA ,FA  -MoM1E,MoM1E ,MoM1E ,MoM1E ,MoM1E ,MoM1E ,MoM1E ,MoM1E ,MoM1E ,MoM1E  -base,base ,base ,base ,base ,base ,base ,base ,base ,base  -RN,RN ,RN ,RN ,RN ,RN ,RN ,RN ,RN ,RN  -SM,SM ,SM ,SM ,SM ,SM ,SM ,SM ,SM ,SM  - -//Items -weapon,Weapon,Arma,Arme,Waffe,Arma,Arma,Broń,, -firearm,Firearm,Arma de Fuego,Arme à feu,Schusswaffe,Arma da Fuoco,Arma de Fogo,Broń Palna,, -heavyweapon,Heavy Weapon,Arma Contundente,Arme lourde,Schwere Waffe,Arma Pesante,Arma Pesada,Broń Ciężka,, -heavy,Heavy Weapon,Arma Contundente,Arme lourde,Schwere Waffe,Arma Pesante,Arma Pesada,Broń Ciężka,, -tome,Tome,Tomo,Tome,Buch,Tomo,Tomo,Tom,, -equipment,Equipment,Equipo,Equipement,Ausrüstung,Equipaggiamento,Equipamento,Ekwipunek,, -lightsource,Lightsource,Fuente de Luz,Source de lumière,Lichtquelle,Fonte di Luce,Fonte de Luz,Źródło Światła,, -bladedweapon,Bladed Weapon,Arma de Filo,Arme tranchante,Klingenwaffe,Arma da Taglio,Arma de Corte,Broń Sieczna,, -bladed,Bladed Weapon,Arma de Filo,Arme tranchante,Klingenwaffe,Arma da Taglio,Arma Cortante,Broń Sieczna,, -spell,Spell,Hechizo,Sort,Zauber,Incantesimo,Feitiço,Zaklęcie,, -key,Key,Llave,Clé,Schlüssel,Chiave,Chave,Klucz,, -evidence,Evidence,Pista,Preuve,Beweis,Prova,Evidência,Dowód,, -ally,Ally,Aliado,Allié,Verbündeter,Alleato,Aliado,Sprzymieńca,, - -//Audio -menu,Menu,Menú,Menu,Menü,Menu,Menu,Menu,, -music,Music,Música,Musique,Musik,Musica,Música,Muzyka,, -quest,Quest,Escenario,Scenario,Szenario,Scenario,Aventura,Scenariusz,, -defeated,Defeated,Derrotado,Echec,Besiegt,Sconfitto,Derrotado,Pokonany,, -newround,New Round,Nueva Ronda,Nouveau tour,Neue Runde,Nuovo Round,Novo Round,Nowa Runda,, -attack,Attack,Ataque,Attaque,Angriff,Attacco,Ataque,Atak,, -unarmed,Unarmed,Sin Arma,Sans arme,Unbewaffnet,Senz'Armi,Desarmado,Bez Broni,, -horror,Horror,Horror,Horreur,Horror,Orrore,Horror,Horror,, -search,Search,Búsqueda,Recherche,Suche,Cerca,Procurar,Przeszukaj,, -explore,Explore,Exploración,Exploration,Erkunden,Esplora,Explorar,Eksploruj,, - -//Heroes -latari,Latari,Latari,Latari,Latari,Latari,Latari,Latari,, -dwarf,Dwarf,Enano,Nain,Zwerg,Nano,Anão,Karzeł,, -gnome,Gnome,Gnomo,Gnome,Gnom,Gnomo,Gnomo,Gnom,, -male,Male,Hombre,Homme,Mann,Maschio,Homem,Mężczyzna,, -female,Female,Mujer,Femme,Frau,Femmina,Mulher,Kobieta,, - -//monsters -undead,Undead,No Muerto,Mort vivant,Untot,Non-Morto,Morto Vivo,Nieumarły,, -humanoid,Humanoid,Humanoide,Humanoïde,Mensch,Umanoide,Humanoide,Humanoid,, -spirit,Spirit,Espíritu,Esprit,Geist,Spirito,Espírito,Duch,, -beast,Beast,Bestia,Bête,Bestie,Bestia,Besta,Bestia,, -agent,Agent,Agente,Agent,Agent,Agente,Agente,Agent,, -lieutenant,Lieutenant,Teniente,Lieutenant,Leutnant,Luogotenente,Tenente,Porucznik,, -small,Small,Pequeño,Petit,Klein,Piccolo,Pequeno,Mały,, -medium,Medium,Mediano,Moyen,Mittel,Medio,Médio,Średni,, -huge,Huge,Gigante,Grand,Groß,Gigante,Gigante,Duży,, -massive,Massive,Masivo,Massif,Massiv,Massivo,Massivo,Masywny,, -building,Building,Edificio,Bâtiment,Gebäude,Costruire,Construção,Budynek,, -melee,Melee,Melé,Mélée,Nahkampf,Corpo a Corpo,Corpo a Corpo,Walka w Zwarciu,, -ranged,Ranged,Distancia,Distance,Fernkampf,Distanza,Distancia,Walka Dystansowa,, -goblin,Goblin,Goblin,Goblin,Goblin,Goblin,Goblin,Goblin,, -cursed,Cursed,Maldito,Maudit,Verflucht,Meledetto,Amaldiçoado,Przeklęty,, -cave,Cave,Cueva,Cave,Höhle,Cava,Caverna,Jaskinia,, -wilderness,Wilderness,Naturaleza,Etendue sauvage,Wildniss,Selvaggio,Região Selvagem,Dzicz,, -civilized,Civilized,Civilización,Civilisé,Zivilisiert,Civilizzato,Civilizado,Ciwilizowany,, -dark,Dark,Oscuridad,Sombre,Dunkel,Oscuro,Escuridão,Mroczny,, -mountain,Mountain,Montaña,Montagne,Berg,Montagna,Montanha,Góra,, -cold,Cold,Frío,Froid,Kalt,Freddo,Frio,Zimno,, -hot,Hot,Caliente,Chaud,Heiß,Caldo,Quente,Gorąco,, -water,Water,Agua,Eau,Wasser,Acqua,Água,Woda,, - -//Tiles -basement,Basement,Sótano,Sous-sol,Keller,Seminterrato,Porão,Podziemie,, -river,River,Río,Rivière,Fluss,Fiume,Rio,Rzeka,, -street,Street,Calle,Rue,Straße,Strada,Estrada,Droga,, -hallway,Hallway,Pasillo,Couloir,Flur,Corridoio,Corredor,Korytarz,, -bathroom,Bathroom,Baño,Salle de bain,Badezimmer,Bagno,Banheiro,Łazienka,, -bedroom,Bedroom,Dormitorio,Chambre,Schlafraum,Camera da Letto,Quarto,Sypialnia,, -kitchen,Kitchen,Cocina,Cuisine,Küche,Cucina,Cozinha,Kuchnia,, -dock,Dock,Muelle,Quai,Hafen,Molo,Doca,Dok,, -storage,Storage,Almacén,Entrepôt,Lager,Magazzino,Armazém,Magazyn,, -outside,Outside,Exterior,Extérieur,Außen,Esterno,Externo,Zewnętrze,, -inside,Inside,Interior,Intérieur,Innen,Interno,Interno,Wnętrze,, -small,Small,Pequeño,Petit,Klein,Piccola,Pequeno,Mały,, -medium,Medium,Mediano,Moyen,Mittel,Media,Médio,Średni,, -big,Big,Grande,Grand,Groß,Grande,Grande,Duży,, -transition,Transition,Transición,Transition,Übergang,Transizione,Transição,Przejście,, -throne,Throne,Trono,Trône,Thron,Trono,Trono,Tron,, -pit,Pit,Abismo,Fosse,Höhle,Abisso,Abismo,Dół,, -farm,Farm,Granja,Grange,Bauernhof,Granaio,Fazenda,Gospodarstwo,, -tomb,Tomb,Tumba,Tombe,Grab,Tomba,Tumba,Grobowiec,, -library,Library,Biblioteca,Bibliothèque,Bibliothek,Biblioteca,Biblioteca,Biblioteka,, -graves,Graves,Cementerio,Cimetière,Gräber,Cimitero,Cemitério,Cmentarz,, -bridge,Bridge,Puente,Pont,Brücke,Ponte,Ponte,Most,, -stairs,Stairs,Escaleras,Escaliers,Treppe,Scale,Escada,Schody,, -torture,Torture,Tortura,Torture,Folterkamer,Tortura,Tortura,Sala Tortur,, -tents,Tents,Tiendas,Tentes,Zelte,Tende,Tendas,Namioty,, -stables,Stables,Establos,Etables,Ställe,Stalle,Estábulo,Stajnie,, -hall,Hall,Salón,Salon,Halle,Salone,Hall,Hol,, -map,Map,Mapa,Carte,Karte,Mappa,Mapa,Mapa,, -bedroom,Bedroom,Dormitorio,Chambre,Schlafraum,Camera da Letto,Quarto,Sypialnia,, -city,City,Ciudad,Ville,Stadt,Città,Cidade,Miasto,, -fountain,Fountain,Fuente,Fontaine,Fontäne,Fontana,Fonte,Fontanna,, -blackrealm,Blackrealm,Reino de Oscuridad,Royaume noir,Dunkelreich,Reame Oscuro,Reino Obscuro,Królestwo Ciemności,, -altar,Altar,Altar,Autel,Altar,Altare,Altar,Ołtarz,, -beds,Beds,Camas,Lits,Betten,Letti,Camas,Łóżka,, -study,Study,Estudio,Etude,Studienzimmer,Studio,Estudo,Pracownia,, -prison,Prison,Prisión,Prison,Gefängnis,Prigione,Prisão,Więzienie,, -plinth,Plinth,Pedestal,Support,Podest,Plinto,Pedestal,Cokół,, -tavern,Tavern,Taberna,Taverne,Taverne,Taverna,Taverna,Tawerna,, -statues,Statues,Estatuas,Statues,Status,Statue,Estátua,Statuły,, -drawbridge,Drawbridge,Puente Levadizo,Pont-levis,Zugbrücke,Ponte Levatoio,Ponte Levadiça,Most Zwodzony,, -rubble,Rubble,Escombros,décombres,Trümmer,Macerie,Escombros,Rumowisko,, -treasure,Treasure,Tesoro,Trésor,Schatz,Tesoro,Tesouro,Skarb,, -stone,Stone,Piedra,Pierre,Stein,Pietra,Pedra,Kamień,, -dirt,Dirt,Mugre,Saleté,Dreck,Sporco,Sujeira,Ziemia,, -timber,Timber,Madera,Bois,Holz,Legname,Madeira,Drzewiasty,, -snow,Snow,Nieve,Neige,Schnee,Neve,Neve,Śnieg,, -lava,Lava,Lava,Lave,Lava,Lava,Lava,Lawa,, -swamp,Swamp,Pantano,Marécage,Sumpf,Palude,Pântano,Mokradła,, -sludge,Sludge,Lodo,Boue,Schlamm,Fango,Lama,Błoto,, - -// Colors -black,Black,Negro,Noir,Schwarz,Nero,Preto,Czarny,, -white,White,Blanco,Blanc,Weiß,Bianco,Branco,Biały,, -red,Red,Rojo,Rouge,Rot,Rosso,Vermelho,Czerwony,, -lime,Lime,Lima,Citron vert,Limette,Lime,Lima,Jasnozielony,, -blue,Blue,Azul,Bleu,Blau,Blu,Azul,Niebieski,, -yellow,Yellow,Amarillo,Jaune,Gelb,Giallo,Amarelo,Żółty,, -aqua,Aqua,Agua,Eau,Aquamarin,Acqua,Aquamarinho,Aqua,, -cyan,Cyan,Cian,Cyan,Türkis,Ciano,Ciano,Błękitny,, -magenta,Magenta,Magenta,Magenta,Magenta,Magenta,Magenta,Magenta,, -fuchsia,Fuchsia,Fucsia,Fuchsia,Fuchsia,Fucsia,Fúcsia,Fuksia,, -silver,Silver,Plata,Argent,Silber,Argento,Prata,Srebrny,, -gray,Gray,Gris,Gris,Grau,Grigio,Cinza,Szary,, -maroon,Maroon,Marrón,Marron,Rotbraun,Marrone,Marrom,Brązowy,, -olive,Olive,Oliva,Olive,Oliv,Oliva,Oliva,Oliwkowy,, -green,Green,Verde,Vert,Grün,Verde,Verde,Zielony,, -purple,Purple,Púrpura,Violet,Violett,Porpora,Roxo,Fioletowy,, -teal,Teal,Turquesa,Sarcelle,Blaugrün,Turchese,Turquesa,Turkusowy,, -navy,Navy,Azul Marino,Bleu marine,Marineblau,Azzurro,Azul Marinho,Granatowy,, - -//Puzzles -image,Image,Mosaico,Image,Bild,Mosaico,Mosaico,Obraz,, -code,Code,Código,Code,Code,Codice,Código,Kod,, -slide,Slide,Mecanismo,Mécanisme,Schieberaetsel,Meccanismo,Mecanismo,Suwak,, -SYMBOL,Symbol,Símbolo,,Symbol,,Símbolo,Symbol,, - -// Events -Ordered,Ordered,Ordenado,Ordonné,Geordnet,Ordinato,Ordenado,Uporządkowany,, -Random,Random,Aleatorio,Aléatoire,Zufällig,Casuale,Aleatório,Losowy,, - -//Inherited from FFG localization files -SET_FIRE,Set Fire,Prender fuego,Mettre le feu,Feuer legen,Incendia,Tocar Fogo,Podpalenie,炎トークンを置く,點火 -INVESTIGATOR_ELIMINATED,Investigator Eliminated,Investigador eliminado,Investigateur éliminé,Ermittler ausgeschieden,Investigatore Eliminato,Investigador Eliminado,Wyeliminowano badacza,探索者の死亡,調查員被消滅 -CLOSE,Close,Cerrar,Fermer,Schließen,Chiudi,Fechar,Zamknij,閉じる,關閉 -PUZZLE_GUESS,Guess,Resolver,Proposer,Raten,Indovina,Tentar,Sprawdź,入力,猜測 - -UP,Up,Arriba,Haut,Oben,Su,Para Cima,Góra,上,上 -DOWN,Down,Abajo,Bas,Unten,Giù,Para Baixo,Dół,下,下 -LEFT,Left,Izquierda,Gauche,Links,Sinistra,Para a Esquerda,Lewo,左,左 -RIGHT,Right,Derecha,Droite,Rechts,Destra,Para a Direita,Prawo,右,右 - -PHASE_INVESTIGATOR,Investigator Phase,Fase de los Investigadores,Phase d'Investigateur,Ermittlerphase,Fase Degli Investigatori,Fase de Investigação,Faza badaczy,探索者フェイズ,調查階段 -PHASE_MYTHOS,Mythos Phase,Fase de Mitos,Phase de Mythe,Mythosphase,Fase Dei Miti,Fase do Mito,Faza mitów,神話フェイズ,神秘階段 -MONSTER_STEP,Monster Step,Paso de activación de monstruos,Étape de monstre,Monsterschritt,Sottofase dei Mostri,Passo dos Monstros,Etap potworów,モンスターの活動ステップ,怪物步驟 -HORROR_STEP,Horror Step,Paso de Horror,Étape d'horreur,Mythosphase,Sottofase di Orrore,Passo dos Horrores,Etap przerażenia,恐怖判定ステップ,恐怖步驟 -END_PHASE,End Phase,Fin de la fase,Terminer la phase,Phase beenden,Fine Fase,Fim de Fase,Koniec fazy,フェイズ終了,結束步驟 - -ATTACK_PROMPT,Weapon type to attack with?,¿Con qué tipo de arma vas a atacar?,Avec quel type d’arme allez-vous attaquer ?,Mit welcher Waffe?,Con quale tipo di arma vuoi attaccare?,Com que tipo de arma você quer atacar?,Jakim rodzajem broni atakujesz?,どの種類の武器で攻撃しますか?,你想在攻擊時使用哪種類型的武器? -ATTACK_WITH_HEAVY_WEAPON, Attack with a [i]Heavy Weapon[/i], Atacar con un [i]Arma pesada[/i], Attaquer avec une [i]Arme Lourde[/i], Mit einer [i]schweren Waffe[/i] angreifen, Attacca con un’[i]Arma Pesante[/i], Atacar com uma [i]Arma Pesada[/i], Atak [i]Ciężką bronią[/i], [i]大型武器[/i]で攻撃, 使用重型武器攻擊 -ATTACK_WITH_BLADED_WEAPON, Attack with a [i]Bladed Weapon[/i], Atacar con un [i]Arma de filo[/i], Attaquer avec une [i]Arme Tranchante[/i], Mit einer [i]Klingenwaffe[/i] angreifen, Attacca con un’[i]Arma da Taglio[/i], Atacar com uma [i]Arma Branca[/i], Atak [i]Bronią sieczną[/i], [i]鋭利武器[/i]で攻撃, 使用銳器攻擊 -ATTACK_WITH_FIREARM, Attack with a [i]Firearm[/i], Atacar con un [i]Arma de fuego[/i], Attaquer avec une [i]Arme à feu[/i], Mit einer [i]Schusswaffe[/i] angreifen, Attacca con un’[i]Arma da Fuoco[/i], Atacar com uma [i]Arma de Fogo[/i], Atak [i]Bronią palną[/i], [i]銃火器[/i]で攻撃, 使用槍械攻擊 -ATTACK_WITH_SPELL, Attack with a Spell, Atacar con un Hechizo, Attaquer avec un Sort, Mit einem Zauber angreifen, Attacca con un Incantesimo, Atacar com um Feitiço, Atak Zaklęciem, 呪文で攻撃, 使用法術攻擊 -ATTACK_WITH_UNARMED, Attack Unarmed, Atacar sin arma, Attaquer à mains nues, Unbewaffnet angreifen, Attacca Senza Armi, Atacar Desarmado, Atak bez broni, 素手で攻撃, 徒手攻擊 - -ACTION_X, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0} diff --git a/unity/Assets/Resources/version.txt b/unity/Assets/Resources/version.txt index 123235af1..e21e727f9 100644 --- a/unity/Assets/Resources/version.txt +++ b/unity/Assets/Resources/version.txt @@ -1 +1 @@ -1.3.1a \ No newline at end of file +1.4.0 \ No newline at end of file diff --git a/unity/Assets/Scripts/Content/ContentData.cs b/unity/Assets/Scripts/Content/ContentData.cs index c51c782a9..b63cae8ea 100644 --- a/unity/Assets/Scripts/Content/ContentData.cs +++ b/unity/Assets/Scripts/Content/ContentData.cs @@ -39,12 +39,7 @@ public class ContentData { /// The path as a string with a trailing '/'. public static string ContentPath() { - if (Application.isEditor) - { - // If running through unity then we assume you are using the git content, with the project at the same level - return Application.dataPath + "/../../content/"; - } - return Application.dataPath + "/content/"; + return Application.streamingAssetsPath + "/content/"; } /// @@ -296,13 +291,11 @@ void LoadContent(ContentPack cp) foreach(KeyValuePair> kv in cp.localizationFiles) { - DictionaryI18n packageDict = DictionaryI18n.ReadFromFileList("", kv.Value, DictionaryI18n.DEFAULT_LANG, Game.Get().currentLang); - if (packageDict == null) + DictionaryI18n packageDict = new DictionaryI18n(); + foreach(string file in kv.Value) { - // Unable to load dictionary - return; + packageDict.AddDataFromFile(file); } - packageDict.setCurrentLanguage(Game.Get().currentLang); LocalizationRead.AddDictionary(kv.Key, packageDict); } @@ -1059,6 +1052,8 @@ public class MonsterData : GenericData public string[] activations; public float healthBase = 0; public float healthPerHero = 0; + public int horror = 0; + public int awareness = 0; // This constuctor only exists for the quest version of this class to use to do nothing public MonsterData() @@ -1100,6 +1095,14 @@ public MonsterData(string name, Dictionary content, string path) { float.TryParse(content["healthperhero"], out healthPerHero); } + if (content.ContainsKey("horror")) + { + int.TryParse(content["horror"], out horror); + } + if (content.ContainsKey("awareness")) + { + int.TryParse(content["awareness"], out awareness); + } } } diff --git a/unity/Assets/Scripts/Content/DictionaryI18n.cs b/unity/Assets/Scripts/Content/DictionaryI18n.cs index 78202568a..2f50a271e 100644 --- a/unity/Assets/Scripts/Content/DictionaryI18n.cs +++ b/unity/Assets/Scripts/Content/DictionaryI18n.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text; +using ValkyrieTools; namespace Assets.Scripts.Content @@ -13,497 +14,259 @@ public class DictionaryI18n private const char QUOTES = '\"'; private const char COMMA = ','; - /// - /// Fixed value included in FFGs Localization files - /// - public const string FFG_LANGS = ".,English,Spanish,French,German,Italian,Portuguese,Polish,Japanese,Chinese,Czech"; /// /// Default initial language is English /// - public const string DEFAULT_LANG = "English"; + public string defaultLanguage = "English"; - // Languages - private string[] languages; - - // Dictionary: Will be used to store all strings of a localization file - private Dictionary dict; - // raw dict - private string[] rawDict; + public string currentLanguage = "English"; - // default language. If current language doesn't have description, default will be used. - public int defaultLanguage { get; set; } + // Each language has it's own dictionary + private Dictionary> data; - // current language. Current language to be used. - public int currentLanguage {get; set;} + // And each language has it's own raw data + Dictionary> rawData; - /* - /// - /// Dictionary constructor from a languagesList - /// - /// - public DictionaryI18n(string languagesList, int newDefaultLanguage) + protected bool loadedForEdit = false; + + public DictionaryI18n() { - languages = languagesList.Split(COMMA); - defaultLanguage = newDefaultLanguage; + data = new Dictionary>(); + rawData = new Dictionary>(); + currentLanguage = Game.Get().currentLang; } - */ - /// - /// Dictionary constructor from a localizacion file and default language - /// - /// - public DictionaryI18n(string[] languagesAndTexts, string newDefaultLanguage,string newCurrentLanguage) + public DictionaryI18n(string newDefaultLanguage) { - // Set languages list with first line of file - languages = languagesAndTexts[0].Split(COMMA); - - // Create dictionary with file lines capacity - dict = new Dictionary(languagesAndTexts.Length); - //Load raw dictionary - rawDict = languagesAndTexts; - - // Get default language - if (newDefaultLanguage != null) - { - setDefaultLanguage(newDefaultLanguage); - } - // set current language - if (newCurrentLanguage != null) - { - setCurrentLanguage(newCurrentLanguage); - } + data = new Dictionary>(); + rawData = new Dictionary>(); + currentLanguage = Game.Get().currentLang; } - /// - /// Create a dict entry with the StringI18n - /// - /// line of localization file - public void Add(EntryI18n currentKeyValues) + public DictionaryI18n(string[] languageData) { - dict[currentKeyValues.key] = currentKeyValues; + data = new Dictionary>(); + rawData = new Dictionary>(); + currentLanguage = Game.Get().currentLang; + AddData(languageData); } - /// - /// Add dict entries from another dict merging rawdata - /// - /// - public void Add(DictionaryI18n dictToCombine) + public DictionaryI18n(string[] languageData, string newDefaultLanguage) { - if (dictToCombine != null) - { - foreach (string key in dictToCombine.dict.Keys) - { - dict[key] = dictToCombine.dict[key]; - } - - int array1OriginalLength = rawDict.Length; - System.Array.Resize(ref rawDict, array1OriginalLength + dictToCombine.rawDict.Length); - System.Array.Copy(dictToCombine.rawDict, 0, rawDict, array1OriginalLength, dictToCombine.rawDict.Length); - } + data = new Dictionary>(); + rawData = new Dictionary>(); + defaultLanguage = newDefaultLanguage; + currentLanguage = Game.Get().currentLang; + AddData(languageData); } - /// - /// Create a dictionary from a list of localization files - /// - /// Base path to localization files - /// List of paths to localization files - /// default language of the new dictionary - /// current language of the new dictionary - /// - public static DictionaryI18n ReadFromFileList(string basePath, IEnumerable localizationFiles, string newDefaultLanguage, string newCurrentLanguage) + public void AddDataFromFile(string file) { - DictionaryI18n finalDict = null; - DictionaryI18n partialDict; + string[] lines; - foreach (string file in localizationFiles) + // Read the whole file + try { - // The partial dicts are created without default and current language and after loading - // all dict the default and current language will be stablished - partialDict = LocalizationRead.ReadFromFilePath(basePath + file, null, null); - if (finalDict == null) - { - finalDict = partialDict; - - } - else - { - finalDict.AddRaw(partialDict); - } + lines = System.IO.File.ReadAllLines(file); + AddData(lines); } - - if (finalDict != null) + catch (System.IO.IOException e) { - finalDict.setDefaultLanguage(newDefaultLanguage); - finalDict.setCurrentLanguage(newCurrentLanguage); + ValkyrieDebug.Log("Error loading localization file " + file + ":" + e.Message); } - - return finalDict; } - /// - /// Adds raw data to the dictionary. Current dict shouldn't have entries neither new dict. Only raw data - /// - /// - public void AddRaw(DictionaryI18n dictToCombine) + public void AddData(string[] languageData) { - if (dictToCombine == null) return; - - if (dict.Count == 0 && dictToCombine.dict.Count == 0) - { - bool found = false; - foreach (string lang in languages) - { - if (lang != "." && lang == dictToCombine.languages[1]) - { - found = true; - } - } - - // If the language already exists don't add anything - // If the new dict has more than one lang don't add anything - if (!found && dictToCombine.languages.Length == 2) - { - List rawOut = new List(); - // Generate the dictionary list - string newLanguagesList = String.Join(COMMA.ToString(), languages) + COMMA + dictToCombine.languages[1]; - rawOut.Add(newLanguagesList); - languages = newLanguagesList.Split(COMMA); - - string currentKey; - string outString; - for(int entryPos = 1; entryPos < rawDict.Length; entryPos++) - { - currentKey = rawDict[entryPos].Split(COMMA)[0] + COMMA; - - outString = rawDict[entryPos] + COMMA; - for (int newEntryPos = 1; newEntryPos < dictToCombine.rawDict.Length; newEntryPos++) - { - if (dictToCombine.rawDict[newEntryPos].StartsWith(currentKey)) - { - outString += dictToCombine.rawDict[newEntryPos].Substring(currentKey.Length); - break; - } - } - - rawOut.Add(outString); - } + string newLanguage = languageData[0].Split(COMMA)[1]; - rawDict = rawOut.ToArray(); - } else - { - ValkyrieTools.ValkyrieDebug.Log("The AddRaw method only merges a dictionary with only one new lang"); - } - } else + if (!rawData.ContainsKey(newLanguage)) { - ValkyrieTools.ValkyrieDebug.Log("The AddRaw method only merges raw dictionaries"); + rawData.Add(newLanguage, new List()); } + rawData[newLanguage].AddRange(languageData); } - - - public void Remove(string key) + protected void MakeEditable() { - if (dict.ContainsKey(key)) - { - dict.Remove(key); - } - RemoveRaw(key); - } + if (loadedForEdit) return; - private void RemoveRaw(string key) - { - RemoveRawPrefix(key + ","); - } + data = new Dictionary>(); - private void RemoveRawPrefix(string key) - { - List newRaw = new List(); - foreach (string s in rawDict) + foreach (KeyValuePair> kv in rawData) { - if (s.IndexOf(key) != 0) + data.Add(kv.Key, new Dictionary()); + foreach (string s in kv.Value) { - newRaw.Add(s); - } - } - rawDict = newRaw.ToArray(); - } + if (s.Trim().IndexOf("//") == 0) continue; - public void RemoveKeyPrefix(string prefix) - { - HashSet toRemove = new HashSet(); - foreach (string s in dict.Keys) - { - if (s.IndexOf(prefix) == 0) - { - toRemove.Add(s); - } - } - foreach (string s in toRemove) - { - Remove(s); - } - RemoveRawPrefix(prefix); - } + string[] components = s.Split(",".ToCharArray(), 2); + if (components.Length != 2) continue; - public void RenamePrefix(string oldPrefix, string newPrefix) - { - HashSet toRemove = new HashSet(); - List toAdd = new List(); - foreach (string s in dict.Keys) - { - if (s.IndexOf(oldPrefix) == 0) - { - string newKey = newPrefix + s.Substring(oldPrefix.Length); - EntryI18n e = dict[s]; - e.key = newKey; - toAdd.Add(e); - toRemove.Add(s); - } - } - foreach (string s in toRemove) - { - Remove(s); - } - foreach (EntryI18n e in toAdd) - { - dict.Add(e.key, e); - } - for (int i = 0; i < rawDict.Length; i++) - { - if (rawDict[i].IndexOf(oldPrefix) == 0) - { - rawDict[i] = newPrefix + rawDict[i].Substring(oldPrefix.Length); + if (!data[kv.Key].ContainsKey(components[0])) + { + data[kv.Key].Add(components[0], ParseEntry(components[1])); + } } } + loadedForEdit = true; } - /// - /// Method for getting all languages of this dictionary - /// - /// - public string[] getLanguages() + public void AddEntry(string key, string value) { - return languages; + AddEntry(key, value, currentLanguage); } - public void setDefaultLanguage(string languageName) + public void AddEntry(string key, string value, string language) { - int newLanguage = getPosFromName(languageName); - if (newLanguage > 0) + MakeEditable(); + if (!data.ContainsKey(language)) { - defaultLanguage = newLanguage; + data.Add(language, new Dictionary()); } - } - - /// - /// Sets current language using the string of the language. - /// If there is no language with this name, the default language - /// remains the same. - /// - /// Name of the language - public void setCurrentLanguage(string languageName) - { - int newLanguage = getPosFromName(languageName); - if (newLanguage == -1 && dict.Count == 0) + if (data[language].ContainsKey(key)) { - // If the language isn't on the list. - // There are 2 options. If we are importing the dictionaries - // we can add the new language. - // Only if all items are raw data. - // Else We set the default language - - AddRaw(new DictionaryI18n(new String[1] { ".," + languageName }, languageName, languageName)); - newLanguage = getPosFromName(languageName); + data[language][key] = value; } - - if (newLanguage > 0) - { - currentLanguage = newLanguage; - } else + else { - currentLanguage = defaultLanguage; + data[language].Add(key, value); } } - /// - /// Get language number from string - /// - /// - /// - private int getPosFromName(string languageName) + public void Remove(string key) { - for (int pos = 1; pos < languages.Length; pos++) + MakeEditable(); + foreach (Dictionary languageData in data.Values) { - if (languages[pos] == languageName) + if (languageData.ContainsKey(key)) { - return pos; + languageData.Remove(key); } } - return -1; } - /// - /// Checks if a key exists in the dictionary - /// gets its value - /// - /// key to find - /// variable to store result if exists - /// true if the key exists in the dictionary - public bool tryGetValue(string v, out EntryI18n valueOut) + public void RemoveKeyPrefix(string prefix) { - bool found = dict.TryGetValue(v, out valueOut); + MakeEditable(); - if (found) + foreach (Dictionary languageData in data.Values) { - return true; - } - else - { - // Search element - for (int pos = 1; pos < rawDict.Length; pos++) + List toRemove = new List(); + foreach (string key in languageData.Keys) { - // if the line is not empty and not slash (/) insert - if (rawDict[pos].StartsWith(v + COMMA)) + if (key.IndexOf(prefix) == 0) { - valueOut = new EntryI18n(this, GetEntry(pos)); - Add(valueOut); - return true; + toRemove.Add(key); } } - return false; + foreach (string key in toRemove) + { + languageData.Remove(key); + } } } - /// - /// Create entries for every raw data. - /// Repeated data will not replace - /// - public void flushRaw() + public void RenamePrefix(string oldPrefix, string newPrefix) { - foreach(string rawLine in rawDict) + MakeEditable(); + + foreach (Dictionary languageData in data.Values) { - string key = rawLine.Split(COMMA)[0]; - // Process non repeated list line - if (key != "." && !dict.ContainsKey(key)) + Dictionary toRename = new Dictionary(); + foreach (string key in languageData.Keys) + { + if (key.IndexOf(oldPrefix) == 0) + { + toRename.Add(key, newPrefix + key.Substring(oldPrefix.Length)); + } + } + foreach (KeyValuePair kv in toRename) { - Add(new EntryI18n(this, rawLine)); + languageData.Add(kv.Value, languageData[kv.Key]); + languageData.Remove(kv.Key); } } } - public string GetEntry(int pos) + public bool KeyExists(string key) { - string r = rawDict[pos]; - int index = pos + 1; - while(!EntryFinished(r) && index < rawDict.Length) + foreach (Dictionary languageData in data.Values) { - r += System.Environment.NewLine + rawDict[index++]; + if (languageData.ContainsKey(key)) return true; } - return r; - } - public bool EntryFinished(string entry) - { - bool quote = false; - for (int i = 0; i < entry.Length; i++) + bool found = false; + foreach (KeyValuePair> kv in rawData) { - char next = '_'; - if (i < (entry.Length - 1)) - { - next = entry[i + 1]; - } - if (!quote && entry[i] == '\\' && next == '\\') - { - return true; - } - if (entry[i] == '\"') + foreach (string raw in kv.Value) { - if (next != '\"') - { - quote = !quote; - } - else + if (raw.IndexOf(key + ',') == 0) { - i++; + if (!data.ContainsKey(kv.Key)) + { + data.Add(kv.Key, new Dictionary()); + } + data[kv.Key].Add(key, ParseEntry(raw.Substring(raw.IndexOf(',') + 1))); + found = true; } } } - return !quote; + return found; } - /// - /// Serialization of dictionary. - /// To save Localization.file - /// - /// - public List Serialize() + public string GetValue(string key) { - List result = new List(); - - // We first generate the languages line - result.Add(string.Join(COMMA.ToString(), languages)); - - // Force raw data to enter the dictionary - flushRaw(); + if (!KeyExists(key)) return key; - // and then generate the multilanguage string for each entry - foreach(EntryI18n entry in dict.Values) + // Key Exists forces the data to be in the dict, don't need to check raw + if (data.ContainsKey(currentLanguage) && data[currentLanguage].ContainsKey(key)) { - // Replace real carry returns with the \n text. - result.Add(entry.ToString()); + return data[currentLanguage][key]; } - - return result; + if (data.ContainsKey(defaultLanguage) && data[defaultLanguage].ContainsKey(key)) + { + return data[defaultLanguage][key]; + } + // Not in current or default, find any match + foreach (Dictionary langData in data.Values) + { + if (langData.ContainsKey(key)) + { + return langData[key]; + } + } + // Should never happen + return ""; } - /// - /// Serialization of dictionary. - /// To save one Localization files per language - /// - /// public Dictionary> SerializeMultiple() { - // Force raw data to enter the dictionary - flushRaw(); + if (!loadedForEdit) return rawData; - Dictionary> totalResult = new Dictionary>(); - - List oneLangResult; - - // For each language we create a new dictionary - for (int oneLang = 1; oneLang < languages.Length; oneLang++) + rawData = new Dictionary>(); + foreach (KeyValuePair> kv in data) { - bool empty = true; - // We first generate the languages line - - oneLangResult = new List(); - oneLangResult.Add(languages[0] + "," + languages[oneLang]); - - // and then generate the multilanguage string for each entry - foreach (EntryI18n entry in dict.Values) + rawData.Add(kv.Key, new List()); + foreach (KeyValuePair entry in kv.Value) { - StringBuilder text = entry.ToString(oneLang); - if (text.Length > 0) - { - empty = false; - // Replace real carry returns with the \n text. - oneLangResult.Add(new StringBuilder() - .Append(entry.key) - .Append(COMMA) - .Append(entry.ToString(oneLang)) - .ToString() - ); - } - } - - if (!empty) - { - totalResult.Add(languages[oneLang], oneLangResult); + rawData[kv.Key].Add(entry.Key + ',' + entry.Value.Replace("\n", "\\n")); } } - return totalResult; + return rawData; + } + + protected string ParseEntry(string entry) + { + string parsedReturn = entry.Replace("\\n", "\n"); + if (parsedReturn.Length > 2 && parsedReturn[0] == '\"') + { + parsedReturn = parsedReturn.Substring(1, parsedReturn.Length - 2); + parsedReturn = parsedReturn.Replace("\"\"", "\""); + } + return parsedReturn; } } } diff --git a/unity/Assets/Scripts/Content/EntryI18n.cs b/unity/Assets/Scripts/Content/EntryI18n.cs deleted file mode 100644 index c09d6fe87..000000000 --- a/unity/Assets/Scripts/Content/EntryI18n.cs +++ /dev/null @@ -1,219 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Text; -using ValkyrieTools; - -namespace Assets.Scripts.Content -{ - /// - /// String in international FFG format. Including all available languages supported by MoM App - /// - public class EntryI18n - { - - private DictionaryI18n referedDictionary; - - /// - /// Instance info of the current translations - /// - private string[] translations; - - /// - /// Creates an empty instance of a Multilanguage String - /// - public EntryI18n(string key,DictionaryI18n dict) - { - referedDictionary = dict; - translations = new string[dict.getLanguages().Length]; - translations[0] = key; - } - - private const char QUOTES = '\"'; - private const char COMMA = ','; - - /// - /// Constructor with the complete localisation elements - /// - /// - public EntryI18n(DictionaryI18n dict,string completeLocalizationString) - { - referedDictionary = dict; - - string newLinedCompleteLocalizationString = completeLocalizationString.Replace("\\n", "\n"); - - if (newLinedCompleteLocalizationString.Contains(QUOTES)) - { - // with quotes, commas inside quotes isn't considered separator - List partialTranslation = new List(newLinedCompleteLocalizationString.Split(COMMA)); - List finalTranslation = new List(); - string currentTranslation = ""; - bool oddity = false; - foreach (string suposedTranslation in partialTranslation) - { - currentTranslation += suposedTranslation; - - // Counting quotes inside string to know oddity. - bool newOddity = (suposedTranslation.Count(ch => ch == QUOTES) % 2) == 1; - - if (oddity ^ newOddity) - { - // If oddity changes we are still inside quotes - currentTranslation += COMMA; - } - else - { - // If opening and closing quotes, we supress it. - if (currentTranslation.Length > 0 && currentTranslation[0] == QUOTES) - { - currentTranslation = currentTranslation.Substring(1, currentTranslation.Length - 2); - } - - // escaping double quotes - finalTranslation.Add(currentTranslation.Replace("\"\"", "\"")); - currentTranslation = ""; - } - - oddity = oddity ^ newOddity; - - } - translations = finalTranslation.ToArray(); - } - else - { - // Without quotes, all commas are separators - translations = newLinedCompleteLocalizationString.Split(COMMA); - } - - if (translations.Length > dict.getLanguages().Length) - { - ValkyrieDebug.Log("Incoherent DictI18n with " + dict.getLanguages().Length + " languages including StringI18n with " + translations.Length + " languages : " + newLinedCompleteLocalizationString + System.Environment.NewLine); - } - } - - // The key is que position 0 of the array - public string key - { - get - { - return translations[0]; - } - set - { - translations[0] = value; - } - } - - public string getSpecificLanguageString(int nLanguage) - { - return translations[nLanguage]; - } - - /// - /// In translation of texts. If we don't have current language text, a - /// specific language text will be got. In order to know if there is a - /// current language text the method HasTextInCurrentLanguage can be used. - /// - /// number of the language to use - /// - public string getCurrentOrDefaultLanguageString() - { - if (HasTextInCurrentLanguage) - { - return currentLanguageString; - } else - { - return getSpecificLanguageString(referedDictionary.defaultLanguage); - } - } - - /// - /// The string value of the key with the current language - /// - public string currentLanguageString - { - get - { - if (referedDictionary.currentLanguage < translations.Length && referedDictionary.currentLanguage > 0) - { - return translations[referedDictionary.currentLanguage]; - } - else - { - return ""; - } - - } - set - { - // Change value - translations[referedDictionary.currentLanguage] = value; - // and update dictionary - referedDictionary.Add(this); - } - } - - public bool HasTextInCurrentLanguage - { - get - { - return currentLanguageString.Length > 0; - } - } - - /// - /// String representation of the multilanguage element - /// - /// - public override string ToString() - { - StringBuilder result = new StringBuilder(); - - bool first = true; - for (int oneTranslation = 0; oneTranslation < translations.Length; oneTranslation++) - { - if (!first) - { - result.Append(COMMA); - } - result.Append(ToString(oneTranslation)); - - if (first) - { - first = false; - } - } - - return result.ToString(); - } - /// - /// String representation of the multilanguage element - /// - /// - public StringBuilder ToString(int nLanguage) - { - StringBuilder result = new StringBuilder(); - - string currentTranslation; - if (nLanguage < translations.Length && translations[nLanguage] != null) - { - // All carryreturns are replaced - currentTranslation = translations[nLanguage] - .Replace(System.Environment.NewLine, "\\n") - .Replace("\n", "\\n") - .Replace("\r", "\\n"); - - if (currentTranslation.Contains(COMMA) || currentTranslation.Contains(QUOTES)) - { - // The serializable text should repeat mid quotes and add initial and final quotes - result.Append(QUOTES).Append(currentTranslation.Replace(QUOTES.ToString(), "\"\"")).Append(QUOTES); - } - else - { - result.Append(currentTranslation); - } - } - - return result; - } - } -} diff --git a/unity/Assets/Scripts/Content/LocalizationRead.cs b/unity/Assets/Scripts/Content/LocalizationRead.cs index 2edfea65c..8b6700588 100644 --- a/unity/Assets/Scripts/Content/LocalizationRead.cs +++ b/unity/Assets/Scripts/Content/LocalizationRead.cs @@ -20,59 +20,10 @@ public static void changeCurrentLangTo(string newLang) { foreach (DictionaryI18n d in dicts.Values) { - d.setCurrentLanguage(newLang); + d.currentLanguage = newLang; } } - // Function takes Unity TextAsset and returns localization dictionary - public static DictionaryI18n ReadFromTextAsset(TextAsset asset, string newCurrentLang) - { - string[] lines; - try - { - // split text into array of lines - lines = asset.text.Split(new string[] { "\r", "\n" }, System.StringSplitOptions.RemoveEmptyEntries); - } - catch (System.Exception e) - { - ValkyrieDebug.Log("Error loading localization from asset " + asset.name + ":" + e.Message); - return null; - } - - // The assets has the english as default language - return new DictionaryI18n(lines,DictionaryI18n.DEFAULT_LANG,newCurrentLang); - } - - // Function takes path to localization file and returns data object - // Returns null on error - public static DictionaryI18n ReadFromFilePath(string path, string newDefaultLang, string newCurrentLang) - { - string[] lines; - - // Read the whole file - try - { - lines = System.IO.File.ReadAllLines(path); - } - catch (System.Exception e) - { - ValkyrieDebug.Log("Error loading localization file " + path + ":" + e.Message); - return null; - } - // Parse text data - return new DictionaryI18n(lines, newDefaultLang,newCurrentLang); - } - - - // Function ini file contents as a string and returns data object - // Returns null on error - public static DictionaryI18n ReadFromString(string content, string newDefaultLang, string newCurrentLang) - { - // split text into array of lines - string[] lines = content.Split(new string[] { "\r", "\n" }, System.StringSplitOptions.RemoveEmptyEntries); - return new DictionaryI18n(lines, newDefaultLang, newCurrentLang); - } - private const int RECURSIVE_LIMIT = 10; // Check for FFG text lookups and insert required text @@ -276,8 +227,7 @@ private static bool CheckDictQuery(string dict, string input) DictionaryI18n currentDict = selectDictionary(dict); if (currentDict == null) return false; - EntryI18n valueOut; - return currentDict.tryGetValue(elements[0], out valueOut); + return currentDict.KeyExists(elements[0]); } /// @@ -287,31 +237,7 @@ private static bool CheckDictQuery(string dict, string input) /// text to insert in current language public static void updateScenarioText(string key, string text) { - EntryI18n entry; - // Search for localization string - if (!dicts["qst"].tryGetValue(key, out entry)) - { - // if not exists, we create a new one - entry = new EntryI18n(key, dicts["qst"]); - } - - entry.currentLanguageString = text; - } - - /// - /// Replaces all dictionary entries with old key and replaces with the new one - /// - /// - /// - public static void replaceScenarioText(string oldKey,string newKey) - { - EntryI18n entry; - // Search for localization string - if (dicts["qst"].tryGetValue(oldKey, out entry)) - { - entry.key = newKey; - dicts["qst"].Add(entry); - } + dicts["qst"].AddEntry(key, text); } /// @@ -324,23 +250,12 @@ private static string DictKeyLookup(string dict, string key) { DictionaryI18n currentDict = selectDictionary(dict); - if (currentDict != null) - { - EntryI18n valueOut; - - if (currentDict.tryGetValue(key, out valueOut)) - { - return valueOut.getCurrentOrDefaultLanguageString(); - } - else - { - return key; - } - } else + if (currentDict == null) { ValkyrieDebug.Log("Error: current dictionary not loaded"); + return key; } - return key; + return currentDict.GetValue(key); } /// diff --git a/unity/Assets/Scripts/Content/QuestData.cs b/unity/Assets/Scripts/Content/QuestData.cs index eed0a28a8..2cb065d49 100644 --- a/unity/Assets/Scripts/Content/QuestData.cs +++ b/unity/Assets/Scripts/Content/QuestData.cs @@ -103,8 +103,11 @@ public void LoadQuestData() } // Reset scenario dict - LocalizationRead.AddDictionary("qst", DictionaryI18n.ReadFromFileList - ("",localizationFiles,game.currentLang,game.currentLang)); + DictionaryI18n qstDict = new DictionaryI18n(game.currentLang); + foreach (string file in localizationFiles) + { + qstDict.AddDataFromFile(file); + } foreach (string f in iniFiles) { @@ -1417,25 +1420,6 @@ virtual public void RemoveReference(string refName) ChangeReference(refName, ""); } - /// - /// Updates de dicionary with new text and generates a StringKey element - /// - /// key to update/create - /// text in current language - /// - protected StringKey AfterRenameUpdateDictionaryTextAndGenKey(string oldkey, string newName) - { - string[] split = oldkey.Split('.'); - string newKey = new StringBuilder() - .Append(newName).Append('.').Append(split[1]).ToString(); - - // update or create scenario text in current language - LocalizationRead.replaceScenarioText(oldkey, newKey); - - //return the stringkey - return new StringKey("qst", newKey); - } - // Save to string (editor) override public string ToString() { @@ -1470,6 +1454,10 @@ public class CustomMonster : QuestComponent public bool healthDefined = false; public string evadeEvent = ""; public string horrorEvent = ""; + public int horror = 0; + public bool horrorDefined = false; + public int awareness = 0; + public bool awarenessDefined = false; public string monstername_key { get { return genKey("monstername"); } } public string info_key { get { return genKey("info"); } } @@ -1541,6 +1529,17 @@ public CustomMonster(string iniName, Dictionary data, string pat { horrorEvent = data["horrorevent"]; } + + if (data.ContainsKey("horror")) + { + horrorDefined = true; + int.TryParse(data["horror"], out horror); + } + if (data.ContainsKey("awareness")) + { + awarenessDefined = true; + int.TryParse(data["awareness"], out awareness); + } } // get path of monster image @@ -1601,6 +1600,16 @@ override public string ToString() { r.Append("horrorevent=").AppendLine(horrorEvent); } + + if (horrorDefined) + { + r.Append("horror=").AppendLine(horror.ToString()); + } + if (awarenessDefined) + { + r.Append("awareness=").AppendLine(awareness.ToString()); + } + return r.ToString(); } } @@ -1798,9 +1807,9 @@ override public void ChangeReference(string oldName, string newName) // Quest ini component has special data public class Quest { - public static int minumumFormat = 3; + public static int minumumFormat = 4; // Increment during changes, and again at release - public static int currentFormat = 6; + public static int currentFormat = 7; public int format = 0; public bool hidden = false; public bool valid = false; @@ -1810,7 +1819,7 @@ public class Quest // Content packs required for quest public string[] packs; // Default language for the text - public string defaultLanguage = DictionaryI18n.DEFAULT_LANG; + public string defaultLanguage = "English"; // raw localization dictionary public DictionaryI18n localizationDict = null; @@ -1839,22 +1848,10 @@ public Quest(string pathIn) //Read the localization data Dictionary localizationData = IniRead.ReadFromIni(path + "/quest.ini", "QuestText"); - if (localizationData == null || localizationData.Keys.Count == 0) - { - localizationDict = new DictionaryI18n( - new string[1] { DictionaryI18n.FFG_LANGS }, defaultLanguage, Game.Get().currentLang); - } - else + localizationDict = new DictionaryI18n(defaultLanguage); + foreach (string file in localizationData.Keys) { - localizationDict = DictionaryI18n.ReadFromFileList - (path + "/", localizationData.Keys, defaultLanguage, Game.Get().currentLang); - if (localizationDict == null) - { - // Unable to load dictionary - return; - } - localizationDict.setDefaultLanguage(defaultLanguage); - localizationDict.setCurrentLanguage(Game.Get().currentLang); + localizationDict.AddDataFromFile(path + '/' + file); } valid = Populate(iniData); @@ -1866,8 +1863,7 @@ public Quest(Dictionary iniData) localizationDict = LocalizationRead.dicts["qst"]; if (localizationDict == null) { - localizationDict = new DictionaryI18n( - new string[1] { DictionaryI18n.FFG_LANGS }, defaultLanguage, Game.Get().currentLang); + localizationDict = new DictionaryI18n(new string[1] { ".," + Game.Get().currentLang }, defaultLanguage); } valid = Populate(iniData); } @@ -1935,8 +1931,7 @@ public bool Populate(Dictionary iniData) if (iniData.ContainsKey("defaultlanguage")) { defaultLanguage = iniData["defaultlanguage"]; - localizationDict.setDefaultLanguage(defaultLanguage); - localizationDict.setCurrentLanguage(Game.Get().currentLang); + localizationDict.defaultLanguage = defaultLanguage; } if (iniData.ContainsKey("hidden")) @@ -1950,7 +1945,7 @@ public bool Populate(Dictionary iniData) } if (minHero < 1) minHero = 1; - maxHero = Game.Get().gameType.MaxHeroes(); + maxHero = Game.Get().gameType.DefaultHeroes(); if (iniData.ContainsKey("maxhero")) { int.TryParse(iniData["maxhero"], out maxHero); @@ -2003,7 +1998,7 @@ override public string ToString() { r.Append("minhero=").AppendLine(minHero.ToString()); } - if (maxHero != Game.Get().gameType.MaxHeroes()) + if (maxHero != Game.Get().gameType.DefaultHeroes()) { r.Append("maxhero=").AppendLine(maxHero.ToString()); } diff --git a/unity/Assets/Scripts/Content/QuestDownload.cs b/unity/Assets/Scripts/Content/QuestDownload.cs index 64846298a..f58b9291f 100644 --- a/unity/Assets/Scripts/Content/QuestDownload.cs +++ b/unity/Assets/Scripts/Content/QuestDownload.cs @@ -13,79 +13,78 @@ public class QuestDownload : MonoBehaviour public Dictionary questList; public WWW download; - public string serverLocation = "https://raw.githubusercontent.com/NPBruce/valkyrie-store/"; public Game game; - IniData remoteManifest; + List remoteQuests; IniData localManifest; DictionaryI18n localizationDict; Dictionary textures; + /// + /// Download required files then draw screen + /// void Start() { + new LoadingScreen(new StringKey("val", "DOWNLOAD_LIST").Translate()); game = Game.Get(); textures = new Dictionary(); - // For development builds use the development branch of the store - if (char.IsNumber(game.version[game.version.Length - 1])) - { - serverLocation += "master/"; - } - else - { - serverLocation += "development/"; - } - string remoteManifest = serverLocation + game.gameType.TypeName() + "/manifest.ini"; - StartCoroutine(Download(remoteManifest, delegate { DownloadImages(); })); + remoteQuests = new List(); + string remoteManifest = GetServerLocation() + "manifest.ini"; + StartCoroutine(Download(remoteManifest, DownloadManifest)); } - public void DownloadImages(Stack images = null) + /// + /// Get the default server list location + /// + /// the path to the remote files + public static string GetServerLocation() { - string remoteDict = serverLocation + game.gameType.TypeName() + "/Localization.txt"; + string[] text = File.ReadAllLines(Application.streamingAssetsPath + "/text/download.txt"); + return text[0] + Game.Get().gameType.TypeName() + "/"; + } - if (images == null) + /// + /// Parse the downloaded remote manifest and start download of individual quest files + /// + public void DownloadManifest() + { + if (download.error != null) Application.Quit(); + IniData remoteManifest = IniRead.ReadFromString(download.text); + foreach (KeyValuePair> kv in remoteManifest.data) { - remoteManifest = IniRead.ReadFromString(download.text); - images = new Stack(); - foreach (KeyValuePair> kv in remoteManifest.data) - { - if (remoteManifest.Get(kv.Key, "image").Length > 0) - { - images.Push(remoteManifest.Get(kv.Key, "image")); - } - } - if (images.Count == 0) - { - StartCoroutine(Download(remoteDict, delegate { ReadManifest(); })); - return; - } - StartCoroutine(Download(serverLocation + game.gameType.TypeName() + "/" + images.Peek(), delegate { DownloadImages(images); })); - return; + remoteQuests.Add(new RemoteQuest(kv)); } + DownloadQuestFiles(); + } - if (download.error == null) - { - textures.Add(images.Pop(), download.texture); - } - else - { - images.Pop(); - } - if (images.Count > 0) + /// + /// Called to check if there are any more quest components to download, when finished downloads dictionary + /// + public void DownloadQuestFiles() + { + foreach (RemoteQuest rq in remoteQuests) { - StartCoroutine(Download(serverLocation + game.gameType.TypeName() + "/" + images.Peek(), delegate { DownloadImages(images); })); - return; + if (!rq.FetchContent(this, DownloadQuestFiles)) return; } - - StartCoroutine(Download(remoteDict, delegate { ReadManifest(); })); + string remoteDict = GetServerLocation() + "Localization.txt"; + StartCoroutine(Download(remoteDict, ReadDict)); } - public void ReadManifest() + /// + /// Read the downloaded dictionary, draw screen + /// + public void ReadDict() { - localizationDict = LocalizationRead.ReadFromString(download.text, DictionaryI18n.DEFAULT_LANG, game.currentLang); + if (download.error != null) Application.Quit(); + localizationDict = new DictionaryI18n(download.text); DrawList(); } + /// + /// Draw download options screen + /// public void DrawList() { + Destroyer.Dialog(); localManifest = IniRead.ReadFromString(""); if (File.Exists(saveLocation() + "/manifest.ini")) { @@ -106,14 +105,14 @@ public void DrawList() // Start here float offset = 0; // Loop through all available quests - foreach (KeyValuePair> kv in remoteManifest.data) + foreach (RemoteQuest rq in remoteQuests) { - string file = kv.Key + ".valkyrie"; + string file = rq.name + ".valkyrie"; LocalizationRead.AddDictionary("qst", localizationDict); - string questName = new StringKey("qst", kv.Key + ".name").Translate(); + string questName = new StringKey("qst", rq.name + ".name").Translate(); int remoteFormat = 0; - int.TryParse(remoteManifest.Get(kv.Key, "format"), out remoteFormat); + int.TryParse(rq.GetData("format"), out remoteFormat); bool formatOK = (remoteFormat >= QuestData.Quest.minumumFormat) && (remoteFormat <= QuestData.Quest.currentFormat); if (!formatOK) continue; @@ -122,8 +121,8 @@ public void DrawList() bool update = true; if (exists) { - string localHash = localManifest.Get(kv.Key, "version"); - string remoteHash = remoteManifest.Get(kv.Key, "version"); + string localHash = localManifest.Get(rq.name, "version"); + string remoteHash = rq.GetData("version"); update = !localHash.Equals(remoteHash); } @@ -142,21 +141,18 @@ public void DrawList() ui = new UIElement(scrollArea.GetScrollTransform()); ui.SetLocation(0.95f, offset, UIScaler.GetWidthUnits() - 4.9f, 3.1f); ui.SetBGColor(bg); - if (update) ui.SetButton(delegate { Selection(file); }); + if (update) ui.SetButton(delegate { Selection(rq); }); offset += 0.05f; // Draw Image ui = new UIElement(scrollArea.GetScrollTransform()); ui.SetLocation(1, offset, 3, 3); ui.SetBGColor(bg); - if (update) ui.SetButton(delegate { Selection(file); }); - - int.TryParse(remoteManifest.Get(kv.Key, "format"), out remoteFormat); - + if (update) ui.SetButton(delegate { Selection(rq); }); - if (textures.ContainsKey(remoteManifest.Get(kv.Key, "image"))) + if (rq.image != null) { - ui.SetImage(textures[remoteManifest.Get(kv.Key, "image")]); + ui.SetImage(rq.image); } ui = new UIElement(scrollArea.GetScrollTransform()); @@ -171,17 +167,17 @@ public void DrawList() { ui.SetText(questName, Color.black); } - if (update) ui.SetButton(delegate { Selection(file); }); + if (update) ui.SetButton(delegate { Selection(rq); }); ui.SetTextAlignment(TextAnchor.MiddleLeft); ui.SetFontSize(Mathf.RoundToInt(UIScaler.GetSmallFont() * 1.3f)); // Duration int lengthMax = 0; - int.TryParse(remoteManifest.Get(kv.Key, "lengthmax"), out lengthMax); + int.TryParse(rq.GetData("lengthmax"), out lengthMax); if (lengthMax > 0) { int lengthMin = 0; - int.TryParse(remoteManifest.Get(kv.Key, "lengthmin"), out lengthMin); + int.TryParse(rq.GetData("lengthmin"), out lengthMin); ui = new UIElement(scrollArea.GetScrollTransform()); ui.SetLocation(UIScaler.GetRight(-11), offset, 2, 1); @@ -201,7 +197,7 @@ public void DrawList() // Difficulty float difficulty = 0; - float.TryParse(remoteManifest.Get(kv.Key, "difficulty"), out difficulty); + float.TryParse(rq.GetData("difficulty"), out difficulty); if (difficulty != 0) { string symbol = "π"; // will @@ -210,7 +206,7 @@ public void DrawList() symbol = new StringKey("val", "ICON_SUCCESS_RESULT").Translate(); } ui = new UIElement(scrollArea.GetScrollTransform()); - ui.SetLocation(UIScaler.GetRight(-12), offset + 1, 7, 2); + ui.SetLocation(UIScaler.GetRight(-13), offset + 1, 9, 2); ui.SetText(symbol + symbol + symbol + symbol + symbol, Color.black); ui.SetBGColor(Color.clear); ui.SetFontSize(UIScaler.GetMediumFont()); @@ -238,7 +234,13 @@ public void DrawList() foreach (KeyValuePair> kv in localManifest.data) { // Only looking for files missing from remote - if (remoteManifest.data.ContainsKey(kv.Key)) continue; + bool onRemote = false; + foreach (RemoteQuest rq in remoteQuests) + { + if (rq.name.Equals(kv.Key)) onRemote = true; + } + if (onRemote) continue; + string type = localManifest.Get(kv.Key, "type"); // Only looking for packages of this game type @@ -287,12 +289,21 @@ public void Cancel() new QuestSelectionScreen(ql); } - public void Selection(string file) + /// + /// Select to download + /// + /// Remote quest to download + public void Selection(RemoteQuest rq) { - string package = serverLocation + game.gameType.TypeName() + "/" + file; - StartCoroutine(Download(package, delegate { Save(file); })); + new LoadingScreen(download, new StringKey("val", "DOWNLOAD_PACKAGE").Translate()); + string package = rq.path + rq.name + ".valkyrie"; + StartCoroutine(Download(package, delegate { Save(rq); })); } + /// + /// Select to delete + /// + /// File name to delete public void Delete(string file) { string toDelete = saveLocation() + "/" + file; @@ -301,25 +312,23 @@ public void Delete(string file) DrawList(); } - public void Save(string file) + /// + /// Called after download finished to save to disk + /// + /// Remote quest to save + public void Save(RemoteQuest rq) { QuestLoader.mkDir(saveLocation()); // Write to disk - using (BinaryWriter writer = new BinaryWriter(File.Open(saveLocation() + "/" + file, FileMode.Create))) + using (BinaryWriter writer = new BinaryWriter(File.Open(saveLocation() + "/" + rq.name + ".valkyrie", FileMode.Create))) { writer.Write(download.bytes); writer.Close(); } - string section = file.Substring(0, file.Length - ".valkyrie".Length); - int localVersion, remoteVersion; - int.TryParse(localManifest.Get(section, "version"), out localVersion); - int.TryParse(remoteManifest.Get(section, "version"), out remoteVersion); - - localManifest.Remove(section); - localManifest.Add(section, remoteManifest.Get(section)); - + localManifest.Remove(rq.name); + localManifest.Add(rq.name, rq.data); if (File.Exists(saveLocation() + "/manifest.ini")) { @@ -331,12 +340,20 @@ public void Save(string file) DrawList(); } + /// + /// Get save directory without trailing '/' + /// + /// localtion to save packages public string saveLocation() { return Game.AppData() + "/Download"; } - // Return to main menu + /// + /// Download and call function + /// + /// Path to download + /// function to call on completion public IEnumerator Download(string file, UnityEngine.Events.UnityAction call) { download = new WWW(file); @@ -345,8 +362,107 @@ public IEnumerator Download(string file, UnityEngine.Events.UnityAction call) { // fixme not fatal ValkyrieDebug.Log(download.error); - Application.Quit(); + //Application.Quit(); } call(); } + + public class RemoteQuest + { + public string path; + public string name; + public Texture2D image; + bool imageError = false; + bool iniError = false; + public Dictionary data; + + /// + /// Constuct remote quest object from manifest data + /// + /// manifest data + public RemoteQuest(KeyValuePair> kv) + { + name = kv.Key; + path = QuestDownload.GetServerLocation(); + data = kv.Value; + if (data.ContainsKey("external")) + { + path = data["external"]; + } + } + + /// + /// Extract manifest value + /// + /// manifest key + /// Value or empty string if not present + public string GetData(string key) + { + if (data.ContainsKey(key)) return data[key]; + return ""; + } + + /// + /// Fetch next required file + /// + /// QuestDownloadObeject to use + /// Function to call after download + /// true if no downloads required + public bool FetchContent(QuestDownload qd, UnityEngine.Events.UnityAction call) + { + if (data.ContainsKey("external")) + { + if (iniError) return true; + + qd.StartCoroutine(qd.Download(path + name + ".ini", delegate { IniFetched(qd.download, call); })); + return false; + } + if (data.ContainsKey("image") && data["image"].Length > 0) + { + if (image == null && !imageError) + { + qd.StartCoroutine(qd.Download(path + data["image"], delegate { ImageFetched(qd.download, call); })); + return false; + } + } + return true; + } + + /// + /// Parse downloaded ini data + /// + /// download object + /// Function to call after parse + public void IniFetched(WWW download, UnityEngine.Events.UnityAction call) + { + if (download.error == null) + { + IniData remoteManifest = IniRead.ReadFromString(download.text); + data = remoteManifest.Get("Quest"); + } + else + { + iniError = true; + } + call(); + } + + /// + /// Parse downloaded image data + /// + /// download object + /// Function to call after parse + public void ImageFetched(WWW download, UnityEngine.Events.UnityAction call) + { + if (download.error == null) + { + image = download.texture; + } + else + { + imageError = true; + } + call(); + } + } } diff --git a/unity/Assets/Scripts/Game.cs b/unity/Assets/Scripts/Game.cs index 0268a6b76..36c93e61c 100644 --- a/unity/Assets/Scripts/Game.cs +++ b/unity/Assets/Scripts/Game.cs @@ -66,6 +66,9 @@ public class Game : MonoBehaviour { // Quest started as test from editor public bool testMode = false; + // List of things that want to know if the mouse is clicked + protected List updateList; + // Import thread public GameSelectionScreen gameSelect; @@ -103,25 +106,22 @@ void Start() config = new ConfigFile(); GameObject go = new GameObject("audio"); audioControl = go.AddComponent