├── .gitattributes ├── .gitignore ├── DynamicReflections ├── DynamicReflections.cs ├── DynamicReflections.csproj ├── DynamicReflections.sln ├── Examples │ ├── Fashionable Mirrors │ │ ├── [DGA] Fashionable Mirrors │ │ │ ├── assets │ │ │ │ ├── double_leaning_mirror.png │ │ │ │ ├── enchanted_mirror.png │ │ │ │ ├── leaning_mirror.png │ │ │ │ └── table_mirror.png │ │ │ ├── content.json │ │ │ ├── i18n │ │ │ │ └── default.json │ │ │ └── manifest.json │ │ └── [DR] Fashionable Mirrors │ │ │ ├── Masks │ │ │ ├── double_leaning_mirror_mask.png │ │ │ ├── enchanted_mirror_mask.png │ │ │ ├── leaning_mirror_mask.png │ │ │ └── table_mirror_mask.png │ │ │ ├── manifest.json │ │ │ └── mirrors.json │ └── [SF] Example Pack │ │ ├── Buildings │ │ └── HallOfMirrors │ │ │ ├── building.json │ │ │ └── building.png │ │ ├── Interiors │ │ ├── hall_of_mirrors.tmx │ │ └── z_mirrors.png │ │ └── manifest.json ├── Framework │ ├── Assets │ │ ├── Shaders │ │ │ ├── mask.fx │ │ │ ├── mask.mgfx │ │ │ ├── opacity.fx │ │ │ ├── opacity.mgfx │ │ │ ├── wavy.fx │ │ │ └── wavy.mgfx │ │ └── Textures │ │ │ ├── night_sky_sheet.png │ │ │ ├── puddles_sheet.png │ │ │ └── sky_effects_sheet.png │ ├── Extensions │ │ └── GameLocationExtensions.cs │ ├── External │ │ └── GenericModConfigMenu │ │ │ ├── GMCMHelper.cs │ │ │ └── ModConfig.cs │ ├── Interfaces │ │ ├── IGenericModConfigMenuApi.cs │ │ └── Internal │ │ │ └── Api.cs │ ├── Managers │ │ ├── ApiManager.cs │ │ ├── AssetManager.cs │ │ ├── MirrorsManager.cs │ │ ├── PuddleManager.cs │ │ └── SkyManager.cs │ ├── Models │ │ ├── ContentPackModel.cs │ │ ├── Mirror.cs │ │ └── Settings │ │ │ ├── MirrorSettings.cs │ │ │ ├── PuddleSettings.cs │ │ │ ├── SkySettings.cs │ │ │ └── WaterSettings.cs │ ├── Patches │ │ ├── Locations │ │ │ └── GameLocationPatch.cs │ │ ├── Objects │ │ │ └── FurniturePatch.cs │ │ ├── PatchTemplate.cs │ │ ├── SMAPI │ │ │ └── DisplayDevicePatch.cs │ │ ├── Tools │ │ │ ├── FishingRodPatch.cs │ │ │ └── ToolPatch.cs │ │ └── xTile │ │ │ └── LayerPatch.cs │ └── Utilities │ │ ├── Extensions │ │ └── RandomExtensions.cs │ │ ├── ModDataKeys.cs │ │ └── SpriteBatchToolkit.cs ├── i18n │ ├── de.json │ ├── default.json │ ├── fr.json │ ├── ja.json │ ├── ko.json │ ├── ru.json │ ├── th.json │ └── zh.json └── manifest.json ├── LICENSE └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET Core 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # ASP.NET Scaffolding 66 | ScaffoldingReadMe.txt 67 | 68 | # StyleCop 69 | StyleCopReport.xml 70 | 71 | # Files built by Visual Studio 72 | *_i.c 73 | *_p.c 74 | *_h.h 75 | *.ilk 76 | *.meta 77 | *.obj 78 | *.iobj 79 | *.pch 80 | *.pdb 81 | *.ipdb 82 | *.pgc 83 | *.pgd 84 | *.rsp 85 | *.sbr 86 | *.tlb 87 | *.tli 88 | *.tlh 89 | *.tmp 90 | *.tmp_proj 91 | *_wpftmp.csproj 92 | *.log 93 | *.tlog 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 298 | *.vbp 299 | 300 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 301 | *.dsw 302 | *.dsp 303 | 304 | # Visual Studio 6 technical files 305 | *.ncb 306 | *.aps 307 | 308 | # Visual Studio LightSwitch build output 309 | **/*.HTMLClient/GeneratedArtifacts 310 | **/*.DesktopClient/GeneratedArtifacts 311 | **/*.DesktopClient/ModelManifest.xml 312 | **/*.Server/GeneratedArtifacts 313 | **/*.Server/ModelManifest.xml 314 | _Pvt_Extensions 315 | 316 | # Paket dependency manager 317 | .paket/paket.exe 318 | paket-files/ 319 | 320 | # FAKE - F# Make 321 | .fake/ 322 | 323 | # CodeRush personal settings 324 | .cr/personal 325 | 326 | # Python Tools for Visual Studio (PTVS) 327 | __pycache__/ 328 | *.pyc 329 | 330 | # Cake - Uncomment if you are using it 331 | # tools/** 332 | # !tools/packages.config 333 | 334 | # Tabs Studio 335 | *.tss 336 | 337 | # Telerik's JustMock configuration file 338 | *.jmconfig 339 | 340 | # BizTalk build output 341 | *.btp.cs 342 | *.btm.cs 343 | *.odx.cs 344 | *.xsd.cs 345 | 346 | # OpenCover UI analysis results 347 | OpenCover/ 348 | 349 | # Azure Stream Analytics local run output 350 | ASALocalRun/ 351 | 352 | # MSBuild Binary and Structured Log 353 | *.binlog 354 | 355 | # NVidia Nsight GPU debugger configuration file 356 | *.nvuser 357 | 358 | # MFractors (Xamarin productivity tool) working folder 359 | .mfractor/ 360 | 361 | # Local History for Visual Studio 362 | .localhistory/ 363 | 364 | # Visual Studio History (VSHistory) files 365 | .vshistory/ 366 | 367 | # BeatPulse healthcheck temp database 368 | healthchecksdb 369 | 370 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 371 | MigrationBackup/ 372 | 373 | # Ionide (cross platform F# VS Code tools) working folder 374 | .ionide/ 375 | 376 | # Fody - auto-generated XML schema 377 | FodyWeavers.xsd 378 | 379 | # VS Code files for those working on multiple tools 380 | .vscode/* 381 | !.vscode/settings.json 382 | !.vscode/tasks.json 383 | !.vscode/launch.json 384 | !.vscode/extensions.json 385 | *.code-workspace 386 | 387 | # Local History for Visual Studio Code 388 | .history/ 389 | 390 | # Windows Installer files from build outputs 391 | *.cab 392 | *.msi 393 | *.msix 394 | *.msm 395 | *.msp 396 | 397 | # JetBrains Rider 398 | *.sln.iml 399 | -------------------------------------------------------------------------------- /DynamicReflections/DynamicReflections.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 3.0.0 4 | net6.0 5 | latest 6 | true 7 | false 8 | false 9 | E:\SteamLibrary\steamapps\common\Stardew Valley\Mods 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Never 24 | 25 | 26 | Always 27 | 28 | 29 | Always 30 | 31 | 32 | Always 33 | 34 | 35 | Always 36 | 37 | 38 | Always 39 | 40 | 41 | Always 42 | 43 | 44 | Always 45 | 46 | 47 | Always 48 | 49 | 50 | Always 51 | 52 | 53 | Always 54 | 55 | 56 | Always 57 | 58 | 59 | Always 60 | 61 | 62 | Always 63 | 64 | 65 | Always 66 | 67 | 68 | Always 69 | 70 | 71 | 72 | 73 | 74 | $(PostBuildEventDependsOn); 75 | PostBuildMacros; 76 | 77 | 78 | powershell -Command "(ls *manifest.json -rec | foreach-object { $f=$_.FullName; (gc -LiteralPath \"$f\") -replace 'REPLACE_ME_WITH_VERSION', '$(Version)' | sc -LiteralPath \"$f\" })" 79 | 80 | powershell Remove-Item -Path 'C:\Users\Floogen\Documents\GitHub Repos\DynamicReflections\DynamicReflections\releases\latest\DynamicReflections"' -Recurse -Force 81 | xcopy /s /y /i "C:\Users\Floogen\Documents\GitHub Repos\DynamicReflections\DynamicReflections\bin\Debug\$(TargetFramework)" "C:\Users\Floogen\Documents\GitHub Repos\DynamicReflections\DynamicReflections\releases\latest\DynamicReflections" 82 | 7z a -tzip "C:\Users\Floogen\Documents\GitHub Repos\DynamicReflections\DynamicReflections\releases\DynamicReflections-$(Version).zip" "C:\Users\Floogen\Documents\GitHub Repos\DynamicReflections\DynamicReflections\releases\latest\DynamicReflections" 83 | 84 | powershell Remove-Item -Path '$(GameModsPath)\DynamicReflections' -Recurse -Force 85 | powershell Remove-Item -Path '$(GameModsPath)\Dynamic Reflections Examples' -Recurse -Force 86 | 87 | xcopy /s /y /i "C:\Users\Floogen\Documents\GitHub Repos\DynamicReflections\DynamicReflections\releases\latest\DynamicReflections" "$(GameModsPath)\DynamicReflections" 88 | xcopy /s /y /i "C:\Users\Floogen\Documents\GitHub Repos\DynamicReflections\DynamicReflections\Examples\*" "$(GameModsPath)\Dynamic Reflections Examples" 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /DynamicReflections/DynamicReflections.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.2.32505.173 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DynamicReflections", "DynamicReflections.csproj", "{B0DA23ED-E495-4F7E-8770-DCFE389C429C}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {B0DA23ED-E495-4F7E-8770-DCFE389C429C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {B0DA23ED-E495-4F7E-8770-DCFE389C429C}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {B0DA23ED-E495-4F7E-8770-DCFE389C429C}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {B0DA23ED-E495-4F7E-8770-DCFE389C429C}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {FAED9AFC-5499-46BB-83B3-45491E89EB8A} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /DynamicReflections/Examples/Fashionable Mirrors/[DGA] Fashionable Mirrors/assets/double_leaning_mirror.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Floogen/DynamicReflections/3b773fd8ba3b07e01f8d039995cdc16b60dab87d/DynamicReflections/Examples/Fashionable Mirrors/[DGA] Fashionable Mirrors/assets/double_leaning_mirror.png -------------------------------------------------------------------------------- /DynamicReflections/Examples/Fashionable Mirrors/[DGA] Fashionable Mirrors/assets/enchanted_mirror.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Floogen/DynamicReflections/3b773fd8ba3b07e01f8d039995cdc16b60dab87d/DynamicReflections/Examples/Fashionable Mirrors/[DGA] Fashionable Mirrors/assets/enchanted_mirror.png -------------------------------------------------------------------------------- /DynamicReflections/Examples/Fashionable Mirrors/[DGA] Fashionable Mirrors/assets/leaning_mirror.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Floogen/DynamicReflections/3b773fd8ba3b07e01f8d039995cdc16b60dab87d/DynamicReflections/Examples/Fashionable Mirrors/[DGA] Fashionable Mirrors/assets/leaning_mirror.png -------------------------------------------------------------------------------- /DynamicReflections/Examples/Fashionable Mirrors/[DGA] Fashionable Mirrors/assets/table_mirror.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Floogen/DynamicReflections/3b773fd8ba3b07e01f8d039995cdc16b60dab87d/DynamicReflections/Examples/Fashionable Mirrors/[DGA] Fashionable Mirrors/assets/table_mirror.png -------------------------------------------------------------------------------- /DynamicReflections/Examples/Fashionable Mirrors/[DGA] Fashionable Mirrors/content.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "$ItemType": "Furniture", 4 | "ID": "Enchanted Mirror", 5 | "Type": "Decoration", 6 | "Configurations": [ 7 | { 8 | "Texture": "assets/enchanted_mirror.png:1@30, assets/enchanted_mirror.png:0@25, assets/enchanted_mirror.png:1@30, assets/enchanted_mirror.png:2@25", 9 | "DisplaySize": { 10 | "X": 1, 11 | "Y": 2 12 | }, 13 | "CollisionHeight": 1, 14 | "TileProperties": { 15 | "0, 1": { 16 | "Buildings": { 17 | "Action": "OpenFashionSense" 18 | } 19 | } 20 | } 21 | } 22 | ] 23 | }, 24 | { 25 | "$ItemType": "Furniture", 26 | "ID": "Leaning Mirror", 27 | "Type": "Decoration", 28 | "Configurations": [ 29 | { 30 | "Texture": "assets/leaning_mirror.png", 31 | "DisplaySize": { 32 | "X": 1, 33 | "Y": 3 34 | }, 35 | "CollisionHeight": 1, 36 | "TileProperties": { 37 | "0, 0": { 38 | "Buildings": { 39 | "Action": "OpenFashionSense" 40 | } 41 | }, 42 | "0, 1": { 43 | "Buildings": { 44 | "Action": "OpenFashionSense" 45 | } 46 | }, 47 | "0, 2": { 48 | "Buildings": { 49 | "Action": "OpenFashionSense" 50 | } 51 | } 52 | } 53 | } 54 | ] 55 | }, 56 | { 57 | "$ItemType": "Furniture", 58 | "ID": "Double Leaning Mirror", 59 | "Type": "Decoration", 60 | "Configurations": [ 61 | { 62 | "Texture": "assets/double_leaning_mirror.png", 63 | "DisplaySize": { 64 | "X": 2, 65 | "Y": 3 66 | }, 67 | "CollisionHeight": 1, 68 | "TileProperties": { 69 | "0, 0": { 70 | "Buildings": { 71 | "Action": "OpenFashionSense" 72 | } 73 | }, 74 | "0, 1": { 75 | "Buildings": { 76 | "Action": "OpenFashionSense" 77 | } 78 | }, 79 | "0, 2": { 80 | "Buildings": { 81 | "Action": "OpenFashionSense" 82 | } 83 | }, 84 | "1, 0": { 85 | "Buildings": { 86 | "Action": "OpenFashionSense" 87 | } 88 | }, 89 | "1, 1": { 90 | "Buildings": { 91 | "Action": "OpenFashionSense" 92 | } 93 | }, 94 | "1, 2": { 95 | "Buildings": { 96 | "Action": "OpenFashionSense" 97 | } 98 | } 99 | } 100 | } 101 | ] 102 | }, 103 | { 104 | "$ItemType": "Furniture", 105 | "ID": "Table Mirror", 106 | "Type": "Decoration", 107 | "Configurations": [ 108 | { 109 | "Texture": "assets/table_mirror.png", 110 | "DisplaySize": { 111 | "X": 2, 112 | "Y": 3 113 | }, 114 | "CollisionHeight": 1, 115 | "TileProperties": { 116 | "0, 0": { 117 | "Buildings": { 118 | "Action": "OpenFashionSense" 119 | } 120 | }, 121 | "0, 1": { 122 | "Buildings": { 123 | "Action": "OpenFashionSense" 124 | } 125 | }, 126 | "0, 2": { 127 | "Buildings": { 128 | "Action": "OpenFashionSense" 129 | } 130 | }, 131 | "1, 0": { 132 | "Buildings": { 133 | "Action": "OpenFashionSense" 134 | } 135 | }, 136 | "1, 1": { 137 | "Buildings": { 138 | "Action": "OpenFashionSense" 139 | } 140 | }, 141 | "1, 2": { 142 | "Buildings": { 143 | "Action": "OpenFashionSense" 144 | } 145 | } 146 | } 147 | } 148 | ] 149 | } 150 | ] -------------------------------------------------------------------------------- /DynamicReflections/Examples/Fashionable Mirrors/[DGA] Fashionable Mirrors/i18n/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "furniture.Leaning Mirror.name": "Leaning Mirror", 3 | "furniture.Leaning Mirror.description": "...", 4 | "furniture.Double Leaning Mirror.name": "Double Leaning Mirror", 5 | "furniture.Double Leaning Mirror.description": "...", 6 | "furniture.Enchanted Mirror.name": "Enchanted Mirror", 7 | "furniture.Enchanted Mirror.description": "...", 8 | "furniture.Table Mirror.name": "Table Mirror", 9 | "furniture.Table Mirror.description": "..." 10 | } -------------------------------------------------------------------------------- /DynamicReflections/Examples/Fashionable Mirrors/[DGA] Fashionable Mirrors/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "[DGA] Fashionable Mirrors", 3 | "Author": "PeacefulEnd", 4 | "Version": "1.0.0", 5 | "Description": "Furniture component for Fashionable Mirrors.", 6 | "UniqueID": "PeacefulEnd.DGA.FashionableMirrors", 7 | "UpdateKeys": [], 8 | "ContentPackFor": { 9 | "UniqueID": "spacechase0.DynamicGameAssets", 10 | "MinimumVersion": "1.4.3" 11 | }, 12 | "Dependencies": [ 13 | { 14 | "UniqueID": "PeacefulEnd.FashionSense", 15 | "MinimumVersion": "4.9.0" 16 | } 17 | ], 18 | "DGA.FormatVersion": 2, 19 | "DGA.ConditionsFormatVersion": "1.23.0" 20 | } 21 | -------------------------------------------------------------------------------- /DynamicReflections/Examples/Fashionable Mirrors/[DR] Fashionable Mirrors/Masks/double_leaning_mirror_mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Floogen/DynamicReflections/3b773fd8ba3b07e01f8d039995cdc16b60dab87d/DynamicReflections/Examples/Fashionable Mirrors/[DR] Fashionable Mirrors/Masks/double_leaning_mirror_mask.png -------------------------------------------------------------------------------- /DynamicReflections/Examples/Fashionable Mirrors/[DR] Fashionable Mirrors/Masks/enchanted_mirror_mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Floogen/DynamicReflections/3b773fd8ba3b07e01f8d039995cdc16b60dab87d/DynamicReflections/Examples/Fashionable Mirrors/[DR] Fashionable Mirrors/Masks/enchanted_mirror_mask.png -------------------------------------------------------------------------------- /DynamicReflections/Examples/Fashionable Mirrors/[DR] Fashionable Mirrors/Masks/leaning_mirror_mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Floogen/DynamicReflections/3b773fd8ba3b07e01f8d039995cdc16b60dab87d/DynamicReflections/Examples/Fashionable Mirrors/[DR] Fashionable Mirrors/Masks/leaning_mirror_mask.png -------------------------------------------------------------------------------- /DynamicReflections/Examples/Fashionable Mirrors/[DR] Fashionable Mirrors/Masks/table_mirror_mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Floogen/DynamicReflections/3b773fd8ba3b07e01f8d039995cdc16b60dab87d/DynamicReflections/Examples/Fashionable Mirrors/[DR] Fashionable Mirrors/Masks/table_mirror_mask.png -------------------------------------------------------------------------------- /DynamicReflections/Examples/Fashionable Mirrors/[DR] Fashionable Mirrors/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "[DR] Fashionable Mirrors", 3 | "Author": "PeacefulEnd", 4 | "Version": "1.0.0", 5 | "Description": "Reflection component for Fashionable Mirrors.", 6 | "UniqueID": "PeacefulEnd.DynamicReflections.FashionableMirrors", 7 | "UpdateKeys": [ "Nexus:12834" ], 8 | "ContentPackFor": { 9 | "UniqueID": "PeacefulEnd.DynamicReflections", 10 | "MinimumVersion": "1.0.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /DynamicReflections/Examples/Fashionable Mirrors/[DR] Fashionable Mirrors/mirrors.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "FurnitureId": "PeacefulEnd.DGA.FashionableMirrors/Enchanted Mirror", 4 | "MaskTexture": "enchanted_mirror_mask.png", 5 | "Mirror": { 6 | "Dimensions": { 7 | "Width": 16, 8 | "Height": 32 9 | }, 10 | "ReflectionOverlay": { 11 | "R": 255, 12 | "G": 255, 13 | "B": 255, 14 | "A": 155 15 | }, 16 | "ReflectionOffset": { 17 | "X": 0, 18 | "Y": 4 19 | } 20 | } 21 | }, 22 | { 23 | "FurnitureId": "PeacefulEnd.DGA.FashionableMirrors/Leaning Mirror", 24 | "MaskTexture": "leaning_mirror_mask.png", 25 | "Mirror": { 26 | "Dimensions": { 27 | "Width": 16, 28 | "Height": 44 29 | }, 30 | "ReflectionOverlay": { 31 | "R": 255, 32 | "G": 255, 33 | "B": 255, 34 | "A": 155 35 | } 36 | } 37 | }, 38 | { 39 | "FurnitureId": "PeacefulEnd.DGA.FashionableMirrors/Double Leaning Mirror", 40 | "MaskTexture": "double_leaning_mirror_mask.png", 41 | "Mirror": { 42 | "Dimensions": { 43 | "Width": 32, 44 | "Height": 44 45 | }, 46 | "ReflectionOverlay": { 47 | "R": 255, 48 | "G": 255, 49 | "B": 255, 50 | "A": 155 51 | } 52 | } 53 | }, 54 | { 55 | "FurnitureId": "PeacefulEnd.DGA.FashionableMirrors/Table Mirror", 56 | "MaskTexture": "table_mirror_mask.png", 57 | "Mirror": { 58 | "Dimensions": { 59 | "Width": 32, 60 | "Height": 48 61 | }, 62 | "ReflectionOverlay": { 63 | "R": 255, 64 | "G": 255, 65 | "B": 255, 66 | "A": 125 67 | } 68 | } 69 | } 70 | ] -------------------------------------------------------------------------------- /DynamicReflections/Examples/[SF] Example Pack/Buildings/HallOfMirrors/building.json: -------------------------------------------------------------------------------- 1 | { 2 | "Id": "PeacefulEnd.DynamicReflections.ExamplePack_HallofMirrors", 3 | "Name": "Hall of Mirrors", 4 | "Description": "...", 5 | "BuildCost": 2500, 6 | "DrawShadow": false, 7 | 8 | "SourceRect": "0 0 112 80", 9 | "Size": { 10 | "X": 8, 11 | "Y": 3 12 | }, 13 | "SortTileOffset": 1, 14 | "CollisionMap": "XXXXXXXO\nXXXXXXXO\nXXXOXXXO", 15 | 16 | "IndoorMap": "hall_of_mirrors", 17 | "TunnelDoors": [ 18 | { 19 | "X": 3, 20 | "Y": 2 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /DynamicReflections/Examples/[SF] Example Pack/Buildings/HallOfMirrors/building.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Floogen/DynamicReflections/3b773fd8ba3b07e01f8d039995cdc16b60dab87d/DynamicReflections/Examples/[SF] Example Pack/Buildings/HallOfMirrors/building.png -------------------------------------------------------------------------------- /DynamicReflections/Examples/[SF] Example Pack/Interiors/hall_of_mirrors.tmx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 22 | 0,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,4353,0, 23 | 0,4369,4369,4369,4369,4369,4369,4369,4369,4369,4369,4369,4369,4369,4369,4369,4369,4369,0, 24 | 0,4705,4706,4705,4706,4705,4706,4705,4385,4385,4706,4705,4706,4705,4706,4705,4706,4705,0, 25 | 0,4689,4690,4689,4690,4689,4690,4689,4690,4689,4690,4689,4690,4689,4690,4689,4690,4689,0, 26 | 0,4705,4706,4705,4706,4705,4706,4705,4706,4705,4706,4705,4706,4705,4706,4705,4706,4705,0, 27 | 0,4689,4690,4689,4690,4689,4690,4689,4690,4689,4690,4689,4690,4689,4690,4689,4690,4689,0, 28 | 0,4705,4706,4705,4706,4705,4706,4705,4706,4705,4706,4705,4706,4705,4706,4705,4706,4705,0, 29 | 0,4689,4690,4689,4690,4689,4690,4689,4690,4689,4690,4689,4690,4689,4690,4689,4690,4689,0, 30 | 0,4705,4706,4705,4706,4705,4706,4705,4706,4705,4706,4705,4706,4705,4706,4705,4706,4705,0, 31 | 0,4689,4690,4689,4690,4689,4690,4689,4690,4689,4690,4689,4690,4689,4690,4689,4690,4689,0, 32 | 0,4705,4706,4705,4706,4705,4706,4705,4706,4705,4706,4705,4706,4705,4706,4705,4706,4705,0, 33 | 0,4689,4690,4689,4690,4689,4690,4689,4690,4689,4690,4689,4690,4689,4690,4689,4690,4689,0, 34 | 0,4705,4706,4705,4706,4705,4706,4705,4706,4705,4706,4705,4706,4705,4706,4705,4706,4705,0, 35 | 0,4689,4690,4689,4690,4689,4690,4689,4690,4689,4690,4689,4690,4689,4690,4689,4690,4689,0, 36 | 0,4705,4706,4705,4706,4705,4706,4705,4706,4705,4706,4705,4706,4705,4706,4705,4706,4705,0, 37 | 0,4689,4690,4689,4690,4689,4690,4689,4690,4689,4690,4689,4690,4689,4690,4689,4690,4689,0 38 | 39 | 40 | 41 | 42 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 43 | 0,0,0,0,0,0,0,0,4929,4933,0,0,0,0,0,0,0,0,0, 44 | 0,0,0,0,0,0,0,0,4929,4929,0,0,0,0,0,0,0,0,0, 45 | 0,0,0,0,0,0,0,0,4930,4930,0,0,0,0,0,0,0,0,0, 46 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 47 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 48 | 0,0,0,0,4918,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 49 | 0,0,0,0,4922,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 50 | 0,0,0,0,4926,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 51 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 52 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 53 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 54 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 55 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 56 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 57 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 58 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 59 | 60 | 61 | 62 | 63 | 2186,2179,2179,2179,2179,2179,2179,2179,2179,2179,2179,2179,2179,2179,2179,2179,2179,2179,2188, 64 | 2241,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2245, 65 | 2241,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2245, 66 | 2241,4385,4385,4385,4385,4385,4385,4385,4927,4928,4385,4385,4385,4385,4385,4385,4385,4385,2245, 67 | 2241,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2245, 68 | 2241,0,0,0,756,0,0,0,0,0,0,0,0,0,0,0,0,0,2245, 69 | 2241,0,0,0,4917,0,0,0,0,0,0,0,0,0,0,0,0,0,2245, 70 | 2241,0,0,0,4921,0,0,0,0,0,0,0,0,0,0,0,0,0,2245, 71 | 2241,0,0,0,4925,0,0,0,0,0,0,0,0,0,0,0,0,0,2245, 72 | 2241,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2245, 73 | 2241,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2245, 74 | 2241,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2245, 75 | 2241,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2245, 76 | 2241,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2245, 77 | 2241,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2245, 78 | 2337,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2344, 79 | 1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1 80 | 81 | 82 | 83 | 84 | 2186,2179,2179,2179,2179,2179,2179,2179,2179,2179,2179,2179,2179,2179,2179,2179,2179,2179,2188, 85 | 2241,0,0,0,0,0,0,0,4919,4920,0,0,0,0,0,0,0,0,2245, 86 | 2241,0,0,0,0,0,0,0,4923,4924,0,0,0,0,0,0,0,0,2245, 87 | 2241,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2245, 88 | 2241,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2245, 89 | 2241,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2245, 90 | 2241,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2245, 91 | 2241,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2245, 92 | 2241,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2245, 93 | 2241,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2245, 94 | 2241,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2245, 95 | 2241,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2245, 96 | 2241,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2245, 97 | 2241,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2245, 98 | 2241,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2245, 99 | 2337,2338,2342,2342,2342,2342,2342,2342,2339,0,2340,2342,2342,2342,2342,2342,2342,2343,2344, 100 | 2177,2177,2177,2177,2177,2177,2177,2177,2273,2342,2307,2177,2177,2177,2177,2177,2177,2177,2177 101 | 102 | 103 | 104 | 105 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 106 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 107 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 108 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 109 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 110 | 0,0,0,0,4913,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 111 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 112 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 113 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 114 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 115 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 116 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 117 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 118 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 119 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 120 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 121 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /DynamicReflections/Examples/[SF] Example Pack/Interiors/z_mirrors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Floogen/DynamicReflections/3b773fd8ba3b07e01f8d039995cdc16b60dab87d/DynamicReflections/Examples/[SF] Example Pack/Interiors/z_mirrors.png -------------------------------------------------------------------------------- /DynamicReflections/Examples/[SF] Example Pack/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "[SF] Example Pack", 3 | "Author": "PeacefulEnd", 4 | "Version": "1.0.0", 5 | "Description": "A pack utilizing Solid Foundations to show how to use the Dynamic Reflections framework.", 6 | "UniqueID": "PeacefulEnd.DynamicReflections.ExamplePack", 7 | "UpdateKeys": [], 8 | "Dependencies": [ 9 | { 10 | "UniqueID": "PeacefulEnd.DynamicReflections", 11 | "MinimumVersion": "1.0.0" 12 | } 13 | ], 14 | "ContentPackFor": { 15 | "UniqueID": "PeacefulEnd.SolidFoundations", 16 | "MinimumVersion": "1.12.0" 17 | } 18 | } -------------------------------------------------------------------------------- /DynamicReflections/Framework/Assets/Shaders/mask.fx: -------------------------------------------------------------------------------- 1 | #if OPENGL 2 | #define SV_POSITION POSITION 3 | #define VS_SHADERMODEL vs_3_0 4 | #define PS_SHADERMODEL ps_3_0 5 | #else 6 | #define VS_SHADERMODEL vs_4_0_level_9_1 7 | #define PS_SHADERMODEL ps_4_0_level_9_1 8 | #endif 9 | 10 | sampler2D TextureSampler : register(s0) 11 | { 12 | Texture = (Texture); 13 | }; 14 | 15 | Texture2D Mask; 16 | sampler MaskSampler { 17 | Texture = ( Mask ); 18 | }; 19 | 20 | //https://community.monogame.net/t/how-to-mask-2d-tile-sprites/15813/2 21 | //https://gamedev.stackexchange.com/questions/38118/best-way-to-mask-2d-sprites-in-xna 22 | 23 | float4 MaskPS(float4 position : SV_Position, float4 color : COLOR0, float2 TextureCoordinates : TEXCOORD0): COLOR0 24 | { 25 | float4 tex = tex2D(TextureSampler, TextureCoordinates) * color; 26 | float4 mask = tex2D(MaskSampler, TextureCoordinates); 27 | 28 | return float4(tex.r, tex.g, tex.b, min(mask.a, tex.a)); 29 | } 30 | 31 | technique Mask 32 | { 33 | pass P0 34 | { 35 | PixelShader = compile PS_SHADERMODEL MaskPS(); 36 | } 37 | }; -------------------------------------------------------------------------------- /DynamicReflections/Framework/Assets/Shaders/mask.mgfx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Floogen/DynamicReflections/3b773fd8ba3b07e01f8d039995cdc16b60dab87d/DynamicReflections/Framework/Assets/Shaders/mask.mgfx -------------------------------------------------------------------------------- /DynamicReflections/Framework/Assets/Shaders/opacity.fx: -------------------------------------------------------------------------------- 1 | #if OPENGL 2 | #define SV_POSITION POSITION 3 | #define VS_SHADERMODEL vs_3_0 4 | #define PS_SHADERMODEL ps_3_0 5 | #else 6 | #define VS_SHADERMODEL vs_4_0_level_9_1 7 | #define PS_SHADERMODEL ps_4_0_level_9_1 8 | #endif 9 | 10 | sampler2D TextureSampler : register(s0) 11 | { 12 | Texture = (Texture); 13 | }; 14 | 15 | float Opacity = 1.0; 16 | 17 | float4 OpacityPS(float4 position : SV_Position, float4 color : COLOR0, float2 TextureCoordinates : TEXCOORD0): COLOR0 18 | { 19 | float4 tex = tex2D(TextureSampler, TextureCoordinates) * color; 20 | 21 | return float4(tex.r, tex.g, tex.b, tex.a * Opacity); 22 | } 23 | 24 | technique OpacityFade 25 | { 26 | pass P0 27 | { 28 | PixelShader = compile PS_SHADERMODEL OpacityPS(); 29 | } 30 | }; -------------------------------------------------------------------------------- /DynamicReflections/Framework/Assets/Shaders/opacity.mgfx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Floogen/DynamicReflections/3b773fd8ba3b07e01f8d039995cdc16b60dab87d/DynamicReflections/Framework/Assets/Shaders/opacity.mgfx -------------------------------------------------------------------------------- /DynamicReflections/Framework/Assets/Shaders/wavy.fx: -------------------------------------------------------------------------------- 1 | #if OPENGL 2 | #define SV_POSITION POSITION 3 | #define VS_SHADERMODEL vs_3_0 4 | #define PS_SHADERMODEL ps_3_0 5 | #else 6 | #define VS_SHADERMODEL vs_4_0_level_9_1 7 | #define PS_SHADERMODEL ps_4_0_level_9_1 8 | #endif 9 | 10 | float percent; 11 | 12 | float4 ColorOverlay; 13 | float Frequency = 100; 14 | float Phase = 0; 15 | float Amplitude = 0.1; 16 | 17 | sampler2D TextureSampler : register(s0) 18 | { 19 | Texture = (Texture); 20 | }; 21 | 22 | float4 WavyPS(float4 position : SV_Position, float4 color : COLOR0, float2 TextureCoordinates : TEXCOORD0) : COLOR0 23 | { 24 | // https://community.monogame.net/t/how-to-use-a-pixel-shader-with-spritebatch-minimal-example-greyscale/12132 25 | //float4 col = tex2D(TextureSampler, TextureCoordinates)* color; 26 | //col.rgb = (col.r + col.g + col.b) / 3.0f * percent; // grey scale and darken 27 | //return col; 28 | 29 | // https://vvvv.org/documentation/tutorial-effects-texture-coordinates 30 | //float2 cord = TextureCoordinates; 31 | //cord.x += sin(cord.y * Frequency + Phase) * Amplitude; 32 | //float4 col = tex2D(TextureSampler, cord) * color; 33 | //return col; 34 | 35 | float2 uv = TextureCoordinates; 36 | //uv.y = -1.0 - uv.y; 37 | uv.x += sin(uv.y * Frequency + Phase) * Amplitude; 38 | return tex2D(TextureSampler, uv) * ColorOverlay; 39 | } 40 | 41 | float4 WavyDarkPS(float4 position : SV_Position, float4 color : COLOR0, float2 TextureCoordinates : TEXCOORD0) : COLOR0 42 | { 43 | float2 uv = TextureCoordinates; 44 | //uv.y = -1.0 - uv.y; 45 | uv.x += sin(uv.y * Frequency + Phase) * Amplitude; 46 | 47 | float4 col = tex2D(TextureSampler, uv) * color; 48 | col.rgb = (col.r + col.g + col.b) / 3.0f * percent; // grey scale and darken 49 | return col; 50 | } 51 | 52 | technique Wavy 53 | { 54 | pass P0 55 | { 56 | PixelShader = compile PS_SHADERMODEL WavyPS(); 57 | } 58 | }; 59 | 60 | technique WavyDark 61 | { 62 | pass P0 63 | { 64 | PixelShader = compile PS_SHADERMODEL WavyPS(); 65 | } 66 | pass P1 67 | { 68 | PixelShader = compile PS_SHADERMODEL WavyDarkPS(); 69 | } 70 | }; -------------------------------------------------------------------------------- /DynamicReflections/Framework/Assets/Shaders/wavy.mgfx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Floogen/DynamicReflections/3b773fd8ba3b07e01f8d039995cdc16b60dab87d/DynamicReflections/Framework/Assets/Shaders/wavy.mgfx -------------------------------------------------------------------------------- /DynamicReflections/Framework/Assets/Textures/night_sky_sheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Floogen/DynamicReflections/3b773fd8ba3b07e01f8d039995cdc16b60dab87d/DynamicReflections/Framework/Assets/Textures/night_sky_sheet.png -------------------------------------------------------------------------------- /DynamicReflections/Framework/Assets/Textures/puddles_sheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Floogen/DynamicReflections/3b773fd8ba3b07e01f8d039995cdc16b60dab87d/DynamicReflections/Framework/Assets/Textures/puddles_sheet.png -------------------------------------------------------------------------------- /DynamicReflections/Framework/Assets/Textures/sky_effects_sheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Floogen/DynamicReflections/3b773fd8ba3b07e01f8d039995cdc16b60dab87d/DynamicReflections/Framework/Assets/Textures/sky_effects_sheet.png -------------------------------------------------------------------------------- /DynamicReflections/Framework/Extensions/GameLocationExtensions.cs: -------------------------------------------------------------------------------- 1 | using StardewValley.Objects; 2 | using StardewValley; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using xTile.Dimensions; 9 | using Microsoft.Xna.Framework; 10 | using Object = StardewValley.Object; 11 | using StardewValley.Extensions; 12 | 13 | namespace DynamicReflections.Framework.Extensions 14 | { 15 | // Copied from the Stardew Valley wiki: https://stardewvalleywiki.com/Modding:Migrate_to_Stardew_Valley_1.6#How_to_update_your_mod 16 | internal static class GameLocationExtensions 17 | { 18 | public static bool isTileOccupiedForPlacement(this GameLocation location, Vector2 tileLocation, Object toPlace = null) 19 | { 20 | return location.CanItemBePlacedHere(tileLocation, toPlace != null && toPlace.isPassable()); 21 | } 22 | 23 | public static bool isTileOccupied(this GameLocation location, Vector2 tileLocation, string characterToIgnore = "", bool ignoreAllCharacters = false) 24 | { 25 | CollisionMask mask = ignoreAllCharacters ? CollisionMask.All & ~CollisionMask.Characters & ~CollisionMask.Farmers : CollisionMask.All; 26 | return location.IsTileOccupiedBy(tileLocation, mask); 27 | } 28 | 29 | public static bool isTileOccupiedIgnoreFloors(this GameLocation location, Vector2 tileLocation, string characterToIgnore = "") 30 | { 31 | return location.IsTileOccupiedBy(tileLocation, CollisionMask.Buildings | CollisionMask.Furniture | CollisionMask.Objects | CollisionMask.Characters | CollisionMask.TerrainFeatures, ignorePassables: CollisionMask.Flooring); 32 | } 33 | 34 | public static bool isTileLocationOpenIgnoreFrontLayers(this GameLocation location, Location tile) 35 | { 36 | return location.map.RequireLayer("Buildings").Tiles[tile.X, tile.Y] == null && !location.isWaterTile(tile.X, tile.Y); 37 | } 38 | 39 | public static bool isTileLocationTotallyClearAndPlaceable(this GameLocation location, int x, int y) 40 | { 41 | return location.isTileLocationTotallyClearAndPlaceable(new Vector2(x, y)); 42 | } 43 | 44 | public static bool isTileLocationTotallyClearAndPlaceableIgnoreFloors(this GameLocation location, Vector2 v) 45 | { 46 | return location.isTileOnMap(v) && !location.isTileOccupiedIgnoreFloors(v) && location.isTilePassable(new Location((int)v.X, (int)v.Y), Game1.viewport) && location.isTilePlaceable(v); 47 | } 48 | 49 | public static bool isTileLocationTotallyClearAndPlaceable(this GameLocation location, Vector2 v) 50 | { 51 | Vector2 pixel = new Vector2((v.X * Game1.tileSize) + Game1.tileSize / 2, (v.Y * Game1.tileSize) + Game1.tileSize / 2); 52 | foreach (Furniture f in location.furniture) 53 | { 54 | if (f.furniture_type.Value != Furniture.rug && !f.isPassable() && f.GetBoundingBox().Contains((int)pixel.X, (int)pixel.Y) && !f.AllowPlacementOnThisTile((int)v.X, (int)v.Y)) 55 | return false; 56 | } 57 | 58 | return location.isTileOnMap(v) && !location.isTileOccupied(v) && location.isTilePassable(new Location((int)v.X, (int)v.Y), Game1.viewport) && location.isTilePlaceable(v); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /DynamicReflections/Framework/External/GenericModConfigMenu/ModConfig.cs: -------------------------------------------------------------------------------- 1 | using DynamicReflections.Framework.Models.Settings; 2 | using StardewModdingAPI; 3 | using StardewValley; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace DynamicReflections.Framework.External.GenericModConfigMenu 11 | { 12 | public class ModConfig 13 | { 14 | public bool AreWaterReflectionsEnabled { get; set; } = true; 15 | public bool AreMirrorReflectionsEnabled { get; set; } = true; 16 | public bool ArePuddleReflectionsEnabled { get; set; } = true; 17 | public bool AreNPCReflectionsEnabled { get; set; } = true; 18 | public bool AreSkyReflectionsEnabled { get; set; } = true; 19 | 20 | public WaterSettings WaterReflectionSettings { get; set; } = new WaterSettings(); 21 | public PuddleSettings PuddleReflectionSettings { get; set; } = new PuddleSettings(); 22 | public SkySettings SkyReflectionSettings { get; set; } = new SkySettings(); 23 | public int MeteorShowerNightChance { get; set; } = 10; 24 | 25 | public Dictionary LocalWaterReflectionSettings { get; set; } = new Dictionary(); 26 | public Dictionary LocalPuddleReflectionSettings { get; set; } = new Dictionary(); 27 | public Dictionary LocalSkyReflectionSettings { get; set; } = new Dictionary(); 28 | public SButton QuickMenuKey { get; set; } = SButton.R; 29 | 30 | public WaterSettings GetCurrentWaterSettings(GameLocation location) 31 | { 32 | if (location is null || LocalWaterReflectionSettings is null || LocalWaterReflectionSettings.ContainsKey(location.NameOrUniqueName) is false || LocalWaterReflectionSettings[location.NameOrUniqueName] is null || LocalWaterReflectionSettings[location.NameOrUniqueName].OverrideDefaultSettings is false) 33 | { 34 | return WaterReflectionSettings; 35 | } 36 | 37 | return LocalWaterReflectionSettings[location.NameOrUniqueName]; 38 | } 39 | 40 | public PuddleSettings GetCurrentPuddleSettings(GameLocation location) 41 | { 42 | if (location is null || LocalPuddleReflectionSettings is null || LocalPuddleReflectionSettings.ContainsKey(location.NameOrUniqueName) is false || LocalPuddleReflectionSettings[location.NameOrUniqueName] is null || LocalPuddleReflectionSettings[location.NameOrUniqueName].OverrideDefaultSettings is false) 43 | { 44 | return PuddleReflectionSettings; 45 | } 46 | 47 | return LocalPuddleReflectionSettings[location.NameOrUniqueName]; 48 | } 49 | 50 | public SkySettings GetCurrentSkySettings(GameLocation location) 51 | { 52 | if (location is null || LocalSkyReflectionSettings is null || LocalSkyReflectionSettings.ContainsKey(location.NameOrUniqueName) is false || LocalSkyReflectionSettings[location.NameOrUniqueName] is null || LocalSkyReflectionSettings[location.NameOrUniqueName].OverrideDefaultSettings is false) 53 | { 54 | return SkyReflectionSettings; 55 | } 56 | 57 | return LocalSkyReflectionSettings[location.NameOrUniqueName]; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /DynamicReflections/Framework/Interfaces/IGenericModConfigMenuApi.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Xna.Framework; 3 | using Microsoft.Xna.Framework.Graphics; 4 | using StardewModdingAPI; 5 | using StardewModdingAPI.Utilities; 6 | using StardewValley; 7 | 8 | namespace DynamicReflections.Framework.Interfaces 9 | { 10 | public interface IGenericModConfigMenuApi 11 | { 12 | void Register(IManifest mod, Action reset, Action save, bool titleScreenOnly = false); 13 | void StartNewPage(IManifest mod, string pageName); 14 | void OverridePageDisplayName(IManifest mod, string pageName, string displayName); 15 | 16 | void RegisterLabel(IManifest mod, string labelName, string labelDesc); 17 | void RegisterPageLabel(IManifest mod, string labelName, string labelDesc, string newPage); 18 | void AddSectionTitle(IManifest mod, Func text, Func tooltip = null); 19 | void AddParagraph(IManifest mod, Func text); 20 | void AddImage(IManifest mod, Func texture, Rectangle? texturePixelArea = null, int scale = Game1.pixelZoom); 21 | void AddPage(IManifest mod, string pageId, Func pageTitle = null); 22 | void AddPageLink(IManifest mod, string pageId, Func text, Func tooltip = null); 23 | 24 | void RegisterSimpleOption(IManifest mod, string optionName, string optionDesc, Func optionGet, Action optionSet); 25 | void AddKeybind(IManifest mod, Func getValue, Action setValue, Func name, Func tooltip = null, string fieldId = null); 26 | void AddTextOption(IManifest mod, Func getValue, Action setValue, Func name, Func tooltip = null, string[] allowedValues = null, Func formatAllowedValue = null, string fieldId = null); 27 | void AddBoolOption(IManifest mod, Func getValue, Action setValue, Func name, Func tooltip = null, string fieldId = null); 28 | void AddNumberOption(IManifest mod, Func getValue, Action setValue, Func name, Func tooltip = null, int? min = null, int? max = null, int? interval = null, Func formatValue = null, string fieldId = null); 29 | void AddNumberOption(IManifest mod, Func getValue, Action setValue, Func name, Func tooltip = null, float? min = null, float? max = null, float? interval = null, Func formatValue = null, string fieldId = null); 30 | 31 | 32 | void OpenModMenu(IManifest mod); 33 | void RegisterComplexOption(IManifest mod, string optionName, string optionDesc, Func widgetUpdate, Func widgetDraw, Action onSave); 34 | void OnFieldChanged(IManifest mod, Action onChange); 35 | void Unregister(IManifest mod); 36 | } 37 | } -------------------------------------------------------------------------------- /DynamicReflections/Framework/Interfaces/Internal/Api.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework.Graphics; 2 | using StardewValley.Buildings; 3 | using StardewValley.Locations; 4 | using StardewValley; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace DynamicReflections.Framework.Interfaces.Internal 12 | { 13 | public interface IApi 14 | { 15 | public bool IsDrawAnyReflection(); 16 | public bool IsDrawingWaterReflection(); 17 | public bool IsDrawingPuddleReflection(); 18 | public bool IsDrawingMirrorReflection(); 19 | 20 | public bool IsFilteringWater(); 21 | public bool IsFilteringPuddles(); 22 | public bool IsFilteringMirrors(); 23 | public bool IsFilteringStars(); 24 | } 25 | 26 | public class Api : IApi 27 | { 28 | public bool IsDrawAnyReflection() 29 | { 30 | return DynamicReflections.isDrawingWaterReflection || DynamicReflections.isDrawingWaterReflection || DynamicReflections.isDrawingPuddles; 31 | } 32 | 33 | public bool IsDrawingWaterReflection() 34 | { 35 | return DynamicReflections.isDrawingWaterReflection; 36 | } 37 | 38 | public bool IsDrawingPuddleReflection() 39 | { 40 | return DynamicReflections.isDrawingPuddles; 41 | } 42 | 43 | public bool IsDrawingMirrorReflection() 44 | { 45 | return DynamicReflections.isDrawingMirrorReflection; 46 | } 47 | 48 | 49 | public bool IsFilteringWater() 50 | { 51 | return DynamicReflections.isFilteringWater; 52 | } 53 | 54 | public bool IsFilteringPuddles() 55 | { 56 | return DynamicReflections.isFilteringPuddles; 57 | } 58 | 59 | public bool IsFilteringMirrors() 60 | { 61 | return DynamicReflections.isFilteringMirror; 62 | } 63 | 64 | public bool IsFilteringStars() 65 | { 66 | return DynamicReflections.isFilteringStar; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /DynamicReflections/Framework/Managers/ApiManager.cs: -------------------------------------------------------------------------------- 1 | using DynamicReflections.Framework.Interfaces; 2 | using StardewModdingAPI; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace DynamicReflections.Framework.Managers 10 | { 11 | internal class ApiManager 12 | { 13 | private IMonitor _monitor; 14 | private IGenericModConfigMenuApi _genericModConfigMenuApi; 15 | 16 | public ApiManager(IMonitor monitor) 17 | { 18 | _monitor = monitor; 19 | } 20 | 21 | internal bool HookIntoGenericModConfigMenu(IModHelper helper) 22 | { 23 | _genericModConfigMenuApi = helper.ModRegistry.GetApi("spacechase0.GenericModConfigMenu"); 24 | 25 | if (_genericModConfigMenuApi is null) 26 | { 27 | _monitor.Log("Failed to hook into spacechase0.GenericModConfigMenu.", LogLevel.Error); 28 | return false; 29 | } 30 | 31 | _monitor.Log("Successfully hooked into spacechase0.GenericModConfigMenu.", LogLevel.Debug); 32 | return true; 33 | } 34 | 35 | public IGenericModConfigMenuApi GetGenericModConfigMenuApi() 36 | { 37 | return _genericModConfigMenuApi; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /DynamicReflections/Framework/Managers/AssetManager.cs: -------------------------------------------------------------------------------- 1 | using DynamicReflections.Framework.Models; 2 | using DynamicReflections.Framework.Models.Settings; 3 | using Microsoft.Xna.Framework.Graphics; 4 | using StardewModdingAPI; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.IO; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | 12 | namespace DynamicReflections.Framework.Managers 13 | { 14 | internal class AssetManager 15 | { 16 | internal Texture2D PuddlesTileSheetTexture { get; } 17 | internal Texture2D NightSkyTileSheetTexture { get; } 18 | internal string SkyEffectsTileSheetTexturePath { get; } 19 | 20 | public AssetManager(IModHelper helper) 21 | { 22 | // Get the asset folder path 23 | var assetFolderPath = helper.ModContent.GetInternalAssetName(Path.Combine("Framework", "Assets")).Name; 24 | 25 | // Load in the puddles tilesheet 26 | PuddlesTileSheetTexture = helper.ModContent.Load(Path.Combine(assetFolderPath, "Textures", "puddles_sheet.png")); 27 | NightSkyTileSheetTexture = helper.ModContent.Load(Path.Combine(assetFolderPath, "Textures", "night_sky_sheet.png")); 28 | SkyEffectsTileSheetTexturePath = helper.ModContent.GetInternalAssetName(Path.Combine(assetFolderPath, "Textures", "sky_effects_sheet.png")).Name; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /DynamicReflections/Framework/Managers/MirrorsManager.cs: -------------------------------------------------------------------------------- 1 | using DynamicReflections.Framework.Models; 2 | using DynamicReflections.Framework.Models.Settings; 3 | using Microsoft.Xna.Framework.Graphics; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace DynamicReflections.Framework.Managers 11 | { 12 | internal class MirrorsManager 13 | { 14 | private Dictionary _furnitureIdToMirrors; 15 | private Dictionary _furnitureIdToMask; 16 | 17 | public MirrorsManager() 18 | { 19 | Reset(); 20 | } 21 | 22 | public void Add(List models) 23 | { 24 | foreach (var model in models) 25 | { 26 | if (model is null || String.IsNullOrEmpty(model.FurnitureId) || model.Mask is null) 27 | { 28 | continue; 29 | } 30 | 31 | _furnitureIdToMirrors[model.FurnitureId.ToLower()] = model.Mirror; 32 | _furnitureIdToMask[model.FurnitureId.ToLower()] = model.Mask; 33 | } 34 | } 35 | 36 | public MirrorSettings? GetSettings(string furnitureId) 37 | { 38 | if (_furnitureIdToMirrors.ContainsKey(furnitureId.ToLower()) is false) 39 | { 40 | return null; 41 | } 42 | 43 | return _furnitureIdToMirrors[furnitureId.ToLower()]; 44 | } 45 | 46 | public Texture2D? GetMask(string furnitureId) 47 | { 48 | if (_furnitureIdToMask.ContainsKey(furnitureId.ToLower()) is false) 49 | { 50 | return null; 51 | } 52 | 53 | return _furnitureIdToMask[furnitureId.ToLower()]; 54 | } 55 | 56 | public void Reset() 57 | { 58 | _furnitureIdToMirrors = new Dictionary(); 59 | _furnitureIdToMask = new Dictionary(); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /DynamicReflections/Framework/Managers/PuddleManager.cs: -------------------------------------------------------------------------------- 1 | using DynamicReflections.Framework.Extensions; 2 | using Microsoft.Xna.Framework; 3 | using StardewValley; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using xTile.Layers; 10 | using xTile.Tiles; 11 | 12 | namespace DynamicReflections.Framework.Managers 13 | { 14 | internal class PuddleManager 15 | { 16 | private const int PUDDLES_POOL = 16; 17 | internal const int DEFAULT_PUDDLE_INDEX = -1; 18 | 19 | internal List puddleRippleSprites = new List(); 20 | private Dictionary _locationToPuddleTiles; 21 | 22 | public void Reset() 23 | { 24 | _locationToPuddleTiles = new Dictionary(); 25 | } 26 | 27 | public void Generate(GameLocation location, int percentOfDiggableTiles = 10, bool force = false) 28 | { 29 | puddleRippleSprites = new List(); 30 | if (location is null || location.Map is null) 31 | { 32 | return; 33 | } 34 | 35 | if (_locationToPuddleTiles is null) 36 | { 37 | Reset(); 38 | } 39 | else if (force is false && _locationToPuddleTiles.ContainsKey(location) is true && _locationToPuddleTiles[location] is not null) 40 | { 41 | return; 42 | } 43 | 44 | GenerateByPercentage(location, percentOfDiggableTiles); 45 | } 46 | 47 | private void GenerateByPercentage(GameLocation location, int percentOfDiggableTiles = 10) 48 | { 49 | var random = new Random((int)((long)Game1.uniqueIDForThisGame + Game1.stats.DaysPlayed * 500 + Game1.ticks + DateTime.Now.Ticks)); 50 | if (location.Map.GetLayer("Back") is var backLayer && backLayer is not null) 51 | { 52 | _locationToPuddleTiles[location] = new bool[backLayer.LayerWidth, backLayer.LayerHeight]; 53 | 54 | List diggableTiles = new List(); 55 | for (int x = 0; x < backLayer.LayerWidth; x++) 56 | { 57 | for (int y = 0; y < backLayer.LayerHeight; y++) 58 | { 59 | if (backLayer.Tiles[x, y] is not null) 60 | { 61 | backLayer.Tiles[x, y].Properties["PuddleIndex"] = DEFAULT_PUDDLE_INDEX; 62 | backLayer.Tiles[x, y].Properties["BigPuddleIndex"] = DEFAULT_PUDDLE_INDEX; 63 | 64 | if (location.isTileLocationTotallyClearAndPlaceable(x, y) is false) 65 | { 66 | continue; 67 | } 68 | 69 | if (String.IsNullOrEmpty(location.doesTileHaveProperty(x, y, "Diggable", "Back")) is false && location.isTileHoeDirt(new Microsoft.Xna.Framework.Vector2(x, y)) is false) 70 | { 71 | diggableTiles.Add(new Point(x, y)); 72 | } 73 | else 74 | { 75 | string stepType = location.doesTileHaveProperty(x, y, "Type", "Buildings"); 76 | if (stepType == null || stepType.Length < 1) 77 | { 78 | stepType = location.doesTileHaveProperty(x, y, "Type", "Back"); 79 | } 80 | 81 | if (stepType == "Dirt" || stepType == "Stone") 82 | { 83 | diggableTiles.Add(new Point(x, y)); 84 | } 85 | } 86 | } 87 | } 88 | } 89 | 90 | if (DynamicReflections.currentPuddleSettings.ShouldGeneratePuddles is false || percentOfDiggableTiles == 0) 91 | { 92 | return; 93 | } 94 | 95 | for (int i = 0; i < diggableTiles.Count / percentOfDiggableTiles; i++) 96 | { 97 | var tilePosition = GetRandomTile(random, diggableTiles); 98 | if (IsBigPuddleTile(backLayer, tilePosition)) 99 | { 100 | continue; 101 | } 102 | 103 | var puddleIndex = random.Next(DEFAULT_PUDDLE_INDEX, PUDDLES_POOL); 104 | backLayer.Tiles[tilePosition.X, tilePosition.Y].Properties["PuddleIndex"] = puddleIndex; 105 | backLayer.Tiles[tilePosition.X, tilePosition.Y].Properties["PuddleEffect"] = random.Next(0, 4); 106 | backLayer.Tiles[tilePosition.X, tilePosition.Y].Properties["PuddleRotation"] = MathHelper.ToRadians(90 * random.Next(0, 4)); 107 | 108 | bool shouldAttemptBigPuddle = random.NextDouble() < DynamicReflections.currentPuddleSettings.BigPuddleChance / 100f; 109 | if (shouldAttemptBigPuddle) 110 | { 111 | bool canMakeBigPuddle = true; 112 | if (diggableTiles.FirstOrDefault(d => d.X == tilePosition.X + 1 && d.Y == tilePosition.Y) is var xOffsetPoint && (xOffsetPoint == default(Point) || IsBigPuddleTile(backLayer, xOffsetPoint))) 113 | { 114 | canMakeBigPuddle = false; 115 | } 116 | if (diggableTiles.FirstOrDefault(d => d.X == tilePosition.X + 1 && d.Y == tilePosition.Y + 1) is var yOffsetPoint && (yOffsetPoint == default(Point) || IsBigPuddleTile(backLayer, yOffsetPoint))) 117 | { 118 | canMakeBigPuddle = false; 119 | } 120 | if (diggableTiles.FirstOrDefault(d => d.X == tilePosition.X && d.Y == tilePosition.Y + 1) is var xyOffsetPoint && (xyOffsetPoint == default(Point) || IsBigPuddleTile(backLayer, xyOffsetPoint))) 121 | { 122 | canMakeBigPuddle = false; 123 | } 124 | 125 | if (canMakeBigPuddle) 126 | { 127 | var puddleEffect = random.Next(0, 4); 128 | var puddleRotation = 0; 129 | var adjustedPuddleIndex = random.Next(0, PUDDLES_POOL); 130 | if (adjustedPuddleIndex % 2 != 0) 131 | { 132 | adjustedPuddleIndex -= 1; 133 | } 134 | 135 | backLayer.Tiles[tilePosition.X, tilePosition.Y].Properties["PuddleIndex"] = puddleEffect is (0 or 1) ? adjustedPuddleIndex : adjustedPuddleIndex + 1; 136 | backLayer.Tiles[tilePosition.X, tilePosition.Y].Properties["BigPuddleIndex"] = puddleEffect is (0 or 2) ? 1 : 2; 137 | backLayer.Tiles[tilePosition.X, tilePosition.Y].Properties["PuddleEffect"] = puddleEffect; 138 | backLayer.Tiles[tilePosition.X, tilePosition.Y].Properties["PuddleRotation"] = puddleRotation; 139 | _locationToPuddleTiles[location][tilePosition.X, tilePosition.Y] = true; 140 | 141 | backLayer.Tiles[tilePosition.X + 1, tilePosition.Y].Properties["PuddleIndex"] = puddleEffect is (0 or 1) ? adjustedPuddleIndex : adjustedPuddleIndex + 1; 142 | backLayer.Tiles[tilePosition.X + 1, tilePosition.Y].Properties["BigPuddleIndex"] = puddleEffect is (0 or 2) ? 2 : 1; 143 | backLayer.Tiles[tilePosition.X + 1, tilePosition.Y].Properties["PuddleEffect"] = puddleEffect; 144 | backLayer.Tiles[tilePosition.X + 1, tilePosition.Y].Properties["PuddleRotation"] = puddleRotation; 145 | _locationToPuddleTiles[location][tilePosition.X + 1, tilePosition.Y] = true; 146 | 147 | backLayer.Tiles[tilePosition.X, tilePosition.Y + 1].Properties["PuddleIndex"] = puddleEffect is (0 or 1) ? adjustedPuddleIndex + 1 : adjustedPuddleIndex; 148 | backLayer.Tiles[tilePosition.X, tilePosition.Y + 1].Properties["BigPuddleIndex"] = puddleEffect is (0 or 2) ? 1 : 2; 149 | backLayer.Tiles[tilePosition.X, tilePosition.Y + 1].Properties["PuddleEffect"] = puddleEffect; 150 | backLayer.Tiles[tilePosition.X, tilePosition.Y + 1].Properties["PuddleRotation"] = puddleRotation; 151 | _locationToPuddleTiles[location][tilePosition.X, tilePosition.Y + 1] = true; 152 | 153 | backLayer.Tiles[tilePosition.X + 1, tilePosition.Y + 1].Properties["PuddleIndex"] = puddleEffect is (0 or 1) ? adjustedPuddleIndex + 1 : adjustedPuddleIndex; 154 | backLayer.Tiles[tilePosition.X + 1, tilePosition.Y + 1].Properties["BigPuddleIndex"] = puddleEffect is (0 or 2) ? 2 : 1; 155 | backLayer.Tiles[tilePosition.X + 1, tilePosition.Y + 1].Properties["PuddleEffect"] = puddleEffect; 156 | backLayer.Tiles[tilePosition.X + 1, tilePosition.Y + 1].Properties["PuddleRotation"] = puddleRotation; 157 | _locationToPuddleTiles[location][tilePosition.X + 1, tilePosition.Y + 1] = true; 158 | } 159 | else 160 | { 161 | _locationToPuddleTiles[location][tilePosition.X, tilePosition.Y] = puddleIndex != DEFAULT_PUDDLE_INDEX; 162 | } 163 | } 164 | else 165 | { 166 | _locationToPuddleTiles[location][tilePosition.X, tilePosition.Y] = puddleIndex != DEFAULT_PUDDLE_INDEX; 167 | } 168 | } 169 | } 170 | } 171 | 172 | private bool IsBigPuddleTile(Layer backLayer, Point tilePosition) 173 | { 174 | if (backLayer.Tiles[tilePosition.X, tilePosition.Y].Properties.ContainsKey("BigPuddleIndex") && Int32.TryParse(backLayer.Tiles[tilePosition.X, tilePosition.Y].Properties["BigPuddleIndex"], out int value) && value != DEFAULT_PUDDLE_INDEX) 175 | { 176 | return true; 177 | } 178 | 179 | return false; 180 | } 181 | 182 | private Point GetRandomTile(Random random, List tilePoints) 183 | { 184 | return tilePoints[random.Next(tilePoints.Count)]; 185 | } 186 | 187 | private void GeneratePerTile(GameLocation location) 188 | { 189 | var random = new Random((int)((long)Game1.uniqueIDForThisGame + Game1.stats.DaysPlayed * 500 + Game1.ticks + DateTime.Now.Ticks)); 190 | if (location.Map.GetLayer("Back") is var backLayer && backLayer is not null) 191 | { 192 | _locationToPuddleTiles[location] = new bool[backLayer.LayerWidth, backLayer.LayerHeight]; 193 | 194 | for (int x = 0; x < backLayer.LayerWidth; x++) 195 | { 196 | for (int y = 0; y < backLayer.LayerHeight; y++) 197 | { 198 | var point = new Point(x, y); 199 | if (backLayer.Tiles[x, y] is not null) 200 | { 201 | var puddleIndex = DEFAULT_PUDDLE_INDEX; 202 | if (backLayer.Tiles[x, y].TileIndexProperties.TryGetValue("Diggable", out _) && DoesPointHaveNeighbor(location, point) is false && location.isTileLocationTotallyClearAndPlaceable(x, y)) 203 | { 204 | puddleIndex = random.NextDouble() < 0.95 ? DEFAULT_PUDDLE_INDEX : random.Next(DEFAULT_PUDDLE_INDEX, PUDDLES_POOL); 205 | _locationToPuddleTiles[location][x, y] = puddleIndex != DEFAULT_PUDDLE_INDEX; 206 | } 207 | 208 | backLayer.Tiles[x, y].Properties["PuddleIndex"] = puddleIndex; 209 | } 210 | } 211 | } 212 | } 213 | } 214 | 215 | public bool DoesPointHaveNeighbor(GameLocation location, Point point) 216 | { 217 | var offsetPoint = new Point(point.X - 1, point.Y); 218 | if (IsTilePuddle(location, offsetPoint.X, offsetPoint.Y) is true) 219 | { 220 | return true; 221 | } 222 | 223 | offsetPoint = new Point(point.X + 1, point.Y); 224 | if (IsTilePuddle(location, offsetPoint.X, offsetPoint.Y) is true) 225 | { 226 | return true; 227 | } 228 | 229 | offsetPoint = new Point(point.X, point.Y - 1); 230 | if (IsTilePuddle(location, offsetPoint.X, offsetPoint.Y) is true) 231 | { 232 | return true; 233 | } 234 | 235 | offsetPoint = new Point(point.X, point.Y + 1); 236 | if (IsTilePuddle(location, offsetPoint.X, offsetPoint.Y) is true) 237 | { 238 | return true; 239 | } 240 | 241 | return false; 242 | } 243 | 244 | public bool IsTilePuddle(GameLocation location, int x, int y) 245 | { 246 | if (_locationToPuddleTiles.ContainsKey(location) is false || _locationToPuddleTiles[location] is null || x < 0 || y < 0 || _locationToPuddleTiles[location].GetLength(0) <= x || _locationToPuddleTiles[location].GetLength(1) <= y) 247 | { 248 | return false; 249 | } 250 | 251 | return _locationToPuddleTiles[location][x, y]; 252 | } 253 | 254 | public List GetPuddleTiles(GameLocation location, bool limitToView = false) 255 | { 256 | var puddles = new List(); 257 | if (_locationToPuddleTiles.ContainsKey(location) is false) 258 | { 259 | return puddles; 260 | } 261 | 262 | int tileWidth = Game1.pixelZoom * 16; 263 | int tileHeight = Game1.pixelZoom * 16; 264 | int tileXMin = ((Game1.viewport.X >= 0) ? (Game1.viewport.X / tileWidth) : ((Game1.viewport.X - tileWidth + 1) / tileWidth)); 265 | int tileYMin = ((Game1.viewport.Y >= 0) ? (Game1.viewport.Y / tileHeight) : ((Game1.viewport.Y - tileHeight + 1) / tileHeight)); 266 | if (tileXMin < 0) 267 | { 268 | tileXMin = 0; 269 | } 270 | if (tileYMin < 0) 271 | { 272 | tileYMin = 0; 273 | } 274 | int tileColumns = 1 + (Game1.viewport.Size.Width - 1) / tileWidth; 275 | int tileRows = 1 + (Game1.viewport.Size.Height - 1) / tileHeight; 276 | int tileXMax = tileXMin + tileColumns; 277 | int tileYMax = tileYMin + tileRows; 278 | 279 | for (int x = 0; x < _locationToPuddleTiles[location].GetLength(0); x++) 280 | { 281 | for (int y = 0; y < _locationToPuddleTiles[location].GetLength(1); y++) 282 | { 283 | if (_locationToPuddleTiles[location][x, y] is true) 284 | { 285 | if (limitToView is false || (limitToView is true && x >= tileXMin && x < tileXMax && y >= tileYMin && y < tileYMax)) 286 | { 287 | puddles.Add(new Point(x, y)); 288 | } 289 | } 290 | } 291 | } 292 | 293 | return puddles; 294 | } 295 | } 296 | } 297 | -------------------------------------------------------------------------------- /DynamicReflections/Framework/Managers/SkyManager.cs: -------------------------------------------------------------------------------- 1 | using DynamicReflections.Framework.Utilities.Extensions; 2 | using Microsoft.Xna.Framework; 3 | using StardewValley; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using xTile.Dimensions; 10 | using xTile.Layers; 11 | using xTile.Tiles; 12 | 13 | namespace DynamicReflections.Framework.Managers 14 | { 15 | internal class SkyManager 16 | { 17 | internal const int SKY_TILES_X = 4; 18 | internal const int SKY_TILES_Y = 5; 19 | internal const int DEFAULT_SKY_INDEX = 0; 20 | 21 | internal List skyEffectSprites = new List(); 22 | private Dictionary _locationToSkyTiles; 23 | private Dictionary> _locationToSkyPoints; 24 | 25 | public void Reset() 26 | { 27 | _locationToSkyTiles = new Dictionary(); 28 | _locationToSkyPoints = new Dictionary>(); 29 | } 30 | 31 | public void Generate(GameLocation location, bool force = false) 32 | { 33 | skyEffectSprites = new List(); 34 | if (location is null || location.Map is null) 35 | { 36 | return; 37 | } 38 | 39 | if (_locationToSkyTiles is null) 40 | { 41 | Reset(); 42 | } 43 | else if (force is false && _locationToSkyTiles.ContainsKey(location) is true && _locationToSkyTiles[location] is not null) 44 | { 45 | return; 46 | } 47 | 48 | GeneratePerTile(location); 49 | } 50 | 51 | private void GeneratePerTile(GameLocation location) 52 | { 53 | var random = new Random((int)((long)Game1.uniqueIDForThisGame + Game1.stats.DaysPlayed * 500 + Game1.ticks + DateTime.Now.Ticks)); 54 | if (location.Map.GetLayer("Back") is var backLayer && backLayer is not null) 55 | { 56 | _locationToSkyTiles[location] = new bool[backLayer.LayerWidth, backLayer.LayerHeight]; 57 | _locationToSkyPoints[location] = new List(); 58 | 59 | double starDensityPercentage = DynamicReflections.currentSkySettings.StarDensityPercentage / 100f; 60 | for (int x = 0; x < backLayer.LayerWidth; x++) 61 | { 62 | for (int y = 0; y < backLayer.LayerHeight; y++) 63 | { 64 | if (location.isWaterTile(x, y) || (DynamicReflections.puddleManager is not null && DynamicReflections.puddleManager.IsTilePuddle(location, x, y))) 65 | { 66 | _locationToSkyTiles[location][x, y] = true; 67 | _locationToSkyPoints[location].Add(new Point(x, y)); 68 | backLayer.Tiles[x, y].Properties["SkyIndex"] = random.NextDouble() > starDensityPercentage ? DEFAULT_SKY_INDEX : random.Next(DEFAULT_SKY_INDEX, SKY_TILES_X * SKY_TILES_Y); 69 | backLayer.Tiles[x, y].Properties["SkyEffect"] = random.Next(0, 4); 70 | backLayer.Tiles[x, y].Properties["SkyAlpha"] = random.NextDouble() < 0.55 ? (float)random.NextDouble() : 1f; 71 | } 72 | } 73 | } 74 | } 75 | } 76 | 77 | internal static Point GetTilePoint(int tileIndex) 78 | { 79 | return new Point(tileIndex % SKY_TILES_X, tileIndex / SKY_TILES_Y); 80 | } 81 | 82 | private static Point GetRandomTile(Random random, List tilePoints) 83 | { 84 | return tilePoints[random.Next(tilePoints.Count)]; 85 | } 86 | 87 | internal void AttemptEffects(GameLocation location) 88 | { 89 | if (_locationToSkyPoints is null || _locationToSkyPoints.ContainsKey(location) is false) 90 | { 91 | return; 92 | } 93 | 94 | if (location.Map.GetLayer("Back") is var backLayer && backLayer is not null) 95 | { 96 | var skyTiles = GetSkyTiles(location, true); 97 | if (skyTiles.Count == 0) 98 | { 99 | return; 100 | } 101 | var randomWaterTilePoint = GetRandomTile(Game1.random, GetSkyTiles(location, true)); 102 | double cometChance = DynamicReflections.currentSkySettings.CometChance / 100f; 103 | 104 | var leftTile = backLayer.PickTile(new Location((randomWaterTilePoint.X - 1) * 64, randomWaterTilePoint.Y * 64), Game1.viewport.Size); 105 | var topTile = backLayer.PickTile(new Location(randomWaterTilePoint.X * 64, (randomWaterTilePoint.Y - 1) * 64), Game1.viewport.Size); 106 | if (leftTile is not null && location.isWaterTile(randomWaterTilePoint.X - 1, randomWaterTilePoint.Y) is false) 107 | { 108 | // Trigger event with this tile as starting point 109 | if (Game1.random.NextDouble() < cometChance) 110 | { 111 | skyEffectSprites.AddRange(GenerateComet(new Point(randomWaterTilePoint.X - 1, randomWaterTilePoint.Y))); 112 | } 113 | else 114 | { 115 | skyEffectSprites.Add(GenerateShootingStar(new Point(randomWaterTilePoint.X - 1, randomWaterTilePoint.Y))); 116 | } 117 | } 118 | else if (topTile is not null && location.isWaterTile(randomWaterTilePoint.X, randomWaterTilePoint.Y - 1) is false) 119 | { 120 | // Trigger event with this tile as starting point 121 | if (Game1.random.NextDouble() < cometChance) 122 | { 123 | skyEffectSprites.AddRange(GenerateComet(new Point(randomWaterTilePoint.X, randomWaterTilePoint.Y - 1))); 124 | } 125 | else 126 | { 127 | skyEffectSprites.Add(GenerateShootingStar(new Point(randomWaterTilePoint.X, randomWaterTilePoint.Y - 1))); 128 | } 129 | } 130 | else if (Game1.random.NextDouble() < 0.075) 131 | { 132 | // Trigger event with this tile as starting point 133 | if (Game1.random.NextDouble() < 0.0001) 134 | { 135 | skyEffectSprites.Add(GenerateEasterEgg(new Point(randomWaterTilePoint.X, randomWaterTilePoint.Y))); 136 | } 137 | else 138 | { 139 | skyEffectSprites.Add(GenerateShootingStar(new Point(randomWaterTilePoint.X, randomWaterTilePoint.Y))); 140 | } 141 | } 142 | } 143 | } 144 | 145 | private TemporaryAnimatedSprite GenerateShootingStar(Point point) 146 | { 147 | var shootingStar = new TemporaryAnimatedSprite(DynamicReflections.assetManager.SkyEffectsTileSheetTexturePath, new Microsoft.Xna.Framework.Rectangle(0, 0, 32, 32), Game1.random.Next(150, 300), 3, 12, new Vector2(point.X, point.Y) * 64f, flicker: false, flipped: false, 0f, 0f, Color.White, (float)(Game1.random.Next(1, 4) + Game1.random.NextDouble()), 0f, 0f, 0f); 148 | 149 | var speed = (float)(Game1.random.NextDouble(DynamicReflections.currentSkySettings.ShootingStarMinSpeed, DynamicReflections.currentSkySettings.ShootingStarMaxSpeed)); 150 | shootingStar.acceleration = new Vector2(speed, speed); 151 | 152 | return shootingStar; 153 | } 154 | 155 | private TemporaryAnimatedSprite GenerateEasterEgg(Point point) 156 | { 157 | var easterEgg = new TemporaryAnimatedSprite("LooseSprites\\Cursors", new Microsoft.Xna.Framework.Rectangle(640, 784, 16, 16), Game1.random.Next(150, 300), 4, 12, new Vector2(point.X, point.Y) * 64f, flicker: false, flipped: false, 0f, 0f, Color.Gray, 3f, 0f, 0f, 0f); 158 | 159 | var speed = (float)(Game1.random.NextDouble(DynamicReflections.currentSkySettings.ShootingStarMinSpeed, DynamicReflections.currentSkySettings.ShootingStarMaxSpeed)); 160 | easterEgg.acceleration = new Vector2(speed, speed); 161 | 162 | return easterEgg; 163 | } 164 | 165 | private List GenerateComet(Point point) 166 | { 167 | float speed = (float)(Game1.random.NextDouble(DynamicReflections.currentSkySettings.CometMinSpeed, DynamicReflections.currentSkySettings.CometMaxSpeed)); 168 | float scale = (float)(Game1.random.Next(1, 3) + Game1.random.NextDouble()); 169 | 170 | var segments = new List(); 171 | int minSegments = DynamicReflections.currentSkySettings.CometSegmentMin; 172 | int maxSegments = DynamicReflections.currentSkySettings.CometSegmentMax < minSegments ? minSegments + 1 : DynamicReflections.currentSkySettings.CometSegmentMax; 173 | int totalSegments = Game1.random.Next(minSegments, minSegments + 1); 174 | for (int i = 0; i < totalSegments; i++) 175 | { 176 | var offset = 0.1f * i; 177 | int numberOfLoops = i == 0 ? totalSegments * 36 : 72; 178 | 179 | var cometSegment = new TemporaryAnimatedSprite(DynamicReflections.assetManager.SkyEffectsTileSheetTexturePath, new Microsoft.Xna.Framework.Rectangle(0, 0, 32, 32), Game1.random.Next(30, 150), i == 0 ? 1 : 3, numberOfLoops, new Vector2(point.X - offset, point.Y - offset) * 64f, flicker: false, flipped: false, 0f, 0f, Color.White, scale - offset * 2, 0f, 0f, 0f); 180 | 181 | cometSegment.acceleration = new Vector2(speed, speed); 182 | segments.Add(cometSegment); 183 | } 184 | 185 | return segments; 186 | } 187 | 188 | private List GetSkyTiles(GameLocation location, bool limitToView = false) 189 | { 190 | var tiles = new List(); 191 | if (_locationToSkyPoints.ContainsKey(location) is false) 192 | { 193 | return tiles; 194 | } 195 | 196 | int tileWidth = Game1.pixelZoom * 16; 197 | int tileHeight = Game1.pixelZoom * 16; 198 | int tileXMin = ((Game1.viewport.X >= 0) ? (Game1.viewport.X / tileWidth) : ((Game1.viewport.X - tileWidth + 1) / tileWidth)); 199 | int tileYMin = ((Game1.viewport.Y >= 0) ? (Game1.viewport.Y / tileHeight) : ((Game1.viewport.Y - tileHeight + 1) / tileHeight)); 200 | if (tileXMin < 0) 201 | { 202 | tileXMin = 0; 203 | } 204 | if (tileYMin < 0) 205 | { 206 | tileYMin = 0; 207 | } 208 | int tileColumns = 1 + (Game1.viewport.Size.Width - 1) / tileWidth; 209 | int tileRows = 1 + (Game1.viewport.Size.Height - 1) / tileHeight; 210 | int tileXMax = tileXMin + tileColumns; 211 | int tileYMax = tileYMin + tileRows; 212 | 213 | foreach (var point in _locationToSkyPoints[location]) 214 | { 215 | if (limitToView is false || (limitToView is true && point.X >= tileXMin && point.X < tileXMax && point.Y >= tileYMin && point.Y < tileYMax)) 216 | { 217 | tiles.Add(new Point(point.X, point.Y)); 218 | } 219 | } 220 | 221 | if (DynamicReflections.puddleManager is not null) 222 | { 223 | tiles.AddRange(DynamicReflections.puddleManager.GetPuddleTiles(location, true)); 224 | } 225 | 226 | return tiles; 227 | } 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /DynamicReflections/Framework/Models/ContentPackModel.cs: -------------------------------------------------------------------------------- 1 | using DynamicReflections.Framework.Models.Settings; 2 | using Microsoft.Xna.Framework; 3 | using Microsoft.Xna.Framework.Graphics; 4 | using StardewValley.Objects; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace DynamicReflections.Framework.Models 12 | { 13 | internal class ContentPackModel 14 | { 15 | public string FurnitureId { get; set; } 16 | public string MaskTexture { get; set; } 17 | public MirrorSettings Mirror { get; set; } 18 | 19 | internal Texture2D Mask { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DynamicReflections/Framework/Models/Mirror.cs: -------------------------------------------------------------------------------- 1 | using DynamicReflections.Framework.Models.Settings; 2 | using Microsoft.Xna.Framework; 3 | using StardewValley.Objects; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace DynamicReflections.Framework.Models 11 | { 12 | internal class Mirror 13 | { 14 | public Point TilePosition { get { return _tilePosition; } set { _tilePosition = value; WorldPosition = new Vector2(value.X, value.Y) * 64f; } } 15 | internal Point _tilePosition; 16 | public Vector2 WorldPosition { get; set; } 17 | public MirrorSettings Settings { get; set; } = new MirrorSettings(); 18 | 19 | public bool IsEnabled { get; set; } 20 | public int ActiveIndex { get; set; } 21 | public Vector2 PlayerReflectionPosition { get; set; } 22 | public Furniture? FurnitureLink { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /DynamicReflections/Framework/Models/Settings/MirrorSettings.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using StardewValley.Objects; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace DynamicReflections.Framework.Models.Settings 10 | { 11 | internal class MirrorSettings 12 | { 13 | public Rectangle Dimensions { get; set; } 14 | public float ReflectionScale { get; set; } // TODO: Implement this property 15 | public Color ReflectionOverlay { get; set; } 16 | public Vector2 ReflectionOffset { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /DynamicReflections/Framework/Models/Settings/PuddleSettings.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using StardewValley.Objects; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace DynamicReflections.Framework.Models.Settings 10 | { 11 | public class PuddleSettings 12 | { 13 | // Note: This property can only override disabling, it cannot force a user to enable reflections 14 | public const string MapProperty_IsEnabled = "ArePuddleReflectionsEnabled"; 15 | public bool AreReflectionsEnabled { get; set; } = true; 16 | 17 | public const string MapProperty_ShouldGeneratePuddles = "ShouldGeneratePuddles"; 18 | public bool ShouldGeneratePuddles { get; set; } = true; 19 | 20 | public const string MapProperty_ShouldPlaySplashSound = "ShouldPlaySplashSound"; 21 | public bool ShouldPlaySplashSound { get; set; } = true; 22 | 23 | public const string MapProperty_ShouldRainSplashPuddles = "ShouldRainSplashPuddles"; 24 | public bool ShouldRainSplashPuddles { get; set; } = true; 25 | 26 | public const string MapProperty_PuddleReflectionOffset = "PuddleReflectionOffset"; 27 | public Vector2 ReflectionOffset { get; set; } = Vector2.Zero; 28 | public const string MapProperty_NPCReflectionOffset = "PuddleNPCReflectionOffset"; 29 | public Vector2 NPCReflectionOffset { get; set; } = new Vector2(0f, 0.3f); 30 | 31 | public const string MapProperty_PuddlePercentageWhileRaining = "PuddlePercentageWhileRaining"; 32 | public int PuddlePercentageWhileRaining { get; set; } = 20; 33 | 34 | public const string MapProperty_PuddlePercentageAfterRaining = "PuddlePercentageAfterRaining"; 35 | public int PuddlePercentageAfterRaining { get; set; } = 10; 36 | 37 | public const string MapProperty_BigPuddleChance = "BigPuddleChance"; 38 | public int BigPuddleChance { get; set; } = 25; 39 | 40 | public const string MapProperty_MillisecondsBetweenRaindropSplashes = "MillisecondsBetweenRaindropSplashes"; 41 | public int MillisecondsBetweenRaindropSplashes { get; set; } = 500; 42 | 43 | public const string MapProperty_ReflectionOverlay = "PuddleReflectionOverlay"; 44 | public Color ReflectionOverlay { get; set; } = new Color(255, 255, 255, 155); 45 | 46 | public const string MapProperty_PuddleColor = "PuddleColor"; 47 | public Color PuddleColor { get; set; } = new Color(91, 91, 91, 91); 48 | 49 | public const string MapProperty_RippleColor = "RippleColor"; 50 | public Color RippleColor { get; set; } = new Color(255, 255, 255, 155); 51 | 52 | 53 | public bool OverrideDefaultSettings { get; set; } 54 | 55 | public void Reset(PuddleSettings referencedSettings = null) 56 | { 57 | if (referencedSettings is null) 58 | { 59 | AreReflectionsEnabled = true; 60 | ShouldGeneratePuddles = true; 61 | ShouldPlaySplashSound = true; 62 | ShouldRainSplashPuddles = true; 63 | ReflectionOffset = Vector2.Zero; 64 | NPCReflectionOffset = new Vector2(0f, 0.3f); 65 | PuddlePercentageWhileRaining = 20; 66 | PuddlePercentageAfterRaining = 10; 67 | BigPuddleChance = 25; 68 | MillisecondsBetweenRaindropSplashes = 500; 69 | ReflectionOverlay = new Color(255, 255, 255, 155); 70 | PuddleColor = new Color(91, 91, 91, 91); 71 | RippleColor = new Color(255, 255, 255, 155); 72 | OverrideDefaultSettings = false; 73 | } 74 | else 75 | { 76 | AreReflectionsEnabled = referencedSettings.AreReflectionsEnabled; 77 | ShouldGeneratePuddles = referencedSettings.ShouldGeneratePuddles; 78 | ShouldPlaySplashSound = referencedSettings.ShouldPlaySplashSound; 79 | ShouldRainSplashPuddles = referencedSettings.ShouldRainSplashPuddles; 80 | ReflectionOffset = referencedSettings.ReflectionOffset; 81 | NPCReflectionOffset = referencedSettings.NPCReflectionOffset; 82 | PuddlePercentageWhileRaining = referencedSettings.PuddlePercentageWhileRaining; 83 | PuddlePercentageAfterRaining = referencedSettings.PuddlePercentageAfterRaining; 84 | BigPuddleChance = referencedSettings.BigPuddleChance; 85 | MillisecondsBetweenRaindropSplashes = referencedSettings.MillisecondsBetweenRaindropSplashes; 86 | ReflectionOverlay = referencedSettings.ReflectionOverlay; 87 | PuddleColor = referencedSettings.PuddleColor; 88 | RippleColor = referencedSettings.RippleColor; 89 | } 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /DynamicReflections/Framework/Models/Settings/SkySettings.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using StardewValley.Objects; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace DynamicReflections.Framework.Models.Settings 10 | { 11 | public class SkySettings 12 | { 13 | // Note: This property can only override disabling, it cannot force a user to enable reflections 14 | public const string MapProperty_IsEnabled = "AreSkyReflectionsEnabled"; 15 | public bool AreReflectionsEnabled { get; set; } = true; 16 | 17 | public const string MapProperty_StarDensityPercentage = "StarDensityPercentage"; 18 | public int StarDensityPercentage { get; set; } = 55; 19 | 20 | public const string MapProperty_AreShootingStarsEnabled = "AreShootingStarsEnabled"; 21 | public bool AreShootingStarsEnabled { get; set; } = true; 22 | 23 | public const string MapProperty_MillisecondsBetweenShootingStarAttempt = "MillisecondsBetweenShootingStarAttempt"; 24 | public int MillisecondsBetweenShootingStarAttempt { get; set; } = 5000; 25 | 26 | public const string MapProperty_MaxShootingStarAttemptsPerInterval = "MaxShootingStarAttemptsPerInterval"; 27 | public int MaxShootingStarAttemptsPerInterval { get; set; } = 5; 28 | 29 | public const string MapProperty_CometChance = "CometChance"; 30 | public int CometChance { get; set; } = 10; 31 | 32 | public const string MapProperty_CometSegmentMin = "CometSegmentMin"; 33 | public int CometSegmentMin { get; set; } = 5; 34 | public const string MapProperty_CometSegmentMax = "CometSegmentMax"; 35 | public int CometSegmentMax { get; set; } = 20; 36 | 37 | 38 | public const string MapProperty_ShootingStarMinSpeed = "ShootingStarMinSpeed"; 39 | public float ShootingStarMinSpeed { get; set; } = 0.01f; 40 | public const string MapProperty_ShootingStarMaxSpeed = "ShootingStarMaxSpeed"; 41 | public float ShootingStarMaxSpeed { get; set; } = 1f; 42 | 43 | public const string MapProperty_CometMinSpeed = "CometMinSpeed"; 44 | public float CometMinSpeed { get; set; } = 0.04f; 45 | public const string MapProperty_CometMaxSpeed = "CometMaxSpeed"; 46 | public float CometMaxSpeed { get; set; } = 0.5f; 47 | 48 | public const string MapProperty_MillisecondsBetweenShootingStarAttemptDuringMeteorShower = "MillisecondsBetweenShootingStarAttemptDuringMeteorShower"; 49 | public int MillisecondsBetweenShootingStarAttemptDuringMeteorShower { get; set; } = 250; 50 | 51 | public const string MapProperty_GettingDarkWaterAlpha = "StartingDarkWaterAlpha"; 52 | public float GettingDarkWaterAlpha { get; set; } = 0.35f; 53 | public const string MapProperty_HalfwayDarkWaterAlpha = "HalfwayDarkWaterAlpha"; 54 | public float HalfwayDarkWaterAlpha { get; set; } = 0.075f; 55 | public const string MapProperty_FinishedDarkWaterAlpha = "FinishedDarkWaterAlpha"; 56 | public float FinishedDarkWaterAlpha { get; set; } = 0.005f; 57 | 58 | 59 | public bool OverrideDefaultSettings { get; set; } 60 | 61 | public void Reset(SkySettings referencedSettings = null) 62 | { 63 | if (referencedSettings is null) 64 | { 65 | AreReflectionsEnabled = true; 66 | StarDensityPercentage = 55; 67 | AreShootingStarsEnabled = true; 68 | MillisecondsBetweenShootingStarAttempt = 5000; 69 | MaxShootingStarAttemptsPerInterval = 5; 70 | CometChance = 10; 71 | CometSegmentMin = 5; 72 | CometSegmentMax = 20; 73 | ShootingStarMinSpeed = 0.01f; 74 | ShootingStarMaxSpeed = 1f; 75 | CometMinSpeed = 0.04f; 76 | CometMaxSpeed = 0.5f; 77 | MillisecondsBetweenShootingStarAttemptDuringMeteorShower = 250; 78 | GettingDarkWaterAlpha = 0.35f; 79 | HalfwayDarkWaterAlpha = 0.075f; 80 | FinishedDarkWaterAlpha = 0.005f; 81 | OverrideDefaultSettings = false; 82 | } 83 | else 84 | { 85 | AreReflectionsEnabled = referencedSettings.AreReflectionsEnabled; 86 | StarDensityPercentage = referencedSettings.StarDensityPercentage; 87 | AreShootingStarsEnabled = referencedSettings.AreShootingStarsEnabled; 88 | MillisecondsBetweenShootingStarAttempt = referencedSettings.MillisecondsBetweenShootingStarAttempt; 89 | MaxShootingStarAttemptsPerInterval = referencedSettings.MaxShootingStarAttemptsPerInterval; 90 | CometChance = referencedSettings.CometChance; 91 | CometSegmentMin = Math.Max(referencedSettings.CometSegmentMin, 1); 92 | CometSegmentMax = Math.Min(referencedSettings.CometSegmentMax, 20); 93 | ShootingStarMinSpeed = Math.Max(referencedSettings.ShootingStarMinSpeed, 0.01f); 94 | ShootingStarMaxSpeed = Math.Min(referencedSettings.ShootingStarMaxSpeed, 1f); 95 | CometMinSpeed = Math.Max(referencedSettings.CometMinSpeed, 0.01f); 96 | CometMaxSpeed = Math.Min(referencedSettings.CometMaxSpeed, 1f); 97 | GettingDarkWaterAlpha = referencedSettings.GettingDarkWaterAlpha; 98 | HalfwayDarkWaterAlpha = referencedSettings.HalfwayDarkWaterAlpha; 99 | FinishedDarkWaterAlpha = referencedSettings.FinishedDarkWaterAlpha; 100 | MillisecondsBetweenShootingStarAttemptDuringMeteorShower = referencedSettings.MillisecondsBetweenShootingStarAttemptDuringMeteorShower; 101 | } 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /DynamicReflections/Framework/Models/Settings/WaterSettings.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using StardewValley.Objects; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace DynamicReflections.Framework.Models.Settings 10 | { 11 | public enum Direction 12 | { 13 | North, 14 | East, 15 | South, 16 | West 17 | } 18 | 19 | public class WaterSettings 20 | { 21 | // Note: This property can only override disabling, it cannot force a user to enable reflections 22 | public const string MapProperty_IsEnabled = "AreWaterReflectionsEnabled"; 23 | public bool AreReflectionsEnabled { get; set; } = true; 24 | 25 | public const string MapProperty_ReflectionDirection = "WaterReflectionDirection"; 26 | public Direction ReflectionDirection { get; set; } = Direction.South; 27 | 28 | public const string MapProperty_ReflectionOverlay = "WaterReflectionOverlay"; 29 | public Color ReflectionOverlay { get; set; } = Color.White; 30 | 31 | public const string MapProperty_ReflectionOffset = "WaterReflectionOffset"; 32 | public Vector2 PlayerReflectionOffset { get; set; } = new Vector2(0f, 1.5f); 33 | public const string MapProperty_NPCReflectionOffset = "WaterNPCReflectionOffset"; 34 | public Vector2 NPCReflectionOffset { get; set; } = new Vector2(0f, 1.1f); 35 | 36 | public const string MapProperty_IsReflectionWavy = "IsWaterReflectionWavy"; 37 | public bool IsReflectionWavy { get; set; } = false; 38 | 39 | public const string MapProperty_WaveSpeed = "WaterReflectionWaveSpeed"; 40 | public float WaveSpeed { get; set; } = 1f; 41 | 42 | public const string MapProperty_WaveAmplitude = "WaterReflectionWaveAmplitude"; 43 | public float WaveAmplitude { get; set; } = 0.01f; 44 | 45 | public const string MapProperty_WaveFrequency = "WaterReflectionWaveFrequency"; 46 | public float WaveFrequency { get; set; } = 50f; 47 | 48 | 49 | public bool OverrideDefaultSettings { get; set; } 50 | 51 | public void Reset(WaterSettings referencedSettings = null) 52 | { 53 | if (referencedSettings is null) 54 | { 55 | AreReflectionsEnabled = true; 56 | ReflectionDirection = Direction.South; 57 | ReflectionOverlay = Color.White; 58 | PlayerReflectionOffset = new Vector2(0f, 1.5f); 59 | NPCReflectionOffset = new Vector2(0f, 1.1f); 60 | IsReflectionWavy = false; 61 | WaveSpeed = 1f; 62 | WaveAmplitude = 0.01f; 63 | WaveFrequency = 50f; 64 | OverrideDefaultSettings = false; 65 | } 66 | else 67 | { 68 | AreReflectionsEnabled = referencedSettings.AreReflectionsEnabled; 69 | ReflectionDirection = referencedSettings.ReflectionDirection; 70 | ReflectionOverlay = referencedSettings.ReflectionOverlay; 71 | PlayerReflectionOffset = referencedSettings.PlayerReflectionOffset; 72 | NPCReflectionOffset = referencedSettings.NPCReflectionOffset; 73 | IsReflectionWavy = referencedSettings.IsReflectionWavy; 74 | WaveSpeed = referencedSettings.WaveSpeed; 75 | WaveAmplitude = referencedSettings.WaveAmplitude; 76 | WaveFrequency = referencedSettings.WaveFrequency; 77 | } 78 | } 79 | 80 | public bool IsFacingCorrectDirection(int direction) 81 | { 82 | if (direction == 0 && ReflectionDirection == Direction.North) 83 | { 84 | return true; 85 | } 86 | else if (direction == 2 && ReflectionDirection == Direction.South) 87 | { 88 | return true; 89 | } 90 | 91 | return false; 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /DynamicReflections/Framework/Patches/Locations/GameLocationPatch.cs: -------------------------------------------------------------------------------- 1 | using DynamicReflections.Framework.Managers; 2 | using DynamicReflections.Framework.Utilities; 3 | using HarmonyLib; 4 | using Microsoft.Xna.Framework; 5 | using Microsoft.Xna.Framework.Graphics; 6 | using StardewModdingAPI; 7 | using StardewValley; 8 | using StardewValley.Buildings; 9 | using StardewValley.Locations; 10 | using StardewValley.Menus; 11 | using System; 12 | using System.Collections.Generic; 13 | using System.Linq; 14 | using System.Reflection.Emit; 15 | using Object = StardewValley.Object; 16 | 17 | namespace DynamicReflections.Framework.Patches.Tools 18 | { 19 | internal class GameLocationPatch : PatchTemplate 20 | { 21 | private readonly Type _type = typeof(GameLocation); 22 | private static double _elapsedMilliseconds; 23 | private static int _cooldownCounter; 24 | private static List _puddlesOnCooldown = new List(); 25 | 26 | internal GameLocationPatch(IMonitor modMonitor, IModHelper modHelper) : base(modMonitor, modHelper) 27 | { 28 | 29 | } 30 | 31 | internal void Apply(Harmony harmony) 32 | { 33 | harmony.Patch(AccessTools.Method(_type, nameof(GameLocation.drawWater), new[] { typeof(SpriteBatch) }), prefix: new HarmonyMethod(GetType(), nameof(DrawWaterPrefix))); 34 | harmony.Patch(AccessTools.Method(_type, nameof(GameLocation.drawWater), new[] { typeof(SpriteBatch) }), prefix: new HarmonyMethod(GetType(), nameof(VisibleFishDrawPrefix))); 35 | harmony.CreateReversePatcher(AccessTools.Method(_type, nameof(GameLocation.drawWater), new[] { typeof(SpriteBatch) }), new HarmonyMethod(GetType(), nameof(DrawWaterReversePatch))).Patch(); 36 | 37 | harmony.Patch(AccessTools.Method(_type, nameof(GameLocation.UpdateWhenCurrentLocation), new[] { typeof(GameTime) }), postfix: new HarmonyMethod(GetType(), nameof(UpdateWhenCurrentLocationPostfix))); 38 | } 39 | 40 | [HarmonyBefore(new string[] { "shekurika.WaterFish" })] 41 | private static bool VisibleFishDrawPrefix(GameLocation __instance, SpriteBatch b) 42 | { 43 | // Draw the sky reflection 44 | if (DynamicReflections.isDrawingWaterReflection is true) 45 | { 46 | DrawWaterReversePatch(__instance, b); 47 | return false; 48 | } 49 | 50 | return true; 51 | } 52 | 53 | [HarmonyAfter(new string[] { "shekurika.WaterFish" })] 54 | private static bool DrawWaterPrefix(GameLocation __instance, SpriteBatch b) 55 | { 56 | // Draw the sky reflection 57 | if (DynamicReflections.shouldSkipWaterOverlay is true) 58 | { 59 | return false; 60 | } 61 | 62 | return true; 63 | } 64 | 65 | internal static void DrawWaterReversePatch(GameLocation __instance, SpriteBatch b) 66 | { 67 | new NotImplementedException("It's a stub!"); 68 | } 69 | 70 | private static void UpdateWhenCurrentLocationPostfix(GameLocation __instance, GameTime time) 71 | { 72 | foreach (var rippleSprite in DynamicReflections.puddleManager.puddleRippleSprites.ToList()) 73 | { 74 | if (rippleSprite.update(time)) 75 | { 76 | DynamicReflections.puddleManager.puddleRippleSprites.Remove(rippleSprite); 77 | } 78 | } 79 | 80 | foreach (var skySprite in DynamicReflections.skyManager.skyEffectSprites.ToList()) 81 | { 82 | if (skySprite.update(time)) 83 | { 84 | DynamicReflections.skyManager.skyEffectSprites.Remove(skySprite); 85 | } 86 | } 87 | 88 | if (DynamicReflections.currentPuddleSettings.ShouldGeneratePuddles is false) 89 | { 90 | return; 91 | } 92 | 93 | var playerTilePosition = Game1.player.TilePoint; 94 | if (__instance.lastTouchActionLocation.Equals(Vector2.Zero) && Int32.TryParse(__instance.doesTileHaveProperty(playerTilePosition.X, playerTilePosition.Y, "PuddleIndex", "Back"), out int puddleIndex) && puddleIndex != PuddleManager.DEFAULT_PUDDLE_INDEX) 95 | { 96 | float xOffset = Game1.player.FacingDirection == 3 ? 64f : 0f; 97 | float yOffset = Game1.player.FacingDirection == 0 ? 64f : 0f; 98 | switch (Game1.player.FacingDirection) 99 | { 100 | case 0: 101 | case 2: 102 | xOffset += 20f; 103 | break; 104 | case 1: 105 | case 3: 106 | yOffset += 20f; 107 | break; 108 | } 109 | 110 | TemporaryAnimatedSprite splashSprite = new TemporaryAnimatedSprite("TileSheets\\animations", new Microsoft.Xna.Framework.Rectangle(0, 0, 64, 64), Game1.random.Next(50, 100), 9, 1, new Vector2(Game1.player.StandingPixel.X - xOffset, Game1.player.StandingPixel.Y - yOffset), flicker: false, flipped: false, 0f, 0f, DynamicReflections.currentPuddleSettings.RippleColor, 1f, 0f, 0f, 0f); 111 | splashSprite.acceleration = new Vector2(Game1.player.xVelocity, Game1.player.yVelocity); 112 | DynamicReflections.puddleManager.puddleRippleSprites.Add(splashSprite); 113 | 114 | TemporaryAnimatedSprite dropletSprite = new TemporaryAnimatedSprite("TileSheets\\animations", new Microsoft.Xna.Framework.Rectangle(2 * 64, 18 * 64, 64, 64), Game1.random.Next(75, 125), 5, 1, new Vector2(playerTilePosition.X, playerTilePosition.Y - 0.5f) * 64f, flicker: false, flipped: false, 0f, 0f, new Color(141, 181, 216, 91), 1f, 0f, 0f, 0f); 115 | splashSprite.acceleration = new Vector2(Game1.player.xVelocity, Game1.player.yVelocity); 116 | __instance.temporarySprites.Add(dropletSprite); 117 | 118 | if (DynamicReflections.currentPuddleSettings.ShouldPlaySplashSound) 119 | { 120 | __instance.playSound(Game1.random.NextDouble() > 0.5 ? "slosh" : "waterSlosh"); 121 | } 122 | __instance.lastTouchActionLocation = new Vector2(playerTilePosition.X, playerTilePosition.Y); 123 | } 124 | 125 | _elapsedMilliseconds += time.ElapsedGameTime.TotalMilliseconds; 126 | if (_elapsedMilliseconds > DynamicReflections.currentPuddleSettings.MillisecondsBetweenRaindropSplashes) 127 | { 128 | _elapsedMilliseconds = 0; 129 | 130 | if (DynamicReflections.currentPuddleSettings.ShouldRainSplashPuddles && Game1.IsRainingHere(__instance)) 131 | { 132 | var puddles = DynamicReflections.puddleManager.GetPuddleTiles(__instance, limitToView: true); 133 | if (puddles.Count > 0) 134 | { 135 | for (int i = 0; i < Game1.random.Next(1, 5); i++) 136 | { 137 | var puddleTile = puddles[Game1.random.Next(puddles.Count)]; 138 | if (_puddlesOnCooldown.Any(p => p.X == puddleTile.X && p.Y == puddleTile.Y) is false && Game1.random.NextDouble() > 0.5) 139 | { 140 | GenerateRipple(__instance, puddleTile); 141 | _puddlesOnCooldown.Add(puddleTile); 142 | } 143 | } 144 | } 145 | 146 | // Iterate the cooldown counter 147 | _cooldownCounter -= 1; 148 | if (_cooldownCounter < 0) 149 | { 150 | _puddlesOnCooldown.Clear(); 151 | _cooldownCounter = 5; 152 | } 153 | } 154 | } 155 | } 156 | 157 | private static void GenerateRipple(GameLocation location, Point puddleTile, bool playSound = false) 158 | { 159 | TemporaryAnimatedSprite splashSprite = new TemporaryAnimatedSprite("TileSheets\\animations", new Microsoft.Xna.Framework.Rectangle(0, 0, 64, 64), Game1.random.Next(50, 100), 9, 1, new Vector2(puddleTile.X, puddleTile.Y) * 64f, flicker: false, flipped: false, 0f, 0f, DynamicReflections.currentPuddleSettings.RippleColor, 1f, 0f, 0f, 0f); 160 | DynamicReflections.puddleManager.puddleRippleSprites.Add(splashSprite); 161 | 162 | TemporaryAnimatedSprite dropletSprite = new TemporaryAnimatedSprite("TileSheets\\animations", new Microsoft.Xna.Framework.Rectangle(2 * 64, 18 * 64, 64, 64), Game1.random.Next(75, 125), 5, 1, new Vector2(puddleTile.X, puddleTile.Y - 0.5f) * 64f, flicker: false, flipped: false, 0f, 0f, new Color(141, 181, 216, 91), 1f, 0f, 0f, 0f); 163 | location.temporarySprites.Add(dropletSprite); 164 | 165 | if (playSound) 166 | { 167 | location.playSound(Game1.random.NextDouble() > 0.5 ? "slosh" : "waterSlosh"); 168 | } 169 | } 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /DynamicReflections/Framework/Patches/Objects/FurniturePatch.cs: -------------------------------------------------------------------------------- 1 | using DynamicReflections.Framework.Models; 2 | using HarmonyLib; 3 | using Microsoft.Xna.Framework; 4 | using Microsoft.Xna.Framework.Graphics; 5 | using Netcode; 6 | using StardewModdingAPI; 7 | using StardewValley; 8 | using StardewValley.Buildings; 9 | using StardewValley.Locations; 10 | using StardewValley.Menus; 11 | using StardewValley.Objects; 12 | using System; 13 | using System.Collections.Generic; 14 | using System.Linq; 15 | using System.Reflection.Emit; 16 | 17 | namespace DynamicReflections.Framework.Patches.Objects 18 | { 19 | internal class FurniturePatch : PatchTemplate 20 | { 21 | private readonly Type _type = typeof(Furniture); 22 | internal FurniturePatch(IMonitor modMonitor, IModHelper modHelper) : base(modMonitor, modHelper) 23 | { 24 | 25 | } 26 | 27 | internal void Apply(Harmony harmony) 28 | { 29 | if (DynamicReflections.modHelper.ModRegistry.IsLoaded("spacechase0.DynamicGameAssets")) 30 | { 31 | try 32 | { 33 | if (Type.GetType("DynamicGameAssets.Game.CustomBasicFurniture, DynamicGameAssets") is Type dgaFurnitureType && dgaFurnitureType != null) 34 | { 35 | harmony.Patch(AccessTools.Method(dgaFurnitureType, nameof(Furniture.draw), new[] { typeof(SpriteBatch), typeof(int), typeof(int), typeof(float) }), prefix: new HarmonyMethod(GetType(), nameof(DrawPrefix))); 36 | harmony.Patch(AccessTools.Method(dgaFurnitureType, nameof(Furniture.draw), new[] { typeof(SpriteBatch), typeof(int), typeof(int), typeof(float) }), postfix: new HarmonyMethod(GetType(), nameof(DrawPostfix))); 37 | } 38 | } 39 | catch (Exception ex) 40 | { 41 | _monitor.Log($"Failed to patch Dynamic Game Assets in {this.GetType().Name}: DR will not be able to apply furniture reflections!", LogLevel.Warn); 42 | _monitor.Log($"Patch for DGA failed in {this.GetType().Name}: {ex}", LogLevel.Trace); 43 | } 44 | } 45 | } 46 | 47 | [HarmonyBefore(new string[] { "spacechase0.DynamicGameAssets", "PeacefulEnd.AlternativeTextures" })] 48 | private static bool DrawPrefix(Furniture __instance, NetInt ___sourceIndexOffset, NetVector2 ___drawPosition, SpriteBatch spriteBatch, int x, int y, float alpha = 1f) 49 | { 50 | if (DynamicReflections.isFilteringMirror is true && DynamicReflections.isDrawingMirrorReflection is false) 51 | { 52 | foreach (var mirror in DynamicReflections.mirrors.Values.ToList()) 53 | { 54 | if (mirror.IsEnabled is false || mirror.FurnitureLink != __instance || DynamicReflections.mirrorsManager.GetMask(__instance.Name) is null) 55 | { 56 | continue; 57 | } 58 | 59 | // Attempt to get the animation frame 60 | Rectangle? sourceRectangle = null; 61 | try 62 | { 63 | var packConfig = DynamicReflections.modHelper.Reflection.GetMethod(__instance, "GetCurrentConfiguration", required: false).Invoke(); 64 | var packTextureName = DynamicReflections.modHelper.Reflection.GetProperty(packConfig, "Texture", required: false).GetValue(); 65 | 66 | var packData = DynamicReflections.modHelper.Reflection.GetProperty(__instance, "Data", required: false).GetValue(); 67 | var actualContentPack = DynamicReflections.modHelper.Reflection.GetField(packData, "pack", required: false).GetValue(); 68 | var textureRectangleData = DynamicReflections.modHelper.Reflection.GetMethod(actualContentPack, "GetTexture", required: false).Invoke(packTextureName, mirror.Settings.Dimensions.Width, mirror.Settings.Dimensions.Height); 69 | sourceRectangle = DynamicReflections.modHelper.Reflection.GetProperty(textureRectangleData, "Rect", required: false).GetValue(); 70 | 71 | // Verify we actually got the source rectangle for the DGA texture 72 | if (sourceRectangle is null) 73 | { 74 | _monitor.LogOnce($"Failed to get texture source rectangle from the DGA item {__instance.Name}", LogLevel.Warn); 75 | return false; 76 | } 77 | } 78 | catch (Exception ex) 79 | { 80 | _monitor.LogOnce($"Failed to get texture source rectangle from the DGA item {__instance.Name}", LogLevel.Warn); 81 | _monitor.LogOnce($"Failed to get texture source rectangle from the DGA item {__instance.Name}: {ex}", LogLevel.Trace); 82 | return false; 83 | } 84 | 85 | spriteBatch.Draw(DynamicReflections.mirrorsManager.GetMask(__instance.Name), Game1.GlobalToLocal(Game1.viewport, ___drawPosition.Value + ((__instance.shakeTimer > 0) ? new Vector2(Game1.random.Next(-1, 2), Game1.random.Next(-1, 2)) : Vector2.Zero)), sourceRectangle, Color.White * alpha, 0f, new Vector2(-mirror.Settings.Dimensions.X, -mirror.Settings.Dimensions.Y), 4f, __instance.Flipped ? SpriteEffects.FlipHorizontally : SpriteEffects.None, (__instance.furniture_type.Value == 12) ? (2E-09f + __instance.tileLocation.Y / 100000f) : ((float)(__instance.boundingBox.Value.Bottom - ((__instance.furniture_type.Value == 6 || __instance.furniture_type.Value == 17 || __instance.furniture_type.Value == 13) ? 48 : 8)) / 10000f)); 86 | 87 | return false; 88 | } 89 | } 90 | return true; 91 | } 92 | 93 | private static void DrawPostfix(Furniture __instance, NetVector2 ___drawPosition, SpriteBatch spriteBatch, int x, int y, float alpha = 1f) 94 | { 95 | foreach (var mirror in DynamicReflections.mirrors.Values.ToList()) 96 | { 97 | if (mirror.IsEnabled is false || mirror.FurnitureLink != __instance) 98 | { 99 | continue; 100 | } 101 | 102 | var layerOffset = (__instance.furniture_type.Value == 12) ? (2E-09f + __instance.tileLocation.Y / 100000f) : ((float)(__instance.boundingBox.Value.Bottom - ((__instance.furniture_type.Value == 6 || __instance.furniture_type.Value == 17 || __instance.furniture_type.Value == 13) ? 48 : 8)) / 10000f); 103 | spriteBatch.Draw(DynamicReflections.maskedPlayerMirrorReflectionRenders[mirror.ActiveIndex], Vector2.Zero, DynamicReflections.maskedPlayerMirrorReflectionRenders[mirror.ActiveIndex].Bounds, Color.White, 0f, Vector2.Zero, 1f, SpriteEffects.None, layerOffset + 0.001f); 104 | 105 | break; 106 | } 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /DynamicReflections/Framework/Patches/PatchTemplate.cs: -------------------------------------------------------------------------------- 1 | using StardewModdingAPI; 2 | 3 | namespace DynamicReflections.Framework.Patches 4 | { 5 | internal class PatchTemplate 6 | { 7 | internal static IMonitor _monitor; 8 | internal static IModHelper _helper; 9 | 10 | internal PatchTemplate(IMonitor modMonitor, IModHelper modHelper) 11 | { 12 | _monitor = modMonitor; 13 | _helper = modHelper; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /DynamicReflections/Framework/Patches/SMAPI/DisplayDevicePatch.cs: -------------------------------------------------------------------------------- 1 | using DynamicReflections.Framework.Managers; 2 | using HarmonyLib; 3 | using Microsoft.Xna.Framework; 4 | using Microsoft.Xna.Framework.Graphics; 5 | using StardewModdingAPI; 6 | using StardewValley; 7 | using StardewValley.Buildings; 8 | using StardewValley.Locations; 9 | using StardewValley.Menus; 10 | using System; 11 | using System.Collections.Generic; 12 | using System.Linq; 13 | using System.Reflection.Emit; 14 | using xTile.Dimensions; 15 | using xTile.Display; 16 | using xTile.Layers; 17 | using xTile.Tiles; 18 | using Object = StardewValley.Object; 19 | 20 | namespace DynamicReflections.Framework.Patches.SMAPI 21 | { 22 | internal class DisplayDevicePatch : PatchTemplate 23 | { 24 | internal DisplayDevicePatch(IMonitor modMonitor, IModHelper modHelper) : base(modMonitor, modHelper) 25 | { 26 | 27 | } 28 | 29 | internal void Apply(Harmony harmony) 30 | { 31 | harmony.Patch(AccessTools.Method("xTile.Display.XnaDisplayDevice:DrawTile", new[] { typeof(Tile), typeof(xTile.Dimensions.Location), typeof(float) }), prefix: new HarmonyMethod(GetType(), nameof(DrawTilePrefix))); 32 | 33 | // Perform PyTK related patches 34 | if (DynamicReflections.modHelper.ModRegistry.IsLoaded("Platonymous.Toolkit")) 35 | { 36 | try 37 | { 38 | if (Type.GetType("PyTK.Types.PyDisplayDevice, PyTK") is Type PyTK && PyTK != null) 39 | { 40 | harmony.Patch(AccessTools.Method(PyTK, "DrawTile", new[] { typeof(Tile), typeof(Location), typeof(float) }), prefix: new HarmonyMethod(GetType(), nameof(PyTKDrawTilePrefix))); 41 | } 42 | } 43 | catch (Exception ex) 44 | { 45 | _monitor.Log($"Failed to patch PyTK in {this.GetType().Name}: DR may not properly display reflections!", LogLevel.Warn); 46 | _monitor.Log($"Patch for PyTK failed in {this.GetType().Name}: {ex}", LogLevel.Trace); 47 | } 48 | } 49 | if (DynamicReflections.modHelper.ModRegistry.IsLoaded("Platonymous.TMXLoader")) 50 | { 51 | try 52 | { 53 | if (Type.GetType("TMXLoader.PyDisplayDevice, TMXLoader") is Type PyTK && PyTK != null) 54 | { 55 | harmony.Patch(AccessTools.Method(PyTK, "DrawTile", new[] { typeof(Tile), typeof(Location), typeof(float) }), prefix: new HarmonyMethod(GetType(), nameof(PyTKDrawTilePrefix))); 56 | } 57 | } 58 | catch (Exception ex) 59 | { 60 | _monitor.Log($"Failed to patch TMXLoader in {this.GetType().Name}: DR may not properly display reflections!", LogLevel.Warn); 61 | _monitor.Log($"Patch for TMXLoader failed in {this.GetType().Name}: {ex}", LogLevel.Trace); 62 | } 63 | } 64 | } 65 | 66 | private static bool PyTKDrawTilePrefix(IDisplayDevice __instance, SpriteBatch ___m_spriteBatchAlpha, ref Vector2 ___m_tilePosition, Tile? tile, Location location, float layerDepth) 67 | { 68 | if (tile is null) 69 | { 70 | return true; 71 | } 72 | 73 | return ActualDrawTilePrefix(tile, location, layerDepth, ___m_spriteBatchAlpha, ref ___m_tilePosition); 74 | } 75 | 76 | private static bool DrawTilePrefix(IDisplayDevice __instance, SpriteBatch ___m_spriteBatchAlpha, Dictionary ___m_tileSheetTextures, ref Vector2 ___m_tilePosition, Tile? tile, Location location, float layerDepth) 77 | { 78 | if (tile is null || ___m_tileSheetTextures is null || tile.TileSheet is null || ___m_tileSheetTextures.ContainsKey(tile.TileSheet) is false) 79 | { 80 | return true; 81 | } 82 | 83 | return ActualDrawTilePrefix(tile, location, layerDepth, ___m_spriteBatchAlpha, ref ___m_tilePosition); 84 | } 85 | 86 | private static bool ActualDrawTilePrefix(Tile? tile, Location location, float layerDepth, SpriteBatch ___m_spriteBatchAlpha, ref Vector2 ___m_tilePosition) 87 | { 88 | if (DynamicReflections.currentWaterSettings.AreReflectionsEnabled is false) 89 | { 90 | return true; 91 | } 92 | 93 | if (DynamicReflections.isFilteringPuddles is true) 94 | { 95 | DrawPuddleTile(tile, ref ___m_tilePosition, ___m_spriteBatchAlpha, location, layerDepth); 96 | return false; 97 | } 98 | 99 | if (DynamicReflections.isFilteringSky is true) 100 | { 101 | DrawSkyTile(tile, ref ___m_tilePosition, ___m_spriteBatchAlpha, location, layerDepth); 102 | return false; 103 | } 104 | 105 | if (DynamicReflections.isFilteringStar is true) 106 | { 107 | DrawStarTile(tile, ref ___m_tilePosition, ___m_spriteBatchAlpha, location, layerDepth); 108 | return false; 109 | } 110 | 111 | if (DynamicReflections.isDrawingWaterReflection is true && tile.TileIndexProperties.TryGetValue("Water", out _) is true) 112 | { 113 | return false; 114 | } 115 | else if (DynamicReflections.isFilteringWater is true && tile.TileIndexProperties.TryGetValue("Water", out _) is false) 116 | { 117 | return false; 118 | } 119 | 120 | return true; 121 | } 122 | 123 | private static void DrawSkyTile(Tile tile, ref Vector2 ___m_tilePosition, SpriteBatch ___m_spriteBatchAlpha, Location location, float layerDepth) 124 | { 125 | if (tile.Properties.TryGetValue("SkyIndex", out var skyValue) && Int32.TryParse(skyValue, out int skyIndex)) 126 | { 127 | ___m_tilePosition.X = location.X; 128 | ___m_tilePosition.Y = location.Y; 129 | Vector2 origin = new Vector2(8f, 8f); 130 | ___m_tilePosition.X += origin.X * (float)Layer.zoom; 131 | ___m_tilePosition.Y += origin.X * (float)Layer.zoom; 132 | ___m_spriteBatchAlpha.Draw(DynamicReflections.assetManager.NightSkyTileSheetTexture, ___m_tilePosition, new Microsoft.Xna.Framework.Rectangle(0, 0, 16, 16), Color.White * DynamicReflections.skyAlpha, 0f, origin, Layer.zoom, SpriteEffects.None, layerDepth); 133 | } 134 | } 135 | 136 | private static void DrawStarTile(Tile tile, ref Vector2 ___m_tilePosition, SpriteBatch ___m_spriteBatchAlpha, Location location, float layerDepth) 137 | { 138 | if (tile.Properties.TryGetValue("SkyIndex", out var skyValue) && Int32.TryParse(skyValue, out int skyIndex) && skyIndex != SkyManager.DEFAULT_SKY_INDEX) 139 | { 140 | var tilePoint = SkyManager.GetTilePoint(skyIndex); 141 | if (tilePoint.X == 0 && tilePoint.Y == 0) 142 | { 143 | return; 144 | } 145 | 146 | int effectIndex = 0; 147 | if (tile.Properties.TryGetValue("SkyEffect", out var skyEffectValue) && Int32.TryParse(skyEffectValue, out int skyEffect)) 148 | { 149 | effectIndex = skyEffect; 150 | } 151 | 152 | float alpha = 1f; 153 | if (tile.Properties.TryGetValue("SkyAlpha", out var skyAlphaValue) && float.TryParse(skyAlphaValue, out float skyAlpha)) 154 | { 155 | alpha = skyAlpha; 156 | } 157 | 158 | ___m_tilePosition.X = location.X; 159 | ___m_tilePosition.Y = location.Y; 160 | Vector2 origin = new Vector2(8f, 8f); 161 | ___m_tilePosition.X += origin.X * (float)Layer.zoom; 162 | ___m_tilePosition.Y += origin.X * (float)Layer.zoom; 163 | ___m_spriteBatchAlpha.Draw(DynamicReflections.assetManager.NightSkyTileSheetTexture, ___m_tilePosition, new Microsoft.Xna.Framework.Rectangle(tilePoint.X * 16, tilePoint.Y * 16, 16, 16), Color.White * alpha, 0f, origin, Layer.zoom, (SpriteEffects)effectIndex, layerDepth); 164 | } 165 | } 166 | 167 | private static void DrawPuddleTile(Tile tile, ref Vector2 ___m_tilePosition, SpriteBatch ___m_spriteBatchAlpha, Location location, float layerDepth) 168 | { 169 | if (tile.Properties.TryGetValue("PuddleIndex", out var puddleValue) && Int32.TryParse(puddleValue, out int puddleIndex) && puddleIndex != PuddleManager.DEFAULT_PUDDLE_INDEX) 170 | { 171 | var tileXOffset = 0; 172 | var tileYOffset = puddleIndex * 16; 173 | if (tile.Properties.TryGetValue("BigPuddleIndex", out var bigPuddleValue) && Int32.TryParse(bigPuddleValue, out int bigPuddleIndex) && bigPuddleIndex != PuddleManager.DEFAULT_PUDDLE_INDEX) 174 | { 175 | tileXOffset = bigPuddleIndex * 16; 176 | } 177 | 178 | int effectIndex = 0; 179 | if (tile.Properties.TryGetValue("PuddleEffect", out var puddleEffectValue) && Int32.TryParse(puddleEffectValue, out int puddleEffect)) 180 | { 181 | effectIndex = puddleEffect; 182 | } 183 | 184 | float rotation = 0f; 185 | if (tile.Properties.TryGetValue("PuddleRotation", out var puddleRotationValue) && float.TryParse(puddleRotationValue, out float puddleRotation)) 186 | { 187 | rotation = puddleRotation; 188 | } 189 | 190 | ___m_tilePosition.X = location.X; 191 | ___m_tilePosition.Y = location.Y; 192 | Vector2 origin = new Vector2(8f, 8f); 193 | ___m_tilePosition.X += origin.X * (float)Layer.zoom; 194 | ___m_tilePosition.Y += origin.X * (float)Layer.zoom; 195 | ___m_spriteBatchAlpha.Draw(DynamicReflections.assetManager.PuddlesTileSheetTexture, ___m_tilePosition, new Microsoft.Xna.Framework.Rectangle(tileXOffset, tileYOffset, 16, 16), DynamicReflections.currentPuddleSettings.PuddleColor, rotation, origin, Layer.zoom, (SpriteEffects)effectIndex, layerDepth); 196 | } 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /DynamicReflections/Framework/Patches/Tools/FishingRodPatch.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Microsoft.Xna.Framework; 3 | using Microsoft.Xna.Framework.Graphics; 4 | using StardewModdingAPI; 5 | using StardewValley; 6 | using StardewValley.Buildings; 7 | using StardewValley.ItemTypeDefinitions; 8 | using StardewValley.Locations; 9 | using StardewValley.Menus; 10 | using StardewValley.Tools; 11 | using System; 12 | using System.Collections.Generic; 13 | using System.Linq; 14 | using System.Reflection.Emit; 15 | using Object = StardewValley.Object; 16 | 17 | namespace DynamicReflections.Framework.Patches.Tools 18 | { 19 | internal class FishingRodPatch : PatchTemplate 20 | { 21 | private readonly Type _type = typeof(FishingRod); 22 | 23 | internal FishingRodPatch(IMonitor modMonitor, IModHelper modHelper) : base(modMonitor, modHelper) 24 | { 25 | 26 | } 27 | 28 | internal void Apply(Harmony harmony) 29 | { 30 | harmony.Patch(AccessTools.Method(_type, nameof(FishingRod.draw), new[] { typeof(SpriteBatch) }), prefix: new HarmonyMethod(GetType(), nameof(DrawPrefix))); 31 | } 32 | 33 | private static bool DrawPrefix(FishingRod __instance, Farmer ___lastUser, int ___fishSize, ItemMetadata ___whichFish, SpriteBatch b) 34 | { 35 | if (DynamicReflections.isFilteringWater || DynamicReflections.isFilteringPuddles) 36 | { 37 | if (__instance.castedButBobberStillInAir) 38 | { 39 | return true; 40 | } 41 | 42 | if (__instance.fishCaught) 43 | { 44 | float yOffset = 4f * (float)Math.Round(Math.Sin(Game1.currentGameTime.TotalGameTime.TotalMilliseconds / 250.0), 2); 45 | if (___whichFish.TypeIdentifier == "(O)") 46 | { 47 | ParsedItemData parsedOrErrorData = ___whichFish.GetParsedOrErrorData(); 48 | Texture2D texture = parsedOrErrorData.GetTexture(); 49 | Rectangle sourceRect = parsedOrErrorData.GetSourceRect(); 50 | 51 | b.Draw(texture, Game1.GlobalToLocal(Game1.viewport, ___lastUser.Position + new Vector2(0f, -56f)), sourceRect, Color.White, (___fishSize == -1 || ___whichFish.QualifiedItemId == "(O)800" || ___whichFish.QualifiedItemId == "(O)798" || ___whichFish.QualifiedItemId == "(O)149" || ___whichFish.QualifiedItemId == "(O)151") ? 0f : ((float)Math.PI * 3f / 4f), new Vector2(8f, 8f), 3f, SpriteEffects.None, (float)___lastUser.StandingPixel.Y / 10000f + 0.002f + 0.06f); 52 | if (__instance.numberOfFishCaught == 2) 53 | { 54 | b.Draw(texture, Game1.GlobalToLocal(Game1.viewport, ___lastUser.Position + new Vector2(-8f, -56f)), sourceRect, Color.White, (___fishSize == -1 || ___whichFish.QualifiedItemId == "(O)800" || ___whichFish.QualifiedItemId == "(O)798" || ___whichFish.QualifiedItemId == "(O)149" || ___whichFish.QualifiedItemId == "(O)151") ? 0f : ((float)Math.PI * 4f / 5f), new Vector2(8f, 8f), 3f, SpriteEffects.None, (float)___lastUser.StandingPixel.Y / 10000f + 0.002f + 0.058f); 55 | } 56 | } 57 | else 58 | { 59 | b.Draw(Game1.mouseCursors, Game1.GlobalToLocal(Game1.viewport, ___lastUser.Position + new Vector2(-124f, -284f + yOffset) + new Vector2(44f, 68f)), new Rectangle(228, 408, 16, 16), Color.White, 0f, Vector2.Zero, 4f, SpriteEffects.None, (float)___lastUser.StandingPixel.Y / 10000f + 0.0001f + 0.06f); 60 | b.Draw(Game1.mouseCursors, Game1.GlobalToLocal(Game1.viewport, ___lastUser.Position + new Vector2(0f, -56f)), new Rectangle(228, 408, 16, 16), Color.White, 0f, new Vector2(8f, 8f), 3f, SpriteEffects.None, (float)___lastUser.StandingPixel.Y / 10000f + 0.002f + 0.06f); 61 | } 62 | } 63 | 64 | return false; 65 | } 66 | 67 | return true; 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /DynamicReflections/Framework/Patches/Tools/ToolPatch.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Microsoft.Xna.Framework; 3 | using Microsoft.Xna.Framework.Graphics; 4 | using StardewModdingAPI; 5 | using StardewValley; 6 | using StardewValley.Buildings; 7 | using StardewValley.Locations; 8 | using StardewValley.Menus; 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Linq; 12 | using System.Reflection.Emit; 13 | using Object = StardewValley.Object; 14 | 15 | namespace DynamicReflections.Framework.Patches.Tools 16 | { 17 | internal class ToolPatch : PatchTemplate 18 | { 19 | private readonly Type _type = typeof(Tool); 20 | 21 | internal ToolPatch(IMonitor modMonitor, IModHelper modHelper) : base(modMonitor, modHelper) 22 | { 23 | 24 | } 25 | 26 | internal void Apply(Harmony harmony) 27 | { 28 | harmony.Patch(AccessTools.Method(_type, nameof(Tool.doesShowTileLocationMarker), null), postfix: new HarmonyMethod(GetType(), nameof(DoesShowTileLocationMarkerPostfix))); 29 | harmony.Patch(AccessTools.Method(_type, nameof(Tool.draw), new[] { typeof(SpriteBatch) }), prefix: new HarmonyMethod(GetType(), nameof(DrawPrefix))); 30 | } 31 | 32 | private static void DoesShowTileLocationMarkerPostfix(Tool __instance, ref bool __result) 33 | { 34 | if (DynamicReflections.isFilteringMirror || DynamicReflections.isFilteringWater || DynamicReflections.isFilteringPuddles) 35 | { 36 | __result = false; 37 | } 38 | } 39 | 40 | private static bool DrawPrefix(Tool __instance, ref Farmer ___lastUser, ref int __state, SpriteBatch b) 41 | { 42 | if (DynamicReflections.isFilteringMirror || DynamicReflections.isFilteringWater || DynamicReflections.isFilteringPuddles) 43 | { 44 | return false; 45 | } 46 | 47 | return true; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /DynamicReflections/Framework/Patches/xTile/LayerPatch.cs: -------------------------------------------------------------------------------- 1 | using DynamicReflections.Framework.Utilities; 2 | using HarmonyLib; 3 | using Microsoft.Xna.Framework; 4 | using Microsoft.Xna.Framework.Graphics; 5 | using StardewModdingAPI; 6 | using StardewValley; 7 | using StardewValley.Buildings; 8 | using StardewValley.Locations; 9 | using StardewValley.Menus; 10 | using System; 11 | using System.Collections.Generic; 12 | using System.Linq; 13 | using System.Reflection.Emit; 14 | using xTile.Dimensions; 15 | using xTile.Display; 16 | using xTile.Layers; 17 | using xTile.Tiles; 18 | using Object = StardewValley.Object; 19 | 20 | namespace DynamicReflections.Framework.Patches.Tiles 21 | { 22 | internal class LayerPatch : PatchTemplate 23 | { 24 | private readonly Type _object = typeof(Layer); 25 | private static Color _waterColor; 26 | 27 | internal LayerPatch(IMonitor modMonitor, IModHelper modHelper) : base(modMonitor, modHelper) 28 | { 29 | 30 | } 31 | 32 | internal void Apply(Harmony harmony) 33 | { 34 | harmony.Patch(AccessTools.Method(_object, "DrawNormal", new[] { typeof(IDisplayDevice), typeof(xTile.Dimensions.Rectangle), typeof(xTile.Dimensions.Location), typeof(int), typeof(float) }), prefix: new HarmonyMethod(GetType(), nameof(DrawNormalPrefix))); 35 | harmony.Patch(AccessTools.Method(_object, "DrawNormal", new[] { typeof(IDisplayDevice), typeof(xTile.Dimensions.Rectangle), typeof(xTile.Dimensions.Location), typeof(int), typeof(float) }), postfix: new HarmonyMethod(GetType(), nameof(DrawNormalPostfix))); 36 | 37 | harmony.CreateReversePatcher(AccessTools.Method(_object, "DrawNormal", new[] { typeof(IDisplayDevice), typeof(xTile.Dimensions.Rectangle), typeof(xTile.Dimensions.Location), typeof(int), typeof(float) }), new HarmonyMethod(GetType(), nameof(DrawNormalReversePatch))).Patch(); 38 | 39 | // Perform PyTK related patches 40 | if (DynamicReflections.modHelper.ModRegistry.IsLoaded("Platonymous.Toolkit")) 41 | { 42 | try 43 | { 44 | if (Type.GetType("PyTK.Extensions.PyMaps, PyTK") is Type PyTK && PyTK != null) 45 | { 46 | harmony.Patch(AccessTools.Method(PyTK, "drawLayer", new[] { typeof(Layer), typeof(IDisplayDevice), typeof(xTile.Dimensions.Rectangle), typeof(int), typeof(Location), typeof(bool) }), prefix: new HarmonyMethod(GetType(), nameof(PyTKDrawLayerPrefix))); 47 | } 48 | } 49 | catch (Exception ex) 50 | { 51 | _monitor.Log($"Failed to patch PyTK in {this.GetType().Name}: DR may not properly display reflections!", LogLevel.Warn); 52 | _monitor.Log($"Patch for PyTK failed in {this.GetType().Name}: {ex}", LogLevel.Trace); 53 | } 54 | } 55 | if (DynamicReflections.modHelper.ModRegistry.IsLoaded("Platonymous.TMXLoader")) 56 | { 57 | try 58 | { 59 | if (Type.GetType("TMXLoader.PyMaps, TMXLoader") is Type PyTK && PyTK != null) 60 | { 61 | harmony.Patch(AccessTools.Method(PyTK, "drawLayer", new[] { typeof(Layer), typeof(IDisplayDevice), typeof(xTile.Dimensions.Rectangle), typeof(int), typeof(Location), typeof(bool) }), prefix: new HarmonyMethod(GetType(), nameof(PyTKDrawLayerPrefix))); 62 | } 63 | } 64 | catch (Exception ex) 65 | { 66 | _monitor.Log($"Failed to patch TMXLoader in {this.GetType().Name}: DR may not properly display reflections!", LogLevel.Warn); 67 | _monitor.Log($"Patch for TMXLoader failed in {this.GetType().Name}: {ex}", LogLevel.Trace); 68 | } 69 | } 70 | } 71 | 72 | private static bool DrawNormalPrefix(Layer __instance, IDisplayDevice displayDevice, xTile.Dimensions.Rectangle mapViewport, Location displayOffset, int pixelZoom, float sort_offset = 0f) 73 | { 74 | if (__instance is null || String.IsNullOrEmpty(__instance.Id)) 75 | { 76 | return true; 77 | } 78 | 79 | DynamicReflections.isDrawingPuddles = false; 80 | DynamicReflections.isDrawingWaterReflection = false; 81 | DynamicReflections.isDrawingMirrorReflection = false; 82 | 83 | if (__instance.Id.Equals("Back", StringComparison.OrdinalIgnoreCase) is true) 84 | { 85 | SpriteBatchToolkit.CacheSpriteBatchSettings(Game1.spriteBatch, endSpriteBatch: true); 86 | 87 | // Pre-render the Mirrors layer (this should always be done, regardless of DynamicReflections.shouldDrawMirrorReflection) 88 | SpriteBatchToolkit.RenderMirrorsLayer(); 89 | if (DynamicReflections.shouldDrawMirrorReflection is true) 90 | { 91 | // Pre-render the mirror reflections 92 | DynamicReflections.isFilteringMirror = true; 93 | SpriteBatchToolkit.RenderMirrorReflectionPlayerSprite(); 94 | DynamicReflections.isFilteringMirror = false; 95 | } 96 | 97 | // Handle preliminary water reflection logic 98 | if (DynamicReflections.shouldDrawWaterReflection is true) 99 | { 100 | DynamicReflections.isFilteringWater = true; 101 | SpriteBatchToolkit.RenderWaterReflectionPlayerSprite(); 102 | } 103 | 104 | // Handle preliminary NPC reflection logic 105 | if (DynamicReflections.modConfig.AreNPCReflectionsEnabled is true) 106 | { 107 | DynamicReflections.isFilteringWater = true; 108 | SpriteBatchToolkit.RenderWaterReflectionNPCs(); 109 | SpriteBatchToolkit.RenderPuddleReflectionNPCs(); 110 | } 111 | 112 | _waterColor = Game1.currentLocation.waterColor.Value; 113 | if (DynamicReflections.modConfig.AreSkyReflectionsEnabled is true) 114 | { 115 | if (DynamicReflections.shouldDrawNightSky) 116 | { 117 | DynamicReflections.isFilteringSky = true; 118 | Game1.currentLocation.waterColor.Value = new Color(60, 240, 255) * DynamicReflections.waterAlpha; 119 | SpriteBatchToolkit.RenderWaterReflectionNightSky(); 120 | } 121 | } 122 | 123 | // Handle preliminary puddles reflection and draw logic 124 | if (DynamicReflections.currentPuddleSettings.ShouldGeneratePuddles is true) 125 | { 126 | DynamicReflections.isFilteringPuddles = true; 127 | SpriteBatchToolkit.RenderPuddles(); 128 | DynamicReflections.isFilteringPuddles = false; 129 | DynamicReflections.isDrawingPuddles = true; 130 | } 131 | if (DynamicReflections.shouldDrawPuddlesReflection is true) 132 | { 133 | DynamicReflections.isFilteringPuddles = true; 134 | SpriteBatchToolkit.RenderPuddleReflectionPlayerSprite(); 135 | DynamicReflections.isFilteringPuddles = false; 136 | } 137 | 138 | // Resume previous SpriteBatch 139 | SpriteBatchToolkit.ResumeCachedSpriteBatch(Game1.spriteBatch); 140 | 141 | // Draw the filtered layer, if needed 142 | SpriteBatchToolkit.HandleBackgroundDraw(); 143 | if (DynamicReflections.isFilteringWater is false && DynamicReflections.isFilteringSky is false) 144 | { 145 | return true; 146 | } 147 | 148 | // Handle Visible Fish Compatability 149 | LayerPatch.DrawNormalReversePatch(__instance, displayDevice, mapViewport, displayOffset, pixelZoom); 150 | DynamicReflections.shouldSkipWaterOverlay = true; 151 | Game1.currentLocation.drawWater(Game1.spriteBatch); 152 | DynamicReflections.shouldSkipWaterOverlay = false; 153 | 154 | // Draw the sky reflection 155 | if (DynamicReflections.shouldDrawNightSky is true) 156 | { 157 | SpriteBatchToolkit.CacheSpriteBatchSettings(Game1.spriteBatch, endSpriteBatch: true); 158 | 159 | SpriteBatchToolkit.DrawNightSky(); 160 | 161 | if (DynamicReflections.isFilteringWater is true) 162 | { 163 | DynamicReflections.isFilteringWater = false; 164 | DynamicReflections.isDrawingWaterReflection = true; 165 | } 166 | 167 | // Resume previous SpriteBatch 168 | SpriteBatchToolkit.ResumeCachedSpriteBatch(Game1.spriteBatch); 169 | } 170 | else if (DynamicReflections.isFilteringWater is true) // Draw the water reflection, if sky reflections aren't currently active 171 | { 172 | SpriteBatchToolkit.CacheSpriteBatchSettings(Game1.spriteBatch, endSpriteBatch: true); 173 | 174 | SpriteBatchToolkit.DrawRenderedCharacters(isWavy: DynamicReflections.currentWaterSettings.IsReflectionWavy); 175 | 176 | DynamicReflections.isFilteringWater = false; 177 | DynamicReflections.isDrawingWaterReflection = true; 178 | 179 | // Resume previous SpriteBatch 180 | SpriteBatchToolkit.ResumeCachedSpriteBatch(Game1.spriteBatch); 181 | } 182 | } 183 | else if (__instance.Id.Equals("Buildings", StringComparison.OrdinalIgnoreCase) is true) 184 | { 185 | Game1.currentLocation.waterColor.Value = _waterColor; 186 | 187 | // Draw the cached Mirrors layer 188 | Game1.spriteBatch.Draw(DynamicReflections.mirrorsLayerRenderTarget, Vector2.Zero, Color.White); 189 | 190 | // Skip drawing the player's reflection if not needed 191 | if (DynamicReflections.shouldDrawMirrorReflection is true) 192 | { 193 | SpriteBatchToolkit.CacheSpriteBatchSettings(Game1.spriteBatch, endSpriteBatch: true); 194 | 195 | //DynamicReflections.isDrawingMirrorReflection = true; 196 | SpriteBatchToolkit.DrawMirrorReflection(DynamicReflections.mirrorsLayerRenderTarget); 197 | 198 | // Resume previous SpriteBatch 199 | SpriteBatchToolkit.ResumeCachedSpriteBatch(Game1.spriteBatch); 200 | } 201 | } 202 | 203 | return true; 204 | } 205 | 206 | private static void DrawNormalPostfix(Layer __instance, IDisplayDevice displayDevice, xTile.Dimensions.Rectangle mapViewport, Location displayOffset, int pixelZoom, float sort_offset = 0f) 207 | { 208 | if (__instance is null || String.IsNullOrEmpty(__instance.Id)) 209 | { 210 | return; 211 | } 212 | 213 | if (__instance.Id.Equals("Back", StringComparison.OrdinalIgnoreCase) is true) 214 | { 215 | if (DynamicReflections.isDrawingPuddles is true) 216 | { 217 | SpriteBatchToolkit.CacheSpriteBatchSettings(Game1.spriteBatch, endSpriteBatch: true); 218 | 219 | // Draw puddle reflection 220 | SpriteBatchToolkit.DrawPuddleReflection(DynamicReflections.puddlesRenderTarget); 221 | 222 | // Resume previous SpriteBatch 223 | SpriteBatchToolkit.ResumeCachedSpriteBatch(Game1.spriteBatch); 224 | 225 | // Draw the puddles ontop of the "Back" layer 226 | DynamicReflections.isFilteringPuddles = true; 227 | LayerPatch.DrawNormalReversePatch(__instance, displayDevice, mapViewport, displayOffset, pixelZoom); 228 | DynamicReflections.isFilteringPuddles = false; 229 | 230 | } 231 | } 232 | } 233 | 234 | internal static void DrawNormalReversePatch(Layer __instance, IDisplayDevice displayDevice, xTile.Dimensions.Rectangle mapViewport, Location displayOffset, int pixelZoom, float sort_offset = 0f) 235 | { 236 | new NotImplementedException("It's a stub!"); 237 | } 238 | 239 | // PyTK related patches 240 | private static void PyTKDrawLayerPrefix(Layer __instance, xTile.Display.IDisplayDevice device, xTile.Dimensions.Rectangle viewport, int pixelZoom, Location offset, bool wrap = false) 241 | { 242 | DrawNormalPrefix(__instance, device, viewport, offset, pixelZoom); 243 | } 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /DynamicReflections/Framework/Utilities/Extensions/RandomExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace DynamicReflections.Framework.Utilities.Extensions 8 | { 9 | public static class RandomExtensions 10 | { 11 | // Referenced from https://stackoverflow.com/questions/1064901/random-number-between-2-double-numbers 12 | public static double NextDouble(this Random random, double minValue, double maxValue) 13 | { 14 | return random.NextDouble() * (maxValue - minValue) + minValue; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /DynamicReflections/Framework/Utilities/ModDataKeys.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace DynamicReflections.Framework.Utilities 8 | { 9 | internal class ModDataKeys 10 | { 11 | // Core keys 12 | internal const string DID_RAIN_YESTERDAY = "DynamicReflections.Flags.ItDidRainYesterday"; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /DynamicReflections/Framework/Utilities/SpriteBatchToolkit.cs: -------------------------------------------------------------------------------- 1 | using DynamicReflections.Framework.Patches.Tiles; 2 | using Microsoft.Xna.Framework; 3 | using Microsoft.Xna.Framework.Graphics; 4 | using StardewModdingAPI; 5 | using StardewValley; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using xTile.Dimensions; 12 | 13 | namespace DynamicReflections.Framework.Utilities 14 | { 15 | public static class SpriteBatchToolkit 16 | { 17 | // General helpers 18 | private static bool _hasCache = false; 19 | private static RenderTarget2D _cachedRenderer; 20 | private static SpriteSortMode _cachedSpriteSortMode; 21 | private static BlendState _cachedBlendState; 22 | private static SamplerState _cachedSamplerState; 23 | private static DepthStencilState _cachedDepthStencilState; 24 | private static RasterizerState _cachedRasterizerState; 25 | private static Effect _cachedSpriteEffect; 26 | private static Matrix? _cachedMatrix; 27 | 28 | public static void CacheSpriteBatchSettings(SpriteBatch spriteBatch, bool endSpriteBatch = false) 29 | { 30 | var reflection = DynamicReflections.modHelper.Reflection; 31 | 32 | _cachedSpriteSortMode = reflection.GetField(spriteBatch, "_sortMode").GetValue(); 33 | _cachedBlendState = reflection.GetField(spriteBatch, "_blendState").GetValue(); 34 | _cachedSamplerState = reflection.GetField(spriteBatch, "_samplerState").GetValue(); 35 | _cachedDepthStencilState = reflection.GetField(spriteBatch, "_depthStencilState").GetValue(); 36 | _cachedRasterizerState = reflection.GetField(spriteBatch, "_rasterizerState").GetValue(); 37 | _cachedSpriteEffect = reflection.GetField(spriteBatch, "_effect").GetValue(); 38 | _cachedMatrix = reflection.GetField(spriteBatch, "_spriteEffect").GetValue().TransformMatrix; 39 | 40 | _hasCache = true; 41 | if (endSpriteBatch is true) 42 | { 43 | spriteBatch.End(); 44 | } 45 | } 46 | 47 | public static bool ResumeCachedSpriteBatch(SpriteBatch spriteBatch) 48 | { 49 | if (_hasCache is false) 50 | { 51 | return false; 52 | } 53 | _hasCache = false; 54 | 55 | spriteBatch.Begin(_cachedSpriteSortMode, _cachedBlendState, _cachedSamplerState, _cachedDepthStencilState, _cachedRasterizerState, _cachedSpriteEffect, _cachedMatrix); 56 | return true; 57 | } 58 | 59 | public static void StartRendering(RenderTarget2D renderTarget2D) 60 | { 61 | var currentRenderer = Game1.graphics.GraphicsDevice.GetRenderTargets(); 62 | if (currentRenderer is not null && currentRenderer.Length > 0 && currentRenderer[0].RenderTarget is not null) 63 | { 64 | _cachedRenderer = currentRenderer[0].RenderTarget as RenderTarget2D; 65 | } 66 | 67 | Game1.graphics.GraphicsDevice.SetRenderTarget(renderTarget2D); 68 | } 69 | 70 | public static void StopRendering() 71 | { 72 | Game1.graphics.GraphicsDevice.SetRenderTarget(_cachedRenderer); 73 | _cachedRenderer = null; 74 | } 75 | 76 | // LayerPatch helper methods 77 | // A note on the Render and Draw prefixed methods: These methods assume SpriteBatch has not been started via SpriteBatch.Begin 78 | internal static void DrawMirrorReflection(Texture2D mask) 79 | { 80 | DynamicReflections.mirrorReflectionEffect.Parameters["Mask"].SetValue(mask); 81 | Game1.spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.NonPremultiplied, SamplerState.PointClamp, effect: DynamicReflections.mirrorReflectionEffect); 82 | 83 | int index = 0; 84 | foreach (var mirrorPosition in DynamicReflections.activeMirrorPositions) 85 | { 86 | var mirror = DynamicReflections.mirrors[mirrorPosition]; 87 | if (mirror.FurnitureLink is null) 88 | { 89 | Game1.spriteBatch.Draw(DynamicReflections.composedPlayerMirrorReflectionRenders[index], Vector2.Zero, Color.White); 90 | } 91 | 92 | index++; 93 | } 94 | 95 | Game1.spriteBatch.End(); 96 | } 97 | 98 | internal static void RenderMirrorsLayer() 99 | { 100 | // Set the render target 101 | SpriteBatchToolkit.StartRendering(DynamicReflections.mirrorsLayerRenderTarget); 102 | 103 | // Draw the scene 104 | Game1.graphics.GraphicsDevice.Clear(Color.Transparent); 105 | 106 | if (Game1.currentLocation is not null && Game1.currentLocation.Map is not null) 107 | { 108 | if (Game1.currentLocation.Map.GetLayer("Mirrors") is var mirrorsLayer && mirrorsLayer is not null) 109 | { 110 | Game1.spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.PointClamp); 111 | 112 | // Draw the "Mirrors" layer 113 | LayerPatch.DrawNormalReversePatch(mirrorsLayer, Game1.mapDisplayDevice, Game1.viewport, Location.Origin, 4); 114 | Game1.spriteBatch.End(); 115 | } 116 | } 117 | 118 | // Drop the render target 119 | SpriteBatchToolkit.StopRendering(); 120 | 121 | Game1.graphics.GraphicsDevice.Clear(Game1.bgColor); 122 | } 123 | 124 | internal static void RenderMirrorsFurniture() 125 | { 126 | // Set the render target 127 | SpriteBatchToolkit.StartRendering(DynamicReflections.mirrorsFurnitureRenderTarget); 128 | 129 | // Draw the scene 130 | Game1.graphics.GraphicsDevice.Clear(Color.Transparent); 131 | 132 | if (Game1.currentLocation is not null && Game1.currentLocation.furniture is not null) 133 | { 134 | Game1.spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.PointClamp); 135 | foreach (var mirror in DynamicReflections.mirrors.Values.ToList()) 136 | { 137 | if (mirror.IsEnabled is false || mirror.FurnitureLink is null) 138 | { 139 | continue; 140 | } 141 | 142 | foreach (var furniture in Game1.currentLocation.furniture) 143 | { 144 | if (mirror.FurnitureLink != furniture) 145 | { 146 | continue; 147 | } 148 | 149 | DynamicReflections.isFilteringMirror = true; 150 | furniture.draw(Game1.spriteBatch, (int)furniture.TileLocation.X, (int)furniture.TileLocation.Y); 151 | DynamicReflections.isFilteringMirror = false; 152 | break; 153 | } 154 | } 155 | Game1.spriteBatch.End(); 156 | } 157 | 158 | // Drop the render target 159 | SpriteBatchToolkit.StopRendering(); 160 | 161 | Game1.graphics.GraphicsDevice.Clear(Game1.bgColor); 162 | } 163 | 164 | internal static void RenderMirrorReflectionPlayerSprite() 165 | { 166 | var oldPosition = Game1.player.Position; 167 | var oldDirection = Game1.player.FacingDirection; 168 | var oldSprite = Game1.player.FarmerSprite; 169 | 170 | Dictionary modDataCache = new Dictionary(); 171 | foreach (var dataKey in Game1.player.modData.Keys) 172 | { 173 | modDataCache[dataKey] = Game1.player.modData[dataKey]; 174 | } 175 | 176 | // Note: Current solution is to utilize RenderTarget2Ds as the player sprite is composed of many other sprites layered on top of each other 177 | // This makes modifying it via shader difficult and even more so difficult with Fashion Sense (as the size of appearances are not bounded) 178 | 179 | // Draw the raw and flattened player sprites 180 | int index = 0; 181 | foreach (var mirrorPosition in DynamicReflections.activeMirrorPositions) 182 | { 183 | var rawReflectionRender = DynamicReflections.inBetweenRenderTarget; 184 | 185 | // Set the render target 186 | SpriteBatchToolkit.StartRendering(rawReflectionRender); 187 | 188 | // Draw the scene 189 | Game1.graphics.GraphicsDevice.Clear(Color.Transparent); 190 | 191 | Game1.spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.PointClamp); 192 | 193 | var mirror = DynamicReflections.mirrors[mirrorPosition]; 194 | var offsetPosition = mirror.PlayerReflectionPosition; 195 | offsetPosition += mirror.Settings.ReflectionOffset * 16; 196 | 197 | Game1.player.Position = offsetPosition; 198 | Game1.player.FacingDirection = DynamicReflections.GetReflectedDirection(oldDirection, true); 199 | Game1.player.FarmerSprite = oldDirection == 0 ? DynamicReflections.mirrorReflectionSprite : oldSprite; 200 | Game1.player.modData["FashionSense.Animation.FacingDirection"] = Game1.player.FacingDirection.ToString(); 201 | 202 | Game1.player.draw(Game1.spriteBatch); 203 | 204 | Game1.spriteBatch.End(); 205 | 206 | SpriteBatchToolkit.StopRendering(); 207 | 208 | // Now use the rawReflectionRender to flip and apply other effects to them 209 | var composedReflectionRender = DynamicReflections.composedPlayerMirrorReflectionRenders[index]; 210 | 211 | // Set the render target 212 | SpriteBatchToolkit.StartRendering(composedReflectionRender); 213 | 214 | // Draw the scene 215 | Game1.graphics.GraphicsDevice.Clear(Color.Transparent); 216 | 217 | Game1.spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.PointClamp); 218 | 219 | Game1.player.FacingDirection = DynamicReflections.GetReflectedDirection(oldDirection, true); 220 | 221 | // Determine if we should flip the sprite on the X-axis (if facing front or back) 222 | var flipEffect = Game1.player.FacingDirection is (0 or 2) ? SpriteEffects.FlipHorizontally : SpriteEffects.None; 223 | 224 | // This variable (flipOffset) is required to re-adjust the flipped screen (as the player sprite may not be in the center) 225 | var flipOffset = Game1.player.FacingDirection is (0 or 2) ? (Game1.viewport.Width * Game1.options.zoomLevel - Game1.GlobalToLocal(Game1.viewport, Game1.player.Position).X * 2) - 64 : 0f; 226 | 227 | // TODO: Implement these for Mirror.ReflectionScale 228 | var scale = new Vector2(1f, 1f); 229 | var scaleOffset = Vector2.Zero; 230 | 231 | Game1.spriteBatch.Draw(rawReflectionRender, new Vector2(-flipOffset, 0f), rawReflectionRender.Bounds, mirror.Settings.ReflectionOverlay, 0f, scaleOffset, scale, flipEffect, 1f); 232 | 233 | Game1.spriteBatch.End(); 234 | 235 | // Drop the render target 236 | SpriteBatchToolkit.StopRendering(); 237 | 238 | // Now draw the individual furniture mask 239 | var furnitureMaskRender = DynamicReflections.mirrorsFurnitureRenderTarget; 240 | 241 | // Set the render target 242 | SpriteBatchToolkit.StartRendering(furnitureMaskRender); 243 | 244 | // Draw the scene 245 | Game1.graphics.GraphicsDevice.Clear(Color.Transparent); 246 | 247 | if (mirror.IsEnabled is true && mirror.FurnitureLink is not null && Game1.currentLocation is not null && Game1.currentLocation.furniture is not null) 248 | { 249 | Game1.spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.PointClamp); 250 | 251 | foreach (var furniture in Game1.currentLocation.furniture) 252 | { 253 | if (mirror.FurnitureLink != furniture) 254 | { 255 | continue; 256 | } 257 | 258 | DynamicReflections.isFilteringMirror = true; 259 | furniture.draw(Game1.spriteBatch, (int)furniture.TileLocation.X, (int)furniture.TileLocation.Y); 260 | DynamicReflections.isFilteringMirror = false; 261 | break; 262 | } 263 | Game1.spriteBatch.End(); 264 | } 265 | 266 | // Drop the render target 267 | SpriteBatchToolkit.StopRendering(); 268 | 269 | // Now draw the masked version, for use by the furniture 270 | var maskedReflectionRender = DynamicReflections.maskedPlayerMirrorReflectionRenders[index]; 271 | 272 | // Set the render target 273 | SpriteBatchToolkit.StartRendering(maskedReflectionRender); 274 | 275 | // Draw the scene 276 | Game1.graphics.GraphicsDevice.Clear(Color.Transparent); 277 | 278 | DynamicReflections.mirrorReflectionEffect.Parameters["Mask"].SetValue(DynamicReflections.mirrorsFurnitureRenderTarget); 279 | Game1.spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.NonPremultiplied, SamplerState.PointClamp, effect: DynamicReflections.mirrorReflectionEffect); 280 | 281 | Game1.spriteBatch.Draw(composedReflectionRender, Vector2.Zero, Color.White); 282 | 283 | Game1.spriteBatch.End(); 284 | 285 | // Drop the render target 286 | SpriteBatchToolkit.StopRendering(); 287 | 288 | index++; 289 | } 290 | 291 | Game1.player.Position = oldPosition; 292 | Game1.player.FacingDirection = oldDirection; 293 | Game1.player.FarmerSprite = oldSprite; 294 | 295 | // Restore modData for Fashion Sense 296 | foreach (var dataKey in modDataCache.Keys) 297 | { 298 | Game1.player.modData[dataKey] = modDataCache[dataKey]; 299 | } 300 | 301 | Game1.graphics.GraphicsDevice.Clear(Game1.bgColor); 302 | } 303 | 304 | internal static void RenderWaterReflectionNightSky() 305 | { 306 | // Set the render target 307 | SpriteBatchToolkit.StartRendering(DynamicReflections.nightSkyRenderTarget); 308 | 309 | // Draw the scene 310 | Game1.graphics.GraphicsDevice.Clear(Color.Transparent); 311 | 312 | if (Game1.currentLocation is not null && Game1.currentLocation.Map is not null) 313 | { 314 | if (Game1.currentLocation.Map.GetLayer("Back") is var backLayer && backLayer is not null) 315 | { 316 | Game1.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp); 317 | 318 | DynamicReflections.isFilteringSky = true; 319 | LayerPatch.DrawNormalReversePatch(backLayer, Game1.mapDisplayDevice, Game1.viewport, Location.Origin, 4); 320 | DynamicReflections.isFilteringSky = false; 321 | 322 | Game1.spriteBatch.End(); 323 | 324 | if (DynamicReflections.isFilteringWater is true) 325 | { 326 | SpriteBatchToolkit.DrawRenderedCharacters(isWavy: DynamicReflections.currentWaterSettings.IsReflectionWavy); 327 | } 328 | 329 | Game1.spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.PointClamp); 330 | 331 | DynamicReflections.isFilteringStar = true; 332 | LayerPatch.DrawNormalReversePatch(backLayer, Game1.mapDisplayDevice, Game1.viewport, Location.Origin, 4); 333 | DynamicReflections.isFilteringStar = false; 334 | 335 | foreach (var skyEffect in DynamicReflections.skyManager.skyEffectSprites.ToList()) 336 | { 337 | skyEffect.draw(Game1.spriteBatch); 338 | } 339 | Game1.spriteBatch.End(); 340 | } 341 | } 342 | 343 | // Drop the render target 344 | SpriteBatchToolkit.StopRendering(); 345 | 346 | Game1.graphics.GraphicsDevice.Clear(Game1.bgColor); 347 | } 348 | 349 | internal static void DrawNightSky() 350 | { 351 | Game1.spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.PointClamp); 352 | Game1.spriteBatch.Draw(DynamicReflections.nightSkyRenderTarget, Vector2.Zero, Color.White); 353 | Game1.spriteBatch.End(); 354 | } 355 | 356 | internal static void RenderWaterReflectionPlayerSprite() 357 | { 358 | // Set the render target 359 | SpriteBatchToolkit.StartRendering(DynamicReflections.playerWaterReflectionRender); 360 | 361 | // Draw the scene 362 | Game1.graphics.GraphicsDevice.Clear(Color.Transparent); 363 | 364 | DrawReflectionViaMatrix(); 365 | 366 | // Drop the render target 367 | SpriteBatchToolkit.StopRendering(); 368 | 369 | Game1.graphics.GraphicsDevice.Clear(Game1.bgColor); 370 | } 371 | 372 | internal static void RenderWaterReflectionNPCs() 373 | { 374 | if (Game1.currentLocation is null || Game1.currentLocation.characters is null) 375 | { 376 | return; 377 | } 378 | 379 | // Set the render target 380 | SpriteBatchToolkit.StartRendering(DynamicReflections.npcWaterReflectionRender); 381 | 382 | // Draw the scene 383 | Game1.graphics.GraphicsDevice.Clear(Color.Transparent); 384 | 385 | foreach (var npc in DynamicReflections.GetActiveNPCs(Game1.currentLocation)) 386 | { 387 | if (DynamicReflections.npcToWaterReflectionPosition.ContainsKey(npc) is false) 388 | { 389 | continue; 390 | } 391 | 392 | if (DynamicReflections.modConfig.GetCurrentWaterSettings(Game1.currentLocation).ReflectionDirection == Models.Settings.Direction.South) 393 | { 394 | var scale = Matrix.CreateScale(1, -1, 1); 395 | var position = Matrix.CreateTranslation(0, Game1.GlobalToLocal(Game1.viewport, DynamicReflections.npcToWaterReflectionPosition[npc]).Y * 2, 0); 396 | 397 | Game1.spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.PointClamp, rasterizerState: DynamicReflections.rasterizer, transformMatrix: scale * position); 398 | } 399 | else 400 | { 401 | Game1.spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.PointClamp); 402 | } 403 | 404 | npc.draw(Game1.spriteBatch); 405 | 406 | Game1.spriteBatch.End(); 407 | } 408 | 409 | // Drop the render target 410 | SpriteBatchToolkit.StopRendering(); 411 | 412 | Game1.graphics.GraphicsDevice.Clear(Game1.bgColor); 413 | } 414 | 415 | 416 | internal static void RenderPuddleReflectionNPCs() 417 | { 418 | if (Game1.currentLocation is null || Game1.currentLocation.characters is null) 419 | { 420 | return; 421 | } 422 | 423 | // Set the render target 424 | SpriteBatchToolkit.StartRendering(DynamicReflections.npcPuddleReflectionRender); 425 | 426 | // Draw the scene 427 | Game1.graphics.GraphicsDevice.Clear(Color.Transparent); 428 | 429 | foreach (var npc in Game1.currentLocation.characters) 430 | { 431 | var scale = Matrix.CreateScale(1, -1, 1); 432 | var position = Matrix.CreateTranslation(0, Game1.GlobalToLocal(Game1.viewport, npc.Position + DynamicReflections.currentPuddleSettings.NPCReflectionOffset * 64).Y * 2, 0); 433 | 434 | Game1.spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.PointClamp, rasterizerState: DynamicReflections.rasterizer, transformMatrix: scale * position); 435 | 436 | npc.draw(Game1.spriteBatch); 437 | 438 | Game1.spriteBatch.End(); 439 | } 440 | 441 | // Drop the render target 442 | SpriteBatchToolkit.StopRendering(); 443 | 444 | Game1.graphics.GraphicsDevice.Clear(Game1.bgColor); 445 | } 446 | 447 | internal static void DrawPuddleReflection(Texture2D mask) 448 | { 449 | DynamicReflections.mirrorReflectionEffect.Parameters["Mask"].SetValue(mask); 450 | Game1.spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied, SamplerState.PointClamp, effect: DynamicReflections.mirrorReflectionEffect); 451 | 452 | if (DynamicReflections.shouldDrawNightSky) 453 | { 454 | Game1.spriteBatch.Draw(DynamicReflections.nightSkyRenderTarget, Vector2.Zero, Color.White); 455 | } 456 | 457 | Game1.spriteBatch.Draw(DynamicReflections.playerPuddleReflectionRender, Vector2.Zero, DynamicReflections.currentPuddleSettings.ReflectionOverlay); 458 | 459 | Game1.spriteBatch.Draw(DynamicReflections.npcPuddleReflectionRender, Vector2.Zero, DynamicReflections.currentPuddleSettings.ReflectionOverlay); 460 | 461 | Game1.spriteBatch.End(); 462 | } 463 | 464 | internal static void RenderPuddles() 465 | { 466 | // Set the render target 467 | SpriteBatchToolkit.StartRendering(DynamicReflections.puddlesRenderTarget); 468 | 469 | // Draw the scene 470 | Game1.graphics.GraphicsDevice.Clear(Color.Transparent); 471 | 472 | if (Game1.currentLocation is not null && Game1.currentLocation.Map is not null) 473 | { 474 | if (Game1.currentLocation.Map.GetLayer("Back") is var backLayer && backLayer is not null) 475 | { 476 | Game1.spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.PointClamp); 477 | 478 | // Draw the "Back" layer with just the puddles 479 | LayerPatch.DrawNormalReversePatch(backLayer, Game1.mapDisplayDevice, Game1.viewport, Location.Origin, 4); 480 | 481 | Game1.spriteBatch.End(); 482 | } 483 | } 484 | 485 | // Drop the render target 486 | SpriteBatchToolkit.StopRendering(); 487 | 488 | Game1.graphics.GraphicsDevice.Clear(Game1.bgColor); 489 | } 490 | 491 | internal static void RenderPuddleReflectionPlayerSprite() 492 | { 493 | // Set the render target 494 | SpriteBatchToolkit.StartRendering(DynamicReflections.playerPuddleReflectionRender); 495 | 496 | // Draw the scene 497 | Game1.graphics.GraphicsDevice.Clear(Color.Transparent); 498 | 499 | var oldPosition = Game1.player.Position; 500 | var oldDirection = Game1.player.FacingDirection; 501 | var oldSprite = Game1.player.FarmerSprite; 502 | 503 | var scale = Matrix.CreateScale(1, -1, 1); 504 | var position = Matrix.CreateTranslation(0, Game1.GlobalToLocal(Game1.viewport, oldPosition).Y * 2, 0); 505 | 506 | Game1.spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.PointClamp, rasterizerState: DynamicReflections.rasterizer, transformMatrix: scale * position); 507 | 508 | var targetPosition = Game1.player.Position; 509 | targetPosition -= DynamicReflections.currentPuddleSettings.ReflectionOffset * 64f; 510 | Game1.player.Position = targetPosition; 511 | 512 | Game1.player.draw(Game1.spriteBatch); 513 | 514 | Game1.player.Position = oldPosition; 515 | Game1.player.FacingDirection = oldDirection; 516 | Game1.player.FarmerSprite = oldSprite; 517 | 518 | Game1.spriteBatch.End(); 519 | 520 | Game1.spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.PointClamp); 521 | 522 | foreach (var rippleSprite in DynamicReflections.puddleManager.puddleRippleSprites.ToList()) 523 | { 524 | rippleSprite.draw(Game1.spriteBatch); 525 | } 526 | 527 | Game1.spriteBatch.End(); 528 | 529 | // Drop the render target 530 | SpriteBatchToolkit.StopRendering(); 531 | 532 | Game1.graphics.GraphicsDevice.Clear(Game1.bgColor); 533 | } 534 | 535 | internal static void DrawReflectionViaMatrix() 536 | { 537 | var oldPosition = Game1.player.Position; 538 | var oldDirection = Game1.player.FacingDirection; 539 | var oldSprite = Game1.player.FarmerSprite; 540 | 541 | if (DynamicReflections.modConfig.GetCurrentWaterSettings(Game1.currentLocation).ReflectionDirection == Models.Settings.Direction.South) 542 | { 543 | var scale = Matrix.CreateScale(1, -1, 1); 544 | var position = Matrix.CreateTranslation(0, Game1.GlobalToLocal(Game1.viewport, DynamicReflections.waterReflectionPosition.Value).Y * 2, 0); 545 | 546 | Game1.spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.PointClamp, rasterizerState: DynamicReflections.rasterizer, transformMatrix: scale * position); 547 | } 548 | else 549 | { 550 | Game1.spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.PointClamp); 551 | 552 | Game1.player.FacingDirection = DynamicReflections.GetReflectedDirection(oldDirection, true); 553 | Game1.player.FarmerSprite = oldDirection == 0 ? DynamicReflections.mirrorReflectionSprite : oldSprite; 554 | Game1.player.modData["FashionSense.Animation.FacingDirection"] = Game1.player.FacingDirection.ToString(); 555 | } 556 | Game1.player.Position = DynamicReflections.waterReflectionPosition.Value; 557 | 558 | Game1.player.draw(Game1.spriteBatch); 559 | 560 | Game1.player.Position = oldPosition; 561 | Game1.player.FacingDirection = oldDirection; 562 | Game1.player.FarmerSprite = oldSprite; 563 | 564 | Game1.spriteBatch.End(); 565 | } 566 | 567 | internal static void DrawRenderedCharacters(bool isWavy = false) 568 | { 569 | if (DynamicReflections.shouldDrawWaterReflection is true) 570 | { 571 | DynamicReflections.waterReflectionEffect.Parameters["ColorOverlay"].SetValue(DynamicReflections.modConfig.WaterReflectionSettings.ReflectionOverlay.ToVector4()); 572 | Game1.spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.PointClamp, effect: isWavy ? DynamicReflections.waterReflectionEffect : null); 573 | Game1.spriteBatch.Draw(DynamicReflections.playerWaterReflectionRender, Vector2.Zero, DynamicReflections.modConfig.GetCurrentWaterSettings(Game1.currentLocation).ReflectionOverlay); 574 | Game1.spriteBatch.End(); 575 | } 576 | 577 | if (DynamicReflections.modConfig.AreNPCReflectionsEnabled is true) 578 | { 579 | Game1.spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.PointClamp); 580 | Game1.spriteBatch.Draw(DynamicReflections.npcWaterReflectionRender, Vector2.Zero, DynamicReflections.modConfig.GetCurrentWaterSettings(Game1.currentLocation).ReflectionOverlay); 581 | Game1.spriteBatch.End(); 582 | } 583 | } 584 | 585 | internal static void HandleBackgroundDraw() 586 | { 587 | if (Game1.background is not null) 588 | { 589 | Game1.background.draw(Game1.spriteBatch); 590 | } 591 | else if (Game1.currentLocation is not null) 592 | { 593 | Game1.currentLocation.drawBackground(Game1.spriteBatch); 594 | } 595 | } 596 | } 597 | } 598 | -------------------------------------------------------------------------------- /DynamicReflections/i18n/de.json: -------------------------------------------------------------------------------- 1 | { 2 | // Config 3 | "config.general_settings.title": "Allgemeine Einstellungen", 4 | "config.general_settings.water_reflections": "Wasserreflektionen aktivieren", 5 | "config.general_settings.mirror_reflections": "Spiegelreflektionen aktivieren", 6 | "config.general_settings.puddle_reflections": "Pfützenreflektionen aktivieren", 7 | "config.general_settings.npc_reflections": "NPC-Reflektionen aktivieren", 8 | "config.general_settings.sky_reflections": "Himmelreflektionen aktivieren", 9 | "config.general_settings.link.click_here": "Hier klicken", 10 | "config.general_settings.link.return_main": "Zurück zur Hauptseite", 11 | "config.general_settings.override_default_settings": "Standardeinstellungen überbrücken", 12 | "config.general_settings.override_default_settings.description": "Falls aktiviert wird dieser Ort die Standardeinstellungen ignorieren. Wird ignoriert wenn am 'Standard'-Ort verwendet.", 13 | "config.general_settings.shortcut_key": "Tastenkürzel für Einstellungen", 14 | "config.general_settings.shortcut_key.description": "Dieses Menü erscheint für Schnellkonfigurationen, wenn die zugewiesene Taste gedrückt wird.", 15 | "config.general_settings.effects": "Effekte", 16 | 17 | "config.general_settings.reflection_offets": "Reflektions-Einstellungen", 18 | "config.general_settings.player_offsets": "Spieler-Reflektion: Offsets", 19 | "config.general_settings.npc_offsets": "NPC-Reflektion: Offsets", 20 | "config.water_settings.offset.x": "X-Offset", 21 | "config.water_settings.offset.y": "Y-Offset", 22 | 23 | "config.location_specific.title": "Ortsspezifische Einstellungen", 24 | "config.location_specific.selector": "Zielort", 25 | "config.location_specific.description": "'Standard' verwenden, um die Standardeinstellungen für alle Orte anzupassen.", 26 | 27 | "config.sky_settings.title": "Himmelsspezifische Einstellungen", 28 | "config.sky_settings.link": "Zu den Himmels-Einstellungen", 29 | "config.sky_settings.shooting_stars_enabled": "Sternschnuppen aktivieren", 30 | "config.sky_settings.star_density": "Sternendichte-Prozentsatz", 31 | "config.sky_settings.meteor_shower_chance": "Chance für Meteorschauer", 32 | "config.sky_settings.meteor_shower_chance.description": "Dieser Prozentsatz gilt für alle Orte.", 33 | "config.sky_settings.shooting_stars.title": "Sternschnuppen-Einstellungen", 34 | "config.sky_settings.max_shooting_stars_per_attempt": "Maximale Anzahl an Sternschnuppen", 35 | "config.sky_settings.max_shooting_stars_per_attempt.description": "Maximale Menge an Sternschnuppen, die pro Intervall generiert werden soll", 36 | "config.sky_settings.comet_chance": "Chance für Kometen", 37 | "config.sky_settings.milliseconds_between_stars": "Pause zwischen Sternschnuppen", 38 | "config.sky_settings.milliseconds_between_stars.description": "Millisekunden zwischen Sternschnuppen.", 39 | "config.sky_settings.milliseconds_between_stars_during_meteor_shower": "Pause zwischen Sternschnuppen (Meteorschauer)", 40 | "config.sky_settings.shooting_star_speed.min": "Sternschnuppen-Geschwindigkeit (Min.)", 41 | "config.sky_settings.shooting_star_speed.max": "Sternschnuppen-Geschwindigkeit (Max.)", 42 | "config.sky_settings.comet.min": "Kometengeschwindigkeit (Min.)", 43 | "config.sky_settings.comet.max": "Kometengeschwindigkeit (Max.)", 44 | "config.sky_settings.comet_segment.min": "Min. Kometensegmente", 45 | "config.sky_settings.comet_segment.max": "Max. Kometensegmente", 46 | "config.sky_settings.star_reflections.title": "Sternenreflektions-Einstellungen", 47 | "config.sky_settings.water_alpha.getting_dark": "Wasser-Alpha (Ausdunkeln zu Beginn)", 48 | "config.sky_settings.water_alpha.halfway_dark": "Wasser-Alpha (Ausdunkeln zur Hälfte)", 49 | "config.sky_settings.water_alpha.finished_dark": "Wasser-Alpha (Ausdunkeln gegen Ende)", 50 | "config.sky_settings.water_alpha.description": "Transparenz des Wasserwelleneffekts für jede Phase eines Sonnenuntergangs", 51 | 52 | "config.water_settings.title": "Wasserspezifische Einstellungen", 53 | "config.water_settings.link": "Zu den Wasser-Einstellungen", 54 | "config.water_settings.reflection_direction": "Reflektionsrichtung", 55 | "config.water_settings.reflection_direction.description": "0 für nördliche, 2 für südliche Reflektionen", 56 | "config.water_settings.color.title": "Overlay-Farbe für Reflektionen", 57 | "config.water_settings.color.r": "Reflektion: Overlay-Farbe (R)", 58 | "config.water_settings.color.g": "Reflektion: Overlay-Farbe (G)", 59 | "config.water_settings.color.b": "Reflektion: Overlay-Farbe (B)", 60 | "config.water_settings.color.a": "Reflektion: Overlay-Farbe (A)", 61 | "config.water_settings.is_wavy": "Wellenförmige Reflektionen aktivieren", 62 | "config.water_settings.wave_speed": "Wellengeschwindigkeit", 63 | "config.water_settings.wave_amplitude": "Wellenamplitude", 64 | "config.water_settings.wave_frequency": "Wellenfrequenz", 65 | 66 | "config.puddle_settings.title": "Pfützenspezifische Einstellungen", 67 | "config.puddle_settings.link": "Zu den Pfützen-Einstellungen", 68 | "config.puddle_settings.should_generate_puddles": "Pfützen generieren", 69 | "config.puddle_settings.should_play_splash_sound": "Spritz-Sound für Pfützen verwenden", 70 | "config.puddle_settings.should_rain_splash_puddles": "Regen verursacht Spritzer in Pfützen", 71 | "config.puddle_settings.puddle_percentage_while_raining": "Pfützen-Prozentsatz während Regen", 72 | "config.puddle_settings.puddle_percentage_after_raining": "Pfützen-Prozentsatz nach Regen", 73 | "config.puddle_settings.big_puddle_chance": "Chance für große Pfützen", 74 | "config.puddle_settings.milliseconds_between_raindrop_splashes": "Pause zwischen Regentropfen-Spritzern", 75 | "config.puddle_settings.milliseconds_between_raindrop_splashes_description": "Millisekunden zwischen Regentropfen-Spritzern", 76 | "config.puddle_settings.puddle_color.title": "Pfützenfarbe", 77 | "config.puddle_settings.puddle_color.r": "Pfützenfarbe (R)", 78 | "config.puddle_settings.puddle_color.g": "Pfützenfarbe (G)", 79 | "config.puddle_settings.puddle_color.b": "Pfützenfarbe (B)", 80 | "config.puddle_settings.puddle_color.a": "Pfützenfarbe (A)", 81 | "config.puddle_settings.ripple_color.title": "Kräuselfarbe", 82 | "config.puddle_settings.ripple_color.r": "Kräuselfarbe (R)", 83 | "config.puddle_settings.ripple_color.g": "Kräuselfarbe (G)", 84 | "config.puddle_settings.ripple_color.b": "Kräuselfarbe (B)", 85 | "config.puddle_settings.ripple_color.a": "Kräuselfarbe (A)" 86 | } 87 | -------------------------------------------------------------------------------- /DynamicReflections/i18n/default.json: -------------------------------------------------------------------------------- 1 | { 2 | // Config 3 | "config.general_settings.title": "General Settings", 4 | "config.general_settings.water_reflections": "Enable Water Reflections", 5 | "config.general_settings.mirror_reflections": "Enable Mirror Reflections", 6 | "config.general_settings.puddle_reflections": "Enable Puddle Reflections", 7 | "config.general_settings.npc_reflections": "Enable NPC Reflections", 8 | "config.general_settings.sky_reflections": "Enable Sky Reflections", 9 | "config.general_settings.link.click_here": "Click Here", 10 | "config.general_settings.link.return_main": "Return to the Main Page", 11 | "config.general_settings.override_default_settings": "Override Default Settings", 12 | "config.general_settings.override_default_settings.description": "If enabled, this location will ignore the default settings. Ignored when used on the 'Default' location.", 13 | "config.general_settings.shortcut_key": "Settings Shortcut Key", 14 | "config.general_settings.shortcut_key.description": "When pressing the given key, this menu will appear for quick configurations.", 15 | "config.general_settings.effects": "Effects", 16 | 17 | "config.general_settings.reflection_offets": "Reflection Settings", 18 | "config.general_settings.player_offsets": "Player Reflection Offsets", 19 | "config.general_settings.npc_offsets": "NPC Reflection Offsets", 20 | "config.water_settings.offset.x": "X Offset", 21 | "config.water_settings.offset.y": "Y Offset", 22 | 23 | "config.location_specific.title": "Location Specific Settings", 24 | "config.location_specific.selector": "Target Location", 25 | "config.location_specific.description": "Use `Default` to modify the default settings for all location.", 26 | 27 | "config.sky_settings.title": "Sky Specific Settings", 28 | "config.sky_settings.link": "Go to Sky Settings", 29 | "config.sky_settings.shooting_stars_enabled": "Enable Shooting Stars", 30 | "config.sky_settings.star_density": "Star Density Percentage", 31 | "config.sky_settings.meteor_shower_chance": "Meteor Shower Chance", 32 | "config.sky_settings.meteor_shower_chance.description": "This percentage is shared across all locations.", 33 | "config.sky_settings.shooting_stars.title": "Shooting Stars Settings", 34 | "config.sky_settings.max_shooting_stars_per_attempt": "Max Amount of Shooting Stars", 35 | "config.sky_settings.max_shooting_stars_per_attempt.description": "The max amount of shooting stars to generate per interval.", 36 | "config.sky_settings.comet_chance": "Comet Chance", 37 | "config.sky_settings.milliseconds_between_stars": "Shooting Stars Cooldown", 38 | "config.sky_settings.milliseconds_between_stars.description": "The milliseconds between shooting stars.", 39 | "config.sky_settings.milliseconds_between_stars_during_meteor_shower": "Shooting Stars Cooldown (Meteor Shower)", 40 | "config.sky_settings.shooting_star_speed.min": "Shooting Stars Speed (Min)", 41 | "config.sky_settings.shooting_star_speed.max": "Shooting Stars Speed (Max)", 42 | "config.sky_settings.comet.min": "Comet Speed (Min)", 43 | "config.sky_settings.comet.max": "Comet Speed (Max)", 44 | "config.sky_settings.comet_segment.min": "Min Comet Segments", 45 | "config.sky_settings.comet_segment.max": "Max Comet Segments", 46 | "config.sky_settings.star_reflections.title": "Star Reflection Settings", 47 | "config.sky_settings.water_alpha.getting_dark": "Water Alpha (Starting Dark Out)", 48 | "config.sky_settings.water_alpha.halfway_dark": "Water Alpha (Halfway Dark Out)", 49 | "config.sky_settings.water_alpha.finished_dark": "Water Alpha (Finished Dark Out)", 50 | "config.sky_settings.water_alpha.description": "Determines the transparency of the water's wave effect for each stage of the sun setting.", 51 | 52 | "config.water_settings.title": "Water Specific Settings", 53 | "config.water_settings.link": "Go to Water Settings", 54 | "config.water_settings.reflection_direction": "Reflection Direction", 55 | "config.water_settings.reflection_direction.description": "0 for North reflections, 2 for South reflections.", 56 | "config.water_settings.color.title": "Reflection Overlay Color", 57 | "config.water_settings.color.r": "Reflection Overlay Color (R)", 58 | "config.water_settings.color.g": "Reflection Overlay Color (G)", 59 | "config.water_settings.color.b": "Reflection Overlay Color (B)", 60 | "config.water_settings.color.a": "Reflection Overlay Color (A)", 61 | "config.water_settings.is_wavy": "Enable Wavy Reflections", 62 | "config.water_settings.wave_speed": "Wave Speed", 63 | "config.water_settings.wave_amplitude": "Wave Amplitude", 64 | "config.water_settings.wave_frequency": "Wave Frequency", 65 | 66 | "config.puddle_settings.title": "Puddle Specific Settings", 67 | "config.puddle_settings.link": "Go to Puddle Settings", 68 | "config.puddle_settings.should_generate_puddles": "Should Generate Puddles", 69 | "config.puddle_settings.should_play_splash_sound": "Should Play Splash Sound", 70 | "config.puddle_settings.should_rain_splash_puddles": "Should Rain Splash Puddles", 71 | "config.puddle_settings.puddle_percentage_while_raining": "Puddle Percentage While Raining", 72 | "config.puddle_settings.puddle_percentage_after_raining": "Puddle Percentage After Raining", 73 | "config.puddle_settings.big_puddle_chance": "Big Puddle Chance", 74 | "config.puddle_settings.milliseconds_between_raindrop_splashes": "Raindrop Splashes Cooldown", 75 | "config.puddle_settings.milliseconds_between_raindrop_splashes_description": "The milliseconds between raindrop splashes.", 76 | "config.puddle_settings.puddle_color.title": "Puddle Color", 77 | "config.puddle_settings.puddle_color.r": "Puddle Color (R)", 78 | "config.puddle_settings.puddle_color.g": "Puddle Color (G)", 79 | "config.puddle_settings.puddle_color.b": "Puddle Color (B)", 80 | "config.puddle_settings.puddle_color.a": "Puddle Color (A)", 81 | "config.puddle_settings.ripple_color.title": "Ripple Color", 82 | "config.puddle_settings.ripple_color.r": "Ripple Color (R)", 83 | "config.puddle_settings.ripple_color.g": "Ripple Color (G)", 84 | "config.puddle_settings.ripple_color.b": "Ripple Color (B)", 85 | "config.puddle_settings.ripple_color.a": "Ripple Color (A)" 86 | } -------------------------------------------------------------------------------- /DynamicReflections/i18n/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | // Config 3 | "config.general_settings.title": "Paramètres généraux", 4 | "config.general_settings.water_reflections": "Reflets de l'eau", 5 | "config.general_settings.mirror_reflections": "Reflets dans les miroirs", 6 | "config.general_settings.puddle_reflections": "Reflets dans les flaques", 7 | "config.general_settings.npc_reflections": "Reflets des PNJ", 8 | "config.general_settings.sky_reflections": "Reflets du ciel", 9 | "config.general_settings.link.click_here": "Cliquez ici", 10 | "config.general_settings.link.return_main": "Retour à la page principale", 11 | "config.general_settings.override_default_settings": "Ignorer les paramètres par défaut", 12 | "config.general_settings.override_default_settings.description": "Si activé, cet emplacement ignorera les paramètres par défaut. Ignoré lorsqu'utilisé sur l'emplacement 'Par défaut'.", 13 | "config.general_settings.shortcut_key": "Raccourci pour les paramètres", 14 | "config.general_settings.shortcut_key.description": "Lorsque vous appuyez sur la touche donnée, ce menu apparaîtra pour des configurations rapides.", 15 | "config.general_settings.effects": "Effets", 16 | 17 | "config.general_settings.reflection_offets": "Paramètres des reflets", 18 | "config.general_settings.player_offsets": "Décalages des reflets du joueur", 19 | "config.general_settings.npc_offsets": "Décalages des reflets des PNJ", 20 | "config.water_settings.offset.x": "Décalage X", 21 | "config.water_settings.offset.y": "Décalage Y", 22 | 23 | "config.location_specific.title": "Paramètres spécifiques à l'emplacement", 24 | "config.location_specific.selector": "Emplacement cible", 25 | "config.location_specific.description": "Utilisez ’Par défaut’ pour modifier les paramètres par défaut de tous les emplacements.", 26 | 27 | "config.sky_settings.title": "Paramètres du Ciel", 28 | "config.sky_settings.link": "Aller aux paramètres du ciel", 29 | "config.sky_settings.shooting_stars_enabled": "Activer les étoiles filantes", 30 | "config.sky_settings.star_density": "Pourcentage de densité des étoiles", 31 | "config.sky_settings.meteor_shower_chance": "Probabilité de pluie de météores", 32 | "config.sky_settings.meteor_shower_chance.description": "Ce pourcentage est partagé entre toutes les zones.", 33 | "config.sky_settings.shooting_stars.title": "Paramètres des étoiles filantes", 34 | "config.sky_settings.max_shooting_stars_per_attempt": "Nombre maximal d'étoiles filantes", 35 | "config.sky_settings.max_shooting_stars_per_attempt.description": "Le nombre maximal d'étoiles filantes à générer par intervalle.", 36 | "config.sky_settings.comet_chance": "Probabilité de comète", 37 | "config.sky_settings.milliseconds_between_stars": "Délai entre les étoiles filantes", 38 | "config.sky_settings.milliseconds_between_stars.description": "Les millisecondes entre les étoiles filantes.", 39 | "config.sky_settings.milliseconds_between_stars_during_meteor_shower": "Délai entre les étoiles filantes\n(pluie de météores)", 40 | "config.sky_settings.shooting_star_speed.min": "Vitesse min des étoiles filantes", 41 | "config.sky_settings.shooting_star_speed.max": "Vitesse max des étoiles filantes", 42 | "config.sky_settings.comet.min": "Vitesse min des comètes", 43 | "config.sky_settings.comet.max": "Vitesse max des comètes", 44 | "config.sky_settings.comet_segment.min": "Segments min de comète", 45 | "config.sky_settings.comet_segment.max": "Segments max de comète", 46 | "config.sky_settings.star_reflections.title": "Paramètres de reflets des étoiles", 47 | "config.sky_settings.water_alpha.getting_dark": "Alpha de l'eau (début du crépuscule)", 48 | "config.sky_settings.water_alpha.halfway_dark": "Alpha de l'eau (mi-crépuscule)", 49 | "config.sky_settings.water_alpha.finished_dark": "Alpha de l'eau (fin du crépuscule)", 50 | "config.sky_settings.water_alpha.description": "Détermine la transparence de l'effet de vague de l'eau pour chaque étape du coucher du soleil.", 51 | 52 | "config.water_settings.title": "Paramètres spécifiques à l'eau", 53 | "config.water_settings.link": "Aller aux paramètres de l'eau", 54 | "config.water_settings.reflection_direction": "Direction des reflets", 55 | "config.water_settings.reflection_direction.description": "0 pour les reflets au nord (North), 2 pour les reflets au sud (South).", 56 | "config.water_settings.color.title": "Couleur du reflet", 57 | "config.water_settings.color.r": "Couleur du reflet (R)", 58 | "config.water_settings.color.g": "Couleur du reflet (V)", 59 | "config.water_settings.color.b": "Couleur du reflet (B)", 60 | "config.water_settings.color.a": "Couleur du reflet (A)", 61 | "config.water_settings.is_wavy": "Activer les reflets ondulés", 62 | "config.water_settings.wave_speed": "Vitesse des vagues", 63 | "config.water_settings.wave_amplitude": "Amplitude des vagues", 64 | "config.water_settings.wave_frequency": "Fréquence des vagues", 65 | 66 | "config.puddle_settings.title": "Paramètres spécifiques aux flaques", 67 | "config.puddle_settings.link": "Aller aux paramètres des flaques", 68 | "config.puddle_settings.should_generate_puddles": "Générer des flaques", 69 | "config.puddle_settings.should_play_splash_sound": "Jouer le son d'éclaboussure", 70 | "config.puddle_settings.should_rain_splash_puddles": "Éclaboussures de pluie dans\nles flaques", 71 | "config.puddle_settings.puddle_percentage_while_raining": "Pourcentage de flaques pendant pluie", 72 | "config.puddle_settings.puddle_percentage_after_raining": "Pourcentage de flaques après la pluie", 73 | "config.puddle_settings.big_puddle_chance": "Probabilité de grandes flaques", 74 | "config.puddle_settings.milliseconds_between_raindrop_splashes": "Délai entre les éclaboussures", 75 | "config.puddle_settings.milliseconds_between_raindrop_splashes_description": "Les millisecondes entre les éclaboussures de gouttes de pluie.", 76 | "config.puddle_settings.puddle_color.title": "Couleur des flaques", 77 | "config.puddle_settings.puddle_color.r": "Couleur des flaques (R)", 78 | "config.puddle_settings.puddle_color.g": "Couleur des flaques (V)", 79 | "config.puddle_settings.puddle_color.b": "Couleur des flaques (B)", 80 | "config.puddle_settings.puddle_color.a": "Couleur des flaques (A)", 81 | "config.puddle_settings.ripple_color.title": "Couleur des ondulations", 82 | "config.puddle_settings.ripple_color.r": "Couleur des ondulations (R)", 83 | "config.puddle_settings.ripple_color.g": "Couleur des ondulations (V)", 84 | "config.puddle_settings.ripple_color.b": "Couleur des ondulations (B)", 85 | "config.puddle_settings.ripple_color.a": "Couleur des ondulations (A)" 86 | } 87 | -------------------------------------------------------------------------------- /DynamicReflections/i18n/ja.json: -------------------------------------------------------------------------------- 1 | { 2 | // Config 3 | "config.general_settings.title": "基本設定", 4 | "config.general_settings.water_reflections": "水の反射を有効", 5 | "config.general_settings.mirror_reflections": "鏡の反射を有効", 6 | "config.general_settings.puddle_reflections": "水たまりの反射を有効", 7 | "config.general_settings.npc_reflections": "NPCの反射を有効", 8 | "config.general_settings.sky_reflections": "空の反射を有効", 9 | "config.general_settings.link.click_here": "ここをクリック", 10 | "config.general_settings.link.return_main": "メインページに戻る", 11 | "config.general_settings.override_default_settings": "デフォルト設定を上書きする", 12 | "config.general_settings.override_default_settings.description": "このオプションを選択すると、デフォルト設定は無視されます。「デフォルト」の場所で使用すると無視されます。", 13 | "config.general_settings.shortcut_key": "ショートカットキー設定", 14 | "config.general_settings.shortcut_key.description": "指定したキーを押すとメニューが開きます。", 15 | "config.general_settings.effects": "エフェクト", 16 | 17 | "config.general_settings.reflection_offets": "反射設定", 18 | "config.general_settings.player_offsets": "プレイヤー反射オフセット", 19 | "config.general_settings.npc_offsets": "NPC反射オフセット", 20 | "config.water_settings.offset.x": "X オフセット", 21 | "config.water_settings.offset.y": "Y オフセット", 22 | 23 | "config.location_specific.title": "ロケーション設定", 24 | "config.location_specific.selector": "ターゲットロケーション", 25 | "config.location_specific.description": "すべての場所をデフォルト設定に変更するには、`デフォルト`を使用します。", 26 | 27 | "config.sky_settings.title": "空の設定", 28 | "config.sky_settings.link": "空の設定に移動", 29 | "config.sky_settings.shooting_stars_enabled": "流れ星を有効", 30 | "config.sky_settings.star_density": "星の密度", 31 | "config.sky_settings.meteor_shower_chance": "流星群の出現率", 32 | "config.sky_settings.meteor_shower_chance.description": "この割合は全ロケーションで共有されます。", 33 | "config.sky_settings.shooting_stars.title": "流れ星の設定", 34 | "config.sky_settings.max_shooting_stars_per_attempt": "流れ星の最大数", 35 | "config.sky_settings.max_shooting_stars_per_attempt.description": "一回に生成される流れ星の最大量です", 36 | "config.sky_settings.comet_chance": "すい星の出現率", 37 | "config.sky_settings.milliseconds_between_stars": "流れ星の間隔", 38 | "config.sky_settings.milliseconds_between_stars.description": "次の流れ星が落ちるまでの間隔(ms)", 39 | "config.sky_settings.milliseconds_between_stars_during_meteor_shower": "流星群の間隔", 40 | "config.sky_settings.shooting_star_speed.min": "流れ星の速度(最小)", 41 | "config.sky_settings.shooting_star_speed.max": "流れ星の速度(最大)", 42 | "config.sky_settings.comet.min": "すい星の速度 (最小)", 43 | "config.sky_settings.comet.max": "すい星の速度 (最大)", 44 | "config.sky_settings.comet_segment.min": "すい星の最小部分", 45 | "config.sky_settings.comet_segment.max": "すい星の最大部分", 46 | "config.sky_settings.star_reflections.title": "星の反射の設定", 47 | "config.sky_settings.water_alpha.getting_dark": "水に映る夜空の透明度 (夕方)", 48 | "config.sky_settings.water_alpha.halfway_dark": "水に映る夜空の透明度 (日没中)", 49 | "config.sky_settings.water_alpha.finished_dark": "水に映る夜空の透明度 (日没時)", 50 | "config.sky_settings.water_alpha.description": "太陽が沈む各段階ごとに波の透明度を決定します。", 51 | 52 | "config.water_settings.title": "水の設定", 53 | "config.water_settings.link": "水の設定に移動", 54 | "config.water_settings.reflection_direction": "反射方向", 55 | "config.water_settings.reflection_direction.description": "北側反射の場合は0、南側反射の場合は2。", 56 | "config.water_settings.color.title": "反射オーバーレイカラー", 57 | "config.water_settings.color.r": "反射オーバーレイカラー (R)", 58 | "config.water_settings.color.g": "反射オーバーレイカラー (G)", 59 | "config.water_settings.color.b": "反射オーバーレイカラー (B)", 60 | "config.water_settings.color.a": "反射オーバーレイカラー (A)", 61 | "config.water_settings.is_wavy": "波状の反射を有効", 62 | "config.water_settings.wave_speed": "波の速度", 63 | "config.water_settings.wave_amplitude": "波の振幅", 64 | "config.water_settings.wave_frequency": "波の頻度", 65 | 66 | "config.puddle_settings.title": "水たまりの設定", 67 | "config.puddle_settings.link": "水たまりの設定に移動", 68 | "config.puddle_settings.should_generate_puddles": "水たまりの生成", 69 | "config.puddle_settings.should_play_splash_sound": "水たまりを歩いた際に音を鳴らす", 70 | "config.puddle_settings.should_rain_splash_puddles": "雨粒での水しぶき", 71 | "config.puddle_settings.puddle_percentage_while_raining": "雨天時の水たまりの割合", 72 | "config.puddle_settings.puddle_percentage_after_raining": "雨上がりの水たまりの割合", 73 | "config.puddle_settings.big_puddle_chance": "大きい水たまりの確率", 74 | "config.puddle_settings.milliseconds_between_raindrop_splashes": "雨粒での水しぶきの間隔", 75 | "config.puddle_settings.milliseconds_between_raindrop_splashes_description": "雨粒で水しぶきがあがる間隔(ms)", 76 | "config.puddle_settings.puddle_color.title": "水たまりの色", 77 | "config.puddle_settings.puddle_color.r": "水たまりの色 (R)", 78 | "config.puddle_settings.puddle_color.g": "水たまりの色 (G)", 79 | "config.puddle_settings.puddle_color.b": "水たまりの色 (B)", 80 | "config.puddle_settings.puddle_color.a": "水たまりの色 (A)", 81 | "config.puddle_settings.ripple_color.title": "波の色", 82 | "config.puddle_settings.ripple_color.r": "波の色 (R)", 83 | "config.puddle_settings.ripple_color.g": "波の色 (G)", 84 | "config.puddle_settings.ripple_color.b": "波の色 (B)", 85 | "config.puddle_settings.ripple_color.a": "波の色 (A)" 86 | } -------------------------------------------------------------------------------- /DynamicReflections/i18n/ko.json: -------------------------------------------------------------------------------- 1 | { 2 | // Config 3 | "config.general_settings.title": "일반 설정", 4 | "config.general_settings.water_reflections": "물 반사 활성화", 5 | "config.general_settings.mirror_reflections": "거울 반사 활성화", 6 | "config.general_settings.puddle_reflections": "물웅덩이 반사 활성화", 7 | "config.general_settings.npc_reflections": "NPC 반사 활성화", 8 | "config.general_settings.sky_reflections": "하늘 반사 활성화", 9 | "config.general_settings.link.click_here": "여기를 클릭하세요", 10 | "config.general_settings.link.return_main": "메인 화면으로 돌아가기", 11 | "config.general_settings.override_default_settings": "기본 설정 재설정", 12 | "config.general_settings.override_default_settings.description": "이 옵션을 선택하면 기본 설정이 무시됩니다. '기본값' 위치에서 사용할 때 무시됩니다.", 13 | "config.general_settings.shortcut_key": "설정 바로가기 키", 14 | "config.general_settings.shortcut_key.description": "지정된 키를 누르면 빠른 구성을 위한 메뉴가 나타납니다.", 15 | "config.general_settings.effects": "효과", 16 | 17 | "config.general_settings.reflection_offets": "반사 설정", 18 | "config.general_settings.player_offsets": "플레이어 반사 지점", 19 | "config.general_settings.npc_offsets": "NPC 반사 지점", 20 | "config.water_settings.offset.x": "X 지점", 21 | "config.water_settings.offset.y": "Y 지점", 22 | 23 | "config.location_specific.title": "위치별 설정", 24 | "config.location_specific.selector": "대상 위치", 25 | "config.location_specific.description": "'기본값'을 사용하여 모든 위치에 대한 기본 설정을 조정합니다.", 26 | 27 | "config.sky_settings.title": "하늘 별 설정", 28 | "config.sky_settings.link": "하늘 설정으로 이동", 29 | "config.sky_settings.shooting_stars_enabled": "유성 활성화", 30 | "config.sky_settings.star_density": "별의 밀도 비율", 31 | "config.sky_settings.meteor_shower_chance": "유성이 떨어질 확률", 32 | "config.sky_settings.meteor_shower_chance.description": "이 확률은 모든 장소에서 적용됩니다.", 33 | "config.sky_settings.shooting_stars.title": "유성 설정", 34 | "config.sky_settings.max_shooting_stars_per_attempt": "유성의 최대 수량", 35 | "config.sky_settings.max_shooting_stars_per_attempt.description": "유성이 떨어지는 간격당 생성될 유성의 최대 수량입니다.", 36 | "config.sky_settings.comet_chance": "별의 생성 확률", 37 | "config.sky_settings.milliseconds_between_stars": "유성이 떨어지는 사이의 대기 시간", 38 | "config.sky_settings.milliseconds_between_stars.description": "유성이 떨어지는 사이의 시간(ms)", 39 | "config.sky_settings.milliseconds_between_stars_during_meteor_shower": "유성이 떨어지는 대기 시간(유성우)", 40 | "config.sky_settings.shooting_star_speed.min": "유성이 떨어지는 속도 (최소)", 41 | "config.sky_settings.shooting_star_speed.max": "유성이 떨어지는 속도 (최대)", 42 | "config.sky_settings.comet.min": "별의 속도 (최소)", 43 | "config.sky_settings.comet.max": "별의 속도 (최대)", 44 | "config.sky_settings.comet_segment.min": "별의 최소 부분", 45 | "config.sky_settings.comet_segment.max": "별의 최대 부분", 46 | "config.sky_settings.star_reflections.title": "별의 반사 설정", 47 | "config.sky_settings.water_alpha.getting_dark": "물에 비친 밤하늘 (일몰 시작)", 48 | "config.sky_settings.water_alpha.halfway_dark": "물에 비친 밤하늘 (일몰 중간)", 49 | "config.sky_settings.water_alpha.finished_dark": "물에 비친 밤하늘 (일몰 끝)", 50 | "config.sky_settings.water_alpha.description": "일몰의 각 단계에 대한 물결 효과의 투명도를 결정합니다.", 51 | 52 | "config.water_settings.title": "물 관련 설정", 53 | "config.water_settings.link": "물 설정으로 이동", 54 | "config.water_settings.reflection_direction": "반사 방향", 55 | "config.water_settings.reflection_direction.description": "북쪽 반사는 0, 남쪽 반사는 2입니다.", 56 | "config.water_settings.color.title": "반사 오버레이 색상", 57 | "config.water_settings.color.r": "반사 오버레이 색상 (R)", 58 | "config.water_settings.color.g": "반사 오버레이 색상 (G)", 59 | "config.water_settings.color.b": "반사 오버레이 색상 (B)", 60 | "config.water_settings.color.a": "반사 오버레이 색상 (A)", 61 | "config.water_settings.is_wavy": "물결 반사 활성화", 62 | "config.water_settings.wave_speed": "물결의 속도", 63 | "config.water_settings.wave_amplitude": "물결의 진폭", 64 | "config.water_settings.wave_frequency": "물결의 빈도", 65 | 66 | "config.puddle_settings.title": "물웅덩이 설정", 67 | "config.puddle_settings.link": "물웅덩이 설정으로 이동", 68 | "config.puddle_settings.should_generate_puddles": "물웅덩이 생성", 69 | "config.puddle_settings.should_play_splash_sound": "물웅덩이를 밟았을 때 첨벙첨벙 소리 재생", 70 | "config.puddle_settings.should_rain_splash_puddles": "물웅덩이에 빗방울이 튀김", 71 | "config.puddle_settings.puddle_percentage_while_raining": "비가 내리는 동안 물웅덩이가 생성되는 비율", 72 | "config.puddle_settings.puddle_percentage_after_raining": "비가 그친 후에 고이는 물웅덩이의 비율", 73 | "config.puddle_settings.big_puddle_chance": "큰 물웅덩이가 생성될 확률", 74 | "config.puddle_settings.milliseconds_between_raindrop_splashes": "빗방울 튀기는 사이의 대기 시간", 75 | "config.puddle_settings.milliseconds_between_raindrop_splashes_description": "빗방울이 튀는 시간(ms).", 76 | "config.puddle_settings.puddle_color.title": "물웅덩이의 색상", 77 | "config.puddle_settings.puddle_color.r": "물웅덩이의 색상 (R)", 78 | "config.puddle_settings.puddle_color.g": "물웅덩이의 색상 (G)", 79 | "config.puddle_settings.puddle_color.b": "물웅덩이의 색상 (B)", 80 | "config.puddle_settings.puddle_color.a": "물웅덩이의 색상 (A)", 81 | "config.puddle_settings.ripple_color.title": "물결의 색상", 82 | "config.puddle_settings.ripple_color.r": "물결의 색상 (R)", 83 | "config.puddle_settings.ripple_color.g": "물결의 색상 (G)", 84 | "config.puddle_settings.ripple_color.b": "물결의 색상 (B)", 85 | "config.puddle_settings.ripple_color.a": "물결의 색상 (A)" 86 | } -------------------------------------------------------------------------------- /DynamicReflections/i18n/ru.json: -------------------------------------------------------------------------------- 1 | { 2 | // Config 3 | "config.general_settings.title": "Общие настройки", 4 | "config.general_settings.water_reflections": "Включить отражения воды", 5 | "config.general_settings.mirror_reflections": "Включить отражения зеркал", 6 | "config.general_settings.puddle_reflections": "Включить отражения луж", 7 | "config.general_settings.npc_reflections": "Включить отражения NPC", 8 | "config.general_settings.sky_reflections": "Включить отражения неба", 9 | "config.general_settings.link.click_here": "Нажмите здесь", 10 | "config.general_settings.link.return_main": "Вернуться на главную страницу", 11 | "config.general_settings.override_default_settings": " Изменить настройки по умолчанию", 12 | "config.general_settings.override_default_settings.description": "Если включено, это местоположение будет игнорировать настройки по умолчанию. Игнорируется при использовании на локации 'По умолчанию'.", 13 | "config.general_settings.shortcut_key": "Клавиша быстрого доступа к настройкам", 14 | "config.general_settings.shortcut_key.description": "При нажатии данной клавиши появляется это меню для быстрых настроек.", 15 | "config.general_settings.effects": "Эффекты", 16 | 17 | "config.general_settings.reflection_offets": "Настройки отражения", 18 | "config.general_settings.player_offsets": "Смещения отражения игрока", 19 | "config.general_settings.npc_offsets": "Смещения отражения NPC", 20 | "config.water_settings.offset.x": "Смещение по X", 21 | "config.water_settings.offset.y": "Смещение по Y", 22 | 23 | "config.location_specific.title": "Настройки для конкретного местоположения", 24 | "config.location_specific.selector": " Целевое местоположение", 25 | "config.location_specific.description": "Используйте `По умолчанию`, чтобы изменить настройки по умолчанию для всех местоположений.", 26 | 27 | "config.sky_settings.title": "Специфические настройки неба", 28 | "config.sky_settings.link": "Перейти к настройкам неба", 29 | "config.sky_settings.shooting_stars_enabled": "Включить падающие звезды", 30 | "config.sky_settings.star_density": "Процент плотности звезд", 31 | "config.sky_settings.meteor_shower_chance": "Шанс метеоритного дождя", 32 | "config.sky_settings.meteor_shower_chance.description": "Этот процент распределяется между всеми локациями", 33 | "config.sky_settings.shooting_stars.title": "Настройки падающих звезд", 34 | "config.sky_settings.max_shooting_stars_per_attempt": "Максимальное количество падающих звезд", 35 | "config.sky_settings.max_shooting_stars_per_attempt.description": "Максимальное количество падающих звезд, генерируемых за интервал", 36 | "config.sky_settings.comet_chance": "Шанс кометы", 37 | "config.sky_settings.milliseconds_between_stars": "Замедление падающих звезд", 38 | "config.sky_settings.milliseconds_between_stars.description": "Миллисекунды между падающими звездами", 39 | "config.sky_settings.milliseconds_between_stars_during_meteor_shower": "Задержка падающих звезд (метеоритный дождь)", 40 | "config.sky_settings.shooting_star_speed.min": "Скорость падающих звезд (мин.)", 41 | "config.sky_settings.shooting_star_speed.max": "Скорость падающих звезд (макс.)", 42 | "config.sky_settings.comet.min": "Скорость кометы (мин.)", 43 | "config.sky_settings.comet.max": "Скорость кометы (Макс)", 44 | "config.sky_settings.comet_segment.min": "Минимальное количество сегментов кометы", 45 | "config.sky_settings.comet_segment.max": "Максимальное количество сегментов кометы", 46 | "config.sky_settings.star_reflections.title": "Настройки отражения звезд", 47 | "config.sky_settings.water_alpha.getting_dark": "Альфа воды (начало затемнения)", 48 | "config.sky_settings.water_alpha.halfway_dark": "Альфа воды (наполовину затемнена)", 49 | "config.sky_settings.water_alpha.finished_dark": "Альфа воды (полностью затемнена)", 50 | "config.sky_settings.water_alpha.description": "Определяет прозрачность эффекта волн на воде для каждого этапа захода солнца.", 51 | 52 | "config.water_settings.title": "Специфические настройки воды", 53 | "config.water_settings.link": "Перейти к настройкам воды", 54 | "config.water_settings.reflection_direction": "Направление отражения", 55 | "config.water_settings.reflection_direction.description": "0 для северных отражений, 2 для южных", 56 | "config.water_settings.color.title": "Цвет наложения отражения", 57 | "config.water_settings.color.r": "Цвет наложения отражения (R)", 58 | "config.water_settings.color.g": "Цвет наложения отражения (G)", 59 | "config.water_settings.color.b": "Цвет наложения отражения (B)", 60 | "config.water_settings.color.a": "Цвет наложения отражения (A)", 61 | "config.water_settings.is_wavy": "Включить волнистые отражения", 62 | "config.water_settings.wave_speed": "Скорость волны", 63 | "config.water_settings.wave_amplitude": "Амплитуда волны", 64 | "config.water_settings.wave_frequency": "Частота волны", 65 | 66 | "config.puddle_settings.title": "Специфические настройки лужи", 67 | "config.puddle_settings.link": "Перейти к настройкам лужи", 68 | "config.puddle_settings.should_generate_puddles": "Должен генерировать лужи", 69 | "config.puddle_settings.should_play_splash_sound": "Должен воспроизводить звук брызг", 70 | "config.puddle_settings.should_rain_splash_puddles": "Должен лить дождь, разбрызгивая лужи", 71 | "config.puddle_settings.puddle_percentage_while_raining": "Процент луж во время дождя", 72 | "config.puddle_settings.puddle_percentage_after_raining": "Процент луж после дождя", 73 | "config.puddle_settings.big_puddle_chance": "Шанс большой лужи", 74 | "config.puddle_settings.milliseconds_between_raindrop_splashes": "Период задержки всплесков капель дождя", 75 | "config.puddle_settings.milliseconds_between_raindrop_splashes_description": "Миллисекунды между брызгами дождя", 76 | "config.puddle_settings.puddle_color.title": "Цвет лужи", 77 | "config.puddle_settings.puddle_color.r": "Цвет лужи (R)", 78 | "config.puddle_settings.puddle_color.g": "Цвет лужи (G)", 79 | "config.puddle_settings.puddle_color.b": "Цвет лужи (B)", 80 | "config.puddle_settings.puddle_color.a": "Цвет лужи (A)", 81 | "config.puddle_settings.ripple_color.title": "Цвет ряби", 82 | "config.puddle_settings.ripple_color.r": "Цвет ряби (R)", 83 | "config.puddle_settings.ripple_color.g": "Цвет ряби (G)", 84 | "config.puddle_settings.ripple_color.b": "Цвет ряби (B)", 85 | "config.puddle_settings.ripple_color.a": "Цвет ряби (A)" 86 | } 87 | -------------------------------------------------------------------------------- /DynamicReflections/i18n/th.json: -------------------------------------------------------------------------------- 1 | { 2 | // Config 3 | "config.general_settings.title": "การตั้งค่าทั่วไป", 4 | "config.general_settings.water_reflections": "เปิดใช้งานการสะท้อนของน้ำ", 5 | "config.general_settings.mirror_reflections": "เปิดใช้งานการสะท้อนของกระจก", 6 | 7 | "config.water_settings.title": "การตั้งค่าเฉพาะน้ำ", 8 | "config.water_settings.reflection_direction": "ทิศทางการสะท้อน", 9 | "config.water_settings.reflection_direction.description": "0 สะท้อนทางทิศเหนือ, 2 สะท้อนทางทิศใต้", 10 | "config.water_settings.color.r": "การสะท้อนสีทับซ้อน (R)", 11 | "config.water_settings.color.g": "การสะท้อนสีทับซ้อน (G)", 12 | "config.water_settings.color.b": "การสะท้อนสีทับซ้อน (B)", 13 | "config.water_settings.color.a": "การสะท้อนสีทับซ้อน (A)", 14 | "config.water_settings.offset.x": "การสะท้อนออฟเซ็ตของ X", 15 | "config.water_settings.offset.y": "การสะท้อนออฟเซ็ตของ Y", 16 | "config.water_settings.is_wavy": "เปิดใช้งานการสะท้อนเป็นคลื่น", 17 | "config.water_settings.wave_speed": "ความเร็วคลื่น", 18 | "config.water_settings.wave_amplitude": "ความกว้างของคลื่น", 19 | "config.water_settings.wave_frequency": "ความถี่ของคลื่น" 20 | } -------------------------------------------------------------------------------- /DynamicReflections/i18n/zh.json: -------------------------------------------------------------------------------- 1 | { 2 | // Config 3 | "config.general_settings.title": "通用设置", 4 | "config.general_settings.water_reflections": "启用水中倒影", 5 | "config.general_settings.mirror_reflections": "启用镜子倒影", 6 | "config.general_settings.puddle_reflections": "启用水洼倒影", 7 | "config.general_settings.npc_reflections": "NPC有倒影", 8 | "config.general_settings.sky_reflections": "水中有星空倒影", 9 | "config.general_settings.link.click_here": "点这里", 10 | "config.general_settings.link.return_main": "回到主页面", 11 | "config.general_settings.override_default_settings": "覆盖默认设置", 12 | "config.general_settings.override_default_settings.description": "如果启用,可以为当前的地点单独设置选项(水、镜子等地点)。如果没有勾选这个选项,那么还是会沿用默认设置。", 13 | "config.general_settings.shortcut_key": "设置菜单快捷键", 14 | "config.general_settings.shortcut_key.description": "按快捷键打开设置菜单", 15 | "config.general_settings.effects": "效果", 16 | 17 | "config.general_settings.reflection_offets": "倒影相关设置", 18 | "config.general_settings.player_offsets": "玩家倒影方位设置", 19 | "config.general_settings.npc_offsets": "NPC倒影方位设置", 20 | "config.water_settings.offset.x": "横轴偏移", 21 | "config.water_settings.offset.y": "纵轴偏移", 22 | 23 | "config.location_specific.title": "单独地点倒影设置", 24 | "config.location_specific.selector": "设置地点", 25 | "config.location_specific.description": "选择默认地点的话,这些选项会适用于所有地点", 26 | 27 | "config.sky_settings.title": "天空倒影设置", 28 | "config.sky_settings.link": "查看天空倒影设置选项", 29 | "config.sky_settings.shooting_stars_enabled": "水里倒影会出现流星", 30 | "config.sky_settings.star_density": "调整星星数量", 31 | "config.sky_settings.meteor_shower_chance": "流星雨出现几率", 32 | "config.sky_settings.meteor_shower_chance.description": "适用于所有地点", 33 | "config.sky_settings.shooting_stars.title": "流星设置选项", 34 | "config.sky_settings.max_shooting_stars_per_attempt": "流星出现的数量", 35 | "config.sky_settings.max_shooting_stars_per_attempt.description": "设置流星一次最多出现的数量", 36 | "config.sky_settings.comet_chance": "彗星出现几率", 37 | "config.sky_settings.milliseconds_between_stars": "流星出现间隔", 38 | "config.sky_settings.milliseconds_between_stars.description": "设置间隔时间,单位为毫秒", 39 | "config.sky_settings.milliseconds_between_stars_during_meteor_shower": "流星雨出现间隔,单位为毫秒", 40 | "config.sky_settings.shooting_star_speed.min": "流星飞行速度(最小)", 41 | "config.sky_settings.shooting_star_speed.max": "流星飞行速度(最大)", 42 | "config.sky_settings.comet.min": "彗星飞行速度(最小)", 43 | "config.sky_settings.comet.max": "彗星飞行速度(最大)", 44 | "config.sky_settings.comet_segment.min": "彗星最小数", 45 | "config.sky_settings.comet_segment.max": "彗星最大数", 46 | "config.sky_settings.star_reflections.title": "星星倒影设置选项", 47 | "config.sky_settings.water_alpha.getting_dark": "天色渐暗", 48 | "config.sky_settings.water_alpha.halfway_dark": "太阳下山", 49 | "config.sky_settings.water_alpha.finished_dark": "黑夜降临", 50 | "config.sky_settings.water_alpha.description": "设置太阳落山前后的水面涟漪效果透明度", 51 | 52 | "config.water_settings.title": "水面倒影设置选项", 53 | "config.water_settings.link": "查看水面倒影设置选项", 54 | "config.water_settings.reflection_direction": "水面倒影朝向", 55 | "config.water_settings.reflection_direction.description": "0表示朝北,2表示朝南。", 56 | "config.water_settings.color.title": "修改倒影颜色", 57 | "config.water_settings.color.r": "RGBA调色(R)", 58 | "config.water_settings.color.g": "RGBA调色(G)", 59 | "config.water_settings.color.b": "RGBA调色(B)", 60 | "config.water_settings.color.a": "RGBA调色(A)", 61 | "config.water_settings.is_wavy": "启用水面涟漪效果", 62 | "config.water_settings.wave_speed": "波动速度", 63 | "config.water_settings.wave_amplitude": "波动振幅", 64 | "config.water_settings.wave_frequency": "波动频率", 65 | 66 | "config.puddle_settings.title": "水洼倒影设置选项", 67 | "config.puddle_settings.link": "查看水洼倒影设置选项", 68 | "config.puddle_settings.should_generate_puddles": "地面是否会产生水洼", 69 | "config.puddle_settings.should_play_splash_sound": "踩水洼时是否有水声", 70 | "config.puddle_settings.should_rain_splash_puddles": "下雨时是否会产生水洼", 71 | "config.puddle_settings.puddle_percentage_while_raining": "下雨时产生水洼几率", 72 | "config.puddle_settings.puddle_percentage_after_raining": "下雨后产生水洼几率", 73 | "config.puddle_settings.big_puddle_chance": "大块水洼产生几率", 74 | "config.puddle_settings.milliseconds_between_raindrop_splashes": "雨水产生水洼间隔", 75 | "config.puddle_settings.milliseconds_between_raindrop_splashes_description": "雨水产生水洼间隔时间,单位为毫秒。", 76 | "config.puddle_settings.puddle_color.title": "水洼颜色调整", 77 | "config.puddle_settings.puddle_color.r": "RGBA调色(R)", 78 | "config.puddle_settings.puddle_color.g": "RGBA调色(G)", 79 | "config.puddle_settings.puddle_color.b": "RGBA调色(B)", 80 | "config.puddle_settings.puddle_color.a": "RGBA调色(A)", 81 | "config.puddle_settings.ripple_color.title": "Ripple Color", 82 | "config.puddle_settings.ripple_color.r": "水面涟漪颜色调整", 83 | "config.puddle_settings.ripple_color.g": "Ripple Color (G)", 84 | "config.puddle_settings.ripple_color.b": "Ripple Color (B)", 85 | "config.puddle_settings.ripple_color.a": "Ripple Color (A)" 86 | 87 | } -------------------------------------------------------------------------------- /DynamicReflections/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Dynamic Reflections", 3 | "Author": "PeacefulEnd", 4 | "Version": "REPLACE_ME_WITH_VERSION", 5 | "Description": "Dynamic water and surface reflections for Stardew Valley.", 6 | "UniqueID": "PeacefulEnd.DynamicReflections", 7 | "EntryDll": "DynamicReflections.dll", 8 | "MinimumApiVersion": "3.14.4", 9 | "UpdateKeys": [ "Nexus:12831" ], 10 | "Dependencies": [] 11 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DynamicReflections 2 | Stardew Valley framework which enables water and mirror reflections within Stardew Valley. 3 | 4 | # Credits 5 | ## Translations 6 | Dynamic Reflections has been generously translated into several languages by the following users: 7 | 8 | * **Chinese** - Puffeeydii 9 | * **French** - CaranudLapin 10 | * **German** - NightFright2k19 11 | * **Japanese** - Celesteria-154 12 | * **Korean** - GlHhwa 13 | * **Russian** - DxTea 14 | * **Thai** - ellipszist 15 | --------------------------------------------------------------------------------