From 0d352c7e57bbd39786defdba355156a5bdb850f4 Mon Sep 17 00:00:00 2001 From: OreCruncher Date: Tue, 7 Jan 2025 08:55:33 -0800 Subject: [PATCH] Update 0.4.2 (#136) * [#135] Compat with Nostalgia Tweaks * Change how overlay manager is wired into Gui * Ensure CREDITS.md is included in Jar * Weight table cleanup; fog diagnostics; fix neoforge GUI layer registration * Rework custom particles * Refactor and arrangement for future port * Remove unused/deleted import * Diagnostic support for particle render collections * Blockitem toolbar sounds; don't fog tint if biome fog disabled * Tighten up weight table handling for morning fog * AcousticCollection enhancement; morning fog weight tables * Add whatsplaying subcommand to /dsmm * Turn off block item toolbar by default; split music in seasons pack * Split blocked sound processing from remap * Sound remapping; adding a variety of step sound replacements * Add organic block sounds; fixup block edge block detection * Dirt paths and cauldrons * Merging of sound mapping configs * More robust merging of sound mapping rules; rug sound for wool steps * Add compass wobble to unnatural dimensions * Small optimization --------- Co-authored-by: OreCruncher --- CHANGELOG.md | 26 ++ build.gradle | 8 + .../org/orecruncher/dsurround/Client.java | 10 +- .../orecruncher/dsurround/Configuration.java | 13 +- .../commands/MusicManagerCommand.java | 4 +- .../handlers/MusicManagerCommandHandler.java | 10 + .../dsurround/config/AcousticEntry.java | 13 +- .../config/AcousticEntryCollection.java | 59 ++++- .../config/{dimension => }/DimensionInfo.java | 11 +- .../dsurround/config/SoundMapping.java | 109 ++++++++ .../dsurround/config/WaterRippleStyle.java | 9 +- .../dsurround/config/biome/BiomeInfo.java | 32 +-- .../dsurround/config/block/BlockInfo.java | 19 +- .../dsurround/config/data/AcousticConfig.java | 7 +- .../config/data/DimensionConfigRule.java | 7 +- .../config/data/SoundMappingConfigRule.java | 34 +++ .../libraries/IDimensionInformation.java | 36 +++ .../config/libraries/IDimensionLibrary.java | 2 +- .../config/libraries/ISoundLibrary.java | 2 + .../config/libraries/impl/BiomeLibrary.java | 2 +- .../libraries/impl/DimensionInformation.java | 60 +++++ .../libraries/impl/DimensionLibrary.java | 2 +- .../config/libraries/impl/ItemLibrary.java | 35 ++- .../config/libraries/impl/SoundLibrary.java | 115 +++++++++ .../dsurround/effects/WaterRippleHandler.java | 10 +- .../effects/entity/BreathEffect.java | 5 +- .../particles/ParticleRenderCollection.java | 173 +++++++++++++ .../effects/particles/ParticleSheets.java | 39 --- .../particles/WaterRippleParticle.java | 3 +- .../eventing/CollectDiagnosticsEvent.java | 1 + .../dsurround/gui/overlay/ClockOverlay.java | 2 +- .../dsurround/gui/overlay/CompassOverlay.java | 91 ++++++- .../gui/overlay/DiagnosticsOverlay.java | 2 + .../dsurround/gui/overlay/OverlayManager.java | 4 +- .../plugins/RuntimeDiagnosticsPlugin.java | 2 +- .../dsurround/lib/BlockPosUtil.java | 39 --- .../orecruncher/dsurround/lib/Library.java | 5 +- .../dsurround/lib/WeightTable.java | 49 +--- .../lib/collections/ObjectArray.java | 12 +- .../dsurround/lib/gui/ColorPalette.java | 24 +- .../lib/seasons/ISeasonalInformation.java | 47 ++-- .../dsurround/lib/seasons/SeasonManager.java | 5 +- .../lib/seasons/compat/SereneSeasons.java | 51 ++-- .../lib/seasons/compat/VanillaSeasons.java | 11 +- .../mixins/audio/MixinMusicManager.java | 17 ++ .../mixins/audio/MixinSoundEngine.java | 4 + .../dsurround/mixins/core/MixinBiome.java | 13 +- .../mixins/core/MixinFogRenderer.java | 19 +- .../dsurround/mixins/core/MixinIngameHud.java | 30 --- .../mixins/core/MixinParticleManager.java | 10 - .../dsurround/mixinutils/IMusicManager.java | 4 + .../dsurround/mixinutils/MixinHelpers.java | 7 + .../dsurround/processing/FogHandler.java | 9 +- .../processing/accents/FootstepAccents.java | 1 - .../processing/accents/LeavesAccent.java | 39 --- .../fog/BiomeFogRangeCalculator.java | 2 +- .../fog/HolisticFogRangeCalculator.java | 21 +- .../fog/MorningFogRangeCalculator.java | 75 +++--- .../fog/WeatherFogRangeCalculator.java | 2 +- .../processing/scanner/BiomeScanner.java | 35 ++- .../processing/scanner/CeilingScanner.java | 2 +- .../processing/scanner/SystemsScanner.java | 19 +- .../dsurround/runtime/audio/SoundFXUtils.java | 7 +- .../runtime/sets/impl/SeasonVariables.java | 9 +- .../runtime/sets/impl/WeatherVariables.java | 6 +- .../dsurround/sound/ISoundFactory.java | 12 +- .../dsurround/sound/SoundFactory.java | 8 +- .../dsurround/sound/SoundInstanceHandler.java | 37 ++- .../dsurround/tags/ItemEffectTags.java | 1 + .../dsconfigs/sound_mappings.json | 13 + .../tags/block/effects/heat_producers.json | 8 + .../dsurround/dsconfigs/sound_factories.json | 18 ++ .../dsurround/dsconfigs/sound_mappings.json | 157 ++++++++++++ .../tags/item/effects/compass_wobble.json | 5 + .../assets/dsurround/lang/en_us.json | 12 +- .../assets/dsurround/lang/pl_pl.json | 2 - .../assets/dsurround/lang/zh_cn.json | 2 - .../resources/assets/dsurround/sounds.json | 239 ++++++++++++++++++ .../footsteps/bluntwood/bluntwood_walk1.ogg | Bin 0 -> 7093 bytes .../footsteps/bluntwood/bluntwood_walk10.ogg | Bin 0 -> 7732 bytes .../footsteps/bluntwood/bluntwood_walk11.ogg | Bin 0 -> 7366 bytes .../footsteps/bluntwood/bluntwood_walk2.ogg | Bin 0 -> 6746 bytes .../footsteps/bluntwood/bluntwood_walk3.ogg | Bin 0 -> 6638 bytes .../footsteps/bluntwood/bluntwood_walk4.ogg | Bin 0 -> 6954 bytes .../footsteps/bluntwood/bluntwood_walk5.ogg | Bin 0 -> 7716 bytes .../footsteps/bluntwood/bluntwood_walk6.ogg | Bin 0 -> 6318 bytes .../footsteps/bluntwood/bluntwood_walk7.ogg | Bin 0 -> 6950 bytes .../footsteps/bluntwood/bluntwood_walk8.ogg | Bin 0 -> 7088 bytes .../footsteps/bluntwood/bluntwood_walk9.ogg | Bin 0 -> 7494 bytes .../sounds/footsteps/dirt/dirt_walk1.ogg | Bin 0 -> 8003 bytes .../sounds/footsteps/dirt/dirt_walk10.ogg | Bin 0 -> 7905 bytes .../sounds/footsteps/dirt/dirt_walk11.ogg | Bin 0 -> 8329 bytes .../sounds/footsteps/dirt/dirt_walk2.ogg | Bin 0 -> 9068 bytes .../sounds/footsteps/dirt/dirt_walk3.ogg | Bin 0 -> 8186 bytes .../sounds/footsteps/dirt/dirt_walk4.ogg | Bin 0 -> 8715 bytes .../sounds/footsteps/dirt/dirt_walk5.ogg | Bin 0 -> 7914 bytes .../sounds/footsteps/dirt/dirt_walk6.ogg | Bin 0 -> 8348 bytes .../sounds/footsteps/dirt/dirt_walk7.ogg | Bin 0 -> 8133 bytes .../sounds/footsteps/dirt/dirt_walk8.ogg | Bin 0 -> 7744 bytes .../sounds/footsteps/dirt/dirt_walk9.ogg | Bin 0 -> 8585 bytes .../sounds/footsteps/grass/grass_walk1.ogg | Bin 0 -> 9332 bytes .../sounds/footsteps/grass/grass_walk10.ogg | Bin 0 -> 10190 bytes .../sounds/footsteps/grass/grass_walk2.ogg | Bin 0 -> 10108 bytes .../sounds/footsteps/grass/grass_walk3.ogg | Bin 0 -> 9464 bytes .../sounds/footsteps/grass/grass_walk4.ogg | Bin 0 -> 9887 bytes .../sounds/footsteps/grass/grass_walk5.ogg | Bin 0 -> 10441 bytes .../sounds/footsteps/grass/grass_walk6.ogg | Bin 0 -> 9947 bytes .../sounds/footsteps/grass/grass_walk7.ogg | Bin 0 -> 10240 bytes .../sounds/footsteps/grass/grass_walk8.ogg | Bin 0 -> 10084 bytes .../sounds/footsteps/grass/grass_walk9.ogg | Bin 0 -> 9903 bytes .../sounds/footsteps/gravel/gravel_walk1.ogg | Bin 0 -> 11414 bytes .../sounds/footsteps/gravel/gravel_walk10.ogg | Bin 0 -> 10335 bytes .../sounds/footsteps/gravel/gravel_walk11.ogg | Bin 0 -> 8471 bytes .../sounds/footsteps/gravel/gravel_walk2.ogg | Bin 0 -> 8824 bytes .../sounds/footsteps/gravel/gravel_walk3.ogg | Bin 0 -> 9971 bytes .../sounds/footsteps/gravel/gravel_walk4.ogg | Bin 0 -> 9677 bytes .../sounds/footsteps/gravel/gravel_walk5.ogg | Bin 0 -> 8917 bytes .../sounds/footsteps/gravel/gravel_walk6.ogg | Bin 0 -> 8752 bytes .../sounds/footsteps/gravel/gravel_walk7.ogg | Bin 0 -> 8805 bytes .../sounds/footsteps/gravel/gravel_walk8.ogg | Bin 0 -> 9468 bytes .../sounds/footsteps/gravel/gravel_walk9.ogg | Bin 0 -> 8995 bytes .../footsteps/metalbar/metalbar_walk1.ogg | Bin 0 -> 9270 bytes .../footsteps/metalbar/metalbar_walk10.ogg | Bin 0 -> 8331 bytes .../footsteps/metalbar/metalbar_walk11.ogg | Bin 0 -> 8227 bytes .../footsteps/metalbar/metalbar_walk2.ogg | Bin 0 -> 9265 bytes .../footsteps/metalbar/metalbar_walk3.ogg | Bin 0 -> 8402 bytes .../footsteps/metalbar/metalbar_walk4.ogg | Bin 0 -> 8655 bytes .../footsteps/metalbar/metalbar_walk5.ogg | Bin 0 -> 8600 bytes .../footsteps/metalbar/metalbar_walk6.ogg | Bin 0 -> 8362 bytes .../footsteps/metalbar/metalbar_walk7.ogg | Bin 0 -> 7574 bytes .../footsteps/metalbar/metalbar_walk8.ogg | Bin 0 -> 8125 bytes .../footsteps/metalbar/metalbar_walk9.ogg | Bin 0 -> 8439 bytes .../footsteps/metalbox/metalbox_walk1.ogg | Bin 0 -> 9044 bytes .../footsteps/metalbox/metalbox_walk2.ogg | Bin 0 -> 10045 bytes .../footsteps/metalbox/metalbox_walk3.ogg | Bin 0 -> 9104 bytes .../footsteps/metalbox/metalbox_walk4.ogg | Bin 0 -> 9848 bytes .../footsteps/metalbox/metalbox_walk5.ogg | Bin 0 -> 9365 bytes .../footsteps/metalbox/metalbox_walk6.ogg | Bin 0 -> 10274 bytes .../footsteps/metalbox/metalbox_walk7.ogg | Bin 0 -> 8852 bytes .../footsteps/metalbox/metalbox_walk8.ogg | Bin 0 -> 8675 bytes .../footsteps/metalbox/metalbox_walk9.ogg | Bin 0 -> 9118 bytes .../sounds/footsteps/mud/mud_walk1.ogg | Bin 0 -> 11025 bytes .../sounds/footsteps/mud/mud_walk2.ogg | Bin 0 -> 10963 bytes .../sounds/footsteps/mud/mud_walk3.ogg | Bin 0 -> 10561 bytes .../sounds/footsteps/mud/mud_walk4.ogg | Bin 0 -> 10089 bytes .../sounds/footsteps/mud/mud_walk5.ogg | Bin 0 -> 11121 bytes .../sounds/footsteps/mud/mud_walk6.ogg | Bin 0 -> 9032 bytes .../footsteps/muffledice/muffledice_walk1.ogg | Bin 0 -> 11732 bytes .../muffledice/muffledice_walk10.ogg | Bin 0 -> 12267 bytes .../footsteps/muffledice/muffledice_walk2.ogg | Bin 0 -> 9986 bytes .../footsteps/muffledice/muffledice_walk3.ogg | Bin 0 -> 14732 bytes .../footsteps/muffledice/muffledice_walk4.ogg | Bin 0 -> 10914 bytes .../footsteps/muffledice/muffledice_walk5.ogg | Bin 0 -> 10393 bytes .../footsteps/muffledice/muffledice_walk6.ogg | Bin 0 -> 11448 bytes .../footsteps/muffledice/muffledice_walk7.ogg | Bin 0 -> 10238 bytes .../footsteps/muffledice/muffledice_walk8.ogg | Bin 0 -> 11975 bytes .../footsteps/muffledice/muffledice_walk9.ogg | Bin 0 -> 10734 bytes .../footsteps/organic/organic_walk1.ogg | Bin 0 -> 9298 bytes .../footsteps/organic/organic_walk2.ogg | Bin 0 -> 10017 bytes .../footsteps/organic/organic_walk3.ogg | Bin 0 -> 9892 bytes .../footsteps/organic/organic_walk4.ogg | Bin 0 -> 10030 bytes .../sounds/footsteps/rug/rug_walk1.ogg | Bin 0 -> 10357 bytes .../sounds/footsteps/rug/rug_walk10.ogg | Bin 0 -> 10041 bytes .../sounds/footsteps/rug/rug_walk11.ogg | Bin 0 -> 9943 bytes .../sounds/footsteps/rug/rug_walk2.ogg | Bin 0 -> 9834 bytes .../sounds/footsteps/rug/rug_walk3.ogg | Bin 0 -> 9958 bytes .../sounds/footsteps/rug/rug_walk4.ogg | Bin 0 -> 10036 bytes .../sounds/footsteps/rug/rug_walk5.ogg | Bin 0 -> 9946 bytes .../sounds/footsteps/rug/rug_walk6.ogg | Bin 0 -> 9800 bytes .../sounds/footsteps/rug/rug_walk7.ogg | Bin 0 -> 10107 bytes .../sounds/footsteps/rug/rug_walk8.ogg | Bin 0 -> 9918 bytes .../sounds/footsteps/rug/rug_walk9.ogg | Bin 0 -> 9786 bytes .../sounds/footsteps/sand/sand_walk1.ogg | Bin 0 -> 9166 bytes .../sounds/footsteps/sand/sand_walk10.ogg | Bin 0 -> 9364 bytes .../sounds/footsteps/sand/sand_walk11.ogg | Bin 0 -> 9307 bytes .../sounds/footsteps/sand/sand_walk2.ogg | Bin 0 -> 9399 bytes .../sounds/footsteps/sand/sand_walk3.ogg | Bin 0 -> 9569 bytes .../sounds/footsteps/sand/sand_walk4.ogg | Bin 0 -> 9351 bytes .../sounds/footsteps/sand/sand_walk5.ogg | Bin 0 -> 9501 bytes .../sounds/footsteps/sand/sand_walk6.ogg | Bin 0 -> 9039 bytes .../sounds/footsteps/sand/sand_walk7.ogg | Bin 0 -> 9303 bytes .../sounds/footsteps/sand/sand_walk8.ogg | Bin 0 -> 9496 bytes .../sounds/footsteps/sand/sand_walk9.ogg | Bin 0 -> 9608 bytes .../sounds/footsteps/snow/snow_walk1.ogg | Bin 0 -> 8061 bytes .../sounds/footsteps/snow/snow_walk10.ogg | Bin 0 -> 9763 bytes .../sounds/footsteps/snow/snow_walk11.ogg | Bin 0 -> 9581 bytes .../sounds/footsteps/snow/snow_walk2.ogg | Bin 0 -> 8484 bytes .../sounds/footsteps/snow/snow_walk3.ogg | Bin 0 -> 8275 bytes .../sounds/footsteps/snow/snow_walk4.ogg | Bin 0 -> 8677 bytes .../sounds/footsteps/snow/snow_walk5.ogg | Bin 0 -> 8762 bytes .../sounds/footsteps/snow/snow_walk6.ogg | Bin 0 -> 8458 bytes .../sounds/footsteps/snow/snow_walk7.ogg | Bin 0 -> 8930 bytes .../sounds/footsteps/snow/snow_walk8.ogg | Bin 0 -> 10251 bytes .../sounds/footsteps/snow/snow_walk9.ogg | Bin 0 -> 8944 bytes .../sounds/footsteps/stone/stone_walk1.ogg | Bin 0 -> 9102 bytes .../sounds/footsteps/stone/stone_walk10.ogg | Bin 0 -> 8650 bytes .../sounds/footsteps/stone/stone_walk11.ogg | Bin 0 -> 8180 bytes .../sounds/footsteps/stone/stone_walk2.ogg | Bin 0 -> 8406 bytes .../sounds/footsteps/stone/stone_walk3.ogg | Bin 0 -> 8242 bytes .../sounds/footsteps/stone/stone_walk4.ogg | Bin 0 -> 8744 bytes .../sounds/footsteps/stone/stone_walk5.ogg | Bin 0 -> 8543 bytes .../sounds/footsteps/stone/stone_walk6.ogg | Bin 0 -> 8107 bytes .../sounds/footsteps/stone/stone_walk7.ogg | Bin 0 -> 8112 bytes .../sounds/footsteps/stone/stone_walk8.ogg | Bin 0 -> 7799 bytes .../sounds/footsteps/stone/stone_walk9.ogg | Bin 0 -> 7666 bytes .../sounds/footsteps/wood/log_walk1.ogg | Bin 0 -> 8742 bytes .../sounds/footsteps/wood/log_walk10.ogg | Bin 0 -> 8541 bytes .../sounds/footsteps/wood/log_walk11.ogg | Bin 0 -> 8823 bytes .../sounds/footsteps/wood/log_walk2.ogg | Bin 0 -> 8645 bytes .../sounds/footsteps/wood/log_walk3.ogg | Bin 0 -> 8423 bytes .../sounds/footsteps/wood/log_walk4.ogg | Bin 0 -> 8283 bytes .../sounds/footsteps/wood/log_walk5.ogg | Bin 0 -> 8903 bytes .../sounds/footsteps/wood/log_walk6.ogg | Bin 0 -> 7998 bytes .../sounds/footsteps/wood/log_walk7.ogg | Bin 0 -> 8323 bytes .../sounds/footsteps/wood/log_walk8.ogg | Bin 0 -> 7625 bytes .../sounds/footsteps/wood/log_walk9.ogg | Bin 0 -> 8290 bytes .../sounds/footsteps/wood/wood_walk1.ogg | Bin 0 -> 8469 bytes .../sounds/footsteps/wood/wood_walk10.ogg | Bin 0 -> 8221 bytes .../sounds/footsteps/wood/wood_walk11.ogg | Bin 0 -> 8516 bytes .../sounds/footsteps/wood/wood_walk2.ogg | Bin 0 -> 8652 bytes .../sounds/footsteps/wood/wood_walk3.ogg | Bin 0 -> 8409 bytes .../sounds/footsteps/wood/wood_walk4.ogg | Bin 0 -> 8480 bytes .../sounds/footsteps/wood/wood_walk5.ogg | Bin 0 -> 8966 bytes .../sounds/footsteps/wood/wood_walk6.ogg | Bin 0 -> 8104 bytes .../sounds/footsteps/wood/wood_walk7.ogg | Bin 0 -> 8096 bytes .../sounds/footsteps/wood/wood_walk8.ogg | Bin 0 -> 7756 bytes .../sounds/footsteps/wood/wood_walk9.ogg | Bin 0 -> 8202 bytes .../src/main/resources/dsurround.mixins.json | 1 - fabric/build.gradle | 4 + .../orecruncher/fabric/mixins/MixinGui.java | 33 +++ .../resources/dsurround.mixins.fabric.json | 15 ++ fabric/src/main/resources/fabric.mod.json | 3 +- gradle.properties | 4 +- neoforge/build.gradle | 4 + .../org/orecruncher/neoforge/NeoForgeMod.java | 13 + .../dsurround_seasons/dsconfigs/biomes.json | 60 ++++- .../dsconfigs/sound_factories.json | 94 ++++++- .../assets/dsurround_seasons/sounds.json | 160 ++++++++++-- versions.json | 3 +- 239 files changed, 1914 insertions(+), 525 deletions(-) rename common/src/main/java/org/orecruncher/dsurround/config/{dimension => }/DimensionInfo.java (89%) create mode 100644 common/src/main/java/org/orecruncher/dsurround/config/SoundMapping.java create mode 100644 common/src/main/java/org/orecruncher/dsurround/config/data/SoundMappingConfigRule.java create mode 100644 common/src/main/java/org/orecruncher/dsurround/config/libraries/IDimensionInformation.java create mode 100644 common/src/main/java/org/orecruncher/dsurround/config/libraries/impl/DimensionInformation.java create mode 100644 common/src/main/java/org/orecruncher/dsurround/effects/particles/ParticleRenderCollection.java delete mode 100644 common/src/main/java/org/orecruncher/dsurround/effects/particles/ParticleSheets.java delete mode 100644 common/src/main/java/org/orecruncher/dsurround/lib/BlockPosUtil.java delete mode 100644 common/src/main/java/org/orecruncher/dsurround/mixins/core/MixinIngameHud.java delete mode 100644 common/src/main/java/org/orecruncher/dsurround/processing/accents/LeavesAccent.java create mode 100644 common/src/main/resources/assets/biomesoplenty/dsconfigs/sound_mappings.json create mode 100644 common/src/main/resources/assets/biomesoplenty/dsconfigs/tags/block/effects/heat_producers.json create mode 100644 common/src/main/resources/assets/dsurround/dsconfigs/sound_mappings.json create mode 100644 common/src/main/resources/assets/dsurround/dsconfigs/tags/item/effects/compass_wobble.json create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/bluntwood/bluntwood_walk1.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/bluntwood/bluntwood_walk10.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/bluntwood/bluntwood_walk11.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/bluntwood/bluntwood_walk2.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/bluntwood/bluntwood_walk3.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/bluntwood/bluntwood_walk4.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/bluntwood/bluntwood_walk5.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/bluntwood/bluntwood_walk6.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/bluntwood/bluntwood_walk7.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/bluntwood/bluntwood_walk8.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/bluntwood/bluntwood_walk9.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/dirt/dirt_walk1.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/dirt/dirt_walk10.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/dirt/dirt_walk11.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/dirt/dirt_walk2.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/dirt/dirt_walk3.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/dirt/dirt_walk4.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/dirt/dirt_walk5.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/dirt/dirt_walk6.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/dirt/dirt_walk7.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/dirt/dirt_walk8.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/dirt/dirt_walk9.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/grass/grass_walk1.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/grass/grass_walk10.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/grass/grass_walk2.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/grass/grass_walk3.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/grass/grass_walk4.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/grass/grass_walk5.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/grass/grass_walk6.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/grass/grass_walk7.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/grass/grass_walk8.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/grass/grass_walk9.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/gravel/gravel_walk1.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/gravel/gravel_walk10.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/gravel/gravel_walk11.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/gravel/gravel_walk2.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/gravel/gravel_walk3.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/gravel/gravel_walk4.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/gravel/gravel_walk5.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/gravel/gravel_walk6.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/gravel/gravel_walk7.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/gravel/gravel_walk8.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/gravel/gravel_walk9.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/metalbar/metalbar_walk1.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/metalbar/metalbar_walk10.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/metalbar/metalbar_walk11.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/metalbar/metalbar_walk2.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/metalbar/metalbar_walk3.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/metalbar/metalbar_walk4.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/metalbar/metalbar_walk5.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/metalbar/metalbar_walk6.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/metalbar/metalbar_walk7.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/metalbar/metalbar_walk8.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/metalbar/metalbar_walk9.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/metalbox/metalbox_walk1.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/metalbox/metalbox_walk2.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/metalbox/metalbox_walk3.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/metalbox/metalbox_walk4.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/metalbox/metalbox_walk5.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/metalbox/metalbox_walk6.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/metalbox/metalbox_walk7.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/metalbox/metalbox_walk8.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/metalbox/metalbox_walk9.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/mud/mud_walk1.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/mud/mud_walk2.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/mud/mud_walk3.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/mud/mud_walk4.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/mud/mud_walk5.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/mud/mud_walk6.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/muffledice/muffledice_walk1.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/muffledice/muffledice_walk10.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/muffledice/muffledice_walk2.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/muffledice/muffledice_walk3.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/muffledice/muffledice_walk4.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/muffledice/muffledice_walk5.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/muffledice/muffledice_walk6.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/muffledice/muffledice_walk7.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/muffledice/muffledice_walk8.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/muffledice/muffledice_walk9.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/organic/organic_walk1.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/organic/organic_walk2.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/organic/organic_walk3.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/organic/organic_walk4.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/rug/rug_walk1.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/rug/rug_walk10.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/rug/rug_walk11.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/rug/rug_walk2.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/rug/rug_walk3.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/rug/rug_walk4.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/rug/rug_walk5.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/rug/rug_walk6.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/rug/rug_walk7.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/rug/rug_walk8.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/rug/rug_walk9.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/sand/sand_walk1.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/sand/sand_walk10.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/sand/sand_walk11.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/sand/sand_walk2.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/sand/sand_walk3.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/sand/sand_walk4.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/sand/sand_walk5.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/sand/sand_walk6.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/sand/sand_walk7.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/sand/sand_walk8.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/sand/sand_walk9.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/snow/snow_walk1.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/snow/snow_walk10.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/snow/snow_walk11.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/snow/snow_walk2.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/snow/snow_walk3.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/snow/snow_walk4.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/snow/snow_walk5.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/snow/snow_walk6.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/snow/snow_walk7.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/snow/snow_walk8.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/snow/snow_walk9.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/stone/stone_walk1.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/stone/stone_walk10.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/stone/stone_walk11.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/stone/stone_walk2.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/stone/stone_walk3.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/stone/stone_walk4.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/stone/stone_walk5.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/stone/stone_walk6.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/stone/stone_walk7.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/stone/stone_walk8.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/stone/stone_walk9.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/wood/log_walk1.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/wood/log_walk10.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/wood/log_walk11.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/wood/log_walk2.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/wood/log_walk3.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/wood/log_walk4.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/wood/log_walk5.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/wood/log_walk6.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/wood/log_walk7.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/wood/log_walk8.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/wood/log_walk9.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/wood/wood_walk1.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/wood/wood_walk10.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/wood/wood_walk11.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/wood/wood_walk2.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/wood/wood_walk3.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/wood/wood_walk4.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/wood/wood_walk5.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/wood/wood_walk6.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/wood/wood_walk7.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/wood/wood_walk8.ogg create mode 100644 common/src/main/resources/assets/dsurround/sounds/footsteps/wood/wood_walk9.ogg create mode 100644 fabric/src/main/java/org/orecruncher/fabric/mixins/MixinGui.java create mode 100644 fabric/src/main/resources/dsurround.mixins.fabric.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 992f809c..bc341862 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,29 @@ +> ### DynamicSurroundings-1.21.1-0.4.2 +**All Loaders** +* JAVA 21+ +* Architectury 13.0.8+ + +**Fabric** +* Fabric Loader >= 0.16.9 +* Fabric API >= 0.110.0+1.21. + +**NeoForge** +* NeoForge 21.1.84+ + +**What's New** +* Experimental: Toolbar sounds will play the "block step" sound for block items. It is off by default, and can be turned on in settings. +* Added whatsplaying subcommand to /dsmm. Will report on the music that is playing in the Music Manager. +* Work in Progress: Brought back a variety of block step sounds. Dynamic Surroundings will perform remapping of sound plays to get an updated sound, which is entirely different from the prior implementation. This is not intended to be full-featured as with prior step simulations. Can be disabled in the configuration (Sound Options -> Sound Remapping). + +**Changes** +* Do not tint biome fog if biome fog effect is disabled. +* Compass overlay will spin wildly if the dimension is not natural, like the Nether. This mirrors vanilla compass behavior. +* Internal modifications and restructure to facilitate porting to MC 1.21.4. Mojang started its refactor for how component data is encoded, not to mention the always welcome "find out what was renamed and where it was moved to" game. + +**Fixes** +* Disabling fog effect actually works +* Compatibility with Nostalgia Tweaks and Distant Horizons world fog effect + > ### DynamicSurroundings-1.21.1-0.4.1 **All Loaders** * JAVA 21+ diff --git a/build.gradle b/build.gradle index 0114ec28..88e8d892 100644 --- a/build.gradle +++ b/build.gradle @@ -58,6 +58,14 @@ subprojects { it.options.release = 21 } + tasks.register('copyJarToMain', Copy) { + duplicatesStrategy = DuplicatesStrategy.INCLUDE + if (project.name != "common" && project.name != "stubs") { + from remapJar + into "../build/libs" + } + } + // Configure Maven publishing. publishing { publications { diff --git a/common/src/main/java/org/orecruncher/dsurround/Client.java b/common/src/main/java/org/orecruncher/dsurround/Client.java index d71bd8f2..a2505a11 100644 --- a/common/src/main/java/org/orecruncher/dsurround/Client.java +++ b/common/src/main/java/org/orecruncher/dsurround/Client.java @@ -9,7 +9,6 @@ import org.orecruncher.dsurround.commands.Commands; import org.orecruncher.dsurround.config.libraries.*; import org.orecruncher.dsurround.config.libraries.impl.*; -import org.orecruncher.dsurround.effects.particles.ParticleSheets; import org.orecruncher.dsurround.gui.overlay.OverlayManager; import org.orecruncher.dsurround.gui.keyboard.KeyBindings; import org.orecruncher.dsurround.lib.GameUtils; @@ -24,6 +23,8 @@ import org.orecruncher.dsurround.eventing.ClientState; import org.orecruncher.dsurround.lib.registry.ReloadListener; import org.orecruncher.dsurround.lib.resources.ResourceUtilities; +import org.orecruncher.dsurround.lib.seasons.ISeasonalInformation; +import org.orecruncher.dsurround.lib.seasons.SeasonManager; import org.orecruncher.dsurround.lib.version.IVersionChecker; import org.orecruncher.dsurround.lib.version.VersionChecker; import org.orecruncher.dsurround.lib.version.VersionResult; @@ -128,6 +129,8 @@ public void initializeClient() { .registerSingleton(ISoundLibrary.class, SoundLibrary.class) .registerSingleton(IBiomeLibrary.class, BiomeLibrary.class) .registerSingleton(IDimensionLibrary.class, DimensionLibrary.class) + .registerSingleton(IDimensionInformation.class, DimensionInformation.class) + .registerFactory(ISeasonalInformation.class, () -> SeasonManager.HANDLER) .registerSingleton(IBlockLibrary.class, BlockLibrary.class) .registerSingleton(IItemLibrary.class, ItemLibrary.class) .registerSingleton(IEntityEffectLibrary.class, EntityEffectLibrary.class) @@ -180,11 +183,6 @@ public void onComplete(Minecraft client) { // of the dependencies to be initialized. container.resolve(Handlers.class); - // Make sure our particle sheets get registered otherwise they will not render. - // These sheets are purely client side - they have to be manhandled into the - // Minecraft environment. - ParticleSheets.register(); - this.logger.info("[%s] Finalization complete", Constants.MOD_ID); } diff --git a/common/src/main/java/org/orecruncher/dsurround/Configuration.java b/common/src/main/java/org/orecruncher/dsurround/Configuration.java index f8cf16cc..ec664c40 100644 --- a/common/src/main/java/org/orecruncher/dsurround/Configuration.java +++ b/common/src/main/java/org/orecruncher/dsurround/Configuration.java @@ -165,6 +165,10 @@ public static class SoundOptions { @Property @Comment("Enables display of toast messages for credited music") public boolean displayToastMessagesForMusic = true; + + @Property + @Comment("Enables sound remapping when sounds are played") + public boolean remapSounds = true; } public static class BlockEffects { @@ -232,6 +236,11 @@ public static class EntityEffects { @Comment("Enable/disable player toolbar sound effects") public boolean enablePlayerToolbarEffect = true; + @Property + @RestartRequired(client = false) + @Comment("Enable/disable sound effects for blocks on the toolbar") + public boolean enableToolbarBlockSounds = false; + @Property @RestartRequired(client = false) @Comment("Enable/disable item swing sound effects from players and mobs") @@ -259,10 +268,6 @@ public static class FootstepAccents { @Property @Comment("Enable/disable accents when the player is walking on squeaky blocks") public boolean enableFloorSqueaks = true; - - @Property - @Comment("Enable/disable accents for when the player is walking on leafy blocks") - public boolean enableLeafAccents = true; } public static class ParticleTweaks { diff --git a/common/src/main/java/org/orecruncher/dsurround/commands/MusicManagerCommand.java b/common/src/main/java/org/orecruncher/dsurround/commands/MusicManagerCommand.java index e8f7ff43..12cc8f68 100644 --- a/common/src/main/java/org/orecruncher/dsurround/commands/MusicManagerCommand.java +++ b/common/src/main/java/org/orecruncher/dsurround/commands/MusicManagerCommand.java @@ -10,12 +10,14 @@ public class MusicManagerCommand extends AbstractClientCommand { private static final String RESET = "reset"; private static final String PAUSE = "pause"; private static final String UNPAUSE = "unpause"; + private static final String WHATS_PLAYING = "whatsplaying"; @Override public void register(CommandDispatcher dispatcher, CommandBuildContext registryAccess) { dispatcher.register(ClientCommandRegistrationEvent.literal(COMMAND) .then(subCommand(RESET, MusicManagerCommandHandler::reset)) .then(subCommand(PAUSE, MusicManagerCommandHandler::pause)) - .then(subCommand(UNPAUSE, MusicManagerCommandHandler::unpause))); + .then(subCommand(UNPAUSE, MusicManagerCommandHandler::unpause)) + .then(subCommand(WHATS_PLAYING, MusicManagerCommandHandler::whatsPlaying))); } } diff --git a/common/src/main/java/org/orecruncher/dsurround/commands/handlers/MusicManagerCommandHandler.java b/common/src/main/java/org/orecruncher/dsurround/commands/handlers/MusicManagerCommandHandler.java index bf7f7f17..3888769c 100644 --- a/common/src/main/java/org/orecruncher/dsurround/commands/handlers/MusicManagerCommandHandler.java +++ b/common/src/main/java/org/orecruncher/dsurround/commands/handlers/MusicManagerCommandHandler.java @@ -2,6 +2,7 @@ import net.minecraft.network.chat.Component; import org.orecruncher.dsurround.lib.GameUtils; +import org.orecruncher.dsurround.lib.config.ConfigurationData; import org.orecruncher.dsurround.mixinutils.IMusicManager; public class MusicManagerCommandHandler { @@ -32,4 +33,13 @@ public static Component pause() { return Component.translatable("dsurround.command.dsmm.pause.failure", t.getMessage()); } } + + public static Component whatsPlaying() { + try { + var result = ((IMusicManager)(GameUtils.getMC().getMusicManager())).dsurround_whatsPlaying(); + return Component.translatable("dsurround.command.dsmm.whatsplaying.success", result); + } catch (Throwable t) { + return Component.translatable("dsurround.command.dsmm.whatsplaying.failure", t.getMessage()); + } + } } diff --git a/common/src/main/java/org/orecruncher/dsurround/config/AcousticEntry.java b/common/src/main/java/org/orecruncher/dsurround/config/AcousticEntry.java index 61018fac..5a043a8a 100644 --- a/common/src/main/java/org/orecruncher/dsurround/config/AcousticEntry.java +++ b/common/src/main/java/org/orecruncher/dsurround/config/AcousticEntry.java @@ -1,6 +1,8 @@ package org.orecruncher.dsurround.config; import com.google.common.base.MoreObjects; +import net.minecraft.util.random.Weight; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.orecruncher.dsurround.lib.WeightTable; import org.orecruncher.dsurround.lib.di.ContainerManager; @@ -11,9 +13,9 @@ public class AcousticEntry implements WeightTable.IItem { private static final IConditionEvaluator CONDITION_EVALUATOR = ContainerManager.resolve(IConditionEvaluator.class); - private static final int DEFAULT_WEIGHT = 10; + private static final Weight DEFAULT_WEIGHT = Weight.of(10); - private final int weight; + private final Weight weight; private final ISoundFactory acoustic; private final Script conditions; @@ -21,19 +23,20 @@ public AcousticEntry(final ISoundFactory acoustic, @Nullable final Script condit this(acoustic, condition, DEFAULT_WEIGHT); } - public AcousticEntry(final ISoundFactory acoustic, @Nullable final Script condition, int weight) { + public AcousticEntry(final ISoundFactory acoustic, @Nullable final Script condition, Weight weight) { this.acoustic = acoustic; this.weight = weight; this.conditions = condition != null ? condition : Script.TRUE; } + @NotNull @Override - public int getWeight() { + public Weight getWeight() { return this.weight; } @Override - public ISoundFactory getItem() { + public ISoundFactory data() { return getAcoustic(); } diff --git a/common/src/main/java/org/orecruncher/dsurround/config/AcousticEntryCollection.java b/common/src/main/java/org/orecruncher/dsurround/config/AcousticEntryCollection.java index ae84912c..99aca4ec 100644 --- a/common/src/main/java/org/orecruncher/dsurround/config/AcousticEntryCollection.java +++ b/common/src/main/java/org/orecruncher/dsurround/config/AcousticEntryCollection.java @@ -1,14 +1,71 @@ package org.orecruncher.dsurround.config; +import net.minecraft.util.random.SimpleWeightedRandomList; +import org.orecruncher.dsurround.lib.WeightTable; import org.orecruncher.dsurround.lib.collections.ObjectArray; +import org.orecruncher.dsurround.lib.random.Randomizer; +import org.orecruncher.dsurround.sound.ISoundFactory; +import java.util.Optional; +import java.util.stream.Stream; + +@SuppressWarnings("unused") public class AcousticEntryCollection extends ObjectArray { + public static final AcousticEntryCollection EMPTY; + + static { + EMPTY = new AcousticEntryCollection() { + @Override + public boolean add(AcousticEntry entry) { + throw new RuntimeException("Cannot add AcousticEntry to EMPTY collection"); + } + @Override + public Stream findMatches() { + return Stream.empty(); + } + @Override + public Optional makeSelection() { + return Optional.empty(); + } + @Override + public SimpleWeightedRandomList matchesAsWeightedList() { + return SimpleWeightedRandomList.empty(); + } + }; + EMPTY.trim(); + } + @Override public boolean add(AcousticEntry entry) { if (this.contains(entry)) return false; - return super.add(entry); } + + /** + * Stream of AcousticEntries that match the current conditions within + * the game. + */ + public Stream findMatches() { + return this.stream().filter(AcousticEntry::matches); + } + + /** + * Creates a SimpleWeightedRandomList based on valid candidates from within + * the collection. + */ + public SimpleWeightedRandomList matchesAsWeightedList() { + var builder = new SimpleWeightedRandomList.Builder(); + this.findMatches().forEach(m -> builder.add(m.getAcoustic(), m.getWeight().asInt())); + return builder.build(); + } + + /** + * Makes a weighted choice from the candidates available in the + * collection. + */ + public Optional makeSelection() { + return WeightTable.makeSelection(this.findMatches(), Randomizer.current()); + } } diff --git a/common/src/main/java/org/orecruncher/dsurround/config/dimension/DimensionInfo.java b/common/src/main/java/org/orecruncher/dsurround/config/DimensionInfo.java similarity index 89% rename from common/src/main/java/org/orecruncher/dsurround/config/dimension/DimensionInfo.java rename to common/src/main/java/org/orecruncher/dsurround/config/DimensionInfo.java index ef2bc045..33d85bd9 100644 --- a/common/src/main/java/org/orecruncher/dsurround/config/dimension/DimensionInfo.java +++ b/common/src/main/java/org/orecruncher/dsurround/config/DimensionInfo.java @@ -1,4 +1,4 @@ -package org.orecruncher.dsurround.config.dimension; +package org.orecruncher.dsurround.config; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.Level; @@ -19,6 +19,7 @@ public class DimensionInfo { protected int spaceHeight; protected boolean alwaysOutside = false; protected boolean playBiomeSounds = true; + protected boolean compassWobble = false; DimensionInfo() { this.name = ResourceLocation.fromNamespaceAndPath(Constants.MOD_ID, "no_dimension"); @@ -37,6 +38,8 @@ public DimensionInfo(final Level world) { // Force sea level based on known world types that give heartburn if (this.isFlatWorld) this.seaLevel = 0; + + this.compassWobble = !world.dimensionType().natural(); } public void update(DimensionConfigRule config) { @@ -49,6 +52,8 @@ public void update(DimensionConfigRule config) { v -> this.cloudHeight = v, () -> this.cloudHeight = this.skyHeight / 2); + config.compassWobble().ifPresent(v -> this.compassWobble = v); + this.spaceHeight = this.skyHeight + SPACE_HEIGHT_OFFSET; } } @@ -85,4 +90,8 @@ public boolean isFlatWorld() { return this.isFlatWorld; } + public boolean getCompassWobble() { + return this.compassWobble; + } + } diff --git a/common/src/main/java/org/orecruncher/dsurround/config/SoundMapping.java b/common/src/main/java/org/orecruncher/dsurround/config/SoundMapping.java new file mode 100644 index 00000000..9a9dba68 --- /dev/null +++ b/common/src/main/java/org/orecruncher/dsurround/config/SoundMapping.java @@ -0,0 +1,109 @@ +package org.orecruncher.dsurround.config; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.Nullable; +import org.orecruncher.dsurround.config.data.SoundMappingConfigRule; +import org.orecruncher.dsurround.lib.IMatcher; +import org.orecruncher.dsurround.lib.collections.ObjectArray; + +import java.util.Optional; + +public record SoundMapping(ResourceLocation soundEvent, ObjectArray rules) { + + public static SoundMapping of(SoundMappingConfigRule rule) { + ObjectArray mappings = new ObjectArray<>(rule.rules().size()); + rule.rules().forEach(r -> mappings.add(Mapping.of(r))); + return new SoundMapping(rule.soundEvent(), mappings); + } + + public boolean isBlockStateNeeded() { + return !this.rules.isEmpty() && !this.rules.getFirst().isDefaultRule(); + } + + public Optional findMatch(@Nullable BlockState state) { + Optional factory = Optional.empty(); + for (var rule : this.rules) { + factory = rule.findMatch(state); + if (factory.isPresent()) + break; + } + return factory; + } + + public void merge(SoundMappingConfigRule mapping) { + if (!this.soundEvent.equals(mapping.soundEvent())) + throw new RuntimeException("Unable to merge sound mapping rule - factories do not match"); + + for (var rule : mapping.rules()) { + var mapped = Mapping.of(rule); + + // Find the first applicable rule that matches the factory that is needed. + // It's possible to have multiple rules with the same factory. It can occur when + // merging two different rule definitions where one has the factory as a default + // and the other has block matchers. + var existingRule = this.rules.stream().filter(r -> r.factory().equals(rule.factory())).findFirst(); + if (existingRule.isPresent()) { + // If it is a default rule, we do not want to merge. Instead, we + // insert prior. We need to preserve the existing rule as default. + if (existingRule.get().isDefaultRule()) { + this.insertBeforeDefaultRule(mapped); + } else { + // Need to add block matcher definitions + existingRule.get().merge(mapped); + } + } else { + // Need to add the rule. If the last rule in the collection is all matches, we need + // to insert prior. Otherwise, we append. + var last = this.rules.getLast(); + if (last.isDefaultRule()) { + this.insertBeforeDefaultRule(mapped); + } else { + this.rules.add(mapped); + } + } + } + } + + private void insertBeforeDefaultRule(Mapping mapping) { + var last = this.rules.getLast(); + if (!last.isDefaultRule()) + throw new RuntimeException("Last rule in sound mapping configuration is not default"); + this.rules.remove(last); + this.rules.add(mapping); + this.rules.add(last); + } + + public record Mapping(ObjectArray> blocks, ResourceLocation factory) { + + public static Mapping of(SoundMappingConfigRule.MappingRule mappingRule) { + ObjectArray> blocks = new ObjectArray<>(mappingRule.blocks().size()); + blocks.addAll(mappingRule.blocks()); + return new Mapping(blocks, mappingRule.factory()); + } + + public Optional findMatch(@Nullable BlockState state) { + if (this.isDefaultRule()) + return Optional.of(this.factory); + // Since the rules have BlockState matching if a null state is provided + // return empty - nothing could be matched. + if (state == null) + return Optional.empty(); + for( var rule : this.blocks) { + if (rule.match(state)) + return Optional.of(this.factory); + } + return Optional.empty(); + } + + public boolean isDefaultRule() { + return this.blocks.isEmpty(); + } + + public void merge(Mapping rule) { + if (!this.factory.equals(rule.factory())) + throw new RuntimeException("Unable to add mapping rule - factories do not match"); + this.blocks.addAll(rule.blocks()); + } + } +} diff --git a/common/src/main/java/org/orecruncher/dsurround/config/WaterRippleStyle.java b/common/src/main/java/org/orecruncher/dsurround/config/WaterRippleStyle.java index 343ae4c6..f2e3cdc4 100644 --- a/common/src/main/java/org/orecruncher/dsurround/config/WaterRippleStyle.java +++ b/common/src/main/java/org/orecruncher/dsurround/config/WaterRippleStyle.java @@ -2,13 +2,12 @@ import net.minecraft.resources.ResourceLocation; import org.orecruncher.dsurround.Constants; -import org.orecruncher.dsurround.effects.particles.ParticleSheets; import org.orecruncher.dsurround.lib.random.Randomizer; public enum WaterRippleStyle { - NONE (ResourceLocation.fromNamespaceAndPath(Constants.MOD_ID, "none")), - PIXELATED_CIRCLE(ParticleSheets.TEXTURE_WATER_RIPPLE_PIXELATED_CIRCLE) { + NONE("none"), + PIXELATED_CIRCLE("textures/particles/pixel_ripples.png") { private final int FRAMES = 7; private final float DELTA = 1F / this.FRAMES; private final int MAX_AGE = this.FRAMES * 2; @@ -36,8 +35,8 @@ public int getMaxAge() { private final ResourceLocation resource; - WaterRippleStyle(final ResourceLocation texture) { - this.resource = texture; + WaterRippleStyle(final String texture) { + this.resource = ResourceLocation.fromNamespaceAndPath(Constants.MOD_ID, texture); } public ResourceLocation getTexture() { diff --git a/common/src/main/java/org/orecruncher/dsurround/config/biome/BiomeInfo.java b/common/src/main/java/org/orecruncher/dsurround/config/biome/BiomeInfo.java index 22862de6..f3a93f50 100644 --- a/common/src/main/java/org/orecruncher/dsurround/config/biome/BiomeInfo.java +++ b/common/src/main/java/org/orecruncher/dsurround/config/biome/BiomeInfo.java @@ -5,6 +5,7 @@ import net.minecraft.network.chat.TextColor; import net.minecraft.resources.ResourceLocation; import net.minecraft.sounds.Music; +import net.minecraft.util.random.Weight; import net.minecraft.world.level.biome.Biome; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.Nullable; @@ -20,7 +21,6 @@ import org.orecruncher.dsurround.lib.logging.ModLog; import org.orecruncher.dsurround.lib.random.IRandomizer; import org.orecruncher.dsurround.lib.registry.RegistryUtils; -import org.orecruncher.dsurround.lib.WeightTable; import org.orecruncher.dsurround.lib.collections.ObjectArray; import org.orecruncher.dsurround.lib.di.ContainerManager; import org.orecruncher.dsurround.lib.logging.IModLog; @@ -51,10 +51,10 @@ public final class BiomeInfo implements Comparable, IBiomeSoundProvid private final boolean isOcean; private final boolean isDeepOcean; private final boolean isCave; - private Collection loopSounds = new AcousticEntryCollection(); - private Collection moodSounds = new AcousticEntryCollection(); - private Collection additionalSounds = new AcousticEntryCollection(); - private Collection musicSounds = new AcousticEntryCollection(); + private AcousticEntryCollection loopSounds = new AcousticEntryCollection(); + private AcousticEntryCollection moodSounds = new AcousticEntryCollection(); + private AcousticEntryCollection additionalSounds = new AcousticEntryCollection(); + private AcousticEntryCollection musicSounds = new AcousticEntryCollection(); private Collection comments = new ObjectArray<>(); private TextColor fogColor; private FogDensity fogDensity; @@ -165,8 +165,8 @@ public boolean hasTrait(String trait) { @Override public Collection findBiomeSoundMatches() { - return this.loopSounds.stream() - .filter(AcousticEntry::matches) + return this.loopSounds + .findMatches() .map(AcousticEntry::getAcoustic) .collect(Collectors.toList()); } @@ -175,7 +175,7 @@ public Collection findBiomeSoundMatches() { public Optional getExtraSound(final SoundEventType type, final IRandomizer random) { @Nullable - Collection sourceList = null; + AcousticEntryCollection sourceList = null; switch (type) { case ADDITION -> { @@ -193,11 +193,7 @@ public Optional getExtraSound(final SoundEventType type, final IR case MUSIC -> sourceList = this.musicSounds; } - if (sourceList == null || sourceList.isEmpty()) - return Optional.empty(); - - var candidates = sourceList.stream().filter(AcousticEntry::matches); - return WeightTable.makeSelection(candidates); + return sourceList == null ? Optional.empty() : sourceList.makeSelection(); } @Override @@ -242,7 +238,7 @@ public void update(final BiomeConfigRule entry) { targetCollection = this.loopSounds; } case MUSIC, MOOD, ADDITION -> { - final int weight = sr.weight(); + final Weight weight = sr.weight(); acousticEntry = new AcousticEntry(factory, sr.conditions(), weight); if (sr.type() == SoundEventType.ADDITION) @@ -265,13 +261,13 @@ else if (sr.type() == SoundEventType.MOOD) public void trim() { if (this.loopSounds.isEmpty()) - this.loopSounds = ImmutableList.of(); + this.loopSounds = AcousticEntryCollection.EMPTY; if (this.moodSounds.isEmpty()) - this.moodSounds = ImmutableList.of(); + this.moodSounds = AcousticEntryCollection.EMPTY; if (this.additionalSounds.isEmpty()) - this.additionalSounds = ImmutableList.of(); + this.additionalSounds = AcousticEntryCollection.EMPTY; if (this.musicSounds.isEmpty()) - this.musicSounds = ImmutableList.of(); + this.musicSounds = AcousticEntryCollection.EMPTY; if (this.comments.isEmpty()) this.comments = ImmutableList.of(); } diff --git a/common/src/main/java/org/orecruncher/dsurround/config/block/BlockInfo.java b/common/src/main/java/org/orecruncher/dsurround/config/block/BlockInfo.java index 659a1b32..ab75b209 100644 --- a/common/src/main/java/org/orecruncher/dsurround/config/block/BlockInfo.java +++ b/common/src/main/java/org/orecruncher/dsurround/config/block/BlockInfo.java @@ -1,8 +1,10 @@ package org.orecruncher.dsurround.config.block; import com.google.common.collect.ImmutableList; +import net.minecraft.resources.ResourceLocation; import net.minecraft.tags.BlockTags; import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.Nullable; import org.orecruncher.dsurround.config.AcousticEntryCollection; import org.orecruncher.dsurround.config.data.AcousticConfig; import org.orecruncher.dsurround.config.libraries.ISoundLibrary; @@ -10,7 +12,6 @@ import org.orecruncher.dsurround.config.data.BlockConfigRule; import org.orecruncher.dsurround.config.libraries.ITagLibrary; import org.orecruncher.dsurround.effects.IBlockEffectProducer; -import org.orecruncher.dsurround.lib.WeightTable; import org.orecruncher.dsurround.lib.collections.ObjectArray; import org.orecruncher.dsurround.lib.di.ContainerManager; import org.orecruncher.dsurround.lib.logging.IModLog; @@ -60,7 +61,9 @@ private static class Reflectance { private static final ITagLibrary TAG_LIBRARY = ContainerManager.resolve(ITagLibrary.class); protected final int version; - protected Collection sounds = new AcousticEntryCollection(); + @Nullable + protected final ResourceLocation stepSound; + protected AcousticEntryCollection sounds = new AcousticEntryCollection(); protected Collection blockEffects = new ObjectArray<>(); protected Script soundChance = new Script("0.01"); @@ -69,12 +72,14 @@ private static class Reflectance { public BlockInfo(int version) { this.version = version; + this.stepSound = null; } public BlockInfo(int version, BlockState state) { this.version = version; this.soundOcclusion = getSoundOcclusionSetting(state); this.soundReflectivity = getSoundReflectionSetting(state); + this.stepSound = state.getSoundType().getStepSound().getLocation(); } public boolean isDefault() { @@ -100,7 +105,6 @@ private void addToBlockEffects(IBlockEffectProducer effect) { this.blockEffects.add(effect); } - // TODO: Eliminate duplicates public void update(BlockConfigRule config) { // Reset of a block clears all registries if (config.clearSounds()) @@ -134,8 +138,7 @@ public Optional getSoundToPlay(final IRandomizer random) { if (this.sounds != null) { var chance = CONDITION_EVALUATOR.eval(this.soundChance); if (chance instanceof Double c && random.nextDouble() < c) { - var candidates = this.sounds.stream().filter(AcousticEntry::matches); - return WeightTable.makeSelection(candidates); + return this.sounds.makeSelection(); } } return Optional.empty(); @@ -147,7 +150,7 @@ public Collection getEffectProducers() { public void trim() { if (this.sounds.isEmpty()) { - this.sounds = ImmutableList.of(); + this.sounds = AcousticEntryCollection.EMPTY; } if (this.blockEffects.isEmpty()) { this.blockEffects = ImmutableList.of(); @@ -333,6 +336,10 @@ public String toString() { .append(this.soundOcclusion) .append("\n"); + if (this.stepSound != null) { + builder.append("step sound: ").append(this.stepSound).append("\n"); + } + if (!this.sounds.isEmpty()) { builder.append("sound chance: ").append(this.soundChance); builder.append("; sounds [\n"); diff --git a/common/src/main/java/org/orecruncher/dsurround/config/data/AcousticConfig.java b/common/src/main/java/org/orecruncher/dsurround/config/data/AcousticConfig.java index db8149b9..bb9abae2 100644 --- a/common/src/main/java/org/orecruncher/dsurround/config/data/AcousticConfig.java +++ b/common/src/main/java/org/orecruncher/dsurround/config/data/AcousticConfig.java @@ -3,6 +3,7 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.random.Weight; import org.orecruncher.dsurround.config.SoundEventType; import org.orecruncher.dsurround.lib.IdentityUtils; import org.orecruncher.dsurround.lib.scripting.Script; @@ -10,14 +11,16 @@ public record AcousticConfig( ResourceLocation factory, Script conditions, - Integer weight, + Weight weight, SoundEventType type) { + private static final Weight DEFAULT_WEIGHT = Weight.of(10); + public static final Codec CODEC = RecordCodecBuilder.create((instance) -> instance.group( IdentityUtils.CODEC.fieldOf("factory").forGetter(AcousticConfig::factory), Script.CODEC.optionalFieldOf("conditions", Script.TRUE).forGetter(AcousticConfig::conditions), - Codec.intRange(0, Integer.MAX_VALUE).optionalFieldOf("weight", 10).forGetter(AcousticConfig::weight), + Weight.CODEC.optionalFieldOf("weight", DEFAULT_WEIGHT).forGetter(AcousticConfig::weight), SoundEventType.CODEC.optionalFieldOf("type", SoundEventType.LOOP).forGetter(AcousticConfig::type) ).apply(instance, AcousticConfig::new)); } \ No newline at end of file diff --git a/common/src/main/java/org/orecruncher/dsurround/config/data/DimensionConfigRule.java b/common/src/main/java/org/orecruncher/dsurround/config/data/DimensionConfigRule.java index ff5284c6..b33bfa3c 100644 --- a/common/src/main/java/org/orecruncher/dsurround/config/data/DimensionConfigRule.java +++ b/common/src/main/java/org/orecruncher/dsurround/config/data/DimensionConfigRule.java @@ -12,7 +12,8 @@ public record DimensionConfigRule( Optional skyHeight, Optional cloudHeight, Optional alwaysOutside, - Optional playBiomeSounds) { + Optional playBiomeSounds, + Optional compassWobble) { public static final Codec CODEC = RecordCodecBuilder.create((instance) -> instance.group( ResourceLocation.CODEC.fieldOf("dimId").forGetter(DimensionConfigRule::dimensionId), @@ -20,7 +21,8 @@ public record DimensionConfigRule( Codec.INT.optionalFieldOf("skyHeight").forGetter(DimensionConfigRule::skyHeight), Codec.INT.optionalFieldOf("cloudHeight").forGetter(DimensionConfigRule::cloudHeight), Codec.BOOL.optionalFieldOf("alwaysOutside").forGetter(DimensionConfigRule::alwaysOutside), - Codec.BOOL.optionalFieldOf("playBiomeSounds").forGetter(DimensionConfigRule::playBiomeSounds)) + Codec.BOOL.optionalFieldOf("playBiomeSounds").forGetter(DimensionConfigRule::playBiomeSounds), + Codec.BOOL.optionalFieldOf("compassWobble").forGetter(DimensionConfigRule::compassWobble)) .apply(instance, DimensionConfigRule::new)); @Override @@ -32,6 +34,7 @@ public String toString() { this.cloudHeight.ifPresent(v -> builder.append(" cloudHeight: ").append(v)); this.alwaysOutside.ifPresent(v -> builder.append(" alwaysOutside: ").append(v)); this.playBiomeSounds.ifPresent(v -> builder.append(" playBiomeSounds: ").append(v)); + this.compassWobble.ifPresent(v -> builder.append(" compassWobble: ").append(v)); return builder.toString(); } diff --git a/common/src/main/java/org/orecruncher/dsurround/config/data/SoundMappingConfigRule.java b/common/src/main/java/org/orecruncher/dsurround/config/data/SoundMappingConfigRule.java new file mode 100644 index 00000000..b712fd7e --- /dev/null +++ b/common/src/main/java/org/orecruncher/dsurround/config/data/SoundMappingConfigRule.java @@ -0,0 +1,34 @@ +package org.orecruncher.dsurround.config.data; + +import com.google.common.collect.ImmutableList; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.state.BlockState; +import org.orecruncher.dsurround.lib.CodecExtensions; +import org.orecruncher.dsurround.lib.IMatcher; +import org.orecruncher.dsurround.lib.IdentityUtils; + +import java.util.List; + +/** + * Sound mapping configuration rule. The order of the rules is important as they are processed sequentially. For + * mappings that have more than one rule, the default is placed as the last entry without any BlockState + * specifications. + */ +public record SoundMappingConfigRule(ResourceLocation soundEvent, List rules) { + + public static final Codec CODEC = RecordCodecBuilder.create((instance) -> + instance.group( + IdentityUtils.CODEC.fieldOf("soundEvent").forGetter(SoundMappingConfigRule::soundEvent), + Codec.list(MappingRule.CODEC).fieldOf("rules").forGetter(SoundMappingConfigRule::rules) + ).apply(instance, SoundMappingConfigRule::new)); + + public record MappingRule(List> blocks, ResourceLocation factory) { + + public static final Codec CODEC = RecordCodecBuilder.create((instance) -> + instance.group( + Codec.list(CodecExtensions.checkBlockStateSpecification(true)).optionalFieldOf("blocks", ImmutableList.of()).forGetter(MappingRule::blocks), + IdentityUtils.CODEC.fieldOf("factory").forGetter(MappingRule::factory)).apply(instance, MappingRule::new)); + } +} diff --git a/common/src/main/java/org/orecruncher/dsurround/config/libraries/IDimensionInformation.java b/common/src/main/java/org/orecruncher/dsurround/config/libraries/IDimensionInformation.java new file mode 100644 index 00000000..389f1e8d --- /dev/null +++ b/common/src/main/java/org/orecruncher/dsurround/config/libraries/IDimensionInformation.java @@ -0,0 +1,36 @@ +package org.orecruncher.dsurround.config.libraries; + +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.resources.ResourceLocation; + +public interface IDimensionInformation { + /** + * The resource location ID of the dimension + */ + ResourceLocation name(); + /** + * The client level for the dimension + */ + ClientLevel level(); + /** + * The sea level configured for the dimension + */ + int seaLevel(); + /** + * Whether the dimension is considered always outside, like Nether. + */ + boolean alwaysOutside(); + /** + * The vertical Y level which is the threshold of outer space. + */ + int getSpaceHeight(); + /** + * The veritical Y level where clouds are expected to be + */ + int getCloudHeight(); + + /** + * Indicates whether the compass should "wobble" making the bearing unreadable + */ + boolean getCompassWobble(); +} diff --git a/common/src/main/java/org/orecruncher/dsurround/config/libraries/IDimensionLibrary.java b/common/src/main/java/org/orecruncher/dsurround/config/libraries/IDimensionLibrary.java index ff68b27c..424a118f 100644 --- a/common/src/main/java/org/orecruncher/dsurround/config/libraries/IDimensionLibrary.java +++ b/common/src/main/java/org/orecruncher/dsurround/config/libraries/IDimensionLibrary.java @@ -1,7 +1,7 @@ package org.orecruncher.dsurround.config.libraries; import net.minecraft.world.level.Level; -import org.orecruncher.dsurround.config.dimension.DimensionInfo; +import org.orecruncher.dsurround.config.DimensionInfo; public interface IDimensionLibrary extends ILibrary { DimensionInfo getData(final Level world); diff --git a/common/src/main/java/org/orecruncher/dsurround/config/libraries/ISoundLibrary.java b/common/src/main/java/org/orecruncher/dsurround/config/libraries/ISoundLibrary.java index 4177c932..974bcf11 100644 --- a/common/src/main/java/org/orecruncher/dsurround/config/libraries/ISoundLibrary.java +++ b/common/src/main/java/org/orecruncher/dsurround/config/libraries/ISoundLibrary.java @@ -1,5 +1,6 @@ package org.orecruncher.dsurround.config.libraries; +import net.minecraft.client.resources.sounds.SoundInstance; import net.minecraft.resources.ResourceLocation; import net.minecraft.sounds.Music; import net.minecraft.sounds.SoundEvent; @@ -21,6 +22,7 @@ public interface ISoundLibrary extends ILibrary { ISoundFactory getSoundFactoryOrDefault(ResourceLocation factoryLocation); ISoundFactory getSoundFactoryForMusic(Music music); + Optional remapSound(SoundInstance soundInstance); boolean isBlocked(final ResourceLocation id); boolean isCulled(final ResourceLocation id); float getVolumeScale(SoundSource category, ResourceLocation id); diff --git a/common/src/main/java/org/orecruncher/dsurround/config/libraries/impl/BiomeLibrary.java b/common/src/main/java/org/orecruncher/dsurround/config/libraries/impl/BiomeLibrary.java index 6274e86c..239c9c9b 100644 --- a/common/src/main/java/org/orecruncher/dsurround/config/libraries/impl/BiomeLibrary.java +++ b/common/src/main/java/org/orecruncher/dsurround/config/libraries/impl/BiomeLibrary.java @@ -42,7 +42,7 @@ public final class BiomeLibrary implements IBiomeLibrary { // Cached list of biome config rules. Need to hold onto them // because they may be needed to handle a dynamic biome load. - private final ObjectArray biomeConfigs = new ObjectArray<>(64); + private final ObjectArray biomeConfigs = new ObjectArray<>(128); // Current version of the configs that are loaded. Used to detect when // configs changed and cached biome info needs a refresh. diff --git a/common/src/main/java/org/orecruncher/dsurround/config/libraries/impl/DimensionInformation.java b/common/src/main/java/org/orecruncher/dsurround/config/libraries/impl/DimensionInformation.java new file mode 100644 index 00000000..e89c0c4f --- /dev/null +++ b/common/src/main/java/org/orecruncher/dsurround/config/libraries/impl/DimensionInformation.java @@ -0,0 +1,60 @@ +package org.orecruncher.dsurround.config.libraries.impl; + +import dev.architectury.event.events.client.ClientLifecycleEvent; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.resources.ResourceLocation; +import org.orecruncher.dsurround.config.DimensionInfo; +import org.orecruncher.dsurround.config.libraries.AssetLibraryEvent; +import org.orecruncher.dsurround.config.libraries.IDimensionInformation; +import org.orecruncher.dsurround.config.libraries.IDimensionLibrary; +import org.orecruncher.dsurround.lib.GameUtils; +import org.orecruncher.dsurround.lib.events.HandlerPriority; + +public class DimensionInformation implements IDimensionInformation { + + private final IDimensionLibrary dimensionLibrary; + private DimensionInfo info; + + public DimensionInformation(IDimensionLibrary dimensionLibrary) { + this.dimensionLibrary = dimensionLibrary; + + // Need to reset the cached dimension info whenever the client world + // changes or if there is a resource reload. + ClientLifecycleEvent.CLIENT_LEVEL_LOAD.register(state -> this.info = null); + AssetLibraryEvent.RELOAD.register((x, y) -> this.info = null, HandlerPriority.HIGH); + } + + public ResourceLocation name() { + return this.getInfo().getName(); + } + + public ClientLevel level() { + return GameUtils.getWorld().orElseThrow(); + } + + public int seaLevel() { + return this.getInfo().getSeaLevel(); + } + + public boolean alwaysOutside() { + return this.getInfo().alwaysOutside(); + } + + public int getSpaceHeight() { + return this.getInfo().getSpaceHeight(); + } + + public int getCloudHeight() { + return this.getInfo().getCloudHeight(); + } + + public boolean getCompassWobble() { + return this.getInfo().getCompassWobble(); + } + + private DimensionInfo getInfo() { + if (this.info == null) + this.info = this.dimensionLibrary.getData(this.level()); + return this.info; + } +} diff --git a/common/src/main/java/org/orecruncher/dsurround/config/libraries/impl/DimensionLibrary.java b/common/src/main/java/org/orecruncher/dsurround/config/libraries/impl/DimensionLibrary.java index 05a590d6..7a9959f4 100644 --- a/common/src/main/java/org/orecruncher/dsurround/config/libraries/impl/DimensionLibrary.java +++ b/common/src/main/java/org/orecruncher/dsurround/config/libraries/impl/DimensionLibrary.java @@ -5,7 +5,7 @@ import net.minecraft.resources.ResourceKey; import net.minecraft.world.level.Level; import org.orecruncher.dsurround.config.data.DimensionConfigRule; -import org.orecruncher.dsurround.config.dimension.DimensionInfo; +import org.orecruncher.dsurround.config.DimensionInfo; import org.orecruncher.dsurround.config.libraries.IDimensionLibrary; import org.orecruncher.dsurround.config.libraries.IReloadEvent; import org.orecruncher.dsurround.lib.collections.ObjectArray; diff --git a/common/src/main/java/org/orecruncher/dsurround/config/libraries/impl/ItemLibrary.java b/common/src/main/java/org/orecruncher/dsurround/config/libraries/impl/ItemLibrary.java index ca12910e..9a887fa8 100644 --- a/common/src/main/java/org/orecruncher/dsurround/config/libraries/impl/ItemLibrary.java +++ b/common/src/main/java/org/orecruncher/dsurround/config/libraries/impl/ItemLibrary.java @@ -10,6 +10,7 @@ import net.minecraft.world.item.Item; import net.minecraft.world.item.*; import org.jetbrains.annotations.Nullable; +import org.orecruncher.dsurround.Configuration; import org.orecruncher.dsurround.config.ItemClassType; import org.orecruncher.dsurround.config.libraries.IItemLibrary; import org.orecruncher.dsurround.config.libraries.IReloadEvent; @@ -32,14 +33,16 @@ public class ItemLibrary implements IItemLibrary { private final ITagLibrary tagLibrary; private final IModLog logger; + private final Configuration config; private final Reference2ObjectOpenHashMap itemEquipFactories = new Reference2ObjectOpenHashMap<>(); private final Reference2ObjectOpenHashMap itemSwingFactories = new Reference2ObjectOpenHashMap<>(); private final Reference2ObjectOpenHashMap itemArmorStepFactories = new Reference2ObjectOpenHashMap<>(); private int version; - public ItemLibrary(ITagLibrary tagLibrary, IModLog logger) { + public ItemLibrary(ITagLibrary tagLibrary, Configuration config, IModLog logger) { this.tagLibrary = tagLibrary; this.logger = ModLog.createChild(logger, "ItemLibrary"); + this.config = config; } @Override @@ -98,7 +101,7 @@ public Stream dump() { if (itemEquipSound != null) return SoundFactoryBuilder .create(itemEquipSound) - .category(SoundSource.PLAYERS).volume(0.5F).pitch(0.8F, 1.2F).build(); + .category(SoundSource.PLAYERS).volume(0.25F).pitch(0.8F, 1.2F).build(); return defaultSoundFactory.get(); } @@ -107,29 +110,33 @@ public Stream dump() { @Nullable private static SoundEvent getEquipableSoundEvent(ItemStack stack) { - var item = stack.getItem(); SoundEvent itemEquipSound = null; - - if (item instanceof Equipable equipment) - itemEquipSound = equipment.getEquipSound().value(); - else if (item instanceof ArmorItem armor) - itemEquipSound = armor.getEquipSound().value(); - + var equipable = Equipable.get(stack); + if (equipable != null) { + itemEquipSound = equipable.getEquipSound().value(); + } return itemEquipSound; } @Nullable private SoundEvent getSoundEvent(ItemStack stack) { - // Look for special Equipment and ArmorItem types since they may have built in equip sounds + // Look for special Equipment and ArmorItem types since they may have built in equipped sounds SoundEvent itemEquipSound = getEquipableSoundEvent(stack); if (itemEquipSound != null) return itemEquipSound; - var item = stack.getItem(); + if (this.config.entityEffects.enableToolbarBlockSounds) { + Item item = stack.getItem(); + if (item instanceof BlockItem blockItem) { + var soundType = blockItem.getBlock().defaultBlockState().getSoundType(); + itemEquipSound = soundType.getStepSound(); + } + } + + if (itemEquipSound != null) + return itemEquipSound; - if (item instanceof ElytraItem elytraItem) - itemEquipSound = elytraItem.getEquipSound().value(); - else if (this.tagLibrary.is(ItemTags.LAVA_BUCKETS, stack)) + if (this.tagLibrary.is(ItemTags.LAVA_BUCKETS, stack)) itemEquipSound = SoundEvents.BUCKET_FILL_LAVA; else if (this.tagLibrary.is(ItemTags.WATER_BUCKETS, stack)) itemEquipSound = SoundEvents.BUCKET_FILL; diff --git a/common/src/main/java/org/orecruncher/dsurround/config/libraries/impl/SoundLibrary.java b/common/src/main/java/org/orecruncher/dsurround/config/libraries/impl/SoundLibrary.java index 88567edb..7a0affd5 100644 --- a/common/src/main/java/org/orecruncher/dsurround/config/libraries/impl/SoundLibrary.java +++ b/common/src/main/java/org/orecruncher/dsurround/config/libraries/impl/SoundLibrary.java @@ -1,22 +1,33 @@ package org.orecruncher.dsurround.config.libraries.impl; +import com.google.common.collect.ImmutableSet; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.UnboundedMapCodec; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import net.minecraft.client.resources.sounds.SoundInstance; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; import net.minecraft.sounds.Music; import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.Nullable; import org.orecruncher.dsurround.Configuration; import org.orecruncher.dsurround.Constants; import org.orecruncher.dsurround.config.IndividualSoundConfigEntry; +import org.orecruncher.dsurround.config.SoundMapping; +import org.orecruncher.dsurround.config.data.SoundMappingConfigRule; import org.orecruncher.dsurround.config.data.SoundMetadataConfig; import org.orecruncher.dsurround.config.libraries.IReloadEvent; import org.orecruncher.dsurround.config.libraries.ISoundLibrary; +import org.orecruncher.dsurround.gui.sound.ConfigSoundInstance; import org.orecruncher.dsurround.lib.CodecExtensions; import org.orecruncher.dsurround.lib.Comparers; +import org.orecruncher.dsurround.lib.GameUtils; import org.orecruncher.dsurround.lib.Library; import org.orecruncher.dsurround.lib.logging.IModLog; import org.orecruncher.dsurround.lib.logging.ModLog; @@ -45,13 +56,19 @@ public final class SoundLibrary implements ISoundLibrary { private static final String SOUNDS_JSON = "sounds.json"; private static final String FACTORY_JSON = "sound_factories.json"; private static final String SOUND_CONFIG_FILE = "soundconfig.json"; + private static final String SOUND_MAPPING_JSON = "sound_mappings.json"; private static final UnboundedMapCodec SOUND_FILE_CODEC = Codec.unboundedMap(Codec.STRING, SoundMetadataConfig.CODEC); private static final Codec> FACTORY_FILE_CODEC = Codec.list(SoundFactory.CODEC); private static final Codec> SOUND_CONFIG_CODEC = Codec.list(IndividualSoundConfigEntry.CODEC); + private static final Codec> SOUND_MAPPING_CODEC = Codec.list(SoundMappingConfigRule.CODEC); private static final ResourceLocation MISSING_RESOURCE = ResourceLocation.fromNamespaceAndPath(Constants.MOD_ID, "missing_sound"); private static final SoundEvent MISSING = SoundEvent.createVariableRangeEvent(MISSING_RESOURCE); + private static final ResourceLocation THUNDER_SOUND = SoundEvents.LIGHTNING_BOLT_THUNDER.getLocation(); + private static final Set SOUND_REMAP_BLOCKED_MOBS = ImmutableSet.of("creeper"); + private static final BlockPos.MutableBlockPos MUTABLE_BLOCK_POS = new BlockPos.MutableBlockPos(); + private final IModLog logger; private final Configuration config; private final Path soundConfigPath; @@ -63,6 +80,7 @@ public final class SoundLibrary implements ISoundLibrary { private final Set blockedSounds = new ObjectOpenHashSet<>(); private final Set culledSounds = new ObjectOpenHashSet<>(); private final List startupSounds = new ArrayList<>(); + private final Map soundRemappings = new Object2ObjectOpenHashMap<>(); private List soundConfiguration = new ArrayList<>(); public SoundLibrary(Configuration config, IModLog logger, IMinecraftDirectories directories) { @@ -91,6 +109,7 @@ public void reload(ResourceUtilities resourceUtilities, IReloadEvent.Scope scope this.myRegistry.clear(); this.soundMetadata.clear(); this.soundFactories.clear(); + this.soundRemappings.clear(); this.loadSoundConfiguration(); // Initializes the internal sound registry once all the other mods have @@ -107,8 +126,13 @@ public void reload(ResourceUtilities resourceUtilities, IReloadEvent.Scope scope var findResults = resourceUtilities.findModResources(FACTORY_FILE_CODEC, FACTORY_JSON); findResults.forEach(this::registerSoundFactories); + + var soundMappings = resourceUtilities.findModResources(SOUND_MAPPING_CODEC, SOUND_MAPPING_JSON); + soundMappings.forEach(this::registerSoundRemappings); + this.logger.info("Number of SoundEvents cached: %d", this.myRegistry.size()); this.logger.info("Number of factories cached: %d", this.soundFactories.size()); + this.logger.info("Number of sound remappings cached: %d", this.soundRemappings.size()); } @Override @@ -209,6 +233,84 @@ public void saveIndividualSoundConfigs(Collection co this.save(); } + @Override + public Optional remapSound(SoundInstance soundInstance) { + + // Sounds played from the sound config menu are not remapped + if (soundInstance instanceof ConfigSoundInstance) + return Optional.empty(); + + var soundLocation = soundInstance.getLocation(); + + // If it is a thunder sound, and replacement is not enabled, don't do anything + if (THUNDER_SOUND.equals(soundLocation)) { + if (!this.config.soundOptions.replaceThunderSounds) + return Optional.empty(); + } else if (!this.config.soundOptions.remapSounds) { + // If it is not a thunder sound and remap is disabled + return Optional.empty(); + } + + var mappingRule = this.soundRemappings.get(soundLocation); + + if (mappingRule != null) { + // Get the BlockState of the block below the sound location if needed + @Nullable + BlockState blockState = null; + if (mappingRule.isBlockStateNeeded()) { + var level = GameUtils.getWorld().orElseThrow(); + var pos = BlockPos.containing(soundInstance.getX(), soundInstance.getY() + 0.25D, soundInstance.getZ()).below(); + blockState = level.getBlockState(pos); + + // If the BlockState is air or not solid, it means we are hanging at a block edge. Scan around looking for + // something that is solid. It's not perfect, but it's 90% down the center. + if (blockState.isAir() || !blockState.isSolid()) { + for (var dir : Direction.Plane.HORIZONTAL) { + blockState = level.getBlockState(MUTABLE_BLOCK_POS.setWithOffset(pos, dir)); + if (!blockState.isAir() && blockState.isSolid()) + break; + } + } + } + + var soundFactory = mappingRule + .findMatch(blockState) + .map(this::getSoundFactoryOrDefault); + + if (soundFactory.isPresent()) { + // Need to force a sound to be selected so we can get the volume + soundInstance.resolve(GameUtils.getSoundManager()); + var volumeScale = soundInstance.getVolume(); + return Optional.of(soundFactory.get().createAtLocation(soundInstance.getX(), soundInstance.getY(), soundInstance.getZ(), volumeScale)); + } + } + + return Optional.empty(); + } + + /** + * Examines the sound location information to determine if it is a mob step sound, and then remaps to a block + * sound similar to what happens with the player. + */ + @Nullable + private ResourceLocation remapMobStepSound(SoundInstance soundInstance) { + var soundLocation = soundInstance.getLocation(); + var path = soundLocation.getPath(); + if (path.startsWith("entity.") && path.endsWith("step")) { + // Get the mob this sound is for. We do not want to convert mobs like creepers. + var mobType = path.substring(7, path.indexOf('.', 7)); + if (!SOUND_REMAP_BLOCKED_MOBS.contains(mobType)) { + var level = GameUtils.getWorld().orElseThrow(); + var pos = BlockPos.containing(soundInstance.getX(), soundInstance.getY(), soundInstance.getZ()).below(); + soundLocation = level.getBlockState(pos).getSoundType().getStepSound().getLocation(); + this.logger.debug("Mob sound remapping from %s to %s", soundInstance.getLocation(), soundLocation); + return soundLocation; + } + } + + return null; + } + private void registerSoundFile(DiscoveredResource> soundFile) { var result = soundFile.resourceContent(); result.forEach((key, value) -> { @@ -225,6 +327,19 @@ private void registerSoundFile(DiscoveredResource> mappings) { + for (var mapping : mappings.resourceContent()) { + if (!this.soundRemappings.containsKey(mapping.soundEvent())) { + this.soundRemappings.put(mapping.soundEvent(), SoundMapping.of(mapping)); + } else { + // Need to merge rules + var existingMapping = this.soundRemappings.get(mapping.soundEvent()); + existingMapping.merge(mapping); + } + } + this.logger.debug("Registered %d sound remappings for namespace %s", mappings.resourceContent().size(), mappings.namespace()); + } + private void registerSoundFactories(DiscoveredResource> factories) { factories.resourceContent().forEach(factory -> this.soundFactories.put(factory.getLocation(), factory)); this.logger.debug("Registered %d sound factories for namespace %s", factories.resourceContent().size(), factories.namespace()); diff --git a/common/src/main/java/org/orecruncher/dsurround/effects/WaterRippleHandler.java b/common/src/main/java/org/orecruncher/dsurround/effects/WaterRippleHandler.java index 46ad4cca..1c6b15ed 100644 --- a/common/src/main/java/org/orecruncher/dsurround/effects/WaterRippleHandler.java +++ b/common/src/main/java/org/orecruncher/dsurround/effects/WaterRippleHandler.java @@ -7,8 +7,8 @@ import org.orecruncher.dsurround.Configuration; import org.orecruncher.dsurround.config.WaterRippleStyle; import org.orecruncher.dsurround.config.libraries.ITagLibrary; +import org.orecruncher.dsurround.effects.particles.ParticleRenderCollection; import org.orecruncher.dsurround.effects.particles.WaterRippleParticle; -import org.orecruncher.dsurround.lib.GameUtils; import org.orecruncher.dsurround.lib.di.ContainerManager; import org.orecruncher.dsurround.tags.FluidTags; @@ -17,6 +17,8 @@ public class WaterRippleHandler { private static final Configuration.BlockEffects CONFIG = ContainerManager.resolve(Configuration.BlockEffects.class); private static final ITagLibrary TAG_LIBRARY = ContainerManager.resolve(ITagLibrary.class); + private static final ParticleRenderCollection.Helper rippleHelper = + new ParticleRenderCollection.Helper<>("WaterRipples", () -> CONFIG.waterRippleStyle.getTexture()); // Fudge factor because the height algo is off. private static final double LIQUID_HEIGHT_ADJUST = (1D / 9D) + 0.1D; @@ -26,10 +28,8 @@ private static boolean doRipples() { } private static void addWaterRipple(ClientLevel world, double x, double y, double z) { - var ripple = new WaterRippleParticle( - CONFIG.waterRippleStyle, - world, x, y, z); - GameUtils.getParticleManager().add(ripple); + var ripple = new WaterRippleParticle(CONFIG.waterRippleStyle, world, x, y, z); + rippleHelper.add(ripple); } public static void createRippleParticle(ClientLevel world, Particle particle, Vec3 position) { diff --git a/common/src/main/java/org/orecruncher/dsurround/effects/entity/BreathEffect.java b/common/src/main/java/org/orecruncher/dsurround/effects/entity/BreathEffect.java index 2aea6e9c..b6430e65 100644 --- a/common/src/main/java/org/orecruncher/dsurround/effects/entity/BreathEffect.java +++ b/common/src/main/java/org/orecruncher/dsurround/effects/entity/BreathEffect.java @@ -2,7 +2,6 @@ import net.minecraft.core.BlockPos; import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; import org.orecruncher.dsurround.effects.particles.FrostBreathParticle; import org.orecruncher.dsurround.lib.GameUtils; @@ -10,7 +9,6 @@ import org.orecruncher.dsurround.lib.seasons.ISeasonalInformation; import org.orecruncher.dsurround.lib.system.ITickCount; import org.orecruncher.dsurround.lib.random.MurmurHash3; -import org.orecruncher.dsurround.lib.world.WorldUtils; public class BreathEffect extends EntityEffectBase { @@ -83,8 +81,7 @@ protected boolean showWaterBubbles(final BlockState headBlock) { protected boolean showFrostBreath(final LivingEntity entity, final BlockState headBlock, final BlockPos pos) { if (headBlock.isAir()) { - final Level world = entity.level(); - return SEASONAL_INFORMATION.isColdTemperature(world, pos); + return SEASONAL_INFORMATION.isColdTemperature(pos); } return false; } diff --git a/common/src/main/java/org/orecruncher/dsurround/effects/particles/ParticleRenderCollection.java b/common/src/main/java/org/orecruncher/dsurround/effects/particles/ParticleRenderCollection.java new file mode 100644 index 00000000..05dbfdc8 --- /dev/null +++ b/common/src/main/java/org/orecruncher/dsurround/effects/particles/ParticleRenderCollection.java @@ -0,0 +1,173 @@ +package org.orecruncher.dsurround.effects.particles; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.VertexConsumer; +import dev.architectury.event.events.client.ClientLifecycleEvent; +import net.minecraft.client.Camera; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.particle.Particle; +import net.minecraft.client.particle.ParticleRenderType; +import net.minecraft.client.particle.TextureSheetParticle; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.orecruncher.dsurround.eventing.ClientEventHooks; +import org.orecruncher.dsurround.eventing.ClientState; +import org.orecruncher.dsurround.eventing.CollectDiagnosticsEvent; +import org.orecruncher.dsurround.lib.GameUtils; +import org.orecruncher.dsurround.lib.collections.ObjectArray; + +import java.lang.ref.WeakReference; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * Special particle that proxies a collection in the particle engine. The commonality + * of the collection is rendering setup. This collection is centered on the player + * to prevent it from going out of scope. It is modeled on the NoRenderParticle in + * Minecraft. + */ +public final class ParticleRenderCollection extends Particle { + + private final Consumer setup; + private final Supplier textureSupplier; + private final ObjectArray particles; + + private ParticleRenderCollection(@NotNull ClientLevel clientLevel, @NotNull Supplier textureSupplier, @Nullable Consumer setup) { + super(clientLevel, 0, 0, 0); + this.setup = Objects.requireNonNullElseGet(setup, () -> this::standardSetup); + this.textureSupplier = textureSupplier; + this.particles = new ObjectArray<>(128); + this.tick(); + } + + @NotNull + @Override + public ParticleRenderType getRenderType() { + // Can't use NO_RENDER as the ParticleEngine will not attempt to render + return ParticleRenderType.CUSTOM; + } + + @Override + public void tick() { + // Keep the particle colocated with the player + var playerPos = GameUtils.getPlayer().orElseThrow().getEyePosition(); + this.setPos(playerPos.x(), playerPos.y(), playerPos.z()); + + if (!this.particles.isEmpty()) { + this.particles.forEach(Particle::tick); + this.particles.removeIf(p -> !p.isAlive()); + } + } + + @Override + public void render(@NotNull VertexConsumer vertexConsumer, @NotNull Camera camera, float tickDelta) { + if (this.particles.isEmpty()) + return; + + RenderSystem.setShaderTexture(0, this.textureSupplier.get()); + this.setup.accept(camera); + this.particles.forEach(p -> p.render(vertexConsumer, camera, tickDelta)); + } + + private void standardSetup(@NotNull Camera camera) { + RenderSystem.depthMask(true); + RenderSystem.enableBlend(); + RenderSystem.defaultBlendFunc(); + } + + public void add(@NotNull TParticle particle) { + // Can only accept custom style particles + if (particle.getRenderType() != this.getRenderType()) + throw new RuntimeException("Can only add render type %s particles to collection".formatted(this.getRenderType())); + this.particles.add(particle); + } + + /** + * Helper that manages related particles in Minecraft's ParticleEngine. The helper will register with events, so + * instances of this class need to be maintained as singletons throughout the lifetime of the client. + */ + public static final class Helper { + + private final String name; + private final Consumer setup; + private final Supplier textureSupplier; + + private WeakReference> particle; + private String diagnostics; + + /** + * Initializes a helper instance used to manage the state of the main particle within the ParticleEngine. + * Particle rendering will use the default setup. + * + * @param name The name of the helper; used in diagnostics + * @param textureSupplier Provides the texture to bind when rendering + */ + public Helper(@NotNull String name, @NotNull Supplier textureSupplier) { + this(name, textureSupplier, null); + } + + /** + * Initializes a helper instance used to manage the state of the main particle within the ParticleEngine. + * + * @param name The name of the helper; used in diagnostics + * @param textureSupplier Provides the texture to bind when rendering + * @param setup Provides for the configuration of the rendering system if the default is not enough + */ + public Helper(@NotNull String name, @NotNull Supplier textureSupplier, @Nullable Consumer setup) { + this.name = name; + this.setup = setup; + this.textureSupplier = textureSupplier; + this.diagnostics = this.name; + + ClientLifecycleEvent.CLIENT_LEVEL_LOAD.register(state -> this.clear()); + ClientEventHooks.COLLECT_DIAGNOSTICS.register(this::collectDiagnostics); + ClientState.TICK_END.register(this::tick); + } + + /** + * Adds a particle to the helper. + * + * @param particle The particle to add + */ + public void add(TParticle particle) { + this.get().add(particle); + } + + @NotNull + private ParticleRenderCollection get() { + var pc = this.particle != null ? this.particle.get() : null; + if (pc == null || !pc.isAlive()) { + pc = new ParticleRenderCollection<>(GameUtils.getWorld().orElseThrow(), this.textureSupplier, this.setup); + this.particle = new WeakReference<>(pc); + GameUtils.getParticleManager().add(pc); + } + return pc; + } + + private void clear() { + var pc = this.particle != null ? this.particle.get() : null; + if (pc != null) { + pc.remove(); + this.particle = null; + } + } + + private void tick(@NotNull Minecraft client) { + var pc = this.particle != null ? this.particle.get() : null; + this.diagnostics = this.name + ": "; + if (pc == null) + this.diagnostics += "Not Set"; + else if (!pc.isAlive()) + this.diagnostics += "DEAD"; + else + this.diagnostics += pc.particles.size(); + } + + private void collectDiagnostics(@NotNull CollectDiagnosticsEvent event) { + event.add(CollectDiagnosticsEvent.Section.Particles, this.diagnostics); + } + } +} diff --git a/common/src/main/java/org/orecruncher/dsurround/effects/particles/ParticleSheets.java b/common/src/main/java/org/orecruncher/dsurround/effects/particles/ParticleSheets.java deleted file mode 100644 index 5062006a..00000000 --- a/common/src/main/java/org/orecruncher/dsurround/effects/particles/ParticleSheets.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.orecruncher.dsurround.effects.particles; - -import net.minecraft.client.renderer.texture.SimpleTexture; -import net.minecraft.resources.ResourceLocation; -import org.orecruncher.dsurround.Constants; -import org.orecruncher.dsurround.Configuration; -import org.orecruncher.dsurround.lib.GameUtils; -import org.orecruncher.dsurround.lib.di.ContainerManager; -import org.orecruncher.dsurround.mixins.core.MixinParticleManager; - -import java.util.ArrayList; - -public final class ParticleSheets { - - private static Configuration.BlockEffects CONFIG; - - public static final ResourceLocation TEXTURE_WATER_RIPPLE_PIXELATED_CIRCLE = ResourceLocation.fromNamespaceAndPath(Constants.MOD_ID, "textures/particles/pixel_ripples.png"); - - public static final net.minecraft.client.particle.ParticleRenderType RIPPLE_RENDER = - new DsurroundParticleRenderType(TEXTURE_WATER_RIPPLE_PIXELATED_CIRCLE) { - @Override - protected ResourceLocation getTexture() { - return CONFIG.waterRippleStyle.getTexture(); - } - }; - - public static void register() { - - CONFIG = ContainerManager.resolve(Configuration.BlockEffects.class); - var manager = GameUtils.getTextureManager(); - manager.register(TEXTURE_WATER_RIPPLE_PIXELATED_CIRCLE, new SimpleTexture(TEXTURE_WATER_RIPPLE_PIXELATED_CIRCLE)); - - var existingSheets = MixinParticleManager.dsurround_getParticleTextureSheets(); - assert existingSheets != null; - existingSheets = new ArrayList<>(existingSheets); - existingSheets.add(RIPPLE_RENDER); - MixinParticleManager.dsurround_setParticleTextureSheets(existingSheets); - } -} diff --git a/common/src/main/java/org/orecruncher/dsurround/effects/particles/WaterRippleParticle.java b/common/src/main/java/org/orecruncher/dsurround/effects/particles/WaterRippleParticle.java index 9402d84e..7c03774e 100644 --- a/common/src/main/java/org/orecruncher/dsurround/effects/particles/WaterRippleParticle.java +++ b/common/src/main/java/org/orecruncher/dsurround/effects/particles/WaterRippleParticle.java @@ -71,7 +71,7 @@ public WaterRippleParticle(WaterRippleStyle rippleStyle, ClientLevel world, doub @Override public @NotNull ParticleRenderType getRenderType() { - return ParticleSheets.RIPPLE_RENDER; + return ParticleRenderType.CUSTOM; } @Override @@ -101,6 +101,7 @@ protected float getV1() { @Override public void render(VertexConsumer vertexConsumer, Camera camera, float tickDelta) { + Vec3 vec3d = camera.getPosition(); float X = (float)(Mth.lerp(tickDelta, this.xo, this.x) - vec3d.x()); float Y = (float)(Mth.lerp(tickDelta, this.yo, this.y) - vec3d.y()); diff --git a/common/src/main/java/org/orecruncher/dsurround/eventing/CollectDiagnosticsEvent.java b/common/src/main/java/org/orecruncher/dsurround/eventing/CollectDiagnosticsEvent.java index 7bc816e0..4d55e0cd 100644 --- a/common/src/main/java/org/orecruncher/dsurround/eventing/CollectDiagnosticsEvent.java +++ b/common/src/main/java/org/orecruncher/dsurround/eventing/CollectDiagnosticsEvent.java @@ -12,6 +12,7 @@ public final class CollectDiagnosticsEvent { public enum Section { Header(false), Systems, + Particles, Timers(false), Environment(false), Emitters, diff --git a/common/src/main/java/org/orecruncher/dsurround/gui/overlay/ClockOverlay.java b/common/src/main/java/org/orecruncher/dsurround/gui/overlay/ClockOverlay.java index a634748f..a13495ef 100644 --- a/common/src/main/java/org/orecruncher/dsurround/gui/overlay/ClockOverlay.java +++ b/common/src/main/java/org/orecruncher/dsurround/gui/overlay/ClockOverlay.java @@ -58,7 +58,7 @@ public void tick(Minecraft client) { this.clockDisplay.clear(); this.clockDisplay.add(this.clock.getFormattedTime()); - this.seasonalInformation.getCurrentSeasonTranslated(player.level()).ifPresent(this.clockDisplay::add); + this.seasonalInformation.getCurrentSeasonTranslated().ifPresent(this.clockDisplay::add); var textRender = GameUtils.getTextRenderer(); this.renderWidth = textRender.width(this.clockDisplay.get(0)); diff --git a/common/src/main/java/org/orecruncher/dsurround/gui/overlay/CompassOverlay.java b/common/src/main/java/org/orecruncher/dsurround/gui/overlay/CompassOverlay.java index 29ed7556..c9a2f63b 100644 --- a/common/src/main/java/org/orecruncher/dsurround/gui/overlay/CompassOverlay.java +++ b/common/src/main/java/org/orecruncher/dsurround/gui/overlay/CompassOverlay.java @@ -11,8 +11,10 @@ import org.joml.Matrix4f; import org.orecruncher.dsurround.Constants; import org.orecruncher.dsurround.Configuration; +import org.orecruncher.dsurround.config.libraries.IDimensionInformation; import org.orecruncher.dsurround.config.libraries.ITagLibrary; import org.orecruncher.dsurround.lib.GameUtils; +import org.orecruncher.dsurround.lib.random.Randomizer; import org.orecruncher.dsurround.tags.ItemEffectTags; public class CompassOverlay extends AbstractOverlay { @@ -30,14 +32,19 @@ public class CompassOverlay extends AbstractOverlay { private static final ResourceLocation COMPASS_TEXTURE = ResourceLocation.fromNamespaceAndPath(Constants.MOD_ID, "textures/compass.png"); private final ITagLibrary tagLibrary; + private final IDimensionInformation dimensionInformation; private final Configuration config; + private final CompassWobble wobbler; private boolean showCompass; + private boolean spinRandomly; private float scale; private float spriteOffset; - public CompassOverlay(Configuration config, ITagLibrary tagLibrary) { + public CompassOverlay(Configuration config, ITagLibrary tagLibrary, IDimensionInformation dimensionInformation) { this.tagLibrary = tagLibrary; + this.dimensionInformation = dimensionInformation; this.config = config; + this.wobbler = new CompassWobble(); this.showCompass = false; this.spriteOffset = this.config.compassAndClockOptions.compassStyle.getSpriteNumber(); this.scale = (float)this.config.compassAndClockOptions.scale; @@ -53,7 +60,20 @@ public void tick(Minecraft client) { var player = GameUtils.getPlayer().orElseThrow(); var mainHandItem = player.getMainHandItem(); var offHandItem = player.getOffhandItem(); - this.showCompass = this.doShowCompass(mainHandItem) || this.doShowCompass(offHandItem); + + var mainHandShow = this.doShowCompass(mainHandItem); + var offHandShow = this.doShowCompass(offHandItem); + this.showCompass = mainHandShow || offHandShow; + + if (mainHandShow) { + this.spinRandomly = this.doCompassSpin(mainHandItem); + } + + if (offHandShow && !this.spinRandomly) { + this.spinRandomly = this.doCompassSpin(offHandItem); + } + + this.wobbler.update(player.level().getGameTime()); } } @@ -61,6 +81,10 @@ private boolean doShowCompass(ItemStack stack) { return this.tagLibrary.is(ItemEffectTags.COMPASSES, stack); } + private boolean doCompassSpin(ItemStack stack) { + return this.dimensionInformation.getCompassWobble() && this.tagLibrary.is(ItemEffectTags.COMPASS_WOBBLE, stack); + } + @Override public void render(GuiGraphics context, float partialTick) { if (!this.showCompass) @@ -72,9 +96,16 @@ public void render(GuiGraphics context, float partialTick) { matrixStack.pushPose(); - final var player = GameUtils.getPlayer().orElseThrow(); + float rotation; - int direction = Mth.floor(((player.getViewYRot(partialTick) * TEXTURE_SIZE) / 360F) + 0.5D) & (TEXTURE_SIZE - 1); + if (this.spinRandomly) { + rotation = this.wobbler.getRandomlySpinningRotation(partialTick); + } else { + final var player = GameUtils.getPlayer().orElseThrow(); + rotation = player.getViewYRot(partialTick); + } + + int direction = Mth.floor(((rotation * TEXTURE_SIZE) / 360F) + 0.5D) & (TEXTURE_SIZE - 1); float x = (context.guiWidth() - BAND_WIDTH * this.scale) / 2F; float y = (context.guiHeight() - CROSSHAIR_OFFSET - BAND_HEIGHT * this.scale) / 2F; @@ -117,4 +148,56 @@ void drawTexturedQuad(PoseStack stack, ResourceLocation texture, float x1, float if (mesh != null) BufferUploader.drawWithShader(mesh); } + + /** + * Cloned from the Minecraft compass code + */ + static class CompassWobble { + private static int TICK_DELAY = 5; + private static float MAX_DELTA_TICK = 1F / 20F; + private float targetRotation; + private float lastRotation; + private float rotation; + private float rotationStep; + private long lastUpdateTick; + private int tickWait; + + public float getRandomlySpinningRotation(float partialTick) { + return Mth.lerp(partialTick, this.lastRotation, this.rotation) * 360F; + } + + public void update(long tickCount) { + if (this.lastUpdateTick != tickCount) { + this.updateRotation(tickCount); + } + } + + private void updateRotation(long tickCount) { + this.lastUpdateTick = tickCount; + this.lastRotation = this.rotation; + + if (this.rotation < this.targetRotation) { + this.rotation += this.rotationStep; + if (this.rotation > this.targetRotation) { + this.rotation = this.targetRotation; + } + } else if (this.rotation > this.targetRotation) { + this.rotation -= this.rotationStep; + if (this.rotation < this.targetRotation) { + this.rotation = this.targetRotation; + } + } + + // If we reached the target rotation, we need to set a new one + // and calculate the stepping to get there. + if (this.rotation == this.targetRotation) { + if (this.tickWait < 0 ) { + this.tickWait = Randomizer.current().nextInt(TICK_DELAY); + } else if (--this.tickWait == 0) { + this.targetRotation = Randomizer.current().nextFloat(); + this.rotationStep = MAX_DELTA_TICK; + } + } + } + } } diff --git a/common/src/main/java/org/orecruncher/dsurround/gui/overlay/DiagnosticsOverlay.java b/common/src/main/java/org/orecruncher/dsurround/gui/overlay/DiagnosticsOverlay.java index b8c3c741..32d71fa6 100644 --- a/common/src/main/java/org/orecruncher/dsurround/gui/overlay/DiagnosticsOverlay.java +++ b/common/src/main/java/org/orecruncher/dsurround/gui/overlay/DiagnosticsOverlay.java @@ -37,6 +37,7 @@ public class DiagnosticsOverlay extends AbstractOverlay { static { COLOR_MAP.put(CollectDiagnosticsEvent.Section.Header, ColorPalette.PUMPKIN_ORANGE); COLOR_MAP.put(CollectDiagnosticsEvent.Section.Systems, ColorPalette.GREEN); + COLOR_MAP.put(CollectDiagnosticsEvent.Section.Particles, ColorPalette.HOT_PINK); COLOR_MAP.put(CollectDiagnosticsEvent.Section.Timers, ColorPalette.KEY_LIME); COLOR_MAP.put(CollectDiagnosticsEvent.Section.Environment, ColorPalette.AQUAMARINE); COLOR_MAP.put(CollectDiagnosticsEvent.Section.Emitters, ColorPalette.SEASHELL); @@ -51,6 +52,7 @@ public class DiagnosticsOverlay extends AbstractOverlay { LEFT_SIDE_LAYOUT.add(CollectDiagnosticsEvent.Section.Header); LEFT_SIDE_LAYOUT.add(CollectDiagnosticsEvent.Section.Environment); LEFT_SIDE_LAYOUT.add(CollectDiagnosticsEvent.Section.Systems); + LEFT_SIDE_LAYOUT.add(CollectDiagnosticsEvent.Section.Particles); LEFT_SIDE_LAYOUT.add(CollectDiagnosticsEvent.Section.Emitters); LEFT_SIDE_LAYOUT.add(CollectDiagnosticsEvent.Section.Sounds); diff --git a/common/src/main/java/org/orecruncher/dsurround/gui/overlay/OverlayManager.java b/common/src/main/java/org/orecruncher/dsurround/gui/overlay/OverlayManager.java index 2b2b032f..9c1a2c28 100644 --- a/common/src/main/java/org/orecruncher/dsurround/gui/overlay/OverlayManager.java +++ b/common/src/main/java/org/orecruncher/dsurround/gui/overlay/OverlayManager.java @@ -1,5 +1,6 @@ package org.orecruncher.dsurround.gui.overlay; +import net.minecraft.client.DeltaTracker; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; import org.orecruncher.dsurround.lib.collections.ObjectArray; @@ -21,7 +22,8 @@ public OverlayManager() { ClientState.TICK_END.register(this::tick); } - public void render(GuiGraphics context, float partialTick) { + public void render(GuiGraphics context, DeltaTracker deltaTracker) { + var partialTick = deltaTracker.getGameTimeDeltaTicks(); this.overlays.forEach(overlay -> overlay.render(context, partialTick)); } diff --git a/common/src/main/java/org/orecruncher/dsurround/gui/overlay/plugins/RuntimeDiagnosticsPlugin.java b/common/src/main/java/org/orecruncher/dsurround/gui/overlay/plugins/RuntimeDiagnosticsPlugin.java index f455b1c7..d2e26b36 100644 --- a/common/src/main/java/org/orecruncher/dsurround/gui/overlay/plugins/RuntimeDiagnosticsPlugin.java +++ b/common/src/main/java/org/orecruncher/dsurround/gui/overlay/plugins/RuntimeDiagnosticsPlugin.java @@ -43,7 +43,7 @@ public void onCollect(CollectDiagnosticsEvent event) { this.clock.update(world); event.add(CollectDiagnosticsEvent.Section.Header, this.clock.getFormattedTime()); - var seasonInfo = this.seasonalInformation.getCurrentSeasonTranslated(world).orElse(Component.literal("UNKNOWN")); + var seasonInfo = this.seasonalInformation.getCurrentSeasonTranslated().orElse(Component.literal("UNKNOWN")); var seasonText = Component.translatable("Season: %s (%s)", seasonInfo, this.seasonalInformation.getProviderName()); event.add(CollectDiagnosticsEvent.Section.Header, seasonText); diff --git a/common/src/main/java/org/orecruncher/dsurround/lib/BlockPosUtil.java b/common/src/main/java/org/orecruncher/dsurround/lib/BlockPosUtil.java deleted file mode 100644 index 3c862368..00000000 --- a/common/src/main/java/org/orecruncher/dsurround/lib/BlockPosUtil.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.orecruncher.dsurround.lib; - -import net.minecraft.core.BlockPos; - -public final class BlockPosUtil { - - public static boolean canFormCuboid(final BlockPos p1, final BlockPos p2) { - return !(p1.getX() == p2.getX() && p1.getZ() == p2.getZ() && p1.getY() == p2.getY()); - } - - public static BlockPos createMinPoint( final BlockPos p1, final BlockPos p2) { - return new BlockPos(Math.min(p1.getX(), p2.getX()), Math.min(p1.getY(), p2.getY()), - Math.min(p1.getZ(), p2.getZ())); - } - - public static BlockPos createMaxPoint( final BlockPos p1, final BlockPos p2) { - return new BlockPos(Math.max(p1.getX(), p2.getX()), Math.max(p1.getY(), p2.getY()), - Math.max(p1.getZ(), p2.getZ())); - } - - /** - * Determines if the test point is contained within the volume described by two - * other points. It is expected that the calling routine has ensured that the - * min/max points are valid. If they are not valid, the results will more than - * likely be erroneous. - * - * @param test The point that is being tested - * @param min The point describing the minimum vertex of the volume - * @param max The point describing the maximum vertex of the volume - * @return Whether the test point is within the boundaries of the volume, - * inclusive - */ - public static boolean contains( final BlockPos test, final BlockPos min, - final BlockPos max) { - return test.getX() >= min.getX() && test.getX() <= max.getX() - && test.getY() >= min.getY() && test.getY() <= max.getY() - && test.getZ() >= min.getZ() && test.getZ() <= max.getZ(); - } -} \ No newline at end of file diff --git a/common/src/main/java/org/orecruncher/dsurround/lib/Library.java b/common/src/main/java/org/orecruncher/dsurround/lib/Library.java index 2ac12af1..079546d7 100644 --- a/common/src/main/java/org/orecruncher/dsurround/lib/Library.java +++ b/common/src/main/java/org/orecruncher/dsurround/lib/Library.java @@ -7,8 +7,6 @@ import org.orecruncher.dsurround.lib.platform.*; import org.orecruncher.dsurround.eventing.ClientState; import org.orecruncher.dsurround.lib.logging.IModLog; -import org.orecruncher.dsurround.lib.seasons.ISeasonalInformation; -import org.orecruncher.dsurround.lib.seasons.SeasonManager; import org.orecruncher.dsurround.lib.system.ISystemClock; import org.orecruncher.dsurround.lib.system.ITickCount; import org.orecruncher.dsurround.lib.system.SystemClock; @@ -52,7 +50,6 @@ private static void configureServiceDependencies() { .registerSingleton(ISystemClock.class, SystemClock.class) .registerSingleton(IMinecraftDirectories.class, modInfo) .registerSingleton(IClientTasking.class, ClientTasking.class) - .registerSingleton(ITickCount.class, TickCounter.class) - .registerSingleton(ISeasonalInformation.class, SeasonManager.HANDLER); + .registerSingleton(ITickCount.class, TickCounter.class); } } diff --git a/common/src/main/java/org/orecruncher/dsurround/lib/WeightTable.java b/common/src/main/java/org/orecruncher/dsurround/lib/WeightTable.java index c0eeb7b4..05e9a122 100644 --- a/common/src/main/java/org/orecruncher/dsurround/lib/WeightTable.java +++ b/common/src/main/java/org/orecruncher/dsurround/lib/WeightTable.java @@ -1,10 +1,10 @@ package org.orecruncher.dsurround.lib; -import org.orecruncher.dsurround.lib.collections.ObjectArray; +import net.minecraft.util.random.WeightedEntry; +import net.minecraft.util.random.WeightedRandom; import org.orecruncher.dsurround.lib.random.IRandomizer; import org.orecruncher.dsurround.lib.random.Randomizer; -import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.stream.Stream; @@ -12,58 +12,27 @@ /** * Classic WeightTable for random weighted selection. */ -@SuppressWarnings("unused") public class WeightTable { public static Optional makeSelection(final Stream> inputStream) { - return makeSelection(inputStream.toList(), Randomizer.current()); + return makeSelection(inputStream, Randomizer.current()); } public static Optional makeSelection(final Stream> inputStream, IRandomizer randomizer) { return makeSelection(inputStream.toList(), randomizer); } - public static Optional makeSelection(final Collection> selections) { - return makeSelection(selections, Randomizer.current()); - } - - public static Optional makeSelection(final Collection> selections, IRandomizer randomizer) { + public static Optional makeSelection(final List> selections, IRandomizer randomizer) { if (selections.isEmpty()) return Optional.empty(); - if (selections.size() == 1) { - T theItem; - if (selections instanceof List> theList) - theItem = theList.getFirst().getItem(); - else if (selections instanceof ObjectArray> theArray) - theItem = theArray.getFirst().getItem(); - else - theItem = selections.iterator().next().getItem(); - return Optional.of(theItem); - } - - int totalWeight = 0; - for (var e : selections) - totalWeight += e.getWeight(); - if (totalWeight == 0) - return Optional.empty(); - - int targetWeight = randomizer.nextInt(totalWeight); - - for (var e : selections) { - targetWeight -= e.getWeight(); - if (targetWeight < 0) - return Optional.of(e.getItem()); - } + if (selections.size() == 1) + return Optional.of(selections.getFirst().data()); - // Shouldn't get here - throw new RuntimeException("Bad weight table - ran off the end"); + return WeightedRandom.getRandomItem(randomizer, selections).map(IItem::data); } - public interface IItem { - - int getWeight(); - - T getItem(); + public interface IItem extends WeightedEntry { + T data(); } } \ No newline at end of file diff --git a/common/src/main/java/org/orecruncher/dsurround/lib/collections/ObjectArray.java b/common/src/main/java/org/orecruncher/dsurround/lib/collections/ObjectArray.java index c597a1d2..48f91b6f 100644 --- a/common/src/main/java/org/orecruncher/dsurround/lib/collections/ObjectArray.java +++ b/common/src/main/java/org/orecruncher/dsurround/lib/collections/ObjectArray.java @@ -62,6 +62,10 @@ public T getFirst() { return this.get(0); } + public T getLast() { + return this.get(this.size() - 1); + } + private void remove0(final int idx) { final Object m = this.data[--this.insertionIdx]; this.data[this.insertionIdx] = null; @@ -71,11 +75,11 @@ private void remove0(final int idx) { @SuppressWarnings("unchecked") @Override - public boolean removeIf(final Predicate pred) { + public boolean removeIf(@NotNull final Predicate filter) { boolean result = false; for (int i = this.insertionIdx - 1; i >= 0; i--) { final T t = (T) this.data[i]; - if (pred.test(t)) { + if (filter.test(t)) { result = true; this.remove0(i); } @@ -117,7 +121,7 @@ public boolean contains(final Object o) { return result; } - @SuppressWarnings({"unchecked", "hiding"}) + @SuppressWarnings({"unchecked", "TypeParameterHidesVisibleType"}) @Override public T @NotNull [] toArray(final T[] a) { // From ArrayList impl @@ -173,7 +177,7 @@ public boolean removeAll(final Collection c) { } @Override - public boolean retainAll(final Collection c) { + public boolean retainAll(@NotNull final Collection c) { return this.removeIf(entry -> !c.contains(entry)); } diff --git a/common/src/main/java/org/orecruncher/dsurround/lib/gui/ColorPalette.java b/common/src/main/java/org/orecruncher/dsurround/lib/gui/ColorPalette.java index 9856d0cb..f2298414 100644 --- a/common/src/main/java/org/orecruncher/dsurround/lib/gui/ColorPalette.java +++ b/common/src/main/java/org/orecruncher/dsurround/lib/gui/ColorPalette.java @@ -2,6 +2,7 @@ import net.minecraft.ChatFormatting; import net.minecraft.network.chat.TextColor; +import net.minecraft.util.FastColor; import net.minecraft.util.Mth; @SuppressWarnings("unused") @@ -74,20 +75,21 @@ public final class ColorPalette { public static final TextColor ANTIQUE_WHITE = of(250,235,215); public static final TextColor PEARLY_PURPLE = of(183,104,162); public static final TextColor FRESH_AIR = of(166,231,255); + public static final TextColor HOT_PINK = of("#ff69b4"); public static final TextColor LEMON = of(254, 251, 1); public static final TextColor ELECTRIC_GREEN = of(0, 237, 1); public static int getRed(int rgb) { - return (rgb >> 16) & 0xFF; + return FastColor.ARGB32.red(rgb); } public static int getGreen(int rgb) { - return (rgb >> 8) & 0xFF; + return FastColor.ARGB32.green(rgb); } public static int getBlue(int rgb) { - return rgb & 0xFF; + return FastColor.ARGB32.blue(rgb); } private static TextColor of(ChatFormatting formatColor) { @@ -98,14 +100,16 @@ private static TextColor of(String formatColor) { return TextColor.parseColor(formatColor).getOrThrow(); } + private static TextColor of(int rgb) { + return TextColor.fromRgb(rgb); + } + private static TextColor of(int red, int green, int blue) { - return TextColor.fromRgb(toRGB(red, green, blue)); + return of(toRGB(red, green, blue)); } static int toRGB(int red, int green, int blue) { - return ((red & 0xFF) << 16) | - ((green & 0xFF) << 8) | - ((blue & 0xFF)); + return FastColor.ARGB32.color(red, green, blue); } public static TextColor lerp(float scale, TextColor start, TextColor end) { @@ -116,9 +120,9 @@ public static TextColor lerp(float scale, TextColor start, TextColor end) { var endGreen = getGreen(end.getValue()); var endBlue = getBlue(end.getValue()); - var red = (int)Mth.lerp(scale, startRed, endRed); - var green = (int)Mth.lerp(scale, startGreen, endGreen); - var blue = (int)Mth.lerp(scale, startBlue, endBlue); + var red = Mth.lerpInt(scale, startRed, endRed); + var green = Mth.lerpInt(scale, startGreen, endGreen); + var blue = Mth.lerpInt(scale, startBlue, endBlue); return of(red, green, blue); } diff --git a/common/src/main/java/org/orecruncher/dsurround/lib/seasons/ISeasonalInformation.java b/common/src/main/java/org/orecruncher/dsurround/lib/seasons/ISeasonalInformation.java index b2dd3aa2..f04cf1ba 100644 --- a/common/src/main/java/org/orecruncher/dsurround/lib/seasons/ISeasonalInformation.java +++ b/common/src/main/java/org/orecruncher/dsurround/lib/seasons/ISeasonalInformation.java @@ -1,10 +1,11 @@ package org.orecruncher.dsurround.lib.seasons; +import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; -import net.minecraft.world.level.Level; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.levelgen.Heightmap; +import org.orecruncher.dsurround.lib.GameUtils; import java.util.Optional; @@ -18,95 +19,99 @@ public interface ISeasonalInformation { /** * Gets the name of the current season, if any */ - Optional getCurrentSeason(Level world); + Optional getCurrentSeason(); /** * Gets the translated season name from the provider resources. */ - Optional getCurrentSeasonTranslated(Level world); + Optional getCurrentSeasonTranslated(); /** * Indicates if the current season is considered Spring. */ - default boolean isSpring(Level world) { + default boolean isSpring() { return true; } /** * Indicates if the current season is considered Summer. */ - default boolean isSummer(Level world) { + default boolean isSummer() { return false; } /** * Indicates if the current season is considered Autumn/Fall. */ - default boolean isAutumn(Level world) { + default boolean isAutumn() { return false; } /** * Indicates if the current season is considered Winter. */ - default boolean isWinter(Level world) { + default boolean isWinter() { return false; } /** * Gets the temperature at the specified block location taking into account any seasonal variance. */ - float getTemperature(Level world, BlockPos blockPos); + float getTemperature(BlockPos blockPos); /** * Indicates whether the temperature at the given position is considered cold. For example, if the temp * is cold, the frost breath effect can be produced. */ - default boolean isColdTemperature(Level world, BlockPos blockPos) { - return this.getTemperature(world, blockPos) < 0.2F; + default boolean isColdTemperature(BlockPos blockPos) { + return this.getTemperature(blockPos) < 0.2F; } /** * Indicates whether the temperature at the given position is considered cold enough for snow. */ - default boolean isSnowTemperature(Level world, BlockPos blockPos) { - return this.getTemperature(world, blockPos) < 0.15F; + default boolean isSnowTemperature(BlockPos blockPos) { + return this.getTemperature(blockPos) < 0.15F; } /** * Gets the Y on the XZ plane at which precipitation will strike. */ - default int getPrecipitationHeight(Level world, BlockPos pos) { - return world.getHeight(Heightmap.Types.MOTION_BLOCKING, pos.getX(), pos.getZ()); + default int getPrecipitationHeight(BlockPos pos) { + return this.level().getHeight(Heightmap.Types.MOTION_BLOCKING, pos.getX(), pos.getZ()); } /** * Gets the possible precipitation that can occur in the biome at the specified position. */ - default Biome.Precipitation getPrecipitationAt(Level world, BlockPos blockPos) { - return world.getBiome(blockPos).value().getPrecipitationAt(blockPos); + default Biome.Precipitation getPrecipitationAt(BlockPos blockPos) { + return this.level().getBiome(blockPos).value().getPrecipitationAt(blockPos); } /** * Gets the active precipitation occurring at the specified position. */ - default Biome.Precipitation getActivePrecipitation(Level world, BlockPos pos) { - if (!world.isRaining()) { + default Biome.Precipitation getActivePrecipitation(BlockPos pos) { + if (!this.level().isRaining()) { // Not currently raining return Biome.Precipitation.NONE; } // If the biome has no rain... - if (this.getPrecipitationAt(world, pos) == Biome.Precipitation.NONE) + if (this.getPrecipitationAt(pos) == Biome.Precipitation.NONE) return Biome.Precipitation.NONE; // Is there a block above that is blocking the rainfall? - var p = this.getPrecipitationHeight(world, pos); + var p = this.getPrecipitationHeight(pos); if (p > pos.getY()) { return Biome.Precipitation.NONE; } // Use the temperature of the biome to get whether it is raining or snowing - return this.isSnowTemperature(world, pos) ? Biome.Precipitation.SNOW : Biome.Precipitation.RAIN; + return this.isSnowTemperature(pos) ? Biome.Precipitation.SNOW : Biome.Precipitation.RAIN; + } + + default ClientLevel level() { + return GameUtils.getWorld().orElseThrow(); } } diff --git a/common/src/main/java/org/orecruncher/dsurround/lib/seasons/SeasonManager.java b/common/src/main/java/org/orecruncher/dsurround/lib/seasons/SeasonManager.java index 809ece41..4fd712c3 100644 --- a/common/src/main/java/org/orecruncher/dsurround/lib/seasons/SeasonManager.java +++ b/common/src/main/java/org/orecruncher/dsurround/lib/seasons/SeasonManager.java @@ -3,6 +3,7 @@ import dev.architectury.platform.Platform; import org.orecruncher.dsurround.Constants; import org.orecruncher.dsurround.lib.Library; +import org.orecruncher.dsurround.lib.di.ContainerManager; import org.orecruncher.dsurround.lib.seasons.compat.SereneSeasons; import org.orecruncher.dsurround.lib.seasons.compat.VanillaSeasons; @@ -12,9 +13,9 @@ public class SeasonManager { static { if (Platform.isModLoaded(Constants.SERENE_SEASONS)) { - HANDLER = new SereneSeasons(); + HANDLER = ContainerManager.resolve(SereneSeasons.class); } else { - HANDLER = new VanillaSeasons(); + HANDLER = ContainerManager.resolve(VanillaSeasons.class); } Library.LOGGER.info("Season provider: %s", HANDLER.getProviderName()); diff --git a/common/src/main/java/org/orecruncher/dsurround/lib/seasons/compat/SereneSeasons.java b/common/src/main/java/org/orecruncher/dsurround/lib/seasons/compat/SereneSeasons.java index 4b534e7b..bf298753 100644 --- a/common/src/main/java/org/orecruncher/dsurround/lib/seasons/compat/SereneSeasons.java +++ b/common/src/main/java/org/orecruncher/dsurround/lib/seasons/compat/SereneSeasons.java @@ -1,9 +1,10 @@ package org.orecruncher.dsurround.lib.seasons.compat; +import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; -import net.minecraft.world.level.Level; import net.minecraft.world.level.biome.Biome; +import org.orecruncher.dsurround.config.libraries.IDimensionInformation; import sereneseasons.api.season.Season; import sereneseasons.api.season.SeasonHelper; import sereneseasons.season.SeasonHooks; @@ -18,20 +19,23 @@ public class SereneSeasons extends AbstractSeasonProvider { private Season.TropicalSeason tropicalSeason; private Component computed; - public SereneSeasons() { + private final IDimensionInformation dimensionInformation; + + public SereneSeasons(IDimensionInformation dimensionInformation) { super("Serene Seasons"); + this.dimensionInformation = dimensionInformation; } @Override - public Optional getCurrentSeason(Level world) { - var helper = SeasonHelper.getSeasonState(world); + public Optional getCurrentSeason() { + var helper = SeasonHelper.getSeasonState(this.level()); var subSeason = helper.getSubSeason(); return Optional.of(Component.literal(subSeason.toString())); } @Override - public Optional getCurrentSeasonTranslated(Level world) { - var helper = SeasonHelper.getSeasonState(world); + public Optional getCurrentSeasonTranslated() { + var helper = SeasonHelper.getSeasonState(this.level()); if (this.subSeason != helper.getSubSeason() || this.tropicalSeason != helper.getTropicalSeason()) { var subSeasonKey = "desc.sereneseasons." + helper.getSeason().toString().toLowerCase(Locale.ROOT); var tropicalSeasonKey = "desc.sereneseasons." + helper.getTropicalSeason().toString().toLowerCase(Locale.ROOT); @@ -45,35 +49,42 @@ public Optional getCurrentSeasonTranslated(Level world) { return Optional.of(this.computed); } - public boolean isSpring(Level world) { - var helper = SeasonHelper.getSeasonState(world); + public boolean isSpring() { + var helper = SeasonHelper.getSeasonState(this.level()); return helper.getSeason() == Season.SPRING; } - public boolean isSummer(Level world) { - var helper = SeasonHelper.getSeasonState(world); + public boolean isSummer() { + var helper = SeasonHelper.getSeasonState(this.level()); return helper.getSeason() == Season.SUMMER; } - public boolean isAutumn(Level world) { - var helper = SeasonHelper.getSeasonState(world); + public boolean isAutumn() { + var helper = SeasonHelper.getSeasonState(this.level()); return helper.getSeason() == Season.AUTUMN; } - public boolean isWinter(Level world) { - var helper = SeasonHelper.getSeasonState(world); + public boolean isWinter() { + var helper = SeasonHelper.getSeasonState(this.level()); return helper.getSeason() == Season.WINTER; } @Override - public Biome.Precipitation getPrecipitationAt(Level world, BlockPos blockPos) { - var biome = world.getBiome(blockPos); - return SeasonHooks.getPrecipitationAtSeasonal(world, biome, blockPos); + public Biome.Precipitation getPrecipitationAt(BlockPos blockPos) { + var level = this.level(); + var biome = level.getBiome(blockPos); + return SeasonHooks.getPrecipitationAtSeasonal(level, biome, blockPos); + } + + @Override + public float getTemperature(BlockPos blockPos) { + var level = this.level(); + var biome = level.getBiome(blockPos); + return SeasonHooks.getBiomeTemperature(level, biome, blockPos); } @Override - public float getTemperature(Level world, BlockPos blockPos) { - var biome = world.getBiome(blockPos); - return SeasonHooks.getBiomeTemperature(world, biome, blockPos); + public ClientLevel level() { + return this.dimensionInformation.level(); } } diff --git a/common/src/main/java/org/orecruncher/dsurround/lib/seasons/compat/VanillaSeasons.java b/common/src/main/java/org/orecruncher/dsurround/lib/seasons/compat/VanillaSeasons.java index ac7e38c6..a9b19d3e 100644 --- a/common/src/main/java/org/orecruncher/dsurround/lib/seasons/compat/VanillaSeasons.java +++ b/common/src/main/java/org/orecruncher/dsurround/lib/seasons/compat/VanillaSeasons.java @@ -2,7 +2,6 @@ import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; -import net.minecraft.world.level.Level; import org.orecruncher.dsurround.mixinutils.IBiomeExtended; import java.util.Optional; @@ -14,18 +13,18 @@ public VanillaSeasons() { } @Override - public Optional getCurrentSeason(Level world) { + public Optional getCurrentSeason() { return Optional.of(Component.translatable("dsurround.text.seasons.spring")); } @Override - public Optional getCurrentSeasonTranslated(Level world) { - return getCurrentSeason(world); + public Optional getCurrentSeasonTranslated() { + return getCurrentSeason(); } @Override - public float getTemperature(Level world, BlockPos blockPos) { - var biome = world.getBiome(blockPos).value(); + public float getTemperature(BlockPos blockPos) { + var biome = this.level().getBiome(blockPos).value(); return ((IBiomeExtended)(Object)biome).dsurround_getTemperature(blockPos); } } diff --git a/common/src/main/java/org/orecruncher/dsurround/mixins/audio/MixinMusicManager.java b/common/src/main/java/org/orecruncher/dsurround/mixins/audio/MixinMusicManager.java index 1ed5f006..a1153acb 100644 --- a/common/src/main/java/org/orecruncher/dsurround/mixins/audio/MixinMusicManager.java +++ b/common/src/main/java/org/orecruncher/dsurround/mixins/audio/MixinMusicManager.java @@ -2,9 +2,11 @@ import net.minecraft.client.resources.sounds.SoundInstance; import net.minecraft.client.sounds.MusicManager; +import net.minecraft.network.chat.Component; import net.minecraft.sounds.Music; import org.jetbrains.annotations.Nullable; import org.orecruncher.dsurround.gui.sound.SoundToast; +import org.orecruncher.dsurround.lib.gui.ColorPalette; import org.orecruncher.dsurround.mixinutils.IMusicManager; import org.orecruncher.dsurround.mixinutils.MixinHelpers; import org.spongepowered.asm.mixin.Mixin; @@ -64,6 +66,21 @@ public void dsurround_setPaused(boolean flag) { } } + @Override + public Component dsurround_whatsPlaying() { + if (this.currentMusic == null) + return Component.translatable("dsurround.text.musicmanager.nothing"); + + // Lookup meta information + var metaData = MixinHelpers.SOUND_LIBRARY.getSoundMetadata(this.currentMusic.getLocation()); + if (metaData == null || Component.empty().equals(metaData.getTitle())) + return Component.literal(this.currentMusic.getLocation().toString()); + + var title = metaData.getTitle().copy().withColor(ColorPalette.PUMPKIN_ORANGE.getValue()); + var author = metaData.getCredits().get(0).author().copy().withColor(ColorPalette.WHEAT.getValue()); + return Component.translatable("dsurround.text.musicmanager.playing", title, author, Component.translationArg(this.currentMusic.getLocation())); + } + @Inject(method = "tick()V", at = @At("HEAD"), cancellable = true) public void dsurround_pauseTickCheck(CallbackInfo ci) { if (this.dsurround_pauseTicking) diff --git a/common/src/main/java/org/orecruncher/dsurround/mixins/audio/MixinSoundEngine.java b/common/src/main/java/org/orecruncher/dsurround/mixins/audio/MixinSoundEngine.java index 845933a0..ba981b62 100644 --- a/common/src/main/java/org/orecruncher/dsurround/mixins/audio/MixinSoundEngine.java +++ b/common/src/main/java/org/orecruncher/dsurround/mixins/audio/MixinSoundEngine.java @@ -60,8 +60,12 @@ public void dsurround_onSoundPlay(SoundInstance soundInstance, CallbackInfo ci, @Inject(method = "play(Lnet/minecraft/client/resources/sounds/SoundInstance;)V", at = @At("HEAD"), cancellable = true) private void dsurround_play(SoundInstance sound, CallbackInfo ci) { try { + // Check to see if the sound is blocked or being culled if (SoundInstanceHandler.shouldBlockSoundPlay(sound)) ci.cancel(); + // Attempt a remapping if configured to do so + if (SoundInstanceHandler.remapSoundPlay(sound)) + ci.cancel(); } catch (final Exception t) { MixinHelpers.LOGGER.error(t, "Error in dsurround_play()!"); } diff --git a/common/src/main/java/org/orecruncher/dsurround/mixins/core/MixinBiome.java b/common/src/main/java/org/orecruncher/dsurround/mixins/core/MixinBiome.java index 2b95ca24..4e624bbd 100644 --- a/common/src/main/java/org/orecruncher/dsurround/mixins/core/MixinBiome.java +++ b/common/src/main/java/org/orecruncher/dsurround/mixins/core/MixinBiome.java @@ -7,6 +7,7 @@ import org.orecruncher.dsurround.config.biome.BiomeInfo; import org.orecruncher.dsurround.lib.random.Randomizer; import org.orecruncher.dsurround.mixinutils.IBiomeExtended; +import org.orecruncher.dsurround.mixinutils.MixinHelpers; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -56,16 +57,18 @@ public Biome.ClimateSettings dsurround_getWeather() { public abstract float dsurround_getTemperature(BlockPos pos); /** - * Obtain fog color from Dynamic Surroundings' config if available. + * Get fog color from Dynamic Surroundings' config if available. * * @param cir Mixin callback result */ @Inject(method = "getFogColor()I", at = @At("HEAD"), cancellable = true) public void dsurround_getFogColor(CallbackInfoReturnable cir) { - if (this.dsurround_info != null) { - var color = this.dsurround_info.getFogColor(); - if (color != null) - cir.setReturnValue(color.getValue()); + if (MixinHelpers.fogOptions.enableFogEffects && MixinHelpers.fogOptions.enableBiomeFog) { + if (this.dsurround_info != null) { + var color = this.dsurround_info.getFogColor(); + if (color != null) + cir.setReturnValue(color.getValue()); + } } } diff --git a/common/src/main/java/org/orecruncher/dsurround/mixins/core/MixinFogRenderer.java b/common/src/main/java/org/orecruncher/dsurround/mixins/core/MixinFogRenderer.java index faa673ff..5bd7fe13 100644 --- a/common/src/main/java/org/orecruncher/dsurround/mixins/core/MixinFogRenderer.java +++ b/common/src/main/java/org/orecruncher/dsurround/mixins/core/MixinFogRenderer.java @@ -1,25 +1,34 @@ package org.orecruncher.dsurround.mixins.core; +import com.llamalad7.mixinextras.sugar.Local; +import com.mojang.blaze3d.systems.RenderSystem; import net.minecraft.client.Camera; import net.minecraft.client.renderer.FogRenderer; -import net.minecraft.world.entity.Entity; import net.minecraft.world.level.material.FogType; import org.orecruncher.dsurround.eventing.ClientEventHooks; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; @Mixin(FogRenderer.class) public class MixinFogRenderer { - @Inject(method = "setupFog(Lnet/minecraft/client/Camera;Lnet/minecraft/client/renderer/FogRenderer$FogMode;FZF)V", at = @At("RETURN"), locals = LocalCapture.CAPTURE_FAILHARD) - private static void dsurround_renderFog(Camera camera, FogRenderer.FogMode fogMode, float f, boolean bl, float g, CallbackInfo ci, FogType fogType, Entity entity, FogRenderer.FogData fogData) { + @Inject(method = "setupFog(Lnet/minecraft/client/Camera;Lnet/minecraft/client/renderer/FogRenderer$FogMode;FZF)V", at = @At("RETURN")) + private static void dsurround_renderFog(Camera camera, FogRenderer.FogMode fogMode, float f, boolean bl, float g, CallbackInfo ci, @Local FogType fogType, @Local FogRenderer.FogData fogData) { if (fogData.mode != FogRenderer.FogMode.FOG_TERRAIN || fogType != FogType.NONE) return; - ClientEventHooks.FOG_RENDER_EVENT.raise().onRenderFog(fogData, f, g); + // At this point, Minecraft has already configured fog. It's possible that another + // mixin fired and configured as well. We cannot trust the state of fogData, so + // we interrogate the shader directly to see what was configured. (Nostalgic Tweaks + // uses this approach.) + var data = new FogRenderer.FogData(fogData.mode); + data.start = RenderSystem.getShaderFogStart(); + data.end = RenderSystem.getShaderFogEnd(); + data.shape = RenderSystem.getShaderFogShape(); + + ClientEventHooks.FOG_RENDER_EVENT.raise().onRenderFog(data, f, g); } } diff --git a/common/src/main/java/org/orecruncher/dsurround/mixins/core/MixinIngameHud.java b/common/src/main/java/org/orecruncher/dsurround/mixins/core/MixinIngameHud.java deleted file mode 100644 index fb3105b9..00000000 --- a/common/src/main/java/org/orecruncher/dsurround/mixins/core/MixinIngameHud.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.orecruncher.dsurround.mixins.core; - -import net.minecraft.client.DeltaTracker; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.Gui; -import net.minecraft.client.gui.GuiGraphics; -import org.orecruncher.dsurround.gui.overlay.OverlayManager; -import org.orecruncher.dsurround.lib.di.ContainerManager; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(Gui.class) -public class MixinIngameHud { - - @Unique - private OverlayManager dsurround_overlayManager; - - @Inject(method = "(Lnet/minecraft/client/Minecraft;)V", at = @At("RETURN")) - public void dsurround_constructor(Minecraft minecraftClient, CallbackInfo ci) { - this.dsurround_overlayManager = ContainerManager.resolve(OverlayManager.class); - } - - @Inject(method = "render", at = @At(value = "RETURN")) - public void dsurround_render(GuiGraphics guiGraphics, DeltaTracker deltaTracker, CallbackInfo ci) { - this.dsurround_overlayManager.render(guiGraphics, deltaTracker.getGameTimeDeltaTicks()); - } -} diff --git a/common/src/main/java/org/orecruncher/dsurround/mixins/core/MixinParticleManager.java b/common/src/main/java/org/orecruncher/dsurround/mixins/core/MixinParticleManager.java index d27c5888..dcb5d20c 100644 --- a/common/src/main/java/org/orecruncher/dsurround/mixins/core/MixinParticleManager.java +++ b/common/src/main/java/org/orecruncher/dsurround/mixins/core/MixinParticleManager.java @@ -2,28 +2,18 @@ import net.minecraft.client.particle.Particle; import net.minecraft.client.particle.ParticleEngine; -import net.minecraft.client.particle.ParticleRenderType; import net.minecraft.client.particle.SpriteSet; import net.minecraft.core.particles.ParticleOptions; import net.minecraft.resources.ResourceLocation; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.gen.Accessor; import org.spongepowered.asm.mixin.gen.Invoker; -import java.util.List; import java.util.Map; @Mixin(ParticleEngine.class) public interface MixinParticleManager { - @Accessor("RENDER_ORDER") - static List dsurround_getParticleTextureSheets() { return null; } - - @Accessor("RENDER_ORDER") - @Mutable - static void dsurround_setParticleTextureSheets(List sheets) { } - @Accessor("spriteSets") Map dsurround_getSpriteAwareFactories(); diff --git a/common/src/main/java/org/orecruncher/dsurround/mixinutils/IMusicManager.java b/common/src/main/java/org/orecruncher/dsurround/mixinutils/IMusicManager.java index 59c83a78..bb46d418 100644 --- a/common/src/main/java/org/orecruncher/dsurround/mixinutils/IMusicManager.java +++ b/common/src/main/java/org/orecruncher/dsurround/mixinutils/IMusicManager.java @@ -1,5 +1,7 @@ package org.orecruncher.dsurround.mixinutils; +import net.minecraft.network.chat.Component; + public interface IMusicManager { String dsurround_getDiagnosticText(); @@ -7,4 +9,6 @@ public interface IMusicManager { void dsurround_doCommand(String command); void dsurround_setPaused(boolean flag); + + Component dsurround_whatsPlaying(); } diff --git a/common/src/main/java/org/orecruncher/dsurround/mixinutils/MixinHelpers.java b/common/src/main/java/org/orecruncher/dsurround/mixinutils/MixinHelpers.java index c07066a5..68c29f8a 100644 --- a/common/src/main/java/org/orecruncher/dsurround/mixinutils/MixinHelpers.java +++ b/common/src/main/java/org/orecruncher/dsurround/mixinutils/MixinHelpers.java @@ -1,16 +1,23 @@ package org.orecruncher.dsurround.mixinutils; import org.orecruncher.dsurround.Configuration; +import org.orecruncher.dsurround.config.libraries.ISoundLibrary; import org.orecruncher.dsurround.config.libraries.ITagLibrary; import org.orecruncher.dsurround.lib.di.ContainerManager; import org.orecruncher.dsurround.lib.logging.IModLog; +/** + * Static class definitions with mixins do not work well. Any statics have been placed + * here for mixin access. + */ public class MixinHelpers { public static final IModLog LOGGER = ContainerManager.resolve(IModLog.class); public static final ITagLibrary TAG_LIBRARY = ContainerManager.resolve(ITagLibrary.class); + public static final ISoundLibrary SOUND_LIBRARY = ContainerManager.resolve(ISoundLibrary.class); public static final Configuration.SoundSystem soundSystemConfig = ContainerManager.resolve(Configuration.SoundSystem.class); public static final Configuration.FootstepAccents footstepAccentsConfig = ContainerManager.resolve(Configuration.FootstepAccents.class); public static final Configuration.ParticleTweaks particleTweaksConfig = ContainerManager.resolve(Configuration.ParticleTweaks.class); public static final Configuration.SoundOptions soundOptions = ContainerManager.resolve(Configuration.SoundOptions.class); + public static final Configuration.FogOptions fogOptions = ContainerManager.resolve(Configuration.FogOptions.class); } diff --git a/common/src/main/java/org/orecruncher/dsurround/processing/FogHandler.java b/common/src/main/java/org/orecruncher/dsurround/processing/FogHandler.java index e3ac1c19..4cdd3b1c 100644 --- a/common/src/main/java/org/orecruncher/dsurround/processing/FogHandler.java +++ b/common/src/main/java/org/orecruncher/dsurround/processing/FogHandler.java @@ -41,11 +41,18 @@ private void renderFog(FogRenderer.FogData data, float renderDistance, float par RenderSystem.setShaderFogStart(this.lastData.start); RenderSystem.setShaderFogEnd(this.lastData.end); RenderSystem.setShaderFogShape(this.lastData.shape); + } else { + // Preserve for diagnostic trace even though action was not taken + this.lastData = data; } } @Override protected void gatherDiagnostics(CollectDiagnosticsEvent event) { - event.add(CollectDiagnosticsEvent.Section.Systems, "Fog: %f/%f, %s, %s".formatted(this.lastData.start, this.lastData.end, this.lastData.shape, this.lastData.mode)); + var text = "Fog: %f/%f, %s, %s ".formatted(this.lastData.start, this.lastData.end, this.lastData.shape, this.lastData.mode); + var disabledText = this.fogCalculator.getDisabledText(); + if (disabledText.isPresent()) + text += disabledText.get(); + event.add(CollectDiagnosticsEvent.Section.Systems, text); } } diff --git a/common/src/main/java/org/orecruncher/dsurround/processing/accents/FootstepAccents.java b/common/src/main/java/org/orecruncher/dsurround/processing/accents/FootstepAccents.java index d1a0446d..fe68ce63 100644 --- a/common/src/main/java/org/orecruncher/dsurround/processing/accents/FootstepAccents.java +++ b/common/src/main/java/org/orecruncher/dsurround/processing/accents/FootstepAccents.java @@ -26,7 +26,6 @@ public FootstepAccents(Configuration config, IItemLibrary itemLibrary) { // Only register these providers if Presence Footsteps is not installed if (!Platform.isModLoaded(Constants.MOD_PRESENCE_FOOTSTEPS)) { this.providers.add(new WaterySurfaceAccent(config)); - this.providers.add(new LeavesAccent(config)); } } diff --git a/common/src/main/java/org/orecruncher/dsurround/processing/accents/LeavesAccent.java b/common/src/main/java/org/orecruncher/dsurround/processing/accents/LeavesAccent.java deleted file mode 100644 index b9eb0c42..00000000 --- a/common/src/main/java/org/orecruncher/dsurround/processing/accents/LeavesAccent.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.orecruncher.dsurround.processing.accents; - -import net.minecraft.core.BlockPos; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.level.block.state.BlockState; -import org.orecruncher.dsurround.Configuration; -import org.orecruncher.dsurround.Constants; -import org.orecruncher.dsurround.lib.collections.ObjectArray; -import org.orecruncher.dsurround.sound.ISoundFactory; -import org.orecruncher.dsurround.tags.BlockEffectTags; - -public class LeavesAccent implements IFootstepAccentProvider { - - private static final ResourceLocation LEAVES_FACTORY = ResourceLocation.fromNamespaceAndPath(Constants.MOD_ID, "footstep/leaves"); - - private final Configuration config; - - LeavesAccent(Configuration config) { - this.config = config; - } - - @Override - public void collect(LivingEntity entity, BlockPos pos, BlockState state, boolean isWaterLogged, ObjectArray acoustics) { - // Check for waterlogged. Don't want to squeak if waterlogged. - if (isWaterLogged) - return; - - if (FootstepAccents.TAG_LIBRARY.is(BlockEffectTags.LEAVES_STEP, state)) { - SOUND_LIBRARY.getSoundFactory(LEAVES_FACTORY) - .ifPresent(acoustics::add); - } - } - - @Override - public boolean isEnabled() { - return this.config.footstepAccents.enableLeafAccents; - } -} \ No newline at end of file diff --git a/common/src/main/java/org/orecruncher/dsurround/processing/fog/BiomeFogRangeCalculator.java b/common/src/main/java/org/orecruncher/dsurround/processing/fog/BiomeFogRangeCalculator.java index 36f5ec1c..40732bf3 100644 --- a/common/src/main/java/org/orecruncher/dsurround/processing/fog/BiomeFogRangeCalculator.java +++ b/common/src/main/java/org/orecruncher/dsurround/processing/fog/BiomeFogRangeCalculator.java @@ -23,7 +23,7 @@ public class BiomeFogRangeCalculator extends VanillaFogRangeCalculator { private float targetScale; public BiomeFogRangeCalculator(IBiomeLibrary biomeLibrary, Configuration.FogOptions fogOptions) { - super("BiomeFogRangeCalculator", fogOptions); + super("Biome", fogOptions); this.biomeLibrary = biomeLibrary; this.activeScale = this.targetScale = 0F; this.lastBlockPos = BlockPos.ZERO; diff --git a/common/src/main/java/org/orecruncher/dsurround/processing/fog/HolisticFogRangeCalculator.java b/common/src/main/java/org/orecruncher/dsurround/processing/fog/HolisticFogRangeCalculator.java index 61fbb957..d855cccd 100644 --- a/common/src/main/java/org/orecruncher/dsurround/processing/fog/HolisticFogRangeCalculator.java +++ b/common/src/main/java/org/orecruncher/dsurround/processing/fog/HolisticFogRangeCalculator.java @@ -11,6 +11,9 @@ import org.orecruncher.dsurround.lib.logging.ModLog; import org.orecruncher.dsurround.lib.seasons.ISeasonalInformation; +import java.util.Optional; +import java.util.stream.Collectors; + public class HolisticFogRangeCalculator implements IFogRangeCalculator { protected final IModLog logger; @@ -37,13 +40,13 @@ public String getName() { @Override public boolean enabled() { - return true; + return this.fogOptions.enableFogEffects; } @NotNull public FogRenderer.FogData render(@NotNull final FogRenderer.FogData data, float renderDistance, float partialTick) { - if (!this.fogOptions.enableFogEffects) + if (!this.enabled()) return data; float start = data.start; @@ -78,4 +81,18 @@ public void tick() { public void disconnect() { this.calculators.forEach(IFogRangeCalculator::disconnect); } + + public Optional getDisabledText() { + if (!this.enabled()) { + return Optional.of("(ALL DISABLED)"); + } + + var result = this.calculators.stream().filter(e -> !e.enabled()).map(IFogRangeCalculator::getName).collect(Collectors.joining(", ")); + + if (result.isEmpty()) { + return Optional.empty(); + } + + return Optional.of("(DISABLED: " + result + ")"); + } } diff --git a/common/src/main/java/org/orecruncher/dsurround/processing/fog/MorningFogRangeCalculator.java b/common/src/main/java/org/orecruncher/dsurround/processing/fog/MorningFogRangeCalculator.java index 51db66ce..e57d8b50 100644 --- a/common/src/main/java/org/orecruncher/dsurround/processing/fog/MorningFogRangeCalculator.java +++ b/common/src/main/java/org/orecruncher/dsurround/processing/fog/MorningFogRangeCalculator.java @@ -2,50 +2,38 @@ import net.minecraft.client.renderer.FogRenderer; import net.minecraft.util.Mth; +import net.minecraft.util.random.SimpleWeightedRandomList; import org.jetbrains.annotations.NotNull; import org.orecruncher.dsurround.Configuration; import org.orecruncher.dsurround.lib.GameUtils; import org.orecruncher.dsurround.lib.MinecraftClock; +import org.orecruncher.dsurround.lib.random.Randomizer; import org.orecruncher.dsurround.lib.seasons.ISeasonalInformation; -import org.orecruncher.dsurround.lib.WeightTable; - -import java.util.List; public class MorningFogRangeCalculator extends VanillaFogRangeCalculator { - private record FogDensityEntry(int weight, FogDensity density) implements WeightTable.IItem { - @Override - public int getWeight() { - return this.weight(); - } - @Override - public FogDensity getItem() { - return this.density(); - } - } - - private static final FogDensityEntry[] SPRING_FOG = { - new FogDensityEntry(30, FogDensity.NORMAL), - new FogDensityEntry(20, FogDensity.MEDIUM), - new FogDensityEntry(10, FogDensity.HEAVY) - }; - - private static final FogDensityEntry[] SUMMER_FOG = { - new FogDensityEntry(20, FogDensity.LIGHT), - new FogDensityEntry(10, FogDensity.NONE) - }; - - private static final FogDensityEntry[] AUTUMN_FOG = { - new FogDensityEntry(10, FogDensity.NORMAL), - new FogDensityEntry(20, FogDensity.MEDIUM), - new FogDensityEntry(10, FogDensity.HEAVY) - }; - - private static final FogDensityEntry[] WINTER_FOG = { - new FogDensityEntry(20, FogDensity.LIGHT), - new FogDensityEntry(20, FogDensity.NORMAL), - new FogDensityEntry(10, FogDensity.MEDIUM) - }; + private static final SimpleWeightedRandomList SPRING_FOG = new SimpleWeightedRandomList.Builder() + .add(FogDensity.NORMAL, 30) + .add(FogDensity.MEDIUM, 20) + .add(FogDensity.HEAVY, 10) + .build(); + + private static final SimpleWeightedRandomList SUMMER_FOG = new SimpleWeightedRandomList.Builder() + .add(FogDensity.LIGHT, 20) + .add(FogDensity.NONE, 10) + .build(); + + private static final SimpleWeightedRandomList AUTUMN_FOG = new SimpleWeightedRandomList.Builder() + .add(FogDensity.NORMAL, 10) + .add(FogDensity.MEDIUM, 20) + .add(FogDensity.HEAVY, 10) + .build(); + + private static final SimpleWeightedRandomList WINTER_FOG = new SimpleWeightedRandomList.Builder() + .add(FogDensity.LIGHT, 20) + .add(FogDensity.NORMAL, 20) + .add(FogDensity.MEDIUM, 10) + .build(); protected final ISeasonalInformation seasonInfo; protected final MinecraftClock clock; @@ -53,7 +41,7 @@ public FogDensity getItem() { protected FogDensity type = FogDensity.NONE; public MorningFogRangeCalculator(ISeasonalInformation seasonInfo, Configuration.FogOptions fogOptions) { - super("MorningFogRangeCalculator", fogOptions); + super("Morning", fogOptions); this.seasonInfo = seasonInfo; this.clock = new MinecraftClock(); } @@ -112,20 +100,19 @@ private float getCelestialAngleDegrees() { @NotNull protected FogDensity getFogType() { - FogDensityEntry[] selections; - var clientLevel = GameUtils.getWorld().orElseThrow(); - if (this.seasonInfo.isSpring(clientLevel)) + SimpleWeightedRandomList selections; + if (this.seasonInfo.isSpring()) selections = SPRING_FOG; - else if (this.seasonInfo.isSummer(clientLevel)) + else if (this.seasonInfo.isSummer()) selections = SUMMER_FOG; - else if (this.seasonInfo.isAutumn(clientLevel)) + else if (this.seasonInfo.isAutumn()) selections = AUTUMN_FOG; - else if (this.seasonInfo.isWinter(clientLevel)) + else if (this.seasonInfo.isWinter()) selections = WINTER_FOG; else // Shouldn't get here, but... return FogDensity.NONE; - return WeightTable.makeSelection(List.of(selections)).orElseThrow(); + return selections.getRandomValue(Randomizer.current()).orElseThrow(); } } \ No newline at end of file diff --git a/common/src/main/java/org/orecruncher/dsurround/processing/fog/WeatherFogRangeCalculator.java b/common/src/main/java/org/orecruncher/dsurround/processing/fog/WeatherFogRangeCalculator.java index bb788980..952cbfda 100644 --- a/common/src/main/java/org/orecruncher/dsurround/processing/fog/WeatherFogRangeCalculator.java +++ b/common/src/main/java/org/orecruncher/dsurround/processing/fog/WeatherFogRangeCalculator.java @@ -11,7 +11,7 @@ public class WeatherFogRangeCalculator extends VanillaFogRangeCalculator { protected static final float END_IMPACT = 0.4F; protected WeatherFogRangeCalculator(Configuration.FogOptions fogOptions) { - super("WeatherFogRangeCalculator", fogOptions); + super("Weather", fogOptions); } @Override diff --git a/common/src/main/java/org/orecruncher/dsurround/processing/scanner/BiomeScanner.java b/common/src/main/java/org/orecruncher/dsurround/processing/scanner/BiomeScanner.java index 13feea95..588aed08 100644 --- a/common/src/main/java/org/orecruncher/dsurround/processing/scanner/BiomeScanner.java +++ b/common/src/main/java/org/orecruncher/dsurround/processing/scanner/BiomeScanner.java @@ -2,14 +2,14 @@ import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; import net.minecraft.core.BlockPos; +import net.minecraft.resources.ResourceLocation; import net.minecraft.tags.FluidTags; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.BiomeManager; import org.orecruncher.dsurround.config.libraries.IBiomeLibrary; -import org.orecruncher.dsurround.config.libraries.IDimensionLibrary; +import org.orecruncher.dsurround.config.libraries.IDimensionInformation; import org.orecruncher.dsurround.config.SyntheticBiome; import org.orecruncher.dsurround.config.biome.BiomeInfo; -import org.orecruncher.dsurround.config.dimension.DimensionInfo; import org.orecruncher.dsurround.lib.GameUtils; public final class BiomeScanner extends AbstractScanner { @@ -22,7 +22,7 @@ public final class BiomeScanner extends AbstractScanner { private static final int SURVEY_VERTICAL_OFFSET = SURVEY_VERTICAL_DIMENSION / 4 - 1; private static final int MAX_SURVEY_VOLUME = SURVEY_HORIZONTAL_DIMENSION * SURVEY_HORIZONTAL_DIMENSION * SURVEY_VERTICAL_DIMENSION; - private DimensionInfo surveyedDimension; + private ResourceLocation surveyedDimension; private boolean isUnderWater; private BiomeInfo logicalBiomeInfo; @@ -33,12 +33,12 @@ public final class BiomeScanner extends AbstractScanner { private Biome surveyedBiome = null; private BlockPos surveyedPosition = BlockPos.ZERO; private final IBiomeLibrary biomeLibrary; - private final IDimensionLibrary dimensionLibrary; + private final IDimensionInformation dimensionInformation; private final CeilingScanner ceilingScanner; - public BiomeScanner(IBiomeLibrary biomeLibrary, IDimensionLibrary dimensionLibrary, CeilingScanner ceilingScanner) { + public BiomeScanner(IBiomeLibrary biomeLibrary, IDimensionInformation dimensionInformation, CeilingScanner ceilingScanner) { this.biomeLibrary = biomeLibrary; - this.dimensionLibrary = dimensionLibrary; + this.dimensionInformation = dimensionInformation; this.ceilingScanner = ceilingScanner; } @@ -46,7 +46,7 @@ public BiomeInfo playerLogicBiomeInfo() { return this.logicalBiomeInfo; } - public DimensionInfo getDimInfo() { + public ResourceLocation getDimInfo() { return this.surveyedDimension; } @@ -63,17 +63,16 @@ public void tick(long tickCount) { var world = player.level(); var position = player.blockPosition(); - var dimensionInfo = this.dimensionLibrary.getData(world); var biomes = world.getBiomeManager(); var playerBiome = biomes.getBiome(position); if (this.surveyedBiome != playerBiome.value() - || !this.surveyedDimension.equals(dimensionInfo) + || !this.surveyedDimension.equals(this.dimensionInformation.name()) || !this.surveyedPosition.equals(position)) { this.surveyedBiome = playerBiome.value(); this.surveyedPosition = position; - this.surveyedDimension = dimensionInfo; + this.surveyedDimension = this.dimensionInformation.name(); this.weights = new Reference2IntOpenHashMap<>(8); @@ -97,7 +96,7 @@ else if (playerBiomeInfo.isOcean()) return; } - this.logicalBiomeInfo = this.resolveBiome(dimensionInfo, biomes, position); + this.logicalBiomeInfo = this.resolveBiome(biomes, position); for (int z = 0; z < SURVEY_HORIZONTAL_DIMENSION; z++) { var dZ = z - SURVEY_HORIZONTAL_OFFSET + this.surveyedPosition.getZ(); @@ -108,7 +107,7 @@ else if (playerBiomeInfo.isOcean()) for (int y = 0; y < SURVEY_VERTICAL_DIMENSION; y++) { var dY = y - SURVEY_VERTICAL_OFFSET + this.surveyedPosition.getY(); this.mutable.setY(dY); - var info = this.resolveBiome(dimensionInfo, biomes, this.mutable); + var info = this.resolveBiome(biomes, this.mutable); this.weights.addTo(info, 1); } } @@ -117,7 +116,7 @@ else if (playerBiomeInfo.isOcean()) } } - private BiomeInfo resolveBiome(DimensionInfo dimInfo, BiomeManager access, BlockPos pos) { + private BiomeInfo resolveBiome(BiomeManager access, BlockPos pos) { // Get the biome at the specified position var biome = access.getBiome(pos).value(); @@ -130,24 +129,24 @@ private BiomeInfo resolveBiome(DimensionInfo dimInfo, BiomeManager access, Block // Are we simulating underground? This is done by checking the Y level. var y = pos.getY(); - if (y < (dimInfo.getSeaLevel() - UNDERGROUND_THRESHOLD_OFFSET)) { + if (y < (this.dimensionInformation.seaLevel() - UNDERGROUND_THRESHOLD_OFFSET)) { return this.biomeLibrary.getBiomeInfo(SyntheticBiome.UNDERGROUND); } - // Inside check overrides everything else. Some dimensions are always out side + // Inside check overrides everything else. Some dimensions are always outside, // so those are excluded (like the nether). - if (!dimInfo.alwaysOutside() && this.ceilingScanner.isReallyInside()) { + if (!this.dimensionInformation.alwaysOutside() && this.ceilingScanner.isReallyInside()) { // If it's not underground, and we are inside, return INSIDE return this.biomeLibrary.getBiomeInfo(SyntheticBiome.INSIDE); } // Are we really up in the sky - if (y >= dimInfo.getSpaceHeight()) { + if (y >= this.dimensionInformation.getSpaceHeight()) { return this.biomeLibrary.getBiomeInfo(SyntheticBiome.SPACE); } // Up in the clouds - if (y >= dimInfo.getCloudHeight()) { + if (y >= this.dimensionInformation.getCloudHeight()) { return this.biomeLibrary.getBiomeInfo(SyntheticBiome.CLOUDS); } diff --git a/common/src/main/java/org/orecruncher/dsurround/processing/scanner/CeilingScanner.java b/common/src/main/java/org/orecruncher/dsurround/processing/scanner/CeilingScanner.java index 92f41293..f86cb754 100644 --- a/common/src/main/java/org/orecruncher/dsurround/processing/scanner/CeilingScanner.java +++ b/common/src/main/java/org/orecruncher/dsurround/processing/scanner/CeilingScanner.java @@ -8,7 +8,7 @@ import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import org.orecruncher.dsurround.config.libraries.IDimensionLibrary; -import org.orecruncher.dsurround.config.dimension.DimensionInfo; +import org.orecruncher.dsurround.config.DimensionInfo; import org.orecruncher.dsurround.config.libraries.ITagLibrary; import org.orecruncher.dsurround.lib.GameUtils; import org.orecruncher.dsurround.lib.collections.ObjectArray; diff --git a/common/src/main/java/org/orecruncher/dsurround/processing/scanner/SystemsScanner.java b/common/src/main/java/org/orecruncher/dsurround/processing/scanner/SystemsScanner.java index 96852c7c..ae096fd1 100644 --- a/common/src/main/java/org/orecruncher/dsurround/processing/scanner/SystemsScanner.java +++ b/common/src/main/java/org/orecruncher/dsurround/processing/scanner/SystemsScanner.java @@ -1,5 +1,6 @@ package org.orecruncher.dsurround.processing.scanner; +import net.minecraft.core.BlockBox; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.world.level.Level; @@ -7,7 +8,6 @@ import org.orecruncher.dsurround.Configuration; import org.orecruncher.dsurround.effects.IBlockEffect; import org.orecruncher.dsurround.effects.IEffectSystem; -import org.orecruncher.dsurround.lib.BlockPosUtil; import org.orecruncher.dsurround.lib.GameUtils; import org.orecruncher.dsurround.lib.collections.ObjectArray; import org.orecruncher.dsurround.lib.di.Cacheable; @@ -64,26 +64,25 @@ public void tick() { final boolean sittingStill = this.lastPos.equals(current); this.lastPos = current; - Predicate pred; + Predicate filter; if (!sittingStill) { var range = this.config.blockEffects.blockEffectRange; - var minPoint = current.offset(-range, -range, -range); - var maxPoint = current.offset(range, range, range); + var blockBox = BlockBox.of(current.offset(-range, -range, -range), current.offset(range, range, range)); - pred = system -> { - if (!BlockPosUtil.contains(system.getPos(), minPoint, maxPoint)) { - system.remove(); - } else { + filter = system -> { + if (blockBox.contains(system.getPos())) { system.tick(); + } else { + system.remove(); } return system.isDone(); }; } else { - pred = EFFECT_PREDICATE; + filter = EFFECT_PREDICATE; } - this.processIfEnabled(true, system -> system.tick(pred)); + this.processIfEnabled(true, system -> system.tick(filter)); } protected void processIfEnabled(boolean clearSystems, Consumer systemConsumer) { diff --git a/common/src/main/java/org/orecruncher/dsurround/runtime/audio/SoundFXUtils.java b/common/src/main/java/org/orecruncher/dsurround/runtime/audio/SoundFXUtils.java index 278ac24e..abeda9aa 100644 --- a/common/src/main/java/org/orecruncher/dsurround/runtime/audio/SoundFXUtils.java +++ b/common/src/main/java/org/orecruncher/dsurround/runtime/audio/SoundFXUtils.java @@ -20,7 +20,6 @@ import org.orecruncher.dsurround.lib.math.ReusableRaycastContext; import org.orecruncher.dsurround.lib.math.ReusableRaycastIterator; import org.orecruncher.dsurround.lib.seasons.ISeasonalInformation; -import org.orecruncher.dsurround.lib.world.WorldUtils; import org.orecruncher.dsurround.runtime.audio.effects.Effects; import org.orecruncher.dsurround.runtime.audio.effects.LowPassData; import org.orecruncher.dsurround.runtime.audio.effects.SourcePropertyFloat; @@ -352,9 +351,9 @@ private static float calculateWeatherAbsorption(final WorldContext ctx, final Ve final BlockPos high = BlockPos.containing(pt2); // Determine the precipitation type at each point - final Biome.Precipitation rt1 = SEASONAL_INFORMATION.getActivePrecipitation(ctx.world, low); - final Biome.Precipitation rt2 = SEASONAL_INFORMATION.getActivePrecipitation(ctx.world, mid); - final Biome.Precipitation rt3 = SEASONAL_INFORMATION.getActivePrecipitation(ctx.world, high); + final Biome.Precipitation rt1 = SEASONAL_INFORMATION.getActivePrecipitation(low); + final Biome.Precipitation rt2 = SEASONAL_INFORMATION.getActivePrecipitation(mid); + final Biome.Precipitation rt3 = SEASONAL_INFORMATION.getActivePrecipitation(high); // Calculate the impact of weather on dampening float factor = calcFactor(rt1, 0.25F); diff --git a/common/src/main/java/org/orecruncher/dsurround/runtime/sets/impl/SeasonVariables.java b/common/src/main/java/org/orecruncher/dsurround/runtime/sets/impl/SeasonVariables.java index dea39ebf..8e78783b 100644 --- a/common/src/main/java/org/orecruncher/dsurround/runtime/sets/impl/SeasonVariables.java +++ b/common/src/main/java/org/orecruncher/dsurround/runtime/sets/impl/SeasonVariables.java @@ -28,11 +28,10 @@ public ISeasonVariables getInterface() { @Override public void update(IVariableAccess variableAccess) { if (GameUtils.isInGame()) { - var world = GameUtils.getWorld().orElseThrow(); - this.isSpring = this.seasonalInformation.isSpring(world); - this.isSummer = this.seasonalInformation.isSummer(world); - this.isAutumn = this.seasonalInformation.isAutumn(world); - this.isWinter = this.seasonalInformation.isWinter(world); + this.isSpring = this.seasonalInformation.isSpring(); + this.isSummer = this.seasonalInformation.isSummer(); + this.isAutumn = this.seasonalInformation.isAutumn(); + this.isWinter = this.seasonalInformation.isWinter(); } else { this.isSpring = false; this.isSummer = false; diff --git a/common/src/main/java/org/orecruncher/dsurround/runtime/sets/impl/WeatherVariables.java b/common/src/main/java/org/orecruncher/dsurround/runtime/sets/impl/WeatherVariables.java index 75d46bb5..dc78d7ef 100644 --- a/common/src/main/java/org/orecruncher/dsurround/runtime/sets/impl/WeatherVariables.java +++ b/common/src/main/java/org/orecruncher/dsurround/runtime/sets/impl/WeatherVariables.java @@ -38,9 +38,9 @@ public void update(IVariableAccess variableAccess) { this.thunderIntensity = world.getThunderLevel(1F); this.isRaining = world.isRaining(); this.isThundering = world.isThundering(); - this.temperature = this.seasonalInformation.getTemperature(world, player.blockPosition()); - this.isFrosty = this.seasonalInformation.isColdTemperature(world, player.blockPosition()); - this.canWaterFreeze = this.seasonalInformation.isSnowTemperature(world, player.blockPosition()); + this.temperature = this.seasonalInformation.getTemperature(player.blockPosition()); + this.isFrosty = this.seasonalInformation.isColdTemperature(player.blockPosition()); + this.canWaterFreeze = this.seasonalInformation.isSnowTemperature(player.blockPosition()); } else { this.rainIntensity = 0F; this.thunderIntensity = 0F; diff --git a/common/src/main/java/org/orecruncher/dsurround/sound/ISoundFactory.java b/common/src/main/java/org/orecruncher/dsurround/sound/ISoundFactory.java index 5b89d556..684c8e4b 100644 --- a/common/src/main/java/org/orecruncher/dsurround/sound/ISoundFactory.java +++ b/common/src/main/java/org/orecruncher/dsurround/sound/ISoundFactory.java @@ -9,6 +9,7 @@ import net.minecraft.world.phys.Vec3; import org.orecruncher.dsurround.lib.math.MathStuff; +@SuppressWarnings("unused") public interface ISoundFactory { /** @@ -89,7 +90,16 @@ default SimpleSoundInstance createAtLocation(Vec3 position) { * This sound instance is not attached. The properties of the sound instance are defined by the underlying * factory settings. */ - SimpleSoundInstance createAtLocation(Vec3 position, float volumeScale); + default SimpleSoundInstance createAtLocation(Vec3 position, float volumeScale) { + return this.createAtLocation(position.x(), position.y(), position.z(), volumeScale); + } + + /** + * Creates a sound instance at the specified location. The provided factor scales the sound volume. + * This sound instance is not attached. The properties of the sound instance are defined by the underlying + * factory settings. + */ + SimpleSoundInstance createAtLocation(double posX, double posY, double posZ, float volumeScale); /** * Creates a Music instance to be used with Minecraft's music manager diff --git a/common/src/main/java/org/orecruncher/dsurround/sound/SoundFactory.java b/common/src/main/java/org/orecruncher/dsurround/sound/SoundFactory.java index 2a3e45ba..4a0bd7a3 100644 --- a/common/src/main/java/org/orecruncher/dsurround/sound/SoundFactory.java +++ b/common/src/main/java/org/orecruncher/dsurround/sound/SoundFactory.java @@ -103,7 +103,7 @@ public EntityBoundSoundInstance attachToEntity(Entity entity) { } @Override - public SimpleSoundInstance createAtLocation(Vec3 position, float volumeScale) { + public SimpleSoundInstance createAtLocation(double posX, double posY, double posZ, float volumeScale) { return new SimpleSoundInstance( this.soundEvent.getLocation(), this.category, @@ -113,9 +113,9 @@ public SimpleSoundInstance createAtLocation(Vec3 position, float volumeScale) { this.isRepeatable, this.repeatDelay, this.attenuation, - position.x(), - position.y(), - position.z(), + posX, + posY, + posZ, this.global); } diff --git a/common/src/main/java/org/orecruncher/dsurround/sound/SoundInstanceHandler.java b/common/src/main/java/org/orecruncher/dsurround/sound/SoundInstanceHandler.java index b91554ca..cda8198b 100644 --- a/common/src/main/java/org/orecruncher/dsurround/sound/SoundInstanceHandler.java +++ b/common/src/main/java/org/orecruncher/dsurround/sound/SoundInstanceHandler.java @@ -5,9 +5,7 @@ import net.minecraft.client.resources.sounds.SoundInstance; import net.minecraft.resources.ResourceLocation; import net.minecraft.sounds.SoundSource; -import net.minecraft.sounds.SoundEvents; import net.minecraft.world.phys.Vec3; -import org.orecruncher.dsurround.Constants; import org.orecruncher.dsurround.Configuration; import org.orecruncher.dsurround.config.libraries.ISoundLibrary; import org.orecruncher.dsurround.gui.sound.ConfigSoundInstance; @@ -17,9 +15,7 @@ import org.orecruncher.dsurround.lib.di.ContainerManager; import org.orecruncher.dsurround.lib.threading.IClientTasking; -import java.util.HashSet; import java.util.Objects; -import java.util.Set; /** * Handles sound block and culling. @@ -31,15 +27,8 @@ public final class SoundInstanceHandler { private static final IAudioPlayer AUDIO_PLAYER = ContainerManager.resolve(IAudioPlayer.class); private static final ITickCount TICK_COUNT = ContainerManager.resolve(ITickCount.class); private static final Configuration.SoundSystem SOUND_SYSTEM_CONFIG = ContainerManager.resolve(Configuration.SoundSystem.class); - private static final Configuration.SoundOptions THUNDERSTORM_CONFIG = ContainerManager.resolve(Configuration.SoundOptions.class); private static final Object2LongOpenHashMap SOUND_CULL = new Object2LongOpenHashMap<>(32); - private static final Set THUNDER_SOUNDS = new HashSet<>(); - private static final ResourceLocation THUNDER_SOUND_FACTORY = ResourceLocation.fromNamespaceAndPath(Constants.MOD_ID, "thunder"); - - static { - THUNDER_SOUNDS.add(SoundEvents.LIGHTNING_BOLT_THUNDER.getLocation()); - } private static boolean isSoundBlocked(final ResourceLocation id) { return SOUND_LIBRARY.isBlocked(id); @@ -79,20 +68,24 @@ public static boolean shouldBlockSoundPlay(final SoundInstance theSound) { return false; final ResourceLocation id = theSound.getLocation(); - - if (THUNDERSTORM_CONFIG.replaceThunderSounds && THUNDER_SOUNDS.contains(id)) { - // Yeah - a bit reentrant but it should be good - var soundFactory = SOUND_LIBRARY.getSoundFactory(THUNDER_SOUND_FACTORY); - if (soundFactory.isPresent()) { - var sound = soundFactory.get().createAtLocation(new Vec3(theSound.getX(), theSound.getY(), theSound.getZ())); - AUDIO_PLAYER.play(sound); - return true; - } - } - return isSoundBlocked(id) || isSoundCulledLogical(id); } + /** + * Remaps the sound to a different one and queues to the sound engine. + * + * @param theSound The sound that is being played + * @return True if the sound was remapped, false otherwise + */ + public static boolean remapSoundPlay(final SoundInstance theSound) { + return SOUND_LIBRARY.remapSound(theSound) + .map(s -> { + AUDIO_PLAYER.play(s); + return true; + }) + .orElse(false); + } + /** * Determines if a sound is in range of a listener based on the sound's properties. * diff --git a/common/src/main/java/org/orecruncher/dsurround/tags/ItemEffectTags.java b/common/src/main/java/org/orecruncher/dsurround/tags/ItemEffectTags.java index 6b0e6763..ccff63d3 100644 --- a/common/src/main/java/org/orecruncher/dsurround/tags/ItemEffectTags.java +++ b/common/src/main/java/org/orecruncher/dsurround/tags/ItemEffectTags.java @@ -22,6 +22,7 @@ public class ItemEffectTags { public static final TagKey SWORDS = of("swords"); public static final TagKey TOOLS = of("tools"); public static final TagKey COMPASSES = of("compasses"); + public static final TagKey COMPASS_WOBBLE = of("compass_wobble"); public static final TagKey CLOCKS = of("clocks"); private static TagKey of(String id) { diff --git a/common/src/main/resources/assets/biomesoplenty/dsconfigs/sound_mappings.json b/common/src/main/resources/assets/biomesoplenty/dsconfigs/sound_mappings.json new file mode 100644 index 00000000..1f41f66b --- /dev/null +++ b/common/src/main/resources/assets/biomesoplenty/dsconfigs/sound_mappings.json @@ -0,0 +1,13 @@ +[ + { + "soundEvent": "minecraft:block.wood.step", + "rules": [ + { + "blocks": [ + "biomesoplenty:glowshroom_block" + ], + "factory": "dsurround:footsteps.organic" + } + ] + } +] \ No newline at end of file diff --git a/common/src/main/resources/assets/biomesoplenty/dsconfigs/tags/block/effects/heat_producers.json b/common/src/main/resources/assets/biomesoplenty/dsconfigs/tags/block/effects/heat_producers.json new file mode 100644 index 00000000..24e7d408 --- /dev/null +++ b/common/src/main/resources/assets/biomesoplenty/dsconfigs/tags/block/effects/heat_producers.json @@ -0,0 +1,8 @@ +{ + "values": [ + { + "id": "biomesoplenty:brimstone_fumarole", + "required": false + } + ] +} \ No newline at end of file diff --git a/common/src/main/resources/assets/dsurround/dsconfigs/sound_factories.json b/common/src/main/resources/assets/dsurround/dsconfigs/sound_factories.json index 81d75575..e4df2735 100644 --- a/common/src/main/resources/assets/dsurround/dsconfigs/sound_factories.json +++ b/common/src/main/resources/assets/dsurround/dsconfigs/sound_factories.json @@ -322,5 +322,23 @@ "min": 1.0, "max": 1.2 } + }, + { + "location": "dsurround:footsteps/dirt_path", + "soundEvent" : "dsurround:footsteps.gravel", + "volume": 0.25, + "pitch": { + "min": 1.0, + "max": 1.2 + } + }, + { + "location": "dsurround:footsteps/ladder", + "soundEvent" : "dsurround:footsteps.bluntwood", + "volume": 0.5, + "pitch": { + "min": 1.3, + "max": 1.4 + } } ] \ No newline at end of file diff --git a/common/src/main/resources/assets/dsurround/dsconfigs/sound_mappings.json b/common/src/main/resources/assets/dsurround/dsconfigs/sound_mappings.json new file mode 100644 index 00000000..a4aefbc8 --- /dev/null +++ b/common/src/main/resources/assets/dsurround/dsconfigs/sound_mappings.json @@ -0,0 +1,157 @@ +[ + { + "soundEvent": "minecraft:entity.lightning_bolt.thunder", + "rules": [ + { + "factory": "dsurround:thunder" + } + ] + }, + { + "soundEvent": "minecraft:block.grass.step", + "rules": [ + { + "blocks": [ + "#dsurround:effects/leaves_step", + "minecraft:hay_block" + ], + "factory": "dsurround:footsteps.leaves_through" + }, + { + "blocks": [ + "minecraft:dirt_path" + ], + "factory": "dsurround:footsteps/dirt_path" + }, + { + "factory": "dsurround:footsteps.grass" + } + ] + }, + { + "soundEvent": "minecraft:block.gravel.step", + "rules": [ + { + "factory": "dsurround:footsteps.gravel" + } + ] + }, + { + "soundEvent": "minecraft:block.sand.step", + "rules": [ + { + "factory": "dsurround:footsteps.sand" + } + ] + }, + { + "soundEvent": "minecraft:block.dirt.step", + "rules": [ + { + "factory": "dsurround:footsteps.dirt" + } + ] + }, + { + "soundEvent": "minecraft:block.stone.step", + "rules": [ + { + "blocks": [ + "#minecraft:cauldrons" + ], + "factory": "dsurround:footsteps.metalbox" + }, + { + "factory": "dsurround:footsteps.stone" + } + ] + }, + { + "soundEvent": "minecraft:block.snow.step", + "rules": [ + { + "factory": "dsurround:footsteps.snow" + } + ] + }, + { + "soundEvent": "minecraft:block.glass.step", + "rules": [ + { + "blocks": [ + "#minecraft:ice", + "#c:ice" + ], + "factory": "dsurround:footsteps.muffledice" + }, + { + "factory": "dsurround:footsteps.wood" + } + ] + }, + { + "soundEvent": "minecraft:block.wood.step", + "rules": [ + { + "blocks": [ + "#minecraft:logs", + "#c:logs" + ], + "factory": "dsurround:footsteps.log" + }, + { + "blocks": [ + "#minecraft:fences", + "#c:fences" + ], + "factory": "dsurround:footsteps.bluntwood" + }, + { + "blocks": [ + "#minecraft:beehives", + "minecraft:red_mushroom_block", + "minecraft:brown_mushroom_block", + "minecraft:mushroom_stem", + "minecraft:pumpkin", + "minecraft:carved_pumpkin", + "minecraft:melon", + "minecraft:cocoa" + ], + "factory": "dsurround:footsteps.organic" + }, + { + "factory": "dsurround:footsteps.wood" + } + ] + }, + { + "soundEvent": "minecraft:block.metal.step", + "rules": [ + { + "blocks": [ + "minecraft:iron_bars" + ], + "factory": "dsurround:footsteps.metalbar" + }, + { + "factory": "dsurround:footsteps.metalbox" + } + ] + }, + { + "soundEvent": "minecraft:block.ladder.step", + "rules": [ + { + "factory": "dsurround:footsteps/ladder" + } + ] + }, + { + "soundEvent": "minecraft:block.wool.step", + "rules": [ + { + "factory": "dsurround:footsteps.rug" + } + ] + } +] \ No newline at end of file diff --git a/common/src/main/resources/assets/dsurround/dsconfigs/tags/item/effects/compass_wobble.json b/common/src/main/resources/assets/dsurround/dsconfigs/tags/item/effects/compass_wobble.json new file mode 100644 index 00000000..4e104c81 --- /dev/null +++ b/common/src/main/resources/assets/dsurround/dsconfigs/tags/item/effects/compass_wobble.json @@ -0,0 +1,5 @@ +{ + "values": [ + "minecraft:compass" + ] +} \ No newline at end of file diff --git a/common/src/main/resources/assets/dsurround/lang/en_us.json b/common/src/main/resources/assets/dsurround/lang/en_us.json index 3e07283c..431ac800 100644 --- a/common/src/main/resources/assets/dsurround/lang/en_us.json +++ b/common/src/main/resources/assets/dsurround/lang/en_us.json @@ -60,6 +60,8 @@ "dsurround.command.dsmm.pause.failure": "Unable to pause the Music Manager: %1$s", "dsurround.command.dsmm.unpause.success": "Music Manager was unpaused", "dsurround.command.dsmm.unpause.failure": "Unable to unpause the Music Manager: %1$s", + "dsurround.command.dsmm.whatsplaying.success": "\u00a72Playing\u00a7r: %1$s", + "dsurround.command.dsmm.whatsplaying.failure": "Unable to determine what the Music Manager is playing: %1$s", "dsurround.config.title": "Dynamic Surroundings Configuration Options", "dsurround.config.tooltip.clientRestartRequired": "CLIENT restart required", "dsurround.config.tooltip.worldRestartRequired": "WORLD restart required", @@ -92,6 +94,8 @@ "dsurround.config.soundOptions.ambientVolumeScaling.tooltip": "Ambient sounds played by the mod will be multiplied by this factor. The unit scale is 1/100, so a setting of 100 would multiply by 1. Volume calculations have several different factors, this setting being one of them. Regardless of these factors, the final volume will be restricted to a range of 0.0 - 1.0.", "dsurround.config.soundOptions.replaceThunderSounds": "Replace Vanilla thunder sounds", "dsurround.config.soundOptions.replaceThunderSounds.tooltip": "Enables replacement of thunder sounds with Dynamic Surroundings' version", + "dsurround.config.soundOptions.remapSounds": "Sound Remapping", + "dsurround.config.soundOptions.remapSounds.tooltip": "Enables sound remapping when sounds are played. Enabling tells Dynamic Surroundings to replace various step sounds with replacement versions.", "dsurround.config.soundOptions.allowScarySounds": "Allow playing scary sounds", "dsurround.config.soundOptions.allowScarySounds.tooltip": "Enables playing scary sounds. Scary can be subjective. It is up to mod pack authors to implement properly within the configuration. Disliked sounds also be blocked using the in-game sound configuration menu.", "dsurround.config.soundOptions.playBiomeMusicWhileCreative": "Play biome music while creative", @@ -128,6 +132,8 @@ "dsurround.config.entityEffects.enableBreathEffect.tooltip": "Enable/disable breath effect in cold biomes", "dsurround.config.entityEffects.enablePlayerToolbarEffect": "Player Toolbar Effects", "dsurround.config.entityEffects.enablePlayerToolbarEffect.tooltip": "Enable/disable player toolbar sound effects", + "dsurround.config.entityEffects.enableToolbarBlockSounds": "Player Toolbar Block Sounds", + "dsurround.config.entityEffects.enableToolbarBlockSounds.tooltip": "Enable/disable sound effects for blocks on the toolbar", "dsurround.config.entityEffects.enableSwingEffect": "Item Swing Effects", "dsurround.config.entityEffects.enableSwingEffect.tooltip": "Enable/disable item swing sound effects from players and mobs", "dsurround.config.entityEffects.enableBrushStepEffect": "Brush Step Effect", @@ -142,8 +148,6 @@ "dsurround.config.footstepAccents.enableWetSurfaceAccents.tooltip": "Enable/disable accents for when it is raining or blocks are waterlogged", "dsurround.config.footstepAccents.enableFloorSqueaks": "Floor Squeaks", "dsurround.config.footstepAccents.enableFloorSqueaks.tooltip": "Enable/disable accents for when the player is walking on squeaky blocks", - "dsurround.config.footstepAccents.enableLeafAccents": "Leafy Surface Accents", - "dsurround.config.footstepAccents.enableLeafAccents.tooltip": "Enable/disable accents for when the player is walking on leafy blocks", "dsurround.config.particleTweaks": "Particle Tweaks", "dsurround.config.particleTweaks.tooltip": "Configuration options for tweaking particle behavior", "dsurround.config.particleTweaks.suppressPlayerParticles": "Suppress Player Potion Effects", @@ -207,5 +211,7 @@ "dsurround.text.reloadassets": "[%1$s\u00a7r] \u00a7aReloaded configurations", "dsurround.text.seasons.spring": "Spring", "dsurround.text.toast.music.title": "Music \"%1$s\"", - "dsurround.text.toast.music.author": "by %1$s" + "dsurround.text.toast.music.author": "by %1$s", + "dsurround.text.musicmanager.nothing": "No music is playing", + "dsurround.text.musicmanager.playing": "%1$s by %2$s (%3$s)" } diff --git a/common/src/main/resources/assets/dsurround/lang/pl_pl.json b/common/src/main/resources/assets/dsurround/lang/pl_pl.json index 17eb732d..1a4b3b9d 100644 --- a/common/src/main/resources/assets/dsurround/lang/pl_pl.json +++ b/common/src/main/resources/assets/dsurround/lang/pl_pl.json @@ -137,8 +137,6 @@ "dsurround.config.footstepAccents.enableWetSurfaceAccents.tooltip": "Włącza/wyłącza akcenty dla deszczu or blocks are waterlogged", "dsurround.config.footstepAccents.enableFloorSqueaks": "Skrzypienie podłogi", "dsurround.config.footstepAccents.enableFloorSqueaks.tooltip": "Włącza/wyłącza akcenty dla gracza chodzącego po skrzypiących blokach", - "dsurround.config.footstepAccents.enableLeafAccents": "Akcenty liściastych powierzchni", - "dsurround.config.footstepAccents.enableLeafAccents.tooltip": "Włącza/wyłącza akcenty dla gracza chodzącego po liściastych blokach", "dsurround.config.particleTweaks": "Ulepszenia cząsteczek", "dsurround.config.particleTweaks.tooltip": "Opcje konfiguracji dla ulepszenia zachowania cząsteczek", "dsurround.config.particleTweaks.suppressPlayerParticles": "Wygaś efekty mikstur gracza", diff --git a/common/src/main/resources/assets/dsurround/lang/zh_cn.json b/common/src/main/resources/assets/dsurround/lang/zh_cn.json index dcfc4522..65d0edf3 100644 --- a/common/src/main/resources/assets/dsurround/lang/zh_cn.json +++ b/common/src/main/resources/assets/dsurround/lang/zh_cn.json @@ -137,8 +137,6 @@ "dsurround.config.footstepAccents.enableWetSurfaceAccents.tooltip": "启用/禁用 当下雨或方块被浸泡时的效果", "dsurround.config.footstepAccents.enableFloorSqueaks": "地板吱吱声", "dsurround.config.footstepAccents.enableFloorSqueaks.tooltip": "启用/禁用 当玩家走在吱吱作响的方块上的效果", - "dsurround.config.footstepAccents.enableLeafAccents": "叶子覆盖表面效果", - "dsurround.config.footstepAccents.enableLeafAccents.tooltip": "启用/禁用 当玩家行走在叶子方块上时的效果", "dsurround.config.particleTweaks": "粒子调整", "dsurround.config.particleTweaks.tooltip": "用于微调粒子行为的配置选项", "dsurround.config.particleTweaks.suppressPlayerParticles": "抑制玩家药水效果", diff --git a/common/src/main/resources/assets/dsurround/sounds.json b/common/src/main/resources/assets/dsurround/sounds.json index 35edc2d4..7e9e42eb 100644 --- a/common/src/main/resources/assets/dsurround/sounds.json +++ b/common/src/main/resources/assets/dsurround/sounds.json @@ -1219,6 +1219,245 @@ "dsurround:footsteps/leaves/leaves_through7" ] }, + "footsteps.bluntwood": { + "ds_category": "player", + "subtitle": "dsurround.sound.caption.footsteps.bluntwood", + "sounds": [ + "dsurround:footsteps/bluntwood/bluntwood_walk1", + "dsurround:footsteps/bluntwood/bluntwood_walk10", + "dsurround:footsteps/bluntwood/bluntwood_walk11", + "dsurround:footsteps/bluntwood/bluntwood_walk2", + "dsurround:footsteps/bluntwood/bluntwood_walk3", + "dsurround:footsteps/bluntwood/bluntwood_walk4", + "dsurround:footsteps/bluntwood/bluntwood_walk5", + "dsurround:footsteps/bluntwood/bluntwood_walk6", + "dsurround:footsteps/bluntwood/bluntwood_walk7", + "dsurround:footsteps/bluntwood/bluntwood_walk8", + "dsurround:footsteps/bluntwood/bluntwood_walk9" + ] + }, + "footsteps.dirt": { + "ds_category": "player", + "subtitle": "dsurround.sound.caption.footsteps.dirt", + "sounds": [ + "dsurround:footsteps/dirt/dirt_walk1", + "dsurround:footsteps/dirt/dirt_walk10", + "dsurround:footsteps/dirt/dirt_walk11", + "dsurround:footsteps/dirt/dirt_walk2", + "dsurround:footsteps/dirt/dirt_walk3", + "dsurround:footsteps/dirt/dirt_walk4", + "dsurround:footsteps/dirt/dirt_walk5", + "dsurround:footsteps/dirt/dirt_walk6", + "dsurround:footsteps/dirt/dirt_walk7", + "dsurround:footsteps/dirt/dirt_walk8", + "dsurround:footsteps/dirt/dirt_walk9" + ] + }, + "footsteps.grass": { + "ds_category": "player", + "subtitle": "dsurround.sound.caption.footsteps.grass", + "sounds": [ + "dsurround:footsteps/grass/grass_walk1", + "dsurround:footsteps/grass/grass_walk10", + "dsurround:footsteps/grass/grass_walk2", + "dsurround:footsteps/grass/grass_walk3", + "dsurround:footsteps/grass/grass_walk4", + "dsurround:footsteps/grass/grass_walk5", + "dsurround:footsteps/grass/grass_walk6", + "dsurround:footsteps/grass/grass_walk7", + "dsurround:footsteps/grass/grass_walk8", + "dsurround:footsteps/grass/grass_walk9" + ] + }, + "footsteps.gravel": { + "ds_category": "player", + "subtitle": "dsurround.sound.caption.footsteps.gravel", + "sounds": [ + "dsurround:footsteps/gravel/gravel_walk1", + "dsurround:footsteps/gravel/gravel_walk10", + "dsurround:footsteps/gravel/gravel_walk11", + "dsurround:footsteps/gravel/gravel_walk2", + "dsurround:footsteps/gravel/gravel_walk3", + "dsurround:footsteps/gravel/gravel_walk4", + "dsurround:footsteps/gravel/gravel_walk5", + "dsurround:footsteps/gravel/gravel_walk6", + "dsurround:footsteps/gravel/gravel_walk7", + "dsurround:footsteps/gravel/gravel_walk8", + "dsurround:footsteps/gravel/gravel_walk9" + ] + }, + "footsteps.mud": { + "ds_category": "player", + "subtitle": "dsurround.sound.caption.footsteps.mud", + "sounds": [ + "dsurround:footsteps/mud/mud_walk1", + "dsurround:footsteps/mud/mud_walk2", + "dsurround:footsteps/mud/mud_walk3", + "dsurround:footsteps/mud/mud_walk4", + "dsurround:footsteps/mud/mud_walk5", + "dsurround:footsteps/mud/mud_walk6" + ] + }, + "footsteps.muffledice": { + "ds_category": "player", + "subtitle": "dsurround.sound.caption.footsteps.muffledice", + "sounds": [ + "dsurround:footsteps/muffledice/muffledice_walk1", + "dsurround:footsteps/muffledice/muffledice_walk10", + "dsurround:footsteps/muffledice/muffledice_walk2", + "dsurround:footsteps/muffledice/muffledice_walk3", + "dsurround:footsteps/muffledice/muffledice_walk4", + "dsurround:footsteps/muffledice/muffledice_walk5", + "dsurround:footsteps/muffledice/muffledice_walk6", + "dsurround:footsteps/muffledice/muffledice_walk7", + "dsurround:footsteps/muffledice/muffledice_walk8", + "dsurround:footsteps/muffledice/muffledice_walk9" + ] + }, + "footsteps.organic": { + "ds_category": "player", + "subtitle": "dsurround.sound.caption.footsteps.organic", + "sounds": [ + "dsurround:footsteps/organic/organic_walk1", + "dsurround:footsteps/organic/organic_walk2", + "dsurround:footsteps/organic/organic_walk3", + "dsurround:footsteps/organic/organic_walk4" + ] + }, + "footsteps.sand": { + "ds_category": "player", + "subtitle": "dsurround.sound.caption.footsteps.sand", + "sounds": [ + "dsurround:footsteps/sand/sand_walk1", + "dsurround:footsteps/sand/sand_walk10", + "dsurround:footsteps/sand/sand_walk11", + "dsurround:footsteps/sand/sand_walk2", + "dsurround:footsteps/sand/sand_walk3", + "dsurround:footsteps/sand/sand_walk4", + "dsurround:footsteps/sand/sand_walk5", + "dsurround:footsteps/sand/sand_walk6", + "dsurround:footsteps/sand/sand_walk7", + "dsurround:footsteps/sand/sand_walk8", + "dsurround:footsteps/sand/sand_walk9" + ] + }, + "footsteps.snow": { + "ds_category": "player", + "subtitle": "dsurround.sound.caption.footsteps.snow", + "sounds": [ + "dsurround:footsteps/snow/snow_walk1", + "dsurround:footsteps/snow/snow_walk10", + "dsurround:footsteps/snow/snow_walk11", + "dsurround:footsteps/snow/snow_walk2", + "dsurround:footsteps/snow/snow_walk3", + "dsurround:footsteps/snow/snow_walk4", + "dsurround:footsteps/snow/snow_walk5", + "dsurround:footsteps/snow/snow_walk6", + "dsurround:footsteps/snow/snow_walk7", + "dsurround:footsteps/snow/snow_walk8", + "dsurround:footsteps/snow/snow_walk9" + ] + }, + "footsteps.stone": { + "ds_category": "player", + "subtitle": "dsurround.sound.caption.footsteps.stone", + "sounds": [ + "dsurround:footsteps/stone/stone_walk1", + "dsurround:footsteps/stone/stone_walk10", + "dsurround:footsteps/stone/stone_walk11", + "dsurround:footsteps/stone/stone_walk2", + "dsurround:footsteps/stone/stone_walk3", + "dsurround:footsteps/stone/stone_walk4", + "dsurround:footsteps/stone/stone_walk5", + "dsurround:footsteps/stone/stone_walk6", + "dsurround:footsteps/stone/stone_walk7", + "dsurround:footsteps/stone/stone_walk8", + "dsurround:footsteps/stone/stone_walk9" + ] + }, + "footsteps.wood": { + "ds_category": "player", + "subtitle": "dsurround.sound.caption.footsteps.wood", + "sounds": [ + "dsurround:footsteps/wood/wood_walk1", + "dsurround:footsteps/wood/wood_walk10", + "dsurround:footsteps/wood/wood_walk11", + "dsurround:footsteps/wood/wood_walk2", + "dsurround:footsteps/wood/wood_walk3", + "dsurround:footsteps/wood/wood_walk4", + "dsurround:footsteps/wood/wood_walk5", + "dsurround:footsteps/wood/wood_walk6", + "dsurround:footsteps/wood/wood_walk7", + "dsurround:footsteps/wood/wood_walk8", + "dsurround:footsteps/wood/wood_walk9" + ] + }, + "footsteps.log": { + "ds_category": "player", + "subtitle": "dsurround.sound.caption.footsteps.log", + "sounds": [ + "dsurround:footsteps/wood/log_walk1", + "dsurround:footsteps/wood/log_walk10", + "dsurround:footsteps/wood/log_walk11", + "dsurround:footsteps/wood/log_walk2", + "dsurround:footsteps/wood/log_walk3", + "dsurround:footsteps/wood/log_walk4", + "dsurround:footsteps/wood/log_walk5", + "dsurround:footsteps/wood/log_walk6", + "dsurround:footsteps/wood/log_walk7", + "dsurround:footsteps/wood/log_walk8", + "dsurround:footsteps/wood/log_walk9" + ] + }, + "footsteps.metalbox": { + "ds_category": "player", + "subtitle": "dsurround.sound.caption.footsteps.metalbox", + "sounds": [ + "dsurround:footsteps/metalbox/metalbox_walk1", + "dsurround:footsteps/metalbox/metalbox_walk2", + "dsurround:footsteps/metalbox/metalbox_walk3", + "dsurround:footsteps/metalbox/metalbox_walk4", + "dsurround:footsteps/metalbox/metalbox_walk5", + "dsurround:footsteps/metalbox/metalbox_walk6", + "dsurround:footsteps/metalbox/metalbox_walk7", + "dsurround:footsteps/metalbox/metalbox_walk8", + "dsurround:footsteps/metalbox/metalbox_walk9" + ] + }, + "footsteps.metalbar": { + "ds_category": "player", + "subtitle": "dsurround.sound.caption.footsteps.metalbar", + "sounds": [ + "dsurround:footsteps/metalbar/metalbar_walk1", + "dsurround:footsteps/metalbar/metalbar_walk10", + "dsurround:footsteps/metalbar/metalbar_walk11", + "dsurround:footsteps/metalbar/metalbar_walk2", + "dsurround:footsteps/metalbar/metalbar_walk3", + "dsurround:footsteps/metalbar/metalbar_walk4", + "dsurround:footsteps/metalbar/metalbar_walk5", + "dsurround:footsteps/metalbar/metalbar_walk6", + "dsurround:footsteps/metalbar/metalbar_walk7", + "dsurround:footsteps/metalbar/metalbar_walk8", + "dsurround:footsteps/metalbar/metalbar_walk9" + ] + }, + "footsteps.rug": { + "ds_category": "player", + "subtitle": "dsurround.sound.caption.footsteps.rug", + "sounds": [ + "dsurround:footsteps/rug/rug_walk1", + "dsurround:footsteps/rug/rug_walk10", + "dsurround:footsteps/rug/rug_walk11", + "dsurround:footsteps/rug/rug_walk2", + "dsurround:footsteps/rug/rug_walk3", + "dsurround:footsteps/rug/rug_walk4", + "dsurround:footsteps/rug/rug_walk5", + "dsurround:footsteps/rug/rug_walk6", + "dsurround:footsteps/rug/rug_walk7", + "dsurround:footsteps/rug/rug_walk8", + "dsurround:footsteps/rug/rug_walk9" + ] + }, "silence": { "sounds": [ "dsurround:ambient/silence" diff --git a/common/src/main/resources/assets/dsurround/sounds/footsteps/bluntwood/bluntwood_walk1.ogg b/common/src/main/resources/assets/dsurround/sounds/footsteps/bluntwood/bluntwood_walk1.ogg new file mode 100644 index 0000000000000000000000000000000000000000..2b3df74d3491ec18117a470f7e70271efa0d37be GIT binary patch literal 7093 zcmeG=XH=6(*OSmfHT1HG2}U6_1p^X=6+(}o5D3jh>D?NN2xzE6DAL7%EFeJ%0TDq2 zMNyiH6hTB#1QEf4isG)K>-tR+cF*pf_nh}V=X-yB=bJOR&&<7d?tSK#nVV;4P>?e~ zf|b_p%O4c<3|Au55K;RN`G(NAD5NE4h2ldo^fo3j-<81(cAX3 zDzSYKN5c^OAV_B z(nARD9+;u9$#N81Y|0-1¬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

9)Q zdyW3lRz>>hDq9~}IiHJ>7N(pl`)SBlgZ$I)hb!eWTCEHe6;W}4lQU*t?1*bm&%G`F zs>zm)SXO}#8mU(~)VBBXg%ek1zxX=3ZQJfQKd}>JZ;Sl7GnS+IpzmAJJ%=RIcLjv} zG-bpPPOMpR@~{PV+fTRbv#~kcM_b+@vgtFn9hPP+?mTYz#bc2W|K$EIuE312bF3?ZxY|eTB#xR6J>O_Lx9CfZp>Ov zJvQ7ZUOU(YqmPbOlAnKeswGx?H>I?iDx}FD7p%%u!;|I7cw`F!7xCloSs6J!(Q^m9#Gx?e?-#DE z;>M5;Jec^AirG7R_yh>|)`v^ZguQW(?Jdc~$NM4S9@8xqe zkM<~Gs${sfJKAn5c%b^T(ba@A${VV7`R@)_1c4Xs_n3GmGc5~NwlJ2^a6wB|@1^9Z z?!AO|@o4SmFR`Bt2Iw1)A{az>OK;g5yqcOO-(p|026{+wkcpv+m5M=C&Kgt&B2@ZO znU898M(RDf09ABrEL_g}+?CXT5(C$8$FZI)gr7_taz>|3*6xXkic5WX_0B6F-<`C0 zIWG3`z?Y;(%9s6{Kj;z!cf&T#ta-FH+iA3-MPsE)2z7;pEqaw@LB!d!`1m8Q)Vukg zs2Ym(r+>IO{bbqW9tp-3kG0Q34K2^+`f3>jrTlnxLNI!#tYX7Ag{|~FG6`HMu`SId zMS`)db1`egxQM-9hAdydc_OK~k;CKJBL>bin^+AE>FG%g=|%ZkdbjnR=|>ysVWJ}2 z3{kuhgT^d6a%sLNy5X#or{h3*_{h`BM;Et`d~S*h!>p6W)FCU-flWqxlr(%zr_-H2 z>Q~y-yi-P&dO0%HXD>^h*Co)G|dNmiRB&R033NqwnpadRk<$$x9r#(@5~`DtSqCubVMS=cK~&I`2;)d^CP$S5=re&@}?=pE>moY zoD>6qk}-wHjkfjSh8kidp2@<8 zOg>vr%PPD#7aZSvKbNc9<uX(pFPuu0VoDlSDh;@bq>&sc~p9>Nhjv zdO8Y|nVu8eo{5z)5yZ(lBb5x*u1lRs7q{c|JDL1$lqBsGLjThEh}M>8l5csnr1N|6 z(D5y7Pq*nfa;44Nh`zMZfCis0>Q8A^!-XGyrw@M7zdPoC`Pf^PEwTQmzs-gPkltbW zwI|+`VSbn}50Ba}Yp;jfxj*5))BRqB7KKlTVs=`7PoC*2-LZoBOyfpo;7Id;#w_J2 zrt!sz18%0@OSj0RTZ8m=#DQb?H<;+MTmfOv%@+eyPO(H@_~ zH_{x&Zhn5&O5V6WHo&vL)xl%QNgp?RH+!>~m{DJYl+=iz&|rC~3MDCPg`BjGWWkGb z-PU7|x0Z~n&Yxx)?)6W6_1pH*o~?-2m&yijr6x?iu-o>{&-Qd~UBX4X)|dp9slur5 z4}PmI{7_dj1iF5FP&#J&L9T4|mLAYykex=wJinS4w&^ynqxu~yi9_ypwOLk`fZm(< dL9_S$^GHq2i=)JMF>Eo@FHt4B+xA7F{s&s2!0Z43 literal 0 HcmV?d00001 diff --git a/common/src/main/resources/assets/dsurround/sounds/footsteps/bluntwood/bluntwood_walk2.ogg b/common/src/main/resources/assets/dsurround/sounds/footsteps/bluntwood/bluntwood_walk2.ogg new file mode 100644 index 0000000000000000000000000000000000000000..5719bee565006c98438a7ee60eee2013b1b4eb27 GIT binary patch literal 6746 zcmeG>c~p~E_Fn>lur`5!ECCY;NDx|r0Rh2*Kx7dF0--EoWtFhB$|k5dK^8*<3xUKK zk%359A}AnOu&AhPiY&5-pjE2~DpXNiO0haG3GHcn=A4;xX8!t}-#afa@7;Ufz2Dv5 zyI zp(&sK(&S{E3;;neV|U!=zRA0AiHyh~DSwaMMk)*;<3Oai2EiaC1Tl@gb-I}&#dHWlK~QDGM&ADZi|_C6&#rUF>}N@4j~m=a z_OUv^?8U`ez5N3KmVzTkYeA4a#3UjG$qy;dFTOX_okjcWnfFO%c?DKwx_M6A>Ckz6 z={?GGr#VBUS_lGY4>=?8xfq%UKJT}IDB?%5i8cJl8^l^{%1u%qE=S$rX?3X|MTuDL z=Ve}BT}Jb=yd9eI;-caYDKA}q#^s_YfXQ!zR1X|@766Qv0wtVf{A6n3ASSQ`%{HB4?&n*0#?-wPB0=^8njmzJgKb5oEW=O5O`u; zM?L>U%8>nBdE$`n%XgD*8q{Z_rSlGIPEHY_^2#j8HYkEXj$lzK?2 zISztyQGbkLe7;U33twBFtZZIa&0(A0FgthCdcZ<5G+Y~Um~Xm@AAU$q&=9-dN5h^)tda65Z<4;hJ$V0zKAHB%%mHa*WPkLr$DcFj=p(zM#-1EP2**^KIrYaYp4AKg zk~x)e>{>p%mc^c99W^W9F$x6Y4q@VrGxHbzZT~%UK*E4dr4guq%A76|))gdAi-pgU zOh>0%;B-i(uKA+_AgHTYb*tnkA^6iQCulYkG)sD*6aC+{2}x=J!*U@=7lN*}&Wt7x z7gc(gySLru_&*`^VD;FQhk{bnw8$*aBlt*VyeWyN?od-ct`|VmO$kg%#0RoG)f{SQ zLg9ewNfhE}ravmjjy(T*^Ij*k%t1p6&9C=vM zSGfQ|z8DzjvUS&M$1Y8$E=}rxC1iCo_Aj!4MnKT8=Bg~95fJo0lJ&oM-v8(Ef7${d z>|pBPqfM#8uZJ`3V9;TL3kT;pLS{yXjF98>yftwl+GI6>!kFxOmsrF3H9(Nngves@ zX95ME9JAlcLkPzJ2!VpXyoUr)1Yin4;^!BfB`^RAU&E5IJTnhpCE|uN8Dg;*+&Az*KropW zqgyH2a%TMiG?^AY$&Cq7cnGjJS}cGgx?m8l3+ZsZt_7*@)4m8{^jeG{7Knwy2rrUI zHA;h`TovI>@xVvZXy!zbGR544OLOrMRnx4!EJZvT0?*}m)#IvZG$I0G8rXqPFfuQ{ zK^}Sys=X-5r9DTWc#&qyQ#9}*9Z=cSW1=KAP8o*;3Y~3U&-ehgP859;z)EQjlH$jc zC`tt+pthzw#ioM#<7A3D1qM0Gtj$i`W~C7*HMbVj^-$ujigoN7h9{>cvtq8GMjX$n zmO|>qzuQxSklHCZeASvJ1Hw3odBrM{qB4*^nI|c5#p3y^=Ot#HOpCY+j0mjM1xyA^ z*e|)hEmR=LcQ*u?H$|l7mI|A@z(og_+?Tl|BoWkH{kUor#evMH-{Jgvx+xU)i*j!p@yk+l<{T!}(JhYHxIterp@gk2jw`)_(iEXzd8?Nhc zY4MilbrOE+sVno^O6t`~ukaI zJ+06lJQ#R!sw$(=q|pPaK|cynR9#4#l_DhdDT5eu(_VrAm1@~2ZN5AO?2s0&CZchm zj|@Tc^0EZ1%m6D19T-=4fMva?Y;_0N2G)5fAVW%5UG`fI43?gjZDlkw8KR663fM?l zfdL4jd95O7URwb_i3}-LItH#TiK21IHW@ZF0T`HOE~9H54^052Ajpfzm9YoM zZnhlc%tUjwBWrcPG|Vt!DXzP>*sWi&J4Z8>?PVZ+pJU2FpvoiYJG;4&Q?LexYjJoZV}gnF*M-%p z34$cQE=X>nME%kYFh|pMvMcGK0<;#Cj>;%BdJyY=xKGi8)sOTnxUS>{K{#-fgNDRG zFqo8$bPbdJY9a5wdaX$KO3K2Yhr>A( zM6tW49H`1lwRl2Qw{Xjj@Gy~6xI5xN#KWh~rbz7D&duLYg>nT9Yt_y}rH9>j3=jRP zyv@)s(`nQDOGd~0#d+ZiO_ABU8mP7foKYADc?xgz75Nf+>EOw|8!pDTYq&noGIfJ> zryrl*x#K}<;<0+Q*|$DxTb*o!tep)euEg4i8s7!Ap}GZ@2ek7@#Y>b=0i6xhz8a-*-!dpjVj%bxxL0X%j~p)kF3m{>lDK%J{WP!gGEK%pqY8U&1&o0aqpd`(iLm% ztF*8kY{S8u*w<&Yo$WN>{n_*_^fgCnjW7nUZS=H^?h16ZX4|K*^~WIOw%D=bwI;q{ z7q@bfvMi3&1!>wfM*NDsY|De+FG$LtZ){U0z}Ke5oxN^&QoWP6u4DTFTF0{yKk~l) zH=lVVkSqE)L*Fz(i#VtvmZAR7AjqxFB@d=BsX2y7nhX{*srC=C@84u3UT7Nr~p2K3>h$GQz z23OFEBsqjXt6Q~LiDmBQe{-QzH}*YOdwlW}|DES9%vs+DE-3js5jvDg57dVT`y*XH zn`bu+pcadQFKy1cm_d4JrK-k8xdgE;T;^6~e0cF#|I;_hcel{o;Dwg-LE48%A@< z=@d^dGEOtSFR)wJ#c57=EH2n3oHx6m?5F`d(%$`{1R30IGOPG@rP?DqjjZY_pSkB^ z@rgB0FMZYheStCW<>c8-uMXbo*3m80LBFiq0=XHw!%V|^jlMkvm0CvCs^*tbS{y3X z^B-Bo!-Wy@N*T*nzZ%IIv{gOexA0N%9OuRK0vo0Hy1(@Y8`t%IP4|{d7~d!42UYIu zSPHW_SMxRAnmQhBeVp+avE%7tiSy6nmF6TQ6kpU96j;8{?e^L}Xc zvz@6(%AoD}q#J9vH4mRC-TE2zY-;R7RL1=~sulAwdbFk;iho;&bSZo`PEv6*FD_RW zm|eezL%N(5#VHf?UddZ-$_f*reMefxzB3qC*XUJ_^uLBu42X|b)cSNeEHfu>1@Zi3 zMi_bqaZzvc-;Y^+@pNKw`TJZ&AC*9@2KAaBnyyu78n1;5vt8GA@Y1=8gQjOy2^wkm z!=-zc+=QRc?6&^;BZlj}s1)VR_D3IKX{cfR5$cU`I=T0hXX?(b>~{@}8M4@Hl1;@l ze0@6VdGPWX*;hpim0bq65XJ|b_Qbinu5`cu@=C&^iZ7zxr}fz5=c0Gi{_-naJ~y;< z$H1r7+xe^CmmEq;t~}aLurfD~w?Re7>$_9z;dZ;b%l?5kC%B;~fr8P$ABerPZAdK` za!`8mqv5P5Og=is#k`GDsbf^Kg;b{6;Q__GK!HWzIC8>>#DrdWfSgQ-OOS1M8FXbifI&kpd zeMcKagP*z(tx{Gq`m4uUWmdySy7m*#zja-5P%l}6?yZbmJ2N4^c{OCy!+M_=z zUBzJl69n5(g{rmkykC5KGS>KI-F(v=HvgtMgBr(BR@-zurRB}?<;|{zceBUOhJWJ3 z7VC2;^(ddHMw>B4rFB#5b?f%I5Gq1IP7fUzPf~>{>MizswgF;##}w@6!=UYj-Mt`| zaRzcqUWW+%0jf~`&JBSRS8hFuTA>?*7Ixg@rfIrtwv$s;_M@!xK<+gr9%&OTeBKhuAaW3}_Z^$0h7vW`6Nt2e4Hfi2CW~~IlkS_Qz zN30{KqunN`nTVgpHpc7JwB;2QFrt#!c}%UgqCCbSCR>!v`}M}#oQs!(wiW-BeN1bH zCA_Mk5Wk|*72&rv-rMN1_(S05zOT-1ANlN-=h=;=l)J%Hl z3+H^}MjoBylYc4^awn6iha2-NlODVY70$d=-XD88>y-b#f5^vBo)mxcj{k0CN58uE zcdYBvDuuCiiSqqdvM-{hTFxgGbz&7i?%h6J)(OX;W?#0q5;GBIxSRL6eUA}WOY1ZY z(da<%pGeR`YhID%7}?LlqSbg&t&oE%e4ICL+~VX+U$=43o@k77 z7%LtjE#^R=ZuCO{BmiuKO!%?Wd~VI;K6sS$d{rzIOV$g1Z3=*u4AH z`E_&pNF{%1s6B8%V$)D`H*EUv1A$)@l0~R23h5`DL&x1D^WX|i&HQ#i+>dRtKg7Lq{1ua?LWN9zA4Ga!;JZLzmE4 zID#Jh2|cW8$*VfedsbZ{FCu}y zl@h)Uc$AnmmzWct#1)Oab1YuvKkW_?y{5nc+OlUaTHIat;bcV??gh}N4s=}4KB8+J zxhKxvHQuQwep6q*Z=c{xh2Nb3-42_BI?>t3z9-LqSlwoLj6A6B=}q<=`v=_zl`M)E zs0jA#uEIz*(mFR-WEM7Lt5t^Fs3?{M2{>i57~U=3(x9y}?@4Jj>de^nOy4smE!9P5 z;)ZPJilc|LXNu#72>qwKN>AV7#SOVD^7!IelG+*B`sz^#)1E65bgWUHZ1vPO zxYz#{?`c&F3eGa~&zJ~We8v1kOyh>CgNlc)+dhUnGVh*=YLN?svv*;|KMu+XVNmY! zASVhR2k5upJ6hdmQ!qzzWI|BbN^G8f{0Zf}Qc^FWLjQP<3#~fUi>__Lk53V4jsYM| z<=4on*Z+ngDhiy3PH9Be zxuUFdoUA!c4nDJxmC37VDUa?yztH+0`yZJD69#r?I?nkonbS@}Z-mLyMD$z|>$q+N zo(}QOEC1{O0NQgEJ%mRI&YNyNLARNpn=^dZG5%AV5T+J1Yykk;0Nl7ZJGyW9c&VGI ztKc@*`w6ZKt&>%n;uoi+LFTw0!-mLXjY);dc9q5BIz9yLIN!KvtS`r1$*z)4kTFNX zxJ808ZJ^wPzzs;?JS3qXh0T2o3@(9zCGiS4jFQ`5lO2}qm)HN*56)APJS^dya_kmb$jzsZ6cK|#an%d((GP|*KK*8k#t|DVVIX$!!xLny;X zlUjx~lVDR2;3&?Ki*dh2W{3ITB1hA<#S1ua>GkdZ&p@$jRd@2pxLE`W?vmaBPx4E^wIzZ2ihM~3ZEpEKT9f);MMf+ zB@Qe(d80gi7>t6Mh{Czday+lHrp6Qs8Wvqq3`SrCZyDVBh5hg{b>Pa9xukM@R%La~ zc?EBjGc0`I0X``WnL<#_e5->H20?)l>2-HK+;FQiLj3%ImuOa+}zkOpi$3VgO9(=+O%z-w6T zg$gh2IUJQndRiQ(hUII)%BC6>Dy(txxg=QVY^FUgf-n<)*i8t_quU9KA9n&j9+JS? zn)(bK59^P!amrK#a1dKND|)k~ny<**8le4=&|M`aS(PkzZe>!*TxMlWB)38Ysjm6M zoq3a@XX z0svcL0WfU{PDsluZ)}GborLhdOd}x)u;%K;RH#th2R~|@r4$MW zI8&Re6D~!t|LNcdF#FRb+c&2v)fyI~j!SFmG^qikEcB=&hx7~w>$!B5&|B_qe1Vo( znpUb8jaDxxvY$hz(E}pQyJv0ZFcsqg*(Bphs0qI?KYBG7s!NWH?|1w zL=LuS8D^h17x+aDN`oSM3UDyGL%Bg4^?w`@3r`6I`#$^=27@G27z@zl$ZC4FEqpQf z$EhrzP8UTFtOmWP1b#&}>8S`ItWQ}4=1p560#>T|qx6O1IJiSpxEk@tp*~^+oklZ< ztniSPkPeQ^JK(aKpTE2VZiDM|6(~bQSCRjF4Gfp==7M}WUW~}+20%6?TVB2exW~ zX2?11t@_D^^4i9wqSy-qK=Ipry1Th^iUD7%qL6B&5$8qE!pAAo>#C_kn5VdcEK*v2 zs;`x70sW?g z9I_=OXQoGG00wzd+d#EZ3uo*r>D(0$8ZNizt^nFPx~!EeZ!y^lt(O-Zkd8>I$}vei z1|e&5f3Z`yBsNt&K8vO&`aj2(1hC4(={h($k>k*M`l~Ql14EpV=>w@qW ziq_4uM%Wvhh_9qaGGG-f9pzP2RR__oM|))5IK4>s%sx3902p|b!-m8F1VY3{v_^=( zT1aK0MOYDEVOjVoaPII*GG)#ER|JY3W%XK$^%rJl6kGD#^xW-n?c-LSYJuV8ezN<| zQwIkO)CpWfqdL%b=3s&i<*QXGgVTW{G^4FK{n;r)^~F$9H#2V%UhBJays(HDIPX2 zP0eB~*ZaXG(cEeuyq(I>s=1AIeSUXs)!M(1RK8Ch z11Y&I2jKTStRcbhlno`%X}9TF#pd0!O)}x!-a!+(_cg3Yqz7R#lDT zNX<`ENs$NTx2tp*3=I-zo4}=BqqaLs5E;;DZW`G|@1hgUJ3uThJ8h@Lio>$IYMC{P zHTy$s*V}9SIPG?hnJW7svf%C4ou;eD-(IQp+_L`3;0FIw+Ac4DYh1->*zi1&eeQdD z%7et7x9fdA$X@VIIJEade{&2q#N@4EV!z>2L+iNwv4@VG%6l#(g9|F*KALBulf5Ur zEZX*dnj+VAs=p2zk9?Hd*b<`b)%4Zf69>8~Hhf|oW*E}1Y8(RcUw-ihQJME~U-b&h zvoFF2ctpfewNjUc!tPVkL&u%3aq6Q!xHl9JOd-D1PijY9DcGMhi|~2jcbE0PB45o| zU9hd*81J(4-jbEf(*D_xb~ls0*L$-*bE#GL{gn@&IG#9wR!e4-YpdVIs%|slk;>W* znh{d3Nyz$3DY_J9I>~vK7A5aF&k3GNPL2Sr&#>CthY!DIZnxP{0u<$^I5u-v4tw>w z)Y(3KVi85fNuH2Y_iNa8($J+)PsW@2p|2(M3kd_&?Z*gVqKWohC+g0#2-+FTSDBDQ;9h_HS_qX=5Mtq`HZO*t9#Acex36!33@x7Zf%EG z3X@V+SJ+FeNxsoZMIW1A!LC6#gSL}Q=Ll6*k7kYfZi9@QLH9oKB##DObVvm_`8>_N zXC+VLHwAPiD9^G!R8*t{bUwvvaxhZ6V}sUQ^*?*2u4IMhK`B|H%o9bQ9)-Ome0Y&v z5Jy%E+km5RCU#${N(g5haD6|vVA-kcbC1X~mj6wADnjKKRNn?eU8U&~gLjcN>27LS zA$?i`Q$M*)ZYuBk7PB5Vt~{=#;F_X?)_7nZU-r$8AGnBozmEqv2Oi$yGd}B0|FGs~ z!uG|(P7*eqBO~65YrRnS(_A?y7kFnL7$=ch{b~@2}Omzg)dFA$S8<`7yBTL%rvWP`4y6 zk^h+etjRoWqslaDLLF?L3|bwOdmm z@5avaeu|bvY2B16^v>DR`qqvgK2Mls)*UQvKR;`-c~^MTU>kFLwkERNk*wBl1+J}A z^LoNWJ4p_3ulF~vubw;2E?nQUiZuUorsEbeGS#vHt;PP%Y;%O#j^~2iD`b+cMqA%_ zXVDiH(|WtmW%!%Rs5aVe=Cs`D>^m_f^Q({jx)kspr<0p-WNg8^RMF!3ywx|`)~_9S znL8`B;~?D`2|gum`bxPBvvb!5{k(5B;*mvDzepsfpFeD`Z?TBOO37h$#!I!JcT>I{ z%&D&Yb|NE_qqVv=IIB7`fTVPOMYkYtH7if z%+>ZD1xHD_G${s2M$iz0^?T9a6CEtQu};;)X54pE)RmNU#*K@OUSnCu1(_NGuWyy! z%nvUnWgO}VoS6B!!muz~NeZ*)`%3i&S^mhSfW!P;KTYLtEE+D(W*B^|543f(S$bn_ z_2A*5D+OO`Ay3OU;Vni zSZ~8md7~YNPsP2aCAg>;FG|)w=-XoCkP)x8XZB0GiPn9{`cva3O($b#PW9)$48a~c zeSaEAC*&f&Dty6Dk93gCybxw#?klO|_Cj|UR)Bv2+K5|d literal 0 HcmV?d00001 diff --git a/common/src/main/resources/assets/dsurround/sounds/footsteps/bluntwood/bluntwood_walk4.ogg b/common/src/main/resources/assets/dsurround/sounds/footsteps/bluntwood/bluntwood_walk4.ogg new file mode 100644 index 0000000000000000000000000000000000000000..e5cd889ef7c8576d05b96d5b7c0512528be775cf GIT binary patch literal 6954 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 a^B3i}qO9KQb{|-@jmLjF`^x#_B<`Q;M(B(H literal 0 HcmV?d00001 diff --git a/common/src/main/resources/assets/dsurround/sounds/footsteps/bluntwood/bluntwood_walk5.ogg b/common/src/main/resources/assets/dsurround/sounds/footsteps/bluntwood/bluntwood_walk5.ogg new file mode 100644 index 0000000000000000000000000000000000000000..69b58a03ac8cae3fb3b94362376e64be5386c17f GIT binary patch literal 7716 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 UD4p_-3tHCV-%E7vsTBwR14_U>m;e9( literal 0 HcmV?d00001 diff --git a/common/src/main/resources/assets/dsurround/sounds/footsteps/bluntwood/bluntwood_walk6.ogg b/common/src/main/resources/assets/dsurround/sounds/footsteps/bluntwood/bluntwood_walk6.ogg new file mode 100644 index 0000000000000000000000000000000000000000..6384342334a90d6ea4ab991a7c6904a9ac049ebe GIT binary patch literal 6318 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 KV|CejV*dlu741v_ literal 0 HcmV?d00001 diff --git a/common/src/main/resources/assets/dsurround/sounds/footsteps/bluntwood/bluntwood_walk8.ogg b/common/src/main/resources/assets/dsurround/sounds/footsteps/bluntwood/bluntwood_walk8.ogg new file mode 100644 index 0000000000000000000000000000000000000000..a2abb21ea25ff843bb6656bdaa33b741ff272ecc GIT binary patch literal 7088 zcmeG=cT|(f_7h472pD=mzytwF03{MwK)?{G5fnm?q7sUP@>D{x8zdkEM5Kv95RlLW z1O-Gz4JuXXBA~8oL9iFFyQ^=KuzPm*ymQ_==e@sv=Qn3E-^{&t?)`3?xnI_8+2Rc( zzCwY$$P1+FagggIe!kt14QSn8U66MF=nxvSM z4kQR*Zi`s9E_Mqxn#u4J@yEJhSD2ZR%q&+}3ppskpVQja>l~eEcB{8;jmA2KGUE`^ zVh(u9YCi-(0l?OC$6RL*W%~kv2B5U=r-I!*mpGN z-lnr&Uy}Km^~T{dmV>51Cch6NJ+SayATU$}6mk~x6RU*q}EA*P@zdmjku4kvZh?K4466LI}5g?^Qw>Wp4ODeiAbPtrG#$- z9_--G?8w@(Ge22At;CClLx5GwOF>&&??as9wRIwNwCH1R#c#%9t|AX$Wnk0%Bs0gk& zydaW|GS3bcnS~A6V#-jf<;9X9L0Z`qmN&$k>$g_qJt?hGEgJ1e_bFO>zNWB9HXmYrsvO#~06bDi_Jr*Ni}z=FD-$F3n&_#~k6w)Jz7zy@5l# z>FV>UaF(7|w6x{CkC>l`DQlo+xXbh`kkVv^Rhv3_9gWA>wZ}w49cA!WJTda zhJFj4z;qQK!0ydLGE{MuxLn=1!-~13#2$Qw?x8GaYE7~yO>-$f?tn;hEC3wzpQ9L; zieaSTs*cCX5vnV4xP(5Vvo}}VFcA)Q*9L4}G+slmmM1okP)#hx{6Y@3OqZ}4tX-q{ zEw|~$d>G((zBr?xQ?a4h4oFQ=exw%0!mKDgAopj-z11I7(i#3!AAF|`gIOHUqG0y* zPlyRT8h`g(O3+07FXMZE89%g1BQ-3r;4fXQI@AOR(@B($d5lbA_wnSl6j`x@=QGD; zyK46l?cO8$YtHCcPBeX|zhsVpsJ$8{&lwYsIkAp*!|-&7 zbJF;;0{{?Y%dZh0CHh`8(=nRG7|qnz$IkcPwJ~98LBr+)pb5aW=Bbg`!9%641eccE zIbKinJGHgAr3d_23TmV@w+tLZ4rfR#P_(H$eoM<6ugUUZMdN(Z+!SmoX?Ub53dSu8 zlxYIRZiM~~3H>#a(2v6AHVXRZz>PWa3Xn%gf>{!I&USwNUp@3aBuRt9KF2pPZzDt+WX-l;r5 z9RRDGkU&^pSmlHv06Q$O9NzW?{B*s4&E`Lc{!Ji)Z3MtUC~%wh4T-FfKyr@Bg3W=T zUot|Atcnm4O9zn^LqF^Sy+=y3MTT6g~21QqtuWwKUZytPMneE^_b&y8M%+7KnZe>kv z!vZf=Cs_Ev1AJT>GKHYp=NHIA7z71rq={KJypWhD$`4{QYin!aeIwxm3C82YG)skB z8jNp%@%W(e{IEde1BmTAV*(u^Kme=&Wz$=I2DQke?JZE%I->{DU@T+?Q;B@}5M_#7 zS+G0B6&FgQ5%7FD3c(q@(XCvxcnSy4YqQZ|wqMQR(A5o+2!8YinOzZ55hzJU;j?G$OQ;0GbS%ut#`( zn=Ale-8KLSjluDp-0~&?yyzr^_a%pj!o!-Y2U~%rxZ+c?{~G=XwhsiLqv~Nn6{QdY z2RKunsS_?mu>a}c2e1h#l7h7yg;Pt8s~(b8*E*vNP%_Y?4r#=v`mml$L5B>vx$;{u z#vDwtCzV>?QfND)&7sjFO}nP7XRsBw=$S;rai|HufRWMErcNQ?TW&MzdNKL5NYhqK z)T_?wJn9-^7beQAYg$Nro1Q_mIj6iPlh{S)D0OwJht#!<>t!mP=^UZgw;)3@o6fgP zME0M@EXiyzZSjlhmj;ElR)B0UVRel<68JD;8$2Zt?0fJd3KHfMiWI3tOh+Pcz#7Daax2B)~8H-*iCC80#>SdBeWOCS#XD_a5doHg8GONG%D2; zvND3KgmiG6-vO63{Ji-ca2s5wprH&AT}9sSH85PdnYQH7jKqk%96DqpVg&~%h(?`9 z(5P!5KcNf}Ry6v|FY)|a!fj$KJqj9_MiA3A-=arB_F_6_JrqR6=Zo3HV>d|>kl9N4 z>WnH)f8C@da+-#vqSy-sK>lPN&CS&5fF2)HQ9v{b%04dU`ib{Iq zObN-yk4BIHi#npIr__YeH}sKo>Wl-8ZG!BDKvQcGQ$u4YjJ@DO+Y2(v0YyP)NE%@g zGE48hy}qbqTe3p{7t_sUWMz`~;(HWU^h5F$3BHA4Ka zg%ko_0>UQbD=Z7|JO1d~6` z9nUw@V+yCg#bc7O%IS`JNl#C2N_^DyqkU{HMcZTHW#@fk3x6#?bPnlf_;%lL*Lq1~ zl^s>Rf+|$+qqMQ%EH@+V{QX;v%CA;lxO#HeXk>HX+zD^xb9g6)WM-Nz|E8&v z_{;vBXWv+!r?1ec$U;jQnWAJjoki_?MlLk8;-+Ux?q1)wbp7FPuf~5`3GUQ9`Lbp3 zqbzdZoN_U}WdFzL6Pq9GF@C221Rrn^WmAVAJ7S9Y^jC6k5j=!i3` zQ#X0WF<>@$laQ%~82Sn{F{HHcNFnj6KwVB}!m-?eq^uB)v)sun^ym3^o9ODCcHMFW zX|lkDqRP3lZR@w?TFzbFcL&pwp4$g_W4`?+Ul(@C(%`Z6nJZqObJt&^q_p30eOQn! zW$yAyb$FsLkH7o&C7IoGzfMg_`8m9v>zu3p@jOZ6l;xGuk6+3Ue`Wa;=`L1TJZ$)o z+O<1f>zOXMZVl~{mvlg~q_onCBk>I%fS;SiYz|37DX%mqa>{_YOwXueCAThAXGKO@ zbrDl}GXL$4c7eH+pbBTCmlfXv^&aZtX;ChHnee#8lp* znLgw2wzwhhhih!3jH`_=7jI6< zL=smw*q>GQUFu_(WxMELB8W?9K?2JUS3)=ahhoJl)q8=)r-Af z%%5H=*ST!dN4l`>>!WsU=JpFWZD{#)f(p)tp)A@z0Dy%xfg9~!_$5%X1`xU$UsT8?Qk*7kkDTH zLhBivd%-PlvqfvCq_DLI`JvvG*0`F^R=N%S6+vgV$+n;m1H+MPJi2< zpVP$5121>IQhT=e%ASNvAGz-jZ?doQE5aF=STih|(ywz|G3Zn&BP|Ybt~Pw5z3?Y8by2j_o96-)iK%=b-(-fwT9OaHd%!4YfX8#bMM5$ zD{@VrpX4sVAgU!$=b+S zZhkt^k;+EArhzD}63OH;%~h#g`+B$%8*S`Hb-zjhYSorIt4{zyKH9FBOj#P?sk}qk zS51^Qi z^@F{)tmu(eA(@(_M3qgXcbysGjtfqC@G?Ex7abv1c5G_icJc1L#XO8!hyZO8QYO8> z>cH*af^)d6bGPc((@y_jUtAgTYjS4kIWOPCaR4j1@5_eYFjdDu+n$DfnWOIDUP!41 zyP+8EqrlNf%R&1{gN*)0x{mZBWaY`=T85HIDWf`=)y&YUOi2m8%hig+rLNs?U>I(s znQn%y)Y)|^-TL)M(|cFGd*_~NH7@o~mf5mzSN5J8k!=y>3R~ivC1{EnlR2L#zwPg= zFVodyZdLwnJ<-(Sg4SNy{W4QR`r^sipbtK~_OX_%+OF@3voOC~qt^EP{rjI{z8+lu z>TpZx+*D|*>D}UC*O}zk&vPxjw@!`?`OxhX3v}ww+RiNc-q!f$S1D8(0u3%(|J;fn z0)pwdyOm@~;14d$PBBr`XoVyfd0S~9P;z&X8D%ie(i>|IF~&LHtd>~Wj?&QUrUVDrcYC110)wU*M<=;9AZmeHN(bXch?`OzV0|Cf4(VuGWX(# z_7PY8hUDSTx|#R0hx~Xa(ooRZ0|fm!c59)YpZc z;PQ?Q9t)RYHwQ1*^&d@MfC`vcBO7OPU631>*S*BnKAF%Vfl*9-6cL~!Z5+vMp|@OL z>s?=%TANfKf$7TBv?x<+O8b%!YtVWv+sIiW)G~3)32y%7Pt(O;tPI91jy_4)59&X) z&qi+9IhksvAN<>w=!a)>zfOK*>^qiEMoIhm^z=#0Za=&~|4h?dol@+1l(DhgjoPVS zHpy3eXMOawAKht~#CBTy@#yEuEs=sn(dXnT=qApJCt>yZUtBcMiL{TW?!4TyZ|>z8 z-Qt3iGPxsP6m_=?RGt`dBoM|2dx25O(+%%c>4i25^^)lWPV352YDR}_-l~DkkKesB zO14c_R?d@BzyJae-1Lmn^Qaiov#_aYiqPXf&8a z^t4m``=eV=rK?<1da_KwWe8G@zfPF2^QKR=<59sTdYg9~UHifBYTn_*f(gZwN%hCR zw9SSkyn0Umwex;-%3D6~AF=j}pN0G3RuVcc2EVDQXMHYIaKFWj-_ZY}zUS=8CiBhr zcKILm%`pD@Z0ytec%B1)$%}0tirkD^7VKbHXdsW-$u{5a(URd{^`^q5K*`MUU$xGT zG~X!??P0CmcPNlakMUa3{=8oQNT~i9@Rf}Nh#$uN+_A^KvG&rLeQzlCrVHkD6E`AurzM>I zaP(nqDRcU#;VWT=(a|)}4aX=$EMhF*2Rr$kxxVc?NUl8Y?U&CGl`^_z^19odt~P5XzLCfs Kh}~%Hjr=E3Oelf? literal 0 HcmV?d00001 diff --git a/common/src/main/resources/assets/dsurround/sounds/footsteps/bluntwood/bluntwood_walk9.ogg b/common/src/main/resources/assets/dsurround/sounds/footsteps/bluntwood/bluntwood_walk9.ogg new file mode 100644 index 0000000000000000000000000000000000000000..fe8518f03af2e2d479d676ad56f16e27fa072920 GIT binary patch literal 7494 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 L__7yT7zO+ff*IyH literal 0 HcmV?d00001 diff --git a/common/src/main/resources/assets/dsurround/sounds/footsteps/dirt/dirt_walk10.ogg b/common/src/main/resources/assets/dsurround/sounds/footsteps/dirt/dirt_walk10.ogg new file mode 100644 index 0000000000000000000000000000000000000000..a14e77f480ed432956c993dbc67eeb9fa1a4edf5 GIT binary patch literal 7905 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 literal 0 HcmV?d00001 diff --git a/common/src/main/resources/assets/dsurround/sounds/footsteps/dirt/dirt_walk11.ogg b/common/src/main/resources/assets/dsurround/sounds/footsteps/dirt/dirt_walk11.ogg new file mode 100644 index 0000000000000000000000000000000000000000..38f1733d87e6c4dd07a8c5a7733a51966e6ce652 GIT binary patch literal 8329 zcmeG=X;2f{)}640uuKAB4;UaIghUAjWYrKr!ln=j0*bOK0o>RWMGXisL_~HWC<90$ z1W-`HU8AxuvM=I>q97^?qN0u?UnekAGgGhXy{hm1`s(Ytef!>X&ON8kcJJxluw%zY z017N!Z$cIaVFwH!Ky)DSyQ6~{F(MGuv3iN*6GS|?3fUl9`R65CDZ

