diff --git a/ReadMe.md b/ReadMe.md
index cb9eb1da..6be2d184 100644
--- a/ReadMe.md
+++ b/ReadMe.md
@@ -1,3 +1,24 @@
+# Modded from the original timespinner randomizer
+
+This Modded Tsrandomizer has an extra options menu for a few Quality of life settings:
+
+-Auto Skip Cutscenes and dialogue instantly
+
+-Change item stack cap (9, 25, 50, 99)
+
+-Toast Popup Speed options (Fast, Vanilla)
+
+-Toasts Block Movement (On, Off)
+
+stack cap is intended to make other players in large multiworlds feel like their 70th Berry Pick-mi up matters.
+
+this QoL Mod is intended to make gameplay smoother for players who know Timespinner by heart and don't want control taken away from them every time they obtain an orb #LetMePlayDamnIt
+
+You can find the menu in the options menu
+
+
+
+
# General
Timespinner Randomizer will randomize the location of items such as equipment, relics, familiars, stat boosts, use items. The logic makes sure that each game you play is beatable.
diff --git a/TsRandomizer.ItemTracker/TsRandomizer.ItemTracker.csproj b/TsRandomizer.ItemTracker/TsRandomizer.ItemTracker.csproj
index 6981e697..5433c113 100644
--- a/TsRandomizer.ItemTracker/TsRandomizer.ItemTracker.csproj
+++ b/TsRandomizer.ItemTracker/TsRandomizer.ItemTracker.csproj
@@ -1,134 +1,129 @@
-
-
-
-
- Debug
- AnyCPU
- {4D86B28B-95A2-4452-9BAA-C8E80F026101}
- WinExe
- TsRandomizerItemTracker
- TsRandomizerItemTracker
- v4.8
- 512
- true
-
-
-
- x86
- true
- full
- false
- ..\..\TestVersions\DRMFreeVersion\
- DEBUG;TRACE
- prompt
- 4
-
-
- x86
- pdbonly
- true
- C:\Program Files %28x86%29\Steam\steamapps\common\Timespinner\
- TRACE
- prompt
- 4
-
-
- true
-
-
-
-
-
- EyeOrbLargeT.ico
-
-
- true
- bin\x64\Debug\
- DEBUG;TRACE
- full
- x64
- prompt
- MinimumRecommendedRules.ruleset
-
-
- bin\x64\Release\
- TRACE
- true
- pdbonly
- x64
- prompt
- MinimumRecommendedRules.ruleset
-
-
- true
- bin\Debug DRM Free %28GoG%29\
- DEBUG;TRACE
- full
- x86
- 7.3
- prompt
-
-
- true
- bin\x64\Debug DRM Free %28GoG%29\
- DEBUG;TRACE
- full
- x64
- 7.3
- prompt
- MinimumRecommendedRules.ruleset
-
-
- bin\Release DRM Free %28GoG%29\
- TRACE
- true
- pdbonly
- x86
- 7.3
- prompt
-
-
- bin\x64\Release DRM Free %28GoG%29\
- TRACE
- true
- pdbonly
- x64
- 7.3
- prompt
- MinimumRecommendedRules.ruleset
-
-
-
- False
- C:\Program Files (x86)\Steam\steamapps\common\Timespinner\FNA.dll
- False
-
-
-
-
-
-
- False
- C:\Program Files (x86)\Steam\steamapps\common\Timespinner\Timespinner.exe
- False
-
-
- False
- ..\..\TestVersions\SteamVerison\TsRandomizer.exe
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ Debug
+ AnyCPU
+ {4D86B28B-95A2-4452-9BAA-C8E80F026101}
+ WinExe
+ TsRandomizerItemTracker
+ TsRandomizerItemTracker
+ v4.8
+ 512
+ true
+
+
+
+ x86
+ true
+ full
+ false
+ ..\..\TestVersions\DRMFreeVersion\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ x86
+ pdbonly
+ true
+ C:\Program Files %28x86%29\Steam\steamapps\common\Timespinner\
+ TRACE
+ prompt
+ 4
+
+
+ true
+
+
+
+
+
+ EyeOrbLargeT.ico
+
+
+ true
+ bin\x64\Debug\
+ DEBUG;TRACE
+ full
+ x64
+ prompt
+ MinimumRecommendedRules.ruleset
+
+
+ bin\x64\Release\
+ TRACE
+ true
+ pdbonly
+ x64
+ prompt
+ MinimumRecommendedRules.ruleset
+
+
+ true
+ bin\Debug DRM Free %28GoG%29\
+ DEBUG;TRACE
+ full
+ x86
+ 7.3
+ prompt
+
+
+ true
+ bin\x64\Debug DRM Free %28GoG%29\
+ DEBUG;TRACE
+ full
+ x64
+ 7.3
+ prompt
+ MinimumRecommendedRules.ruleset
+
+
+ bin\Release DRM Free %28GoG%29\
+ TRACE
+ true
+ pdbonly
+ x86
+ 7.3
+ prompt
+
+
+ bin\x64\Release DRM Free %28GoG%29\
+ TRACE
+ true
+ pdbonly
+ x64
+ 7.3
+ prompt
+ MinimumRecommendedRules.ruleset
+
+
+
+ ..\..\TestVersions\DebugDRMFreeGoG\FNA.dll
+
+
+
+
+
+
+ ..\..\TestVersions\DebugDRMFreeGoG\Timespinner.exe
+
+
+ ..\..\TestVersions\DebugDRMFreeGoG\TsRandomizer.exe
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/TsRandomizer.SeedGeneratah/TsRandomizer.SeedGeneratah.csproj b/TsRandomizer.SeedGeneratah/TsRandomizer.SeedGeneratah.csproj
index 91c94d1e..fb586415 100644
--- a/TsRandomizer.SeedGeneratah/TsRandomizer.SeedGeneratah.csproj
+++ b/TsRandomizer.SeedGeneratah/TsRandomizer.SeedGeneratah.csproj
@@ -1,75 +1,71 @@
-
-
-
-
- Debug
- AnyCPU
- {C30E617E-83DB-4C7C-9E45-0166C781A0A8}
- Exe
- TsRandomizerSeedGeneratah
- TsRandomizerSeedGeneratah
- v4.0
- 512
- true
- Client
-
-
- x86
- true
- full
- false
- C:\Program Files %28x86%29\Steam\steamapps\common\Timespinner\
- DEBUG;TRACE
- prompt
- 4
-
-
- x86
- pdbonly
- true
- C:\Program Files %28x86%29\Steam\steamapps\common\Timespinner\
- TRACE
- prompt
- 4
-
-
- true
- bin\Debug DRM Free %28GoG%29\
- DEBUG;TRACE
- full
- x86
- 7.3
- prompt
-
-
- bin\Release DRM Free %28GoG%29\
- TRACE
- true
- pdbonly
- x86
- 7.3
- prompt
-
-
-
-
- False
- ..\..\TestVersions\SteamVersion\Timespinner.exe
- False
-
-
- False
- ..\..\TestVersions\SteamVerison\TsRandomizer.exe
- False
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ Debug
+ AnyCPU
+ {C30E617E-83DB-4C7C-9E45-0166C781A0A8}
+ Exe
+ TsRandomizerSeedGeneratah
+ TsRandomizerSeedGeneratah
+ v4.0
+ 512
+ true
+ Client
+
+
+ x86
+ true
+ full
+ false
+ C:\Program Files %28x86%29\Steam\steamapps\common\Timespinner\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ x86
+ pdbonly
+ true
+ C:\Program Files %28x86%29\Steam\steamapps\common\Timespinner\
+ TRACE
+ prompt
+ 4
+
+
+ true
+ bin\Debug DRM Free %28GoG%29\
+ DEBUG;TRACE
+ full
+ x86
+ 7.3
+ prompt
+
+
+ bin\Release DRM Free %28GoG%29\
+ TRACE
+ true
+ pdbonly
+ x86
+ 7.3
+ prompt
+
+
+
+
+ ..\..\TestVersions\DebugDRMFreeGoG\Timespinner.exe
+
+
+ ..\..\TestVersions\DebugDRMFreeGoG\TsRandomizer.exe
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/TsRandomizer.sln b/TsRandomizer.sln
index e99f34b5..0471c57a 100644
--- a/TsRandomizer.sln
+++ b/TsRandomizer.sln
@@ -1,118 +1,112 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.31829.152
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TsRandomizer", "TsRandomizer\TsRandomizer.csproj", "{694E46C5-FD46-4CC8-8B71-D381E154CCF7}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{CD8103A2-141D-4AD0-B2FE-533D86875C9C}"
- ProjectSection(SolutionItems) = preProject
- ReadMe.md = ReadMe.md
- EndProjectSection
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TsRandomizer.ItemTracker", "TsRandomizer.ItemTracker\TsRandomizer.ItemTracker.csproj", "{4D86B28B-95A2-4452-9BAA-C8E80F026101}"
- ProjectSection(ProjectDependencies) = postProject
- {694E46C5-FD46-4CC8-8B71-D381E154CCF7} = {694E46C5-FD46-4CC8-8B71-D381E154CCF7}
- EndProjectSection
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TsRandomizer.SeedGeneratah", "TsRandomizer.SeedGeneratah\TsRandomizer.SeedGeneratah.csproj", "{C30E617E-83DB-4C7C-9E45-0166C781A0A8}"
- ProjectSection(ProjectDependencies) = postProject
- {694E46C5-FD46-4CC8-8B71-D381E154CCF7} = {694E46C5-FD46-4CC8-8B71-D381E154CCF7}
- EndProjectSection
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug DRM Free (GoG)|Any CPU = Debug DRM Free (GoG)|Any CPU
- Debug DRM Free (GoG)|x64 = Debug DRM Free (GoG)|x64
- Debug DRM Free (Humble)|Any CPU = Debug DRM Free (Humble)|Any CPU
- Debug DRM Free (Humble)|x64 = Debug DRM Free (Humble)|x64
- Debug|Any CPU = Debug|Any CPU
- Debug|x64 = Debug|x64
- Release DRM Free (GoG)|Any CPU = Release DRM Free (GoG)|Any CPU
- Release DRM Free (GoG)|x64 = Release DRM Free (GoG)|x64
- Release DRM Free (Humble)|Any CPU = Release DRM Free (Humble)|Any CPU
- Release DRM Free (Humble)|x64 = Release DRM Free (Humble)|x64
- Release|Any CPU = Release|Any CPU
- Release|x64 = Release|x64
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Debug DRM Free (GoG)|Any CPU.ActiveCfg = Debug DRM Free (GoG)|Any CPU
- {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Debug DRM Free (GoG)|Any CPU.Build.0 = Debug DRM Free (GoG)|Any CPU
- {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Debug DRM Free (GoG)|x64.ActiveCfg = Debug DRM Free (GoG)|Any CPU
- {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Debug DRM Free (GoG)|x64.Build.0 = Debug DRM Free (GoG)|Any CPU
- {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Debug DRM Free (Humble)|Any CPU.ActiveCfg = Debug DRM Free|Any CPU
- {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Debug DRM Free (Humble)|Any CPU.Build.0 = Debug DRM Free|Any CPU
- {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Debug DRM Free (Humble)|x64.ActiveCfg = Debug DRM Free|Any CPU
- {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Debug DRM Free (Humble)|x64.Build.0 = Debug DRM Free|Any CPU
- {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Debug|x64.ActiveCfg = Debug|Any CPU
- {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Debug|x64.Build.0 = Debug|Any CPU
- {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Release DRM Free (GoG)|Any CPU.ActiveCfg = Release DRM Free (GoG)|Any CPU
- {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Release DRM Free (GoG)|Any CPU.Build.0 = Release DRM Free (GoG)|Any CPU
- {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Release DRM Free (GoG)|x64.ActiveCfg = Release DRM Free (GoG)|Any CPU
- {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Release DRM Free (GoG)|x64.Build.0 = Release DRM Free (GoG)|Any CPU
- {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Release DRM Free (Humble)|Any CPU.ActiveCfg = Release|Any CPU
- {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Release DRM Free (Humble)|Any CPU.Build.0 = Release|Any CPU
- {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Release DRM Free (Humble)|x64.ActiveCfg = Release DRM Free|Any CPU
- {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Release DRM Free (Humble)|x64.Build.0 = Release DRM Free|Any CPU
- {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Release|Any CPU.Build.0 = Release|Any CPU
- {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Release|x64.ActiveCfg = Release|Any CPU
- {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Release|x64.Build.0 = Release|Any CPU
- {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Debug DRM Free (GoG)|Any CPU.ActiveCfg = Debug DRM Free (GoG)|Any CPU
- {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Debug DRM Free (GoG)|Any CPU.Build.0 = Debug DRM Free (GoG)|Any CPU
- {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Debug DRM Free (GoG)|x64.ActiveCfg = Debug DRM Free (GoG)|x64
- {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Debug DRM Free (GoG)|x64.Build.0 = Debug DRM Free (GoG)|x64
- {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Debug DRM Free (Humble)|Any CPU.ActiveCfg = Debug|Any CPU
- {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Debug DRM Free (Humble)|Any CPU.Build.0 = Debug|Any CPU
- {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Debug DRM Free (Humble)|x64.ActiveCfg = Debug|x64
- {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Debug DRM Free (Humble)|x64.Build.0 = Debug|x64
- {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Debug|x64.ActiveCfg = Debug|x64
- {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Debug|x64.Build.0 = Debug|x64
- {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Release DRM Free (GoG)|Any CPU.ActiveCfg = Release DRM Free (GoG)|Any CPU
- {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Release DRM Free (GoG)|Any CPU.Build.0 = Release DRM Free (GoG)|Any CPU
- {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Release DRM Free (GoG)|x64.ActiveCfg = Release DRM Free (GoG)|x64
- {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Release DRM Free (GoG)|x64.Build.0 = Release DRM Free (GoG)|x64
- {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Release DRM Free (Humble)|Any CPU.ActiveCfg = Release|Any CPU
- {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Release DRM Free (Humble)|Any CPU.Build.0 = Release|Any CPU
- {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Release DRM Free (Humble)|x64.ActiveCfg = Release|x64
- {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Release DRM Free (Humble)|x64.Build.0 = Release|x64
- {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Release|Any CPU.Build.0 = Release|Any CPU
- {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Release|x64.ActiveCfg = Release|x64
- {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Release|x64.Build.0 = Release|x64
- {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Debug DRM Free (GoG)|Any CPU.ActiveCfg = Debug DRM Free (GoG)|Any CPU
- {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Debug DRM Free (GoG)|Any CPU.Build.0 = Debug DRM Free (GoG)|Any CPU
- {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Debug DRM Free (GoG)|x64.ActiveCfg = Debug DRM Free (GoG)|Any CPU
- {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Debug DRM Free (GoG)|x64.Build.0 = Debug DRM Free (GoG)|Any CPU
- {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Debug DRM Free (Humble)|Any CPU.ActiveCfg = Debug|Any CPU
- {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Debug DRM Free (Humble)|Any CPU.Build.0 = Debug|Any CPU
- {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Debug DRM Free (Humble)|x64.ActiveCfg = Debug|Any CPU
- {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Debug DRM Free (Humble)|x64.Build.0 = Debug|Any CPU
- {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Debug|x64.ActiveCfg = Debug|Any CPU
- {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Debug|x64.Build.0 = Debug|Any CPU
- {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Release DRM Free (GoG)|Any CPU.ActiveCfg = Release DRM Free (GoG)|Any CPU
- {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Release DRM Free (GoG)|Any CPU.Build.0 = Release DRM Free (GoG)|Any CPU
- {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Release DRM Free (GoG)|x64.ActiveCfg = Release DRM Free (GoG)|Any CPU
- {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Release DRM Free (GoG)|x64.Build.0 = Release DRM Free (GoG)|Any CPU
- {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Release DRM Free (Humble)|Any CPU.ActiveCfg = Release|Any CPU
- {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Release DRM Free (Humble)|Any CPU.Build.0 = Release|Any CPU
- {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Release DRM Free (Humble)|x64.ActiveCfg = Release|Any CPU
- {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Release DRM Free (Humble)|x64.Build.0 = Release|Any CPU
- {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Release|Any CPU.Build.0 = Release|Any CPU
- {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Release|x64.ActiveCfg = Release|Any CPU
- {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Release|x64.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {5C37E60C-1A8D-4AE6-812F-A9C3301D87B9}
- EndGlobalSection
-EndGlobal
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.31829.152
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TsRandomizer", "TsRandomizer\TsRandomizer.csproj", "{694E46C5-FD46-4CC8-8B71-D381E154CCF7}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{CD8103A2-141D-4AD0-B2FE-533D86875C9C}"
+ ProjectSection(SolutionItems) = preProject
+ ReadMe.md = ReadMe.md
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TsRandomizer.ItemTracker", "TsRandomizer.ItemTracker\TsRandomizer.ItemTracker.csproj", "{4D86B28B-95A2-4452-9BAA-C8E80F026101}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TsRandomizer.SeedGeneratah", "TsRandomizer.SeedGeneratah\TsRandomizer.SeedGeneratah.csproj", "{C30E617E-83DB-4C7C-9E45-0166C781A0A8}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug DRM Free (GoG)|Any CPU = Debug DRM Free (GoG)|Any CPU
+ Debug DRM Free (GoG)|x64 = Debug DRM Free (GoG)|x64
+ Debug DRM Free (Humble)|Any CPU = Debug DRM Free (Humble)|Any CPU
+ Debug DRM Free (Humble)|x64 = Debug DRM Free (Humble)|x64
+ Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
+ Release DRM Free (GoG)|Any CPU = Release DRM Free (GoG)|Any CPU
+ Release DRM Free (GoG)|x64 = Release DRM Free (GoG)|x64
+ Release DRM Free (Humble)|Any CPU = Release DRM Free (Humble)|Any CPU
+ Release DRM Free (Humble)|x64 = Release DRM Free (Humble)|x64
+ Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Debug DRM Free (GoG)|Any CPU.ActiveCfg = Debug DRM Free (GoG)|Any CPU
+ {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Debug DRM Free (GoG)|Any CPU.Build.0 = Debug DRM Free (GoG)|Any CPU
+ {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Debug DRM Free (GoG)|x64.ActiveCfg = Debug DRM Free (GoG)|Any CPU
+ {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Debug DRM Free (GoG)|x64.Build.0 = Debug DRM Free (GoG)|Any CPU
+ {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Debug DRM Free (Humble)|Any CPU.ActiveCfg = Debug DRM Free|Any CPU
+ {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Debug DRM Free (Humble)|Any CPU.Build.0 = Debug DRM Free|Any CPU
+ {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Debug DRM Free (Humble)|x64.ActiveCfg = Debug DRM Free|Any CPU
+ {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Debug DRM Free (Humble)|x64.Build.0 = Debug DRM Free|Any CPU
+ {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Debug|x64.Build.0 = Debug|Any CPU
+ {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Release DRM Free (GoG)|Any CPU.ActiveCfg = Release DRM Free (GoG)|Any CPU
+ {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Release DRM Free (GoG)|Any CPU.Build.0 = Release DRM Free (GoG)|Any CPU
+ {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Release DRM Free (GoG)|x64.ActiveCfg = Release DRM Free (GoG)|Any CPU
+ {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Release DRM Free (GoG)|x64.Build.0 = Release DRM Free (GoG)|Any CPU
+ {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Release DRM Free (Humble)|Any CPU.ActiveCfg = Release|Any CPU
+ {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Release DRM Free (Humble)|Any CPU.Build.0 = Release|Any CPU
+ {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Release DRM Free (Humble)|x64.ActiveCfg = Release DRM Free|Any CPU
+ {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Release DRM Free (Humble)|x64.Build.0 = Release DRM Free|Any CPU
+ {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Release|x64.ActiveCfg = Release|Any CPU
+ {694E46C5-FD46-4CC8-8B71-D381E154CCF7}.Release|x64.Build.0 = Release|Any CPU
+ {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Debug DRM Free (GoG)|Any CPU.ActiveCfg = Debug DRM Free (GoG)|Any CPU
+ {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Debug DRM Free (GoG)|Any CPU.Build.0 = Debug DRM Free (GoG)|Any CPU
+ {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Debug DRM Free (GoG)|x64.ActiveCfg = Debug DRM Free (GoG)|x64
+ {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Debug DRM Free (GoG)|x64.Build.0 = Debug DRM Free (GoG)|x64
+ {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Debug DRM Free (Humble)|Any CPU.ActiveCfg = Debug DRM Free (GoG)|Any CPU
+ {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Debug DRM Free (Humble)|Any CPU.Build.0 = Debug DRM Free (GoG)|Any CPU
+ {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Debug DRM Free (Humble)|x64.ActiveCfg = Debug DRM Free (GoG)|x64
+ {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Debug DRM Free (Humble)|x64.Build.0 = Debug DRM Free (GoG)|x64
+ {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Debug|x64.ActiveCfg = Debug|x64
+ {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Debug|x64.Build.0 = Debug|x64
+ {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Release DRM Free (GoG)|Any CPU.ActiveCfg = Release DRM Free (GoG)|Any CPU
+ {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Release DRM Free (GoG)|Any CPU.Build.0 = Release DRM Free (GoG)|Any CPU
+ {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Release DRM Free (GoG)|x64.ActiveCfg = Release DRM Free (GoG)|x64
+ {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Release DRM Free (GoG)|x64.Build.0 = Release DRM Free (GoG)|x64
+ {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Release DRM Free (Humble)|Any CPU.ActiveCfg = Release DRM Free (GoG)|Any CPU
+ {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Release DRM Free (Humble)|Any CPU.Build.0 = Release DRM Free (GoG)|Any CPU
+ {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Release DRM Free (Humble)|x64.ActiveCfg = Release DRM Free (GoG)|x64
+ {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Release DRM Free (Humble)|x64.Build.0 = Release DRM Free (GoG)|x64
+ {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Release|x64.ActiveCfg = Release|x64
+ {4D86B28B-95A2-4452-9BAA-C8E80F026101}.Release|x64.Build.0 = Release|x64
+ {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Debug DRM Free (GoG)|Any CPU.ActiveCfg = Debug DRM Free (GoG)|Any CPU
+ {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Debug DRM Free (GoG)|Any CPU.Build.0 = Debug DRM Free (GoG)|Any CPU
+ {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Debug DRM Free (GoG)|x64.ActiveCfg = Debug DRM Free (GoG)|Any CPU
+ {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Debug DRM Free (GoG)|x64.Build.0 = Debug DRM Free (GoG)|Any CPU
+ {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Debug DRM Free (Humble)|Any CPU.ActiveCfg = Debug DRM Free (GoG)|Any CPU
+ {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Debug DRM Free (Humble)|Any CPU.Build.0 = Debug DRM Free (GoG)|Any CPU
+ {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Debug DRM Free (Humble)|x64.ActiveCfg = Debug DRM Free (GoG)|Any CPU
+ {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Debug DRM Free (Humble)|x64.Build.0 = Debug DRM Free (GoG)|Any CPU
+ {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Debug|x64.Build.0 = Debug|Any CPU
+ {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Release DRM Free (GoG)|Any CPU.ActiveCfg = Release DRM Free (GoG)|Any CPU
+ {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Release DRM Free (GoG)|Any CPU.Build.0 = Release DRM Free (GoG)|Any CPU
+ {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Release DRM Free (GoG)|x64.ActiveCfg = Release DRM Free (GoG)|Any CPU
+ {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Release DRM Free (GoG)|x64.Build.0 = Release DRM Free (GoG)|Any CPU
+ {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Release DRM Free (Humble)|Any CPU.ActiveCfg = Release DRM Free (GoG)|Any CPU
+ {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Release DRM Free (Humble)|Any CPU.Build.0 = Release DRM Free (GoG)|Any CPU
+ {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Release DRM Free (Humble)|x64.ActiveCfg = Release DRM Free (GoG)|Any CPU
+ {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Release DRM Free (Humble)|x64.Build.0 = Release DRM Free (GoG)|Any CPU
+ {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Release|x64.ActiveCfg = Release|Any CPU
+ {C30E617E-83DB-4C7C-9E45-0166C781A0A8}.Release|x64.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {5C37E60C-1A8D-4AE6-812F-A9C3301D87B9}
+ EndGlobalSection
+EndGlobal
diff --git a/TsRandomizer/LevelObjects/ItemManipulator.cs b/TsRandomizer/LevelObjects/ItemManipulator.cs
index 4792976b..6c9e92d1 100644
--- a/TsRandomizer/LevelObjects/ItemManipulator.cs
+++ b/TsRandomizer/LevelObjects/ItemManipulator.cs
@@ -2,6 +2,8 @@
using System.Collections.Generic;
using System.Linq;
using Timespinner.GameAbstractions.Gameplay;
+using Timespinner.GameAbstractions.Inventory;
+using Timespinner.GameAbstractions.Saving;
using Timespinner.GameObjects.BaseClasses;
using TsRandomizer.Extensions;
using TsRandomizer.IntermediateObjects;
@@ -32,7 +34,7 @@ abstract class ItemManipulator : LevelObject
public readonly ItemInfo ItemInfo;
public readonly ItemLocation ItemLocation;
- protected ItemManipulator(Mobile typedObject, GameplayScreen gameplayScreen, ItemLocation itemLocation)
+ protected ItemManipulator(Mobile typedObject, GameplayScreen gameplayScreen, ItemLocation itemLocation)
: base(typedObject, gameplayScreen)
{
ItemInfo = itemLocation?.ItemInfo;
@@ -46,6 +48,8 @@ protected void AwardContainedItem()
if (ItemInfo.Identifier.LootType == LootType.ConstRelic)
LevelReflected.UnlockRelic(ItemInfo.Identifier.Relic);
+ ApplyStackCap(Level.GameSave);
+
OnItemPickup();
}
@@ -60,6 +64,27 @@ protected void OnItemPickup()
public static void Initialize(ItemLocationMap itemLocations) => itemLocationMap = itemLocations;
+ public static void ApplyStackCap(GameSave save)
+ {
+ int cap = QoLSettings.Current.StackCap;
+ var inventory = save.Inventory.UseItemInventory.Inventory;
+
+ foreach (var kvp in inventory)
+ {
+ var item = kvp.Value;
+
+ // Leave special currency items at vanilla cap
+ var type = (EInventoryUseItemType)kvp.Key;
+ if (type == EInventoryUseItemType.MagicMarbles
+ || type == EInventoryUseItemType.EssenceCrystal
+ || type == EInventoryUseItemType.GoldRing
+ || type == EInventoryUseItemType.GoldNecklace)
+ continue;
+
+ item.StackCap = cap;
+ }
+ }
+
public static ItemManipulator GenerateShadowObject(
Type levelObjectType, Mobile obj, GameplayScreen gameplayScreen, ItemLocationMap itemLocations)
{
@@ -67,8 +92,8 @@ public static ItemManipulator GenerateShadowObject(
var itemLocation = itemLocations[itemKey];
if (itemLocation == null)
- {
- //Console.Out.WriteLine($"UnmappedItem: {itemKey}");
+ {
+ //Console.Out.WriteLine($"UnmappedItem: {itemKey}");
return null;
}
@@ -80,7 +105,7 @@ protected void ShowItemAwardPopup()
if (ItemInfo is CustomItem customItem)
GameplayScreen.ShowItemPickupBar(customItem.Name);
else if (ItemInfo is ProgressiveItemInfo progressiveItem)
- Level.ShowItemAwardPopup(progressiveItem.PreviousItem.Identifier); //since this script runs delayed, the item will already have updated
+ Level.ShowItemAwardPopup(progressiveItem.PreviousItem.Identifier); //since this script runs delayed, the item will already have updated
else
Level.ShowItemAwardPopup(ItemInfo.Identifier);
}
@@ -100,4 +125,4 @@ protected void UpdateRelicOrbGetToastToItem()
reflectedScript.Arguments = ScriptActionQueueExtensions.ReplacedArguments;
}
}
-}
+}
\ No newline at end of file
diff --git a/TsRandomizer/Program.cs b/TsRandomizer/Program.cs
index 759cf633..8133a402 100644
--- a/TsRandomizer/Program.cs
+++ b/TsRandomizer/Program.cs
@@ -1,36 +1,39 @@
-using System;
-
-namespace TsRandomizer
-{
- public static class Program
- {
- [STAThread]
- public static int Main()
- {
- WithExceptionLogging(() => {
- var platformHelper = DummyPlatformHelper.CreateInstance();
-
- new TimeSpinnerGame(platformHelper).Run();
- });
-
- Environment.Exit(0);
- return 0;
- }
-
- static void WithExceptionLogging(Action action)
- {
-#if DEBUG
- action();
-#else
- try
- {
- action();
- }
- catch (Exception e)
- {
- ExceptionLogger.LogException(e);
- }
-#endif
- }
- }
-}
+using System;
+
+
+namespace TsRandomizer
+{
+ public static class Program
+ {
+ [STAThread]
+ public static int Main()
+ {
+ WithExceptionLogging(() => {
+
+
+ var platformHelper = DummyPlatformHelper.CreateInstance();
+
+ new TimeSpinnerGame(platformHelper).Run();
+ });
+
+ Environment.Exit(0);
+ return 0;
+ }
+
+ static void WithExceptionLogging(Action action)
+ {
+#if DEBUG
+ action();
+#else
+ try
+ {
+ action();
+ }
+ catch (Exception e)
+ {
+ ExceptionLogger.LogException(e);
+ }
+#endif
+ }
+ }
+}
diff --git a/TsRandomizer/Screens/GameSettingsScreen.cs b/TsRandomizer/Screens/GameSettingsScreen.cs
index 0ce2ecc5..ebc05844 100644
--- a/TsRandomizer/Screens/GameSettingsScreen.cs
+++ b/TsRandomizer/Screens/GameSettingsScreen.cs
@@ -14,8 +14,8 @@
namespace TsRandomizer.Screens
{
- [TimeSpinnerType("Timespinner.GameStateManagement.Screens.PauseMenu.JournalMenuScreen")]
- // ReSharper disable once UnusedMember.Global
+ [TimeSpinnerType("Timespinner.GameStateManagement.Screens.PauseMenu.JournalMenuScreen")]
+ // ReSharper disable once UnusedMember.Global
class GameSettingsScreen : Screen
{
static readonly Type MenuEntryType =
@@ -25,6 +25,7 @@ class GameSettingsScreen : Screen
readonly SeedSelectionMenuScreen seedSelectionScreen;
readonly OptionsMenuScreen optionsMenuScreen;
+ readonly bool isQoLMenu;
GameSave save;
SettingCollection settings;
@@ -37,6 +38,12 @@ public override void Initialize(ItemLocationMap itemLocationMap, GCM gameContent
{
gcm = gameContentManager;
+ if (isQoLMenu)
+ {
+ QoLSettingsMenu.BuildMenu(GameScreen, 0);
+ return;
+ }
+
var gameplayScreen = ScreenManager.FirstOrDefault();
save = gameplayScreen?.Save;
@@ -49,9 +56,9 @@ public override void Initialize(ItemLocationMap itemLocationMap, GCM gameContent
Dynamic._menuTitle = "Randomizer Settings";
ResetMenu();
}
- else
- {
- // Journal is being used as a journal, replace Feats and Quests with Objectives and Statistics
+ else
+ {
+ // Journal is being used as a journal, replace Feats and Quests with Objectives and Statistics
SetStatistics(itemLocationMap);
SetObjectives();
}
@@ -66,8 +73,8 @@ public override void Unload()
gcm.UpdateMinimapColors(settings);
if (IsInGame)
- {
- var gameplayScreen = ScreenManager.FirstOrDefault();
+ {
+ var gameplayScreen = ScreenManager.FirstOrDefault();
if (gameplayScreen != null)
SpriteManager.ReloadCustomSprites(gameplayScreen.Level, gcm, settings);
}
@@ -78,8 +85,8 @@ public override void Unload()
}
}
catch
- {
- // ignored
+ {
+ // ignored
}
}
@@ -87,6 +94,8 @@ public GameSettingsScreen(ScreenManager screenManager, GameScreen passwordMenuSc
{
seedSelectionScreen = screenManager.FirstOrDefault();
optionsMenuScreen = screenManager.FirstOrDefault();
+ isQoLMenu = QoLSettingsMenu.IsQoLMenuPending;
+ QoLSettingsMenu.IsQoLMenuPending = false;
}
public static GameScreen Create(ScreenManager screenManager)
@@ -121,8 +130,8 @@ object CreateDefaultsMenu(GameSettingCategoryInfo[] menusToClear, bool isSubmenu
}
void OnDefaultsSelected(GameSettingCategoryInfo[] menusToClear, bool isSubmenu)
- {
- // Clear the root menu
+ {
+ // Clear the root menu
if (!IsInGame && !isSubmenu)
{
settings = new SettingCollection();
@@ -135,7 +144,7 @@ void OnDefaultsSelected(GameSettingCategoryInfo[] menusToClear, bool isSubmenu)
{
var setting = settingsFunc(settings);
- if(IsInGame && !setting.CanBeChangedInGame)
+ if (IsInGame && !setting.CanBeChangedInGame)
continue;
setting.SetDefault();
@@ -198,7 +207,7 @@ void CreateMenuForCategory(GameSettingCategoryInfo category)
foreach (var settingFunc in category.SettingsPerCategory)
submenu.Add(CreateMenuForSetting(settingFunc(settings)).AsTimeSpinnerMenuEntry());
- submenu.Add(CreateDefaultsMenu(new [] { category }, true));
+ submenu.Add(CreateDefaultsMenu(new[] { category }, true));
Dynamic.ChangeMenuCollection(collection, true);
((object)Dynamic._selectedMenuCollection).AsDynamic().SetSelectedIndex(0);
@@ -206,20 +215,22 @@ void CreateMenuForCategory(GameSettingCategoryInfo category)
object FetchCollection(string submenu)
{
- dynamic collection;
- // Multiple submenus can share the same inventory collection
- // as the in-use collection is cleared before use.
+ dynamic collection;
+ // Multiple submenus can share the same inventory collection
+ // as the in-use collection is cleared before use.
+
switch (submenu)
- {
- // Currently using quest layout for most, other layouts may be useful for other menus
- // Leaving as switch to easily add new menus as Memories, Letters, Files, Quests, Bestiary, Feats
+ {
+ // Currently using quest layout for most, other layouts may be useful for other menus
+ // Leaving as switch to easily add new menus as Memories, Letters, Files, Quests, Bestiary, Feats
/*
// TODO: Sprites needs a functional way to write a FeatsMenuEntry before we can properly preview sprites
// and use this layout
case "Sprites":
collection = Dynamic._featsInventory;
break;
- */
+ */
+
default:
collection = ((object)Dynamic._questInventory).AsDynamic();
break;
@@ -266,6 +277,7 @@ void ToggleSetting(GameSetting setting)
setting.UpdateMenuEntry(menuEntry);
}
+
void SetStatistics(ItemLocationMap itemLocationMap)
{
var gameplayScreen = ScreenManager.FirstOrDefault();
@@ -303,14 +315,14 @@ void SetObjectives()
menuEntry.AsDynamic().Text = "Goal: Dad Percent";
menuEntry.AsDynamic().Description = "Clear the boss fight at the top of Emperor's Tower.";
}
- menuEntry.AsDynamic()._isUnlocked = save.GetSaveBool("TsRandoGoalCleared");
-
- // Remove all extra feat entries
- // Currently bonus objectives are not available, only goals
+ menuEntry.AsDynamic()._isUnlocked = save.GetSaveBool("TsRandoGoalCleared");
+
+ // Remove all extra feat entries
+ // Currently bonus objectives are not available, only goals
+
var bonusTaskCount = 0;
while (((IList)selectedMenu.Entries).Count > bonusTaskCount + 1)
((IList)selectedMenu.Entries).RemoveAt(bonusTaskCount + 1);
-
}
}
-}
+}
\ No newline at end of file
diff --git a/TsRandomizer/Screens/GameplayScreen.cs b/TsRandomizer/Screens/GameplayScreen.cs
index 4229474b..b8a6f53c 100644
--- a/TsRandomizer/Screens/GameplayScreen.cs
+++ b/TsRandomizer/Screens/GameplayScreen.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections;
using System.Reflection;
using Archipelago.MultiClient.Net.Enums;
using Microsoft.Xna.Framework;
@@ -22,8 +23,8 @@
namespace TsRandomizer.Screens
{
- [TimeSpinnerType("Timespinner.GameStateManagement.Screens.InGame.GameplayScreen")]
- // ReSharper disable once UnusedMember.Global
+ [TimeSpinnerType("Timespinner.GameStateManagement.Screens.InGame.GameplayScreen")]
+ // ReSharper disable once UnusedMember.Global
class GameplayScreen : Screen
{
static readonly MethodInfo LoadingScreenLoadMethod = TimeSpinnerType
@@ -104,6 +105,7 @@ public override void Initialize(ItemLocationMap _, GCM gameContentManager)
ItemTrackerUplink.UpdateState(ItemTrackerState.FromItemLocationMap(ItemLocations));
ItemManipulator.Initialize(ItemLocations);
+ ItemManipulator.ApplyStackCap(Level.GameSave);
if (settings.DamageRando.Value != "Off")
OrbDamageManager.PopulateOrbLookups(Level.GameSave, settings.DamageRando.Value, settings.DamageRandoOverrides.Value);
@@ -113,8 +115,8 @@ public override void Initialize(ItemLocationMap _, GCM gameContentManager)
BestiaryManager.RefreshBossSaveFlags(Level);
if (Seed.Options.Archipelago)
- HandleArchipelago(settings);
-
+ HandleArchipelago(settings);
+
#if DEBUG
saveFile.DataKeyBools["TS_INSTAGIB"] = true;
@@ -187,12 +189,52 @@ public override void Update(GameTime gameTime, InputState input)
UpdateGenericScripts(Level);
+ if (QoLSettings.Current.AutoSkipCutscenes)
+ TrySkipCutscene();
+
+ if (QoLSettings.Current.AutoSkipDialogue)
+ TrySkipDialogue();
+ if (QoLSettings.Current.StackCap != 9)
+ ItemManipulator.ApplyStackCap(Level.GameSave);
+
#if DEBUG
TimespinnerAfterDark(input);
#endif
HandleCurrentGifts();
}
+ void TrySkipCutscene()
+ {
+ var level = Level;
+ if (level == null) return;
+
+ var levelDynamic = LevelReflected;
+ if (!(bool)levelDynamic.IsPlayerInputBlocked) return;
+ if ((bool)levelDynamic.IsActiveScriptUnskippable) return;
+ if ((bool)levelDynamic.IsDoingPlayerDeathCutscene) return;
+
+ var activeScripts = (IList)levelDynamic._activeScripts;
+ foreach (var script in activeScripts)
+ {
+ var s = script.AsDynamic();
+ if (s.ScriptType.ToString() == "CutsceneStart" && (float)s.Arguments.X >= 1f)
+ {
+ levelDynamic.SkipCutscene();
+ Dynamic._isShowingSkipCutscenePrompt = false;
+ return;
+ }
+ }
+ }
+
+ void TrySkipDialogue()
+ {
+ var dialogueBox = (object)Dynamic._currentDialogue;
+ if (dialogueBox == null) return;
+ var d = dialogueBox.AsDynamic();
+ if ((bool)d.IsFinished) return;
+ d.FinishDialogue();
+ }
+
void UpdateGenericScripts(Level level)
{
if (hpCap <= level.MainHero.MaxHP)
@@ -271,7 +313,7 @@ void DrawReceivedGifts(SpriteBatch spriteBatch, SpriteFont menuFont)
}
void DrawRoomId(SpriteBatch spriteBatch, SpriteFont menuFont)
- {
+ {
#if DEBUG
if (ItemLocations == null)
return;
@@ -287,7 +329,6 @@ void DrawRoomId(SpriteBatch spriteBatch, SpriteFont menuFont)
#endif
}
-
public void HideItemPickupBar() => ((object)Dynamic._itemGetBanner).AsDynamic()._displayTimer = 3f;
public void ChangeItemPickupBar(string name) => ((object)Dynamic._itemGetBanner).AsDynamic()._itemName = name;
@@ -301,8 +342,8 @@ public void ShowItemPickupBar(string name)
itemBanner.ItemCategory = EInventoryCategoryType.UseItem;
itemBanner._itemName = name;
itemBanner._displayTimer = 0f;
- }
-
+ }
+
#if DEBUG
void TimespinnerAfterDark(InputState input)
{
@@ -317,4 +358,4 @@ void TimespinnerAfterDark(InputState input)
}
#endif
}
-}
+}
\ No newline at end of file
diff --git a/TsRandomizer/Screens/Menu/QoLSettingsMenu.cs b/TsRandomizer/Screens/Menu/QoLSettingsMenu.cs
new file mode 100644
index 00000000..9f7b2bfd
--- /dev/null
+++ b/TsRandomizer/Screens/Menu/QoLSettingsMenu.cs
@@ -0,0 +1,87 @@
+using System;
+using System.Collections;
+using Microsoft.Xna.Framework;
+using Timespinner.GameAbstractions;
+using Timespinner.GameAbstractions.Saving;
+using TsRandomizer.Extensions;
+using TsRandomizer.Screens.Menu;
+using TsRandomizer.IntermediateObjects;
+using ScreenManager = Timespinner.GameStateManagement.ScreenManager.ScreenManager;
+using GameScreen = Timespinner.GameStateManagement.ScreenManager.GameScreen;
+
+namespace TsRandomizer.Screens
+{
+ public static class QoLSettingsMenu
+ {
+ static readonly Type JournalMenuType =
+ TimeSpinnerType.Get("Timespinner.GameStateManagement.Screens.PauseMenu.JournalMenuScreen");
+ static readonly Type MenuEntryType =
+ TimeSpinnerType.Get("Timespinner.GameStateManagement.MenuEntry");
+
+ public static bool IsQoLMenuPending { get; set; }
+
+ internal static GameScreen Create(ScreenManager screenManager)
+ {
+ GCM gcm = screenManager.AsDynamic().GCM;
+ gcm.LoadAllResources(screenManager.AsDynamic().GeneralContentManager, screenManager.GraphicsDevice);
+
+ void OnExit() { }
+
+ return (GameScreen)Activator.CreateInstance(JournalMenuType, GameSave.DemoSave, gcm, (Action)OnExit);
+ }
+
+ public static void BuildMenu(GameScreen gameScreen, int selectedIndex)
+ {
+ var d = gameScreen.AsDynamic();
+ var emptyList = new object[0].ToList(MenuEntryType);
+
+ d._menuTitle = "QoL Menu";
+
+ ((object)d._primaryMenuCollection).AsDynamic()._entries = emptyList;
+ ((object)d._memoriesInventoryCollection).AsDynamic()._entries = emptyList;
+ ((object)d._lettersInventoryCollection).AsDynamic()._entries = emptyList;
+ ((object)d._filesInventoryCollection).AsDynamic()._entries = emptyList;
+ ((object)d._questInventory).AsDynamic()._entries = emptyList;
+ ((object)d._bestiaryInventory).AsDynamic()._entries = emptyList;
+ ((object)d._featsInventory).AsDynamic()._entries = emptyList;
+
+ var menuList = new object[0].ToList(MenuEntryType);
+
+ void AddEntry(string label, Action onSelect, string description)
+ {
+ int idx = ((IList)menuList).Count;
+ var entry = MenuEntry.Create(label, _ => { onSelect(); BuildMenu(gameScreen, idx); });
+ entry.IsCenterAligned = false;
+ entry.DoesDrawLargeShadow = false;
+ entry.Description = description;
+ ((IList)menuList).Add(entry.AsTimeSpinnerMenuEntry());
+ }
+
+ AddEntry(GetCutsceneLabel(), () => { QoLSettings.Current.AutoSkipCutscenes = !QoLSettings.Current.AutoSkipCutscenes; QoLSettings.Save(); }, "Automatically skips cutscenes without any button press.");
+ AddEntry(GetDialogueLabel(), () => { QoLSettings.Current.AutoSkipDialogue = !QoLSettings.Current.AutoSkipDialogue; QoLSettings.Save(); }, "Dialogue boxes advance instantly without input.");
+ AddEntry(GetStackCapLabel(), () => { var steps = new[] { 9, 25, 50, 99 }; var idx2 = Array.IndexOf(steps, QoLSettings.Current.StackCap); QoLSettings.Current.StackCap = steps[(idx2 + 1) % steps.Length]; QoLSettings.Save(); }, "Maximum stack size for consumable items. Click to cycle: 9, 25, 50, 99. Shop cap mirrors this value (restart required for shop to apply).");
+ AddEntry(GetFastToastLabel(), () => { QoLSettings.Current.FastToastPopups = !QoLSettings.Current.FastToastPopups; QoLSettings.Save(); }, "Fast: toasts appear and disappear instantly. Vanilla: original timing.");
+ AddEntry(GetToastBlockLabel(), () => { QoLSettings.Current.ToastsBlockMovement = !QoLSettings.Current.ToastsBlockMovement; QoLSettings.Save(); }, "On: toasts freeze movement (vanilla). Off: toasts never block movement.");
+
+ var questCol = ((object)d._questInventory);
+ questCol.AsDynamic()._entries = menuList;
+ d.ChangeMenuCollection(questCol, false);
+ ((object)d._selectedMenuCollection).AsDynamic().SetSelectedIndex(selectedIndex);
+ }
+
+ static string GetCutsceneLabel() =>
+ $"Auto Skip Cutscenes: {(QoLSettings.Current.AutoSkipCutscenes ? "On" : "Off")}";
+
+ static string GetDialogueLabel() =>
+ $"Auto Skip Dialogue: {(QoLSettings.Current.AutoSkipDialogue ? "On" : "Off")}";
+
+ static string GetStackCapLabel() =>
+ $"Stack Cap: {QoLSettings.Current.StackCap}";
+
+ static string GetFastToastLabel() =>
+ $"Toast Popup Speed: {(QoLSettings.Current.FastToastPopups ? "Fast" : "Vanilla")}";
+
+ static string GetToastBlockLabel() =>
+ $"Toasts Block Movement: {(QoLSettings.Current.ToastsBlockMovement ? "On" : "Off")}";
+ }
+}
\ No newline at end of file
diff --git a/TsRandomizer/Screens/OptionsMenuScreen.cs b/TsRandomizer/Screens/OptionsMenuScreen.cs
index 76d9c0a4..caff442a 100644
--- a/TsRandomizer/Screens/OptionsMenuScreen.cs
+++ b/TsRandomizer/Screens/OptionsMenuScreen.cs
@@ -10,8 +10,8 @@
namespace TsRandomizer.Screens
{
- [TimeSpinnerType("Timespinner.GameStateManagement.Screens.PauseMenu.OptionsMenuScreen")]
- // ReSharper disable once UnusedMember.Global
+ [TimeSpinnerType("Timespinner.GameStateManagement.Screens.PauseMenu.OptionsMenuScreen")]
+ // ReSharper disable once UnusedMember.Global
class OptionsMenuScreen : Screen
{
static readonly Type MainMenuEntryType = TimeSpinnerType.Get("Timespinner.GameStateManagement.MenuEntry");
@@ -23,6 +23,7 @@ public OptionsMenuScreen(ScreenManager screenManager, GameScreen gameScreen) : b
public override void Initialize(ItemLocationMap itemLocationMap, GCM gameContentManager)
{
AddSettingButton(MenuEntry.Create("Randomizer Settings", OpenSettingsMenu));
+ AddSettingButton(MenuEntry.Create("Randomizer QoL Settings", OpenQoLSettingsMenu));
}
void AddSettingButton(MenuEntry settingButton)
@@ -42,5 +43,12 @@ void OpenSettingsMenu(PlayerIndex playerIndex)
ScreenManager.AddScreen(gameSettingsMenu, playerIndex);
}
+
+ void OpenQoLSettingsMenu(PlayerIndex playerIndex)
+ {
+ QoLSettingsMenu.IsQoLMenuPending = true;
+ var qolMenu = QoLSettingsMenu.Create(ScreenManager);
+ ScreenManager.AddScreen(qolMenu, playerIndex);
+ }
}
-}
+}
\ No newline at end of file
diff --git a/TsRandomizer/Screens/Screen.cs b/TsRandomizer/Screens/Screen.cs
index 12e3d109..db2d8ad2 100644
--- a/TsRandomizer/Screens/Screen.cs
+++ b/TsRandomizer/Screens/Screen.cs
@@ -75,6 +75,8 @@ public virtual void Update(GameTime gameTime, InputState input)
{
}
+
+ public virtual void HandleInput(InputState input) { }
public virtual void Draw(SpriteBatch spriteBatch, SpriteFont menuFont)
{
}
diff --git a/TsRandomizer/Screens/ScreenManager.cs b/TsRandomizer/Screens/ScreenManager.cs
index dffc950f..643ec5b5 100644
--- a/TsRandomizer/Screens/ScreenManager.cs
+++ b/TsRandomizer/Screens/ScreenManager.cs
@@ -19,20 +19,57 @@ class ScreenManager : Timespinner.GameStateManagement.ScreenManager.ScreenManage
{
static readonly Type GamePlayScreenType =
TimeSpinnerType.Get("Timespinner.GameStateManagement.Screens.InGame.GameplayScreen");
+ static readonly Type BaseToastPopupType =
+ TimeSpinnerType.Get("Timespinner.GameStateManagement.Screens.InGame.BaseToastPopup");
+
+ static readonly System.Reflection.FieldInfo FreezeField =
+ BaseToastPopupType.GetField("_doesFreezeGameplay", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
+ static readonly System.Reflection.FieldInfo TimeToWaitField =
+ BaseToastPopupType.GetField("_timeToWait", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
+ static readonly System.Reflection.FieldInfo TotalDisplayField =
+ BaseToastPopupType.GetField("_totalDisplayTime", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
+ static readonly System.Reflection.FieldInfo TimeBeforeFlashingField =
+ BaseToastPopupType.GetField("_timeBeforeFlashing", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
+ static readonly System.Reflection.FieldInfo TimeToFlashField =
+ BaseToastPopupType.GetField("_timeToFlash", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
+ static readonly System.Reflection.FieldInfo TimeToFadeField =
+ BaseToastPopupType.GetField("_timeToFade", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
+ static readonly System.Reflection.PropertyInfo IsOverlayProp =
+ BaseToastPopupType.BaseType?.GetProperty("IsOverlayScreen", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
+ static readonly System.Reflection.PropertyInfo WaitForInputProp =
+ BaseToastPopupType.GetProperty("DoesWaitForInputToFinish", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
+ static readonly System.Reflection.PropertyInfo HasReceivedInputProp =
+ BaseToastPopupType.GetProperty("HasReceivedInputToClose", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
+
+ static readonly string LevelUpToastTypeName =
+ "Timespinner.GameStateManagement.Screens.InGame.Toasts.CharacterLevelUpToast";
+ static readonly System.Reflection.FieldInfo ControlTimerField =
+ TimeSpinnerType.Get(LevelUpToastTypeName)
+ .GetField("_controlTimer", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
+
+ // Only patch toasts that relate to item/level pickups.
+ // AreaTitleToast (room name on screen transition) must be left alone.
+ static readonly HashSet PatchableToastTypeNames = new HashSet
+ {
+ "Timespinner.GameStateManagement.Screens.InGame.Toasts.RelicOrbGetToast",
+ "Timespinner.GameStateManagement.Screens.InGame.Toasts.CharacterLevelUpToast",
+ "Timespinner.GameStateManagement.Screens.InGame.Toasts.OrbLevelUpToast",
+ "Timespinner.GameStateManagement.Screens.InGame.Toasts.StatMaxUpToast",
+ "Timespinner.GameStateManagement.Screens.InGame.Toasts.QuestCompleteToast",
+ };
readonly LookupDictionary hookedScreens
= new LookupDictionary(s => s.GameScreen);
readonly List foundScreens = new List(20);
+ readonly HashSet toastScreensPatched = new HashSet();
ItemLocationMap itemLocationMap;
public readonly dynamic Dynamic;
-
public GCM GameContentManager => Dynamic.GCM;
-
+
public static Log Log;
public static GameConsole Console;
-
public static bool IsConsoleOpen;
public ScreenManager(TimespinnerGame game, PlatformHelper platformHelper) : base(game, platformHelper)
@@ -43,34 +80,29 @@ public ScreenManager(TimespinnerGame game, PlatformHelper platformHelper) : base
protected override void LoadContent()
{
base.LoadContent();
-
GameContentManager.LatinFont.DefaultCharacter = '?';
-
Log = new Log();
Console = new GameConsole(this, GameContentManager);
-
Console.AddCommand(new ConnectCommand(this));
}
public override void Update(GameTime gameTime)
{
var input = (InputState)Dynamic._input;
-
DetectNewScreens();
UpdateScreens(gameTime, input);
-
Overlay.UpdateAll(gameTime, input, Jukebox);
-
if (input.IsNewKeyPress(Keys.OemTilde))
ToggleConsole();
-
- base.Update(gameTime);
+ base.Update(gameTime);
+ // Apply AFTER base.Update so we overwrite whatever the toast's own
+ // Update just set — otherwise we lose the race every frame.
+ ForceToastSettings();
}
public void ToggleConsole()
{
IsConsoleOpen = !IsConsoleOpen;
-
if (IsConsoleOpen)
AddScreen(Console, null);
else
@@ -80,45 +112,70 @@ public void ToggleConsole()
public override void Draw(GameTime gameTime)
{
base.Draw(gameTime);
-
DrawGameplayScreens();
-
Overlay.DrawAll(SpriteBatch, new Rectangle(0, 0, ScreenSize.X, ScreenSize.Y), GameContentManager);
}
void DetectNewScreens()
{
foundScreens.Clear();
-
foreach (var screen in GetScreens())
{
if (hookedScreens.Contains(screen))
{
foundScreens.Add(screen);
-
if (screen.GetType() == GamePlayScreenType)
itemLocationMap = ((GameplayScreen)hookedScreens[screen]).ItemLocations;
-
continue;
}
-
- if(!Screen.RegisteredTypes.TryGetValue(screen.GetType(), out var handlerType))
+ if (!Screen.RegisteredTypes.TryGetValue(screen.GetType(), out var handlerType))
continue;
-
var screenHandler = (Screen)Activator.CreateInstance(handlerType, this, screen);
hookedScreens.Add(screenHandler);
foundScreens.Add(screen);
-
screenHandler.Initialize(itemLocationMap, GameContentManager);
}
-
if (foundScreens.Count != hookedScreens.Count)
hookedScreens.Filter(foundScreens, s => s.Unload());
}
- void UpdateScreens(GameTime gameTime, InputState input)
+ void ForceToastSettings()
{
- foreach (var screen in hookedScreens)
+ foreach (var screen in GetScreens())
+ if (BaseToastPopupType.IsInstanceOfType(screen)
+ && PatchableToastTypeNames.Contains(screen.GetType().FullName))
+ ApplyToastSettings(screen);
+ }
+
+ static void ApplyToastSettings(GameScreen screen)
+ {
+ if (QoLSettings.Current.FastToastPopups)
+ {
+ TimeToWaitField.SetValue(screen, 0f);
+ WaitForInputProp?.SetValue(screen, false);
+ HasReceivedInputProp?.SetValue(screen, true);
+
+ float before = (float)TimeBeforeFlashingField.GetValue(screen);
+ float flash = (float)TimeToFlashField.GetValue(screen);
+ float fade = (float)TimeToFadeField.GetValue(screen);
+ TotalDisplayField.SetValue(screen, before + flash + fade);
+ }
+
+ if (!QoLSettings.Current.ToastsBlockMovement)
+ {
+ FreezeField.SetValue(screen, false);
+ IsOverlayProp?.SetValue(screen, true);
+
+ if (screen.GetType().FullName == LevelUpToastTypeName)
+ ControlTimerField?.SetValue(screen, 0.75f);
+ }
+ }
+
+ void UpdateScreens(GameTime gameTime, InputState input)
+ {
+ foreach (var screen in hookedScreens)
+ screen.HandleInput(input);
+ foreach (var screen in hookedScreens)
screen.Update(gameTime, input);
}
@@ -135,7 +192,6 @@ public void CopyScreensFrom(Timespinner.GameStateManagement.ScreenManager.Screen
}
public T FirstOrDefault() where T : Screen => (T)hookedScreens.FirstOrDefault(s => s.GetType() == typeof(T));
-
public GameScreen FirstOrDefaultTimespinnerOfType(Type type) => ((List)Dynamic._screens).FirstOrDefault(s => s.GetType() == type);
}
-}
+}
\ No newline at end of file
diff --git a/TsRandomizer/Screens/ShopMenuScreen.cs b/TsRandomizer/Screens/ShopMenuScreen.cs
index b81890b7..ccdf6073 100644
--- a/TsRandomizer/Screens/ShopMenuScreen.cs
+++ b/TsRandomizer/Screens/ShopMenuScreen.cs
@@ -1,48 +1,240 @@
-using System.Collections;
-using System.Linq;
-using Timespinner.GameAbstractions.Inventory;
-using Timespinner.GameAbstractions.Saving;
-using Timespinner.GameStateManagement.ScreenManager;
-using TsRandomizer.Extensions;
-using TsRandomizer.IntermediateObjects;
-
-namespace TsRandomizer.Screens
-{
- [TimeSpinnerType("Timespinner.GameStateManagement.Screens.Shop.ShopMenuScreen")]
- // ReSharper disable once UnusedMember.Global
- class ShopMenuScreen : Screen
- {
- public ShopMenuScreen(ScreenManager screenManager, GameScreen screen) : base(screenManager, screen)
- {
- var gameSettings = ((GameSave)Dynamic._saveFile).GetSettings();
-
- // Menu count varies on relics/items/equipment etc. being in inventory
- // Last menu is always helper functions that don't have an _items
- // but aren't otherwise distinguishable
- foreach (var i in Enumerable.Range(0, ((IList)Dynamic._subMenuCollections).Count - 1))
- {
- var shopMenu = ((IList)Dynamic._subMenuCollections)[i].AsDynamic();
- foreach (var shopMenuEntry in shopMenu._items)
- {
- var dynamicShopMenuEntry = ((object)shopMenuEntry).AsDynamic();
-
- var item = (InventoryItem)dynamicShopMenuEntry.Item;
- if (item.NameKey == "inv_use_MagicMarbles")
- {
- item.IsSellable = false;
- dynamicShopMenuEntry.ShopPrice = -1;
- }
-
- int currentPrice = dynamicShopMenuEntry.ShopPrice;
- if (currentPrice == 0)
- {
- // Set a price for "priceless" items
- dynamicShopMenuEntry.ShopPrice = 2000;
- currentPrice = dynamicShopMenuEntry.ShopPrice;
- }
- dynamicShopMenuEntry.ShopPrice = (int)(currentPrice * gameSettings.ShopMultiplier.Value);
- }
- }
- }
- }
-}
+using System.Collections;
+using System.Linq;
+using System.Reflection;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Input;
+using Timespinner.GameAbstractions.Inventory;
+using Timespinner.GameAbstractions.Saving;
+using Timespinner.GameStateManagement.ScreenManager;
+using TsRandomizer.Extensions;
+using TsRandomizer.IntermediateObjects;
+using System;
+
+namespace TsRandomizer.Screens
+{
+ [TimeSpinnerType("Timespinner.GameStateManagement.Screens.Shop.ShopMenuScreen")]
+ // ReSharper disable once UnusedMember.Global
+ class ShopMenuScreen : Screen
+ {
+ static readonly MethodInfo GetSelectedCategoryMethod = TimeSpinnerType
+ .Get("Timespinner.GameStateManagement.Screens.Shop.ShopMenuScreen")
+ .GetMethod("GetSelectedCategory", BindingFlags.NonPublic | BindingFlags.Instance);
+
+ static readonly MethodInfo GetSelectedShopEntryMethod = TimeSpinnerType
+ .Get("Timespinner.GameStateManagement.Screens.Shop.ShopMenuEntryCollection")
+ .GetMethod("GetSelectedShopEntry", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
+
+ static readonly Type MenuDescriptionType = TimeSpinnerType
+ .Get("Timespinner.GameStateManagement.Screens.BaseClasses.Menu.MenuDescription");
+
+ static readonly Type EInventoryItemIconType = TimeSpinnerType
+ .Get("Timespinner.GameAbstractions.Inventory.EInventoryItemIcon");
+
+ // Timers for held-button repeat on quantity adjustment
+ float _rightHeldTime;
+ float _leftHeldTime;
+ const float HoldDelay = 0.4f;
+ const float HoldRepeat = 0.1f;
+ float _rightLastRepeat;
+ float _leftLastRepeat;
+
+ public ShopMenuScreen(ScreenManager screenManager, GameScreen screen) : base(screenManager, screen)
+ {
+ var gameSettings = ((GameSave)Dynamic._saveFile).GetSettings();
+
+ // Menu count varies on relics/items/equipment etc. being in inventory
+ // Last menu is always helper functions that don't have an _items
+ // but aren't otherwise distinguishable
+ foreach (var i in Enumerable.Range(0, ((IList)Dynamic._subMenuCollections).Count - 1))
+ {
+ var shopMenu = ((IList)Dynamic._subMenuCollections)[i].AsDynamic();
+ foreach (var shopMenuEntry in shopMenu._items)
+ {
+ var dynamicShopMenuEntry = ((object)shopMenuEntry).AsDynamic();
+ var item = (InventoryItem)dynamicShopMenuEntry.Item;
+ if (item.NameKey == "inv_use_MagicMarbles")
+ {
+ item.IsSellable = false;
+ dynamicShopMenuEntry.ShopPrice = -1;
+ }
+ int currentPrice = dynamicShopMenuEntry.ShopPrice;
+ if (currentPrice == 0)
+ {
+ // Set a price for "priceless" items
+ dynamicShopMenuEntry.ShopPrice = 2000;
+ currentPrice = dynamicShopMenuEntry.ShopPrice;
+ }
+ dynamicShopMenuEntry.ShopPrice = (int)(currentPrice * gameSettings.ShopMultiplier.Value);
+
+ if (item is InventoryUseItem useItem)
+ {
+ var type = useItem.UseItemType;
+ if (type != EInventoryUseItemType.MagicMarbles
+ && type != EInventoryUseItemType.EssenceCrystal
+ && type != EInventoryUseItemType.GoldRing
+ && type != EInventoryUseItemType.GoldNecklace)
+ useItem.StackCap = QoLSettings.Current.StackCap;
+ }
+ }
+ }
+ }
+
+ public override void Update(GameTime gameTime, InputState input)
+ {
+ int cap = QoLSettings.Current.StackCap;
+ if (cap <= 9) return;
+
+ bool isBuying = (bool)Dynamic._isBuying;
+ if (!isBuying) return;
+
+ float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
+
+ // Detect raw held state for repeat logic
+ bool rightHeld = false;
+ bool leftHeld = false;
+ for (int i = 0; i < input.CurrentGamePadStates.Length; i++)
+ {
+ rightHeld |= input.CurrentGamePadStates[i].IsButtonDown(Buttons.DPadRight)
+ || input.CurrentGamePadStates[i].IsButtonDown(Buttons.LeftThumbstickRight)
+ || input.CurrentGamePadStates[i].IsButtonDown(Buttons.RightThumbstickRight);
+ leftHeld |= input.CurrentGamePadStates[i].IsButtonDown(Buttons.DPadLeft)
+ || input.CurrentGamePadStates[i].IsButtonDown(Buttons.LeftThumbstickLeft)
+ || input.CurrentGamePadStates[i].IsButtonDown(Buttons.RightThumbstickLeft);
+ }
+ for (int i = 0; i < input.CurrentKeyboardStates.Length; i++)
+ {
+ rightHeld |= input.CurrentKeyboardStates[i].IsKeyDown(Keys.Right);
+ leftHeld |= input.CurrentKeyboardStates[i].IsKeyDown(Keys.Left);
+ }
+
+ bool rightNew = input.IsNewButtonPress(Buttons.DPadRight)
+ || input.IsNewButtonPress(Buttons.LeftThumbstickRight)
+ || input.IsNewButtonPress(Buttons.RightThumbstickRight)
+ || input.IsNewKeyPress(Keys.Right);
+
+ bool leftNew = input.IsNewButtonPress(Buttons.DPadLeft)
+ || input.IsNewButtonPress(Buttons.LeftThumbstickLeft)
+ || input.IsNewButtonPress(Buttons.RightThumbstickLeft)
+ || input.IsNewKeyPress(Keys.Left);
+
+ // Update hold timers
+ if (rightHeld)
+ {
+ _rightHeldTime += dt;
+ if (rightNew) _rightLastRepeat = 0f;
+ }
+ else
+ {
+ _rightHeldTime = 0f;
+ _rightLastRepeat = 0f;
+ }
+
+ if (leftHeld)
+ {
+ _leftHeldTime += dt;
+ if (leftNew) _leftLastRepeat = 0f;
+ }
+ else
+ {
+ _leftHeldTime = 0f;
+ _leftLastRepeat = 0f;
+ }
+
+ // Fire on initial press or after hold delay with repeat interval
+ bool rightPressed = rightNew
+ || (rightHeld && _rightHeldTime >= HoldDelay && _rightHeldTime - _rightLastRepeat >= HoldRepeat);
+ bool leftPressed = leftNew
+ || (leftHeld && _leftHeldTime >= HoldDelay && _leftHeldTime - _leftLastRepeat >= HoldRepeat);
+
+ if (rightPressed && _rightHeldTime >= HoldDelay) _rightLastRepeat = _rightHeldTime;
+ if (leftPressed && _leftHeldTime >= HoldDelay) _leftLastRepeat = _leftHeldTime;
+
+ if (!rightPressed && !leftPressed) return;
+
+ var selectedCategory = GetSelectedCategoryMethod?.Invoke(GameScreen, null);
+ if (selectedCategory == null) return;
+
+ var selectedEntry = GetSelectedShopEntryMethod?.Invoke(selectedCategory, null);
+ if (selectedEntry == null) return;
+
+ var entryDynamic = ((object)selectedEntry).AsDynamic();
+ var item = (InventoryItem)entryDynamic.Item;
+ if (!(item is InventoryUseItem useItem2)) return;
+
+ var type2 = useItem2.UseItemType;
+ if (type2 == EInventoryUseItemType.MagicMarbles
+ || type2 == EInventoryUseItemType.EssenceCrystal
+ || type2 == EInventoryUseItemType.GoldRing
+ || type2 == EInventoryUseItemType.GoldNecklace)
+ return;
+
+ var save = (GameSave)Dynamic._saveFile;
+ int currentCount = save.Inventory.UseItemInventory.Inventory.ContainsKey(item.Key)
+ ? save.Inventory.UseItemInventory.Inventory[item.Key].Count
+ : 0;
+
+ int maxCanBuy = cap - currentCount;
+ if (maxCanBuy <= 0) return;
+
+ int currentQty = (int)entryDynamic.QuanityToBuy;
+ // Vanilla cap is 9 total, so vanilla allows buying up to (9 - currentCount)
+ int vanillaCap = 9 - currentCount;
+
+ // Only act when we are already at or above the vanilla cap,
+ // so below that the vanilla input handling works as normal.
+ if (rightPressed && currentQty >= vanillaCap && currentQty < maxCanBuy)
+ entryDynamic.QuanityToBuy = currentQty + 1;
+
+ if (leftPressed && currentQty > vanillaCap && currentQty != 1)
+ entryDynamic.QuanityToBuy = currentQty - 1;
+ }
+
+ public override void HandleInput(InputState input)
+ {
+ int cap = QoLSettings.Current.StackCap;
+ if (cap <= 9) return;
+
+ bool isBuying = (bool)Dynamic._isBuying;
+ if (!isBuying) return;
+
+ if (!input.IsNewPressConfirm(null)) return;
+
+ var selectedCategory = GetSelectedCategoryMethod?.Invoke(GameScreen, null);
+ if (selectedCategory == null) return;
+
+ var selectedEntry = GetSelectedShopEntryMethod?.Invoke(selectedCategory, null);
+ if (selectedEntry == null) return;
+
+ var entryDynamic = ((object)selectedEntry).AsDynamic();
+ var item = (InventoryItem)entryDynamic.Item;
+ if (!(item is InventoryUseItem)) return;
+
+ var save = (GameSave)Dynamic._saveFile;
+ int currentCount = save.Inventory.UseItemInventory.Inventory.ContainsKey(item.Key)
+ ? save.Inventory.UseItemInventory.Inventory[item.Key].Count
+ : 0;
+
+ if (currentCount < 9) return;
+
+ // Player holds 9+ and tries to confirm - show message immediately in HandleInput phase
+ Dynamic.PlayErrorSound();
+ var noneIcon = Enum.ToObject(EInventoryItemIconType, 0);
+ var ctor = MenuDescriptionType.GetConstructors(
+ System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
+ if (ctor.Length > 0)
+ {
+ var menuDesc = ctor[0].Invoke(new object[]
+ {
+ "Vanilla cap reached - hold fewer than 9 to buy more. (We cannot fix this, sorry!)",
+ (object)Dynamic.DescriptionFont,
+ noneIcon,
+ (object)Dynamic.GCM.SpMenuIcons,
+ (object)Dynamic.GCM.SpUIButtons,
+ (bool)Dynamic.IsDescriptionCentered,
+ (object)Dynamic.DescriptionControllerMapping
+ });
+ Dynamic.CurrentDescription = menuDesc;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/TsRandomizer/Screens/ToastPopupScreen.cs b/TsRandomizer/Screens/ToastPopupScreen.cs
new file mode 100644
index 00000000..2aa1065d
--- /dev/null
+++ b/TsRandomizer/Screens/ToastPopupScreen.cs
@@ -0,0 +1,102 @@
+using Microsoft.Xna.Framework;
+using Timespinner.GameAbstractions;
+using Timespinner.GameStateManagement.ScreenManager;
+using TsRandomizer.IntermediateObjects;
+using TsRandomizer.Randomisation;
+
+namespace TsRandomizer.Screens
+{
+ [TimeSpinnerType("Timespinner.GameStateManagement.Screens.InGame.BaseToastPopup")]
+ [TimeSpinnerType("Timespinner.GameStateManagement.Screens.InGame.Toasts.RelicOrbGetToast")]
+ [TimeSpinnerType("Timespinner.GameStateManagement.Screens.InGame.Toasts.CharacterLevelUpToast")]
+ [TimeSpinnerType("Timespinner.GameStateManagement.Screens.InGame.Toasts.OrbLevelUpToast")]
+ [TimeSpinnerType("Timespinner.GameStateManagement.Screens.InGame.Toasts.StatMaxUpToast")]
+ [TimeSpinnerType("Timespinner.GameStateManagement.Screens.InGame.Toasts.QuestCompleteToast")]
+ // ReSharper disable once UnusedMember.Global
+ class ToastPopupScreen : Screen
+ {
+ static readonly string BaseToastTypeName =
+ "Timespinner.GameStateManagement.Screens.InGame.BaseToastPopup";
+ static readonly string LevelUpToastTypeName =
+ "Timespinner.GameStateManagement.Screens.InGame.Toasts.CharacterLevelUpToast";
+
+ // Cached via reflection once, reused across all toast instances
+ static System.Reflection.FieldInfo _freezeField;
+ static System.Reflection.FieldInfo _timeToWaitField;
+ static System.Reflection.FieldInfo _totalDisplayField;
+ static System.Reflection.FieldInfo _timeBeforeFlashingField;
+ static System.Reflection.FieldInfo _timeToFlashField;
+ static System.Reflection.FieldInfo _timeToFadeField;
+ static System.Reflection.PropertyInfo _isOverlayProp;
+ static System.Reflection.PropertyInfo _waitForInputProp;
+ static System.Reflection.PropertyInfo _hasReceivedInputProp;
+ static System.Reflection.FieldInfo _controlTimerField;
+
+ bool _isLevelUpToast;
+
+ public ToastPopupScreen(ScreenManager screenManager, GameScreen gameScreen)
+ : base(screenManager, gameScreen)
+ {
+ }
+
+ public override void Initialize(ItemLocationMap itemLocationMap, GCM gameContentManager)
+ {
+ _isLevelUpToast = GameScreen.GetType().FullName == LevelUpToastTypeName;
+
+ var flags = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance;
+ var publicFlags = System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance;
+
+ if (_freezeField == null)
+ {
+ var baseType = TimeSpinnerType.Get(BaseToastTypeName);
+ var levelUpType = TimeSpinnerType.Get(LevelUpToastTypeName);
+
+ _freezeField = baseType.GetField("_doesFreezeGameplay", flags);
+ _timeToWaitField = baseType.GetField("_timeToWait", flags);
+ _totalDisplayField = baseType.GetField("_totalDisplayTime", flags);
+ _timeBeforeFlashingField = baseType.GetField("_timeBeforeFlashing", flags);
+ _timeToFlashField = baseType.GetField("_timeToFlash", flags);
+ _timeToFadeField = baseType.GetField("_timeToFade", flags);
+ _isOverlayProp = GameScreen.GetType().BaseType?.GetProperty("IsOverlayScreen", publicFlags);
+ _waitForInputProp = baseType.GetProperty("DoesWaitForInputToFinish", publicFlags);
+ _hasReceivedInputProp = baseType.GetProperty("HasReceivedInputToClose", publicFlags);
+ _controlTimerField = levelUpType.GetField("_controlTimer", flags);
+ }
+
+ // FastToastPopups: collapse the wait phase to zero so the toast flies through
+ if (QoLSettings.Current.FastToastPopups)
+ {
+ _timeToWaitField.SetValue(GameScreen, 0f);
+
+ float before = (float)_timeBeforeFlashingField.GetValue(GameScreen);
+ float flash = (float)_timeToFlashField.GetValue(GameScreen);
+ float fade = (float)_timeToFadeField.GetValue(GameScreen);
+ _totalDisplayField.SetValue(GameScreen, before + flash + fade);
+ }
+
+ // ToastsBlockMovement: control whether the toast freezes gameplay
+ if (!QoLSettings.Current.ToastsBlockMovement)
+ {
+ _freezeField.SetValue(GameScreen, false);
+ _isOverlayProp?.SetValue(GameScreen, true);
+
+ // CharacterLevelUpToast has its own _controlTimer that stalls input
+ if (_isLevelUpToast)
+ _controlTimerField?.SetValue(GameScreen, 0.75f);
+ }
+ }
+
+ public override void Update(GameTime gameTime, InputState input)
+ {
+ // FastToastPopups suppresses the button-press requirement.
+ // The original Harmony patch blocked set_DoesWaitForInputToFinish globally
+ // on all toasts. We replicate that by overriding every frame, since
+ // RelicOrbGetToast sets it back to true during its own update/script handling.
+ if (QoLSettings.Current.FastToastPopups)
+ {
+ _waitForInputProp?.SetValue(GameScreen, false);
+ _hasReceivedInputProp?.SetValue(GameScreen, true);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/TsRandomizer/Settings/QoLSettings.cs b/TsRandomizer/Settings/QoLSettings.cs
new file mode 100644
index 00000000..53be3d89
--- /dev/null
+++ b/TsRandomizer/Settings/QoLSettings.cs
@@ -0,0 +1,59 @@
+using System;
+using System.IO;
+using Newtonsoft.Json;
+
+namespace TsRandomizer
+{
+ public class QoLSettingsData
+ {
+ public bool AutoSkipCutscenes { get; set; } = true;
+ public bool AutoSkipDialogue { get; set; } = true;
+ public int StackCap { get; set; } = 99;
+ public bool FastToastPopups { get; set; } = true;
+ public bool ToastsBlockMovement { get; set; } = false;
+ }
+
+ public static class QoLSettings
+ {
+ static readonly string SettingsPath = Path.Combine(
+ AppDomain.CurrentDomain.BaseDirectory, "qol-settings.json");
+
+ public static QoLSettingsData Current { get; private set; } = Load();
+
+ public static QoLSettingsData Load()
+ {
+ try
+ {
+ if (File.Exists(SettingsPath))
+ {
+ var json = File.ReadAllText(SettingsPath);
+ Current = JsonConvert.DeserializeObject(json) ?? new QoLSettingsData();
+ }
+ else
+ {
+ Current = new QoLSettingsData();
+ Save();
+ }
+ }
+ catch
+ {
+ Current = new QoLSettingsData();
+ }
+
+ return Current;
+ }
+
+ public static void Save()
+ {
+ try
+ {
+ var json = JsonConvert.SerializeObject(Current, Formatting.Indented);
+ File.WriteAllText(SettingsPath, json);
+ }
+ catch
+ {
+ // ignored
+ }
+ }
+ }
+}
diff --git a/TsRandomizer/TsRandomizer.csproj b/TsRandomizer/TsRandomizer.csproj
index c9aad11f..23f74900 100644
--- a/TsRandomizer/TsRandomizer.csproj
+++ b/TsRandomizer/TsRandomizer.csproj
@@ -1,421 +1,422 @@
-
-
-
-
- Debug
- AnyCPU
- {694E46C5-FD46-4CC8-8B71-D381E154CCF7}
- WinExe
- TsRandomizer
- TsRandomizer
- v4.8
- 512
- true
- false
- publish\
- true
- Disk
- false
- Foreground
- 7
- Days
- false
- false
- true
- 0
- 1.0.0.%2a
- false
- true
-
-
-
- x86
- true
- full
- false
- ..\..\TestVersions\SteamVerison\
- TRACE;DEBUG;NET40
- prompt
- 4
- false
- true
-
-
- x86
- pdbonly
- true
- C:\Program Files %28x86%29\Steam\steamapps\common\Timespinner\
- TRACE
- prompt
- 4
- false
- true
-
-
-
-
-
- GunOrbLargeT.ico
-
-
- true
- ..\..\TestVersions\DRMFreeVersion\
- DEBUG;TRACE
- full
- x86
- prompt
- MinimumRecommendedRules.ruleset
- false
-
-
- ..\..\TestVersions\DRMFreeVersion\
- TRACE
- true
- pdbonly
- x86
- prompt
- MinimumRecommendedRules.ruleset
- false
-
-
- true
- ..\..\TestVersions\DRMFreeGoG\
- DEBUG;TRACE
- full
- x86
- 7.3
- prompt
- MinimumRecommendedRules.ruleset
-
-
- ..\..\TestVersions\DRMFreeGoG\
- TRACE
- true
- true
- pdbonly
- x86
- 7.3
- prompt
-
-
-
- ..\packages\Archipelago.Gifting.Net.0.4.3\lib\net452\Archipelago.Gifting.Net.dll
-
-
- ..\packages\Archipelago.MultiClient.Net.6.7.0\lib\net45\Archipelago.MultiClient.Net.dll
-
-
- C:\Program Files (x86)\Steam\steamapps\common\Timespinner\FNA.dll
- ~/.local/share/Steam/steamapps/common/Timespinner/FNA.dll
- ..\..\..\TestVersions\DRMFreeVersion\FNA.dll
- False
-
-
-
- ..\packages\Archipelago.MultiClient.Net.6.7.0\lib\net45\Newtonsoft.Json.dll
-
-
-
-
- False
-
-
- ..\..\..\TestVersions\DRMFreeVersion\Timespinner.exe
- False
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- False
- .NET Framework 3.5 SP1
- false
-
-
-
-
-
-
-
-
+
+
+
+
+ Debug
+ AnyCPU
+ {694E46C5-FD46-4CC8-8B71-D381E154CCF7}
+ WinExe
+ TsRandomizer
+ TsRandomizer
+ v4.8
+ 512
+ true
+ false
+ publish\
+ true
+ Disk
+ false
+ Foreground
+ 7
+ Days
+ false
+ false
+ true
+ 0
+ 1.0.0.%2a
+ false
+ true
+
+
+
+ x86
+ true
+ full
+ false
+ ..\..\TestVersions\SteamVerison\
+ TRACE;DEBUG;NET40
+ prompt
+ 4
+ false
+ true
+
+
+ x86
+ pdbonly
+ true
+ C:\Program Files %28x86%29\Steam\steamapps\common\Timespinner\
+ TRACE
+ prompt
+ 4
+ false
+ true
+
+
+
+
+
+ GunOrbLargeT.ico
+
+
+ true
+ ..\..\TestVersions\DRMFreeVersion\
+ DEBUG;TRACE
+ full
+ x86
+ prompt
+ MinimumRecommendedRules.ruleset
+ false
+
+
+ ..\..\TestVersions\DRMFreeVersion\
+ TRACE
+ true
+ pdbonly
+ x86
+ prompt
+ MinimumRecommendedRules.ruleset
+ false
+
+
+ true
+ ..\..\TestVersions\DebugDRMFreeGoG\
+ TRACE
+ full
+ x86
+ 7.3
+ prompt
+ MinimumRecommendedRules.ruleset
+
+
+ ..\..\TestVersions\ReleaseDRMFreeGoG\
+ TRACE
+ false
+ false
+ pdbonly
+ x86
+ 7.3
+ prompt
+
+
+
+ ..\packages\Archipelago.Gifting.Net.0.4.4\lib\net452\Archipelago.Gifting.Net.dll
+
+
+ ..\packages\Archipelago.MultiClient.Net.6.7.0\lib\net45\Archipelago.MultiClient.Net.dll
+
+
+ False
+ ..\..\TestVersions\DebugDRMFreeGoG\FNA.dll
+
+
+
+ ..\packages\Archipelago.MultiClient.Net.6.7.0\lib\net45\Newtonsoft.Json.dll
+
+
+
+
+ False
+
+
+ False
+ ..\..\TestVersions\DebugDRMFreeGoG\Timespinner.exe
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ False
+ .NET Framework 3.5 SP1
+ false
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/TsRandomizer/TsRandomizer.csproj.user b/TsRandomizer/TsRandomizer.csproj.user
new file mode 100644
index 00000000..ca342d1e
--- /dev/null
+++ b/TsRandomizer/TsRandomizer.csproj.user
@@ -0,0 +1,6 @@
+
+
+
+ ShowAllFiles
+
+
\ No newline at end of file
diff --git a/TsRandomizer/packages.config b/TsRandomizer/packages.config
index 6cc40bbd..f990d3b3 100644
--- a/TsRandomizer/packages.config
+++ b/TsRandomizer/packages.config
@@ -1,5 +1,6 @@
-
-
-
-
+
+
+
+
+
\ No newline at end of file