oe>}@3;(zz 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 literal 0 HcmV?d00001 diff --git a/common/src/main/resources/assets/dsurround/sounds/footsteps/dirt/dirt_walk2.ogg b/common/src/main/resources/assets/dsurround/sounds/footsteps/dirt/dirt_walk2.ogg new file mode 100644 index 0000000000000000000000000000000000000000..da34cde610f30e71fa2a0a6ffa33fc79fbe7bb5a GIT binary patch literal 9068 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

l+u5W!0RJ6po$6}hXCz)MYzu6$eIVf<)e|Cr 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 literal 0 HcmV?d00001 diff --git a/common/src/main/resources/assets/dsurround/sounds/footsteps/dirt/dirt_walk3.ogg b/common/src/main/resources/assets/dsurround/sounds/footsteps/dirt/dirt_walk3.ogg new file mode 100644 index 0000000000000000000000000000000000000000..b17c93d6bb7e2b794eb600bc2b56db0d93a21284 GIT binary patch literal 8186 zcmeG=do+~W+wY9qxHcFX8a2ZxBOK!vQjI~F2xG>rh;hFa263D^?u`jaLmDb!TqYDX zN++dUbI)C=lu)S1>6EVDJHt8aoZtG^`hIJDzrViq?X}-Mdq2;!pXYsUdq49!1O_?- zP++}%^j(`Mej<-UHbY{MMA3rjd=S*KdY$BVh+uLP;=teeSL5&GWAuKJxGGEh@h?qi zjgtyU;1(Paq~j13hz<*;xd`~9ZP9wVx(2#NdM11hvfyL2-@?JtioDC>$dNF#m0xf) zL}ZNvj%eWm0bl@-sA`97O&rLf0stHUimP|zCv-M_e43C{X^lu=@MqK2)&oB?8o}Ay zrV8bRZV*OC8~7;zfH07VgR!Hgi1SS!H!ID`Iw@;*@Mmcmx-6x%U3rf@7BFWfiSxVW zHp8UdML_KV0t}OiAlqQl{v2?e0vZ!nUO?-@RiNlS_zrZ6jOnYgGmb=YT$v+Dv#N|m zCTWj)(BC$RJ)yrd|AbD36G0|_4gx*U;Jbi8KLL=>d5zziT4*2V1gn zT}JiGsyoT4Kh)D6A{#pE@1jzyMkrKQs;B4C@R8W?k^Jy67s13O+`S?E@jtsjbu}D7bFBEpl^lw zZv#xlG3Vm41LJcABfBD)UH)U_bP4bZ118AYMR4 zFy{mELlR;7IX(ijB!X;_mtYoBYmz_$#-*2N_5gdO*F=UrQ(T289J-f5DcsgrRZvLp zGnp$2>rY^B(owOPDep;%4TAP7^ME0Sqf@&W0B2iam(6K>#M z_W|~7Wg`NdWv~jj@fs;>`~*zteN|pXefLbBgF7-F6^1s7dxEo7zoNIj;(Qn=cYP2S zgbxAqo6k71{d_X|Shgq)fhotF*&KaJ=1eiZ6IZr5D|;`gD#ekkw2c#;EYKVc0IBf5 zM=>THNn>CtilQVmE6Z}3nq3;#di1)r_(OF|pXtKY7Py5J{`w$E%W%ZSH;XrWfnIB3 z6UyO@dTi%_0gmIWWfbUCv|o<7sFEN*(hH)%tSC;F_`Bnlu>Gb=oW|~}$J!5S^}aW7STT4gZv}>nvx4VCg_t2le`h1lAw&Nms{cr=|3riT zfg8u%8;(8u__sb5^Y_dF(?-=Kd)nronUnp(I9ts&7wew8RggJV=PtaiD*2bpDGp** z6fi3o%sEE3Mn-;c2AkVh64q6_aO;2Ue`OAsFkrVLW3B#?In8*K1(-bbTJ|ezI_`Q1 zo(|Dg8~^SA0BFvU+Q&aiuuf#{5whV3S({4PMg5=J2tTzz!{z~i5&*PZe>oV{pH*z5 zY0VqWb(+Dpp_G}$$u4wh1p|idNeoQ_vlX8&V_IG`tn7?aqEqN$7z)Ey+O(XE6V--+ zaSH=3DFHGa5NtOTyH5!8Bfr@W0lh0g_sZG|APgEbzc(PPn60n>S3g*LA%lK?U*`e} zazH@9zP4_DZ`Lfot6ARae*rHCtHhe_6`JXui?i#R@FM&Cw21Ct;QZuXieOQv?9xTwiyvzA(=9hX9HG?j_)DpU=rq{qNcQ`_O+82uaicfD@p=2vj#T z+t;0tt0n2}4)h8IDH9M7J~5R4cAE^;6~#0R{L}%s2r%V{=pHn1dTE@`2`=e8t+j8+ z^0a?2d+yv;gWeT8*^;^Va`FBZQ14)72^R`pFQC~)_+~c>B5BDu26d7EG!|%nZk#_8 zU-BxxB#6!Jiqz^}v2;S%J5#B7(?R)j86_I*axPaB6x1)cqNr5QAoe==mX2BfI<@D< z3v=-$8qD%4Zmpyf!U`;Wzyo|t1Y`<=xbwFNd>9Dw+{jpCS-SvTC(83o4CZpV;C%z7 zfCOVP{z}FCEw$Ubfw36Ru{?iw(J2tyRj&m)q8S39n_;F8D(hjJ>~Ags2<3WJfC0ur zrVk0vk@A%zN?h`>Bidm6$Yf0%M}nxSo<}yf;gpf}NZOoyvIr(GmsEu=Ba?9=K%$B< z__)E+(yN7m_h7Z>%fGbeutXAmwuml=;UK}vrV8Q9uW?wpc(BmfPkLPd!nSezdO+A2 zvMIm#vBhzsK@zaGCcZ*NgZ0OGx(pEl5Z0{C4BM|OM-iA?0cf7$yX(R>W_hq}Zh8EL zxr}mdNN$+`QpNqto_vJBPEnrg)>Q8<3F4cVE6Fb^Db%qveu2y7F5GY6n{_P4=P_tR z&`Qmq$v_i!@~>|#NdRz&007OaJ~63hO6r=yiw?@aFH`X_99VO8qRZe!8(dn>Kf@oA zNC5%x&>FCy3YGQ+2XH1gSIu7vVE@s<1whB92{rFemAfBU%fz>Pq{_CpDf(9P##A$9 z>)QrBYIvf)nRSi4@sQp|q*`XJHqRxrR|F_9GX@Cicg z%Z~-n<*+JprV03B;2)<;EHYUTJzzEHNW^iJ&Vm_;!{~E6g{y#a*w$L#@`MnT9bz~Z7(P-2+_RcD+Qjh<^YhCBQ7ja z+)NM!P-v_T1V|G}myuN^WI~0eUkr)@XxM2bRoOZub}L25sx2D0db2ra1E8e5DR|?? z0slnFTQ?U7Fmo6Yeo{yS4H4TmxzxJpLPUyuG?Szv_&=8@1b|f@b`!zU(twUq*}Mgf zQB}jL3%)L_S4{xG|GL1xh2$Du`a#ULZd<#OrbK})VCg6UmzC{BS)b?-vte|?Y%?B+ zlK=n>9_3(zp#ca)z(%l!tbMf*=IRNs0>1pR@Tk7tS5>2(W?z?=KCf=ywd056+vOh+ zo$b5LOz}I7P4K!pW`;k6CNsbN0{G;#j`xefMQw{}GU|nQ-^!&4I|aX;`o_MH>SGWv zOrC3cbjl+tDE)Ez5#P#w>ao{ib~64hQFTuCYd_~d9~->S9Jxo+Z_|aTSf)PS*jvOr ziVj0%rF%b4IeypiSL(a)>y!+RWMrR=ncfk7Ry)t}RL^56PNep5m4ei!nJS8t6F%^J@z!vj==qJ`vU`7tJgovLJTLz=;~P&AFughHn3VIPU1&;1X9opcc5@lH z`HoUiN#T}gaTR|5`QhP{%E3-^)LqNeU*WGS2i`#SKJ<2S8+Aiie ziu&?4K+?%!;YeV3-t#KI7gVavZ*{ZAlmx>~YB?K93oh)6U8#$7-rIBc^$jog8!}nl zmm|*TZd{u-O~6XJ>+{H8haaS0dO#P3Sn{*F}H`{{zDCDr`!meLm3GkQ{`X6Ktb z%W-BzSvgaT#wlw`o2KIBbdE}Xz9>x@NpoWckL;};ZwPth!%pX=iNsQeb?p@J;jN_M zB#eQLqVms63DaIz3Xr-HHP3Lr8e3U69?^@tn_l_$@{HHy*fgTazX_76p&BMstA}R2 zh<>Xg-C@62vTS^O-X(d%)eK;(!Thp)WpqdUBU#Zqj<_>b6XV7^NfvhfT&7N4L6lnG z&TBib9V}bQQ#}~*vAd#O3dQQK{bV@f)m?Tl+>Gjx`^b!5vbk&1b>onRQXKB!hA~(~ zQ<-#Ci~q%KSYzP$&B>g7Q_BD$;jf83^nfc7`qL8hxrnqThRf7`;Z6N5vDj^xOMA@Z z&`QgFN);Ql-ds=1Qs;(n74{A3W`@Cp(ppOX#L zCi}qPr87w-w|DZQvku%UN$9GFp9^)7dGHER;^4oh&*)0v(byEojap`&S@?{z0z;he zz)mK>4!RU6PDylBkvbQ^fP5d!kjlI~f8rLr(kvw8v8s?UusOI#69HEVig;(^>vAda z6(J?z-KJ9;{oW9nx*L)eYHPrG*IU%xhv}?^=PP$ zq+xv_YM|y?Q&A4oHUUcbT;O)#KjrfVY|a~NH~{2^ZzFLbtysQ-4c zG-&)iZ?x}hV|e=$srih(l;`ImzgI>cKsD*8MD2==3cFoW!(F{G^SVWcWuZbEf6}j< z^SZt~=k=5n4k*~)eARV}_y-+z`KGADhZ+pk)uLuqMS@X0;>5g`Uo$$IHlnpKawWqMObn2getk&u|^s~*E4t`S#j($9}78YzytXjgj_Dq7Ms zL&W4T`7kxOm4vzpt2$7O^YG}ERaN~f zxo7&5ZtXiUhF@t|xS9{^wO_>ip1R}pYxc#NK4MkJ>Zqy^qM=IR9Ed7DR=qKXW`?t?6kDMwCb)jlM8jINm)#*Ifj2urfj%bUr%RM_Cm47Ta z`3~B3F7daNt=G|B^DfE_Thf%KqP)#R-k*eA*wNz-o!pkw+`Va53-VUpX3A4^+%z`M zOvu42XWOX?u4b%(y^~NOgRN1n;!-o-;9rr(VXHEO3-Xzs;qD4;S6dNyv}F&2LlB0) zKxe{A(X3Aa6S~(f27W!Pqd}qEmvg&Td56mD`KsDhAyUdt>8Tz!Epz!}WKs3-g^t_I z-*#GS-`~)WO!{e@m8Vt|@Z*~mJ4&T|CfV`=r3(=|=>;8a{d`JKboUdv4gETSxjxdi z4c{GK9{MJB?BlcAgW83vF`GCOaYtgZoc-quC@`OxePW$qe$a4&9JE~I{Op_i*Sen^ zcwV_Ysb?a#+2(rX)}Rh^$Q?q7m7)1g8Hpur^Ow;RfXW~Sn>wJ(65cG%)ocgPRO_LT znCnU|Ee~nR<)*6h-i!>cLW|7fbX{E8HeZ$LGp=@8yJI^)?0&-0-KOS_mor#?;g}OwK2%t>Y8zEN9q0{Z0D7X*6`mIc z;hA5sy@lByGs9)05A8s%hCKV&^)+&ADJkA!zhfc0`s*8_k%Qd!D~Hs!hxlPcZVz1- zQ(b&+bgd_*DX(!0rB`zYEW$Z?pH$!Bp@wk(_t^vYA3U#5oRg$*fc(9v!&3N$+JnF& zgBKCLjJlW^=)j%JAN&y1OP!gHzaOxu)j0rB*&H07dYz@|K%}TMvy|&bkxp8QJa?4U z8yT4bQ6sS-roGg?han2Gb;^S&J$r%vxn)N{kg7(Tpi>sNk$c86TY)}r9JVB-55n`V>Fyb#>%B1gLaWICH80v^H(>LoBfGCt{`cGW zuP5^!%LO-%($5Ap(C~pRNtMz!e`&8Hp?A}Mm26$TFnv{sq6~yBE9R;WD&IeFJFSdy zJ$=c17fuyR)>14)E}Hjb{A9Yxb+1U{%s!6uYQdt8Q0fOoIO1xA+YyL4MDhpVX5|Jb z#+X`_ZWOjWo%sm!Wxsaj$)bxLxz3`nq7ShO9ut<4qmQXU{_<@TKa>2^r-tBeB5I>< zufKi%uy9xeuWBxgi+gj){ju@<60dOMQM+SEDQNkRWAngEkt1TydDo5KC*Md}^6)?f zhHA)tg&QOA?Ip9pEng2cnfl#FE8{z(;NLh#i@SFZy2njSFV@8g8QKHYt9wy8d!(MS zkF-92-!i3w2p=RpR46o7Mg|NFkG=g8y~kOVL4st9=I8b7JQ72G$8xqrH|}M|Nfxc{ zmAdu4j-mgIVLrA;F*4+1bCp=KR(5s>twaOUp5hO;-cY444@tL)^?6jHeb%7pgQ@T< z3zxtq8OZ0Hkw(CRblVB?v$KU&#Vgj)fh{;k10o@_;?U$d`>}(M9;D|-5)k5x@5W~g zZADwbtwdSZ^`EbRu{w|B9pWucl?}1n3Zv~M9op5U_VvmUuj1n-cWYiA`o!zw#D3LP z>iJ*>j18=fMKv{@u=J$4x={}&!#^JUq?YWsw6N3MhXQ%mBaXpfRUQMZW#EH_g)$ZXT6t_V1`Uho+uF~4j1w#bO z+VkT)>s*-+gLU0ygjpwJFFdWQ90U7!a;y4Ixk%*x^p*V2M~}O7Xx%&fBdv#HTgk#_7st>Wo=Ro3PQ$2mjOQa;{JwlY)%Mf2!&88`mApLV^WB`!bj{Y4%Xkl0 zVR_&1*2CWL2 zB|>sa3v$p;0!l7^d-Z+k{mggwDrNm2w}XaT|M-}HzGWyuJce@`HT(W8X#;8e)nsOj z5zymJ-?<>F`OKw@RhDp^D4a)jT(MVruE?p^!kYAqX|zJZ-Rgeem&;1;o?zS}>khSl z)sH*RJ9GD5z6Tw5{3&|z>?`_=uFZ)_q1w}10s*%>hp%NcroC_%_h>MMS<_> zWxZA+t@G{T%T1VIx)Y@CRwVJ4gt2C4IjDnejyIJmayhnc=+U6Aw3VHtbE^S`f1Rl* zHiWwy>WU08vv^qw`z)k~bp5@WJSrTtO3ua@UW=}s2 z%!}%*0Ma$zZJ#mgxpBc0GS)@u{Wa*jg*(-FI^d$mxz+dtkG9^0YLy+`WWyh1>f)u< z8_QAKr+Y6q-+uD#)2|#YkH!hzs-?c^<=bOZA%FY^L?(Wd(Qsb1pL%+)9CvNS2rhQy RonGF7RSzX#|7?cPe*r9DIW+(P literal 0 HcmV?d00001 diff --git a/common/src/main/resources/assets/dsurround/sounds/footsteps/dirt/dirt_walk4.ogg b/common/src/main/resources/assets/dsurround/sounds/footsteps/dirt/dirt_walk4.ogg new file mode 100644 index 0000000000000000000000000000000000000000..41e92f823c7ece6c5b4b8bc73f2886f70bdd6416 GIT binary patch literal 8715 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 literal 0 HcmV?d00001 diff --git a/common/src/main/resources/assets/dsurround/sounds/footsteps/dirt/dirt_walk5.ogg b/common/src/main/resources/assets/dsurround/sounds/footsteps/dirt/dirt_walk5.ogg new file mode 100644 index 0000000000000000000000000000000000000000..f4b920c23156fc55f5e0cf6c432a61a03f451d41 GIT binary patch literal 7914 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 CC6JE* literal 0 HcmV?d00001 diff --git a/common/src/main/resources/assets/dsurround/sounds/footsteps/dirt/dirt_walk6.ogg b/common/src/main/resources/assets/dsurround/sounds/footsteps/dirt/dirt_walk6.ogg new file mode 100644 index 0000000000000000000000000000000000000000..2afab56e71ad9516a7874845891b01654e088a6b GIT binary patch literal 8348 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 V%r#VL&-IMPtIhk)tf4f3{|BNge_8+l literal 0 HcmV?d00001 diff --git a/common/src/main/resources/assets/dsurround/sounds/footsteps/dirt/dirt_walk7.ogg b/common/src/main/resources/assets/dsurround/sounds/footsteps/dirt/dirt_walk7.ogg new file mode 100644 index 0000000000000000000000000000000000000000..78b8627c9ebdea94280f8ee8b2658618f674ad6c GIT binary patch literal 8133 zcmeG=XH-+!);A$Qs3z15s)q@3KkH=Mp0468NZvr%$k|?)_QNP@BRALchgBoSlC*&Q1;nf9*yRlKArUUr7Lf0aQFZFYGyS@!p!E%xk2htXel`mXM^GC6i#1{lw!fCi5wA(PUl` zF5=D$Y7Y?L7@7ph3X|~1fM@2C8Tj&C@&kN@e0aZBH#%O_aG@;Ifhd44bFfjXD$63- zXgu-=|8S50S@=iOuV@;A2r~I&;Oc<}p9KW^a)BJq>-^T$LIYIL5`cBOL|>i!z_9$l zviu-&i=)_<;T;;IBwZ(M6BlQTac7r(E}ovpL&u{+$8$m_T)7k1Q1`~rslRuw>Szd1 ztZa~>jL%TQXS~wSfYB0bpaAGP7*LF3ywXpdwMyL*nBjjiCm<=W@>1SHRgoZ<1oSP8 z^EANo6O8#2sR1W5xMN;LU|#uu+6j~%HGlxxviq3)dbbS2@^dZR1Asmykg(lh>XzX; z-QmWT5$4?yw!K--z3c-*e>eejJJ<-~>K0DM-I>ON;<|&QIs@W+9d-7O{s-MjB9Jqi8$JZkZ{CxruEIF9IYUkRL&LY%+Yx5MS`8yM>bm2uKIhr5 zi^8uOSOqiLk36alxA)CX2{>{oS$#Vx>aagy{LmW&*g@^jMj9M$z3b@3aI z@_XFqxBuoD_r^0bYk%nzk$=q`Fm03!QeRp9J#$iD>ZdAOXJFkkRJfT_b*bo0Rp~!6 zr63LuQ=w5;e5R*g5-|EoXjULKu6 zPT$}H3bL1gfPH=4vSQpKZqg!d@xKzXv045fvOtYML4)EOvOtYMLH{LL|BdJUe;)s* zEdYicL=^l<5lb*zp;Ubca1Lvlfwmsfq58TH=}=^C#nJ9kIwE;|qwCi@#~Oqc5TPSd z3QyD7l*hL>9`|Q?2%rN&L>`|b%!-33H~k}rz+FSO;-zp$S|HSTl!hZpc<23V!%_i& z3gHK+fcOK#pU+h4CItZc89we{eW9P>2LY&l?xo;upTkU2`qym!bLc+=LZ~HU;s0(?y0|Pu1S%L(FLk#6Sot(}VDSn)e?-l^T3p{s_=o`^-oYl`^2A1}u ztM3^#PxlMVo4=%@Gq7rlES*1DE-<(X>K(`^WkJE~1vEP^$Lx9(8+B0!N{RGHFuQQ>*V5+{fd$^pHzy=D`&CPKtY4tE6T;ilajXqzP-(O zaD&=Yx@i7nDUMNI#kwlwC}9B>KHvd9!3#14L9E5w1P%-Yc}mmQSzdh$-5|>Lqz1BB zEbzX8I)elgk$y78oGn+k_5l--o)g)A?tITd?1OrB&=D;V0NnyN?5(Va%k90j1W3r% zD*<#c7E-)zw3xy^Vno3bZ(E`j#+O7=!!remYFn~NrdG@{(heI9W)6uLlbvBxg)SqJ z@Vo$3K_C3N!4nc|V89Ak?fGyn?RhNGM(cG!xEO|s0xO#;2_H_4la-+b7CLq|Z*oDH zD$}fVDMoK|TVkKMKP|i4cIWZf!=0ou-&G*W8jo%X5yqic}fp zfz}!2CyVBj%2`1fWn4%V>rZ=f5L`Qjd2U#9OJ6C4V_udJr>JyxnMmLixGdJ&wnmOw zCnCM4KqG=yY5`3Kny`m+eX9!rfc;SbP`mCONy{v)YXL7hDCfSUX~FSe&DDc0LlCX- z32A>1e+bnX1R#QI!GbDS#0MO}nb>>{XUT>AR|i)B9hJb-Vn-9HRxXe@&nqQcF9yK* zL64f!wHB~oJ(qy+8M3xwvQb-UsCWk(n_70R@w_~ZIIpR^v=JQFZYhq6Z4K$&YQ?6>TU4sXBfm~yKfUsp!zujiU{RQPHcuGL1 zPr;WkFi3nju>iUpUPVeV03Qr|aVp6ok+{(VR)Y>iJhLoC>opg_sZRs(e*Fd<1X!tN zjga0Jgo8V{h3gjP7^u%Wf@EW(0kXn@tT=SwxUmCVRxz_Sc7WT!bpisE!KEw9`lAL0 zm)08WED~-Vk(J>Avf;7<2T%~nW&=U8*#q+9$lzkRqw~fR&m7}yTgQ3?g9aw4t~;3wu^Y<+5U9v(DRPC(KE+sNK^c`|ZtVF2fbf+plC_3KoDvgN zmP6bk8SX%0;KD^oHC4oi=-1e6h88U=-gyUqHmToK5aVZy=c~;|8FuAf$qiB~@eu~k zSW^IqO%s3#7Pk=i0C_al3IZhXCW<1J2q{pWS1(8S05m*ZMhRJm!m2p)Sae1J*Kf6? zZ31Lux9cIC~ih$l$5bsxL+4GswM#7d|lw&LOppqMj*y2s_R$Mb3R}*SUL(KkjMdf%X8iQ zR`edYbyBZ@4FI6Qqa18-Gys8c*>Km8^{*B%AssH3%a>CY&KG!+<&6vs4Od<+uWjG9 zeY=LC@lHei@4py*UtU;Qe7#rzk$!^nM1B&!HMIK;qnQ7*_^w``FbV!UDRnR)h~Q*5TkdO7}WSEwe(GEv`w=2x=il#lIYhFi&}Tao=Slalk|W8LTa3 z3@xQX5vs^1qH`&qOPzOHvapKk-~1d z>jyV|<}LCqzS(5A8<949&|K@xtGB^E>|?PP&($3cFuQ!m>OAgO8lmWjUB7?LbZDKU zT$sE)9LPRj3qTv73obT0LhCOTl$9b?e<`%pd%{yR`4l&#s{j+Ei08}k-Y$mxA-D zL8tK5J0Q$?&`XjBj+rO+O%gwHw838P$mf+Yr)ZC!Q|A?MxThulZDY!19_GFNg<%rQT0ZZL0BDSU zzaxJ?ui%pCIfF%N69F18%So!3dB;RdMQ&bjlb)(tNnG?&8eBGLD-yrv{j$UXqfXxiN zaNhuW*8j6Rwwz3}*%kTti_1{-!Jy5kU5;p5f!lmquD702SBqEqc)to=lsl>zFYd-VBAk|s0AL@#LNRgsb1msR+)+Z041zeNklrALkp^zLXk_6+s4di$o1 z-{h6M-1(l+pOKV%(-GswGuIB4-%Sa-+RL-pTb?zUvSu37TA5X}k8ow)BiyZ;vSe{b zv^4PM;X?-;Kb(Cguoi4$_<7(Mfl~VM7G()(?yf{q?xAX0dMhp$|T4FY`W}k_frJ64@dS& zCw-Kg$+6Fk@4Y$!^nbez@7ZFPWN=6t($s-JMs=miW{|ss>O9VWTf_(?L6vULl%~F5 z-+FQluMZXN_$GU}f+s9_RrjeK|7=vFtV|M$$fkRx=rH?(>I%15s^dH*2H zxIo~IY%EWqdp&omltl8Lzk6uP8zo|9f97N00xSxZpeZ`A^RaDZ&(*m@F^}m`teBJC zRI~r-q>il7#%nS&)`=J;Zv|n?(Zj^&Rw#WI>%>!Q_;(~|KR!j zc49HQp(w(?vhYIQ5vXtJk8iRf5?#yl!8#78?rIk)nVKx6Hk3p)jDbMx7I3Vr47Ig2 z-PHbT^RII0YvLxbkWk(|(E9z$BbE)!lHy?C^cnO0vVynyTGp;QFZx>u${_vh9*zWi zY72aYV$8GC5O8}%dzVgD&~-XpGWdKa+3A>snXvAAQI^!Ge`L#H>2@+&s;-tij&MW@ zoAyuo*W4wgip7P(NHYbY!$ERM?0^v=!rIuyp?dctdPU*#_CY>SvF~+a-&VAvNTu1< z`#P?UDthEzUT&qN{E{Kbqn*#k?ik>CA#2_eP=>f zl!Tv#$<2S*o7�INdP%Y(_g?RetSQbkMGf#3$&Ekt4RX;tZMa!&tI?>sp#QHQ<)M5aUD%SyCzK!BI}e?;#y&T z?%Wlt`ma1S4pAM|r-3*MttM*I%NzH1_C0Kn*?G!vpKvbK_9@z%e)cL#6LRU6X&d6_ z{=#}SoRGW!mM3?jCk<)cua=Qpj1@7)2OV70y9A}29g)GclXd!Tw#kfvVSnAdTXP;s zc}cT_$m0x*m%JE43n zwO>e$M;@>mzId8ba9NwJDyzH{KaWwg?NT@QU$6Bv5H0t&64meszJw*~U?xuch8D#S zqRlIl&8AOu+&cXglixUTU^j(HEY^()m^}}B388*`*dr&v_TD}4Bc?FWr`-? zC3LA```N+QLyoCEaI0%u3-=xTrl9~a@m0NYrds;5txDqXE&Ds)t3w0kjyKLujS2e- ztME5lN9nzgwfZ<_xGhxM7xSt9n1|-C&$O#ut1O!ZhMZMC)R8SI=DZ-~z z2{*7op@fDsd>)U@CCwGicrX4UbM@(CW0T=I-kIIs7yTO~Q@^Y2(1~2Fz8PEcTa3kg zeu4E?*jvAX;BeyW$17t5ihPiY)U<#4zSNyJWH-G(HP}({Y=>^<=?08RA3;R=)dOdK z*N;J$-o1318DJi0%hNQH3RWW0O4GaERheS)6kgsxsDcs8bD%r$FDt%}4WwExEH)kp ziO&)EI&@MBR_`M;?K;bZkog6KuGRMJ;pq?+lbY5wd488Fx_ycGe0dfrVxA-`OlD7p z!gEW5oNA=n%jr<5twCO>C@)M@CBu8tLPPlRFj@@N7t6CB;tG!~%{{8-BXya7*PGRz z8}gAzF_SBPbJ#+sGe1f;N88yQloZ#_i$ZUEGFc_rPLo-jdk~*=aQ~(Fb z!W}0*+@vlpJS8;jR2n(3>j&QCo(6q)XQY{${Wc65b&+V?QUK$P34jg1=sMC4UHXuM zHhlUu)D4){)j)ZkFU+Z2E z#SY;;FV0dfM*sftW!sByC!93qJn~fDtn{i)i9HB*lpdRXH{^_CKhY^G{DN}T_=wy! zt&NsXO@=9%uNX*7M}0Ec4Lk-8PYti3Aig}eeho1)9WP#?8(TX_#wQd;O}gbK23F$8 z9&T*1PpFSKL)Nu6bpL)abLC;GrkEHFHf)9CA8rA91mh{#EWg+`^w{F^bi4!KR^ifg z3Rd`_t7N}g^}*(lx64JhPMy7f{K=1Z#M-s#uGziwevmZ1wcoed ruCD666I}G$?}sp+e)r=QH(xPN;^62nYzFNEbp! zf{HClm0qMPMG!2bIG|&9zMH_znwj<1dT*`o{rc8-*2%f&?7h$4ceiu)&HiJ@4gfsB zW|Q#Sn2bl$?n9IzQK2V&DB&Csk44QU$$JQQauc$jv-8i!*~!7wuk#<4ApZW3#=F5u z2_$f(gavHhfASb2nBwEY<&UsI=;`X>bq)25IUFRwpXEOD{T7yFQ}fW!V1%U~B?7{~ z!2wM)cYy#<0HCVa$+lC6(wqSR1^`7hJF?Go-~BjyCbrTFeul=GE!J2Kou=IaXK(Lb zlRwi7!om@FKWPBq1E^>yad1G+rNhmNku@##TdQw;CJbs^%#cfKgQ>_8MkmpPCyRb`oE z5_a4@{Ow)Ar{Sw+pAZQ!BFN;AfvX1sd>0Vt#|3gYZ}8ht3jt6;O8_?M@ZLtn{$a)b zUlj)>)Ep(%hV`(cWP=0xrp`{5V@}Qoojp8$3K@$E8OsitaN$l|Lfl$HX8zu}sv{sk zk*aZqDmp_2ov~;{%E`>5E3GOV+7sl#HXNvX3P*DsvexstSd;Es%I6h_5=+6e*O7=QrUvg?H6Mz=bN669LA8vuPuAfviYYFmZl zyTVPZA}qQh>>g!0Jz^ae`NIjI+rdVV(zZNc(skKnP|9F%6yGmp?})b_{SW#9cq}(w zKt+%RN3#Q|Q2jJ-u34xcTiIf$x#)%@kbqXagkTIY*87dc8PAKV;CZ7DlAZE2ZdK*v zg%237l$l z;9j>O#&YE?I5Wg0$E<(WV5z<2@NkvyFjbW6wsoPGAof7>s_fq`;2 z2O)0w5J10qPs(-{#35o*1$^Mh3gl(wh)d#^i*ViOGUbb@dr4LC4rDov@`yOD<_G{t zfc-U!kx8;XG~~7XlfszFvJ5(=NAr50UavN1sA(J6`MRMEW-f|r9wBKPjJfz;WGxql z*BM&}m9xg(waUQ&N0)D86zEihUz(YK95+9ba!!I-Q4}ZqSI50W4N52t|D_N9st5-2 zMm%$a+4*2ph})IOnHz~7laX^1F>@0ak8Dlycgy};-%uUY1mL6-Hyty5QgB`Q@%xBE z8xG#g9IF%Xu5`sm=_uPKl>LZy{~CV(I(}Hn5)79E(jLRok)tyH2Mj$%4g5!){l}vG zr&|0E-H35(iJANOmp&2s*USOaM#VUF(faS1llsgkRn;Z~<(8q&&77*-!k1Mg|Hz!8 z0Q$8Y`ZXGTg_f$BoJ~n)FmIIv_td>^{kQ$k%mEVy?3QGd<=--gjZ-uSlc!1Betknn z(_!#*h_KxHR|fz9J5AJ}2Zv@7fqAwLrsW0e~C;v^6h{oE*GZ zWR0<6jb}JMM|CL5(~IItePaa>M)2%4JYn5rbN#=1pzL|^gPgv} z1r)R&4gvedn!RSimNI2aS^lqtY;IQkhb&MdP|%>%rYuk+P|$x#)_>!D|DVVIX$yd1 z2N4HDT0)@w9G(nRH_h$TS!%_i&3K0aT zfK(6R&vz