Repository: Corosauce/weather2 Branch: 1.20 Commit: c023f4606218 Files: 283 Total size: 1.0 MB Directory structure: gitextract_0mtt1k7r/ ├── .gitattributes ├── .github/ │ └── workflows/ │ └── gradle.yml ├── .gitignore ├── CONTRIBUTING.md ├── CREDITS.txt ├── LICENSE.txt ├── README.txt ├── build.gradle ├── changelog.txt ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── libs/ │ └── coroutil-forge-1.20.1-1.3.7.jar ├── settings.gradle └── src/ ├── generated/ │ └── resources/ │ ├── .cache/ │ │ ├── 0d0f48ca72ebc11ea3eaf67230fd125a3c581fa7 │ │ ├── 59eb3dbb5f86130e09b3c62d89b9525ee01cf52d │ │ ├── 9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e │ │ └── f991d9ad226694c30732bf45648e031ffa7d50dc │ ├── assets/ │ │ └── minecraft/ │ │ └── atlases/ │ │ ├── blocks.json │ │ └── particles.json │ └── data/ │ └── weather2/ │ ├── advancements/ │ │ └── recipes/ │ │ └── misc/ │ │ ├── anemometer.json │ │ ├── sand_layer.json │ │ ├── tornado_sensor.json │ │ ├── tornado_siren.json │ │ ├── weather_deflector.json │ │ ├── weather_forecast.json │ │ ├── weather_item.json │ │ ├── wind_turbine.json │ │ └── wind_vane.json │ ├── loot_tables/ │ │ └── blocks/ │ │ ├── anemometer.json │ │ ├── sand_layer.json │ │ ├── tornado_sensor.json │ │ ├── tornado_siren.json │ │ ├── weather_deflector.json │ │ ├── weather_forecast.json │ │ ├── wind_turbine.json │ │ └── wind_vane.json │ └── recipes/ │ ├── anemometer.json │ ├── sand_layer.json │ ├── tornado_sensor.json │ ├── tornado_siren.json │ ├── weather_deflector.json │ ├── weather_forecast.json │ ├── weather_item.json │ ├── wind_turbine.json │ └── wind_vane.json └── main/ ├── java/ │ ├── extendedrenderer/ │ │ ├── ExtendedRenderer.java │ │ ├── ParticleManagerExtended.java │ │ ├── ParticleRegistry2ElectricBubbleoo.java │ │ ├── WeatherSpriteSourceProvider.java │ │ └── particle/ │ │ ├── ParticleRegistry.java │ │ ├── behavior/ │ │ │ ├── ParticleBehaviorFog.java │ │ │ ├── ParticleBehaviorSandstorm.java │ │ │ └── ParticleBehaviors.java │ │ └── entity/ │ │ ├── DustEmitter.java │ │ ├── EntityRotFX.java │ │ ├── ParticleCrossSection.java │ │ ├── ParticleCube.java │ │ ├── ParticleEmitter.java │ │ ├── ParticleTexExtraRender.java │ │ ├── ParticleTexFX.java │ │ ├── ParticleTexLeafColor.java │ │ ├── PivotingParticle.java │ │ └── WaterDropParticleImpl.java │ └── weather2/ │ ├── ClientRegistry.java │ ├── ClientTickHandler.java │ ├── ClientWeatherHelper.java │ ├── ClientWeatherProxy.java │ ├── DeferredHelper.java │ ├── EntityRegistry.java │ ├── EventHandlerForge.java │ ├── IWindHandler.java │ ├── IWorldData.java │ ├── PacketNBTFromClient.java │ ├── PacketNBTFromServer.java │ ├── PerlinNoiseHelper.java │ ├── ServerTickHandler.java │ ├── ServerWeatherProxy.java │ ├── SoundRegistry.java │ ├── Weather.java │ ├── WeatherBlocks.java │ ├── WeatherItems.java │ ├── WeatherNetworking.java │ ├── WeatherTab.java │ ├── WorldNBTData.java │ ├── block/ │ │ ├── AnemometerBlock.java │ │ ├── DeflectorBlock.java │ │ ├── ForecastBlock.java │ │ ├── SandLayerBlock.java │ │ ├── SensorBlock.java │ │ ├── SirenBlock.java │ │ ├── WeatherMachineBlock.java │ │ ├── WindTurbineBlock.java │ │ └── WindVaneBlock.java │ ├── blockentity/ │ │ ├── AnemometerBlockEntity.java │ │ ├── DeflectorBlockEntity.java │ │ ├── SensorBlockEntity.java │ │ ├── SirenBlockEntity.java │ │ ├── WeatherMachineBlockEntity.java │ │ ├── WindTurbineBlockEntity.java │ │ └── WindVaneBlockEntity.java │ ├── client/ │ │ ├── MovingSoundStreamingSource.java │ │ ├── SceneEnhancer.java │ │ ├── entity/ │ │ │ ├── model/ │ │ │ │ ├── AnemometerModel.java │ │ │ │ ├── WindTurbineModel.java │ │ │ │ └── WindVaneModel.java │ │ │ ├── particle/ │ │ │ │ ├── ParticleHail.java │ │ │ │ └── ParticleSandstorm.java │ │ │ └── render/ │ │ │ └── LightningBoltWeatherNewRenderer.java │ │ └── tile/ │ │ ├── AnemometerEntityRenderer.java │ │ ├── WindTurbineEntityRenderer.java │ │ └── WindVaneEntityRenderer.java │ ├── command/ │ │ ├── CommandWeather2Client.java │ │ └── WeatherCommand.java │ ├── config/ │ │ ├── ClientConfigData.java │ │ ├── ConfigDebug.java │ │ ├── ConfigFoliage.java │ │ ├── ConfigMisc.java │ │ ├── ConfigParticle.java │ │ ├── ConfigSand.java │ │ ├── ConfigSnow.java │ │ ├── ConfigSound.java │ │ ├── ConfigStorm.java │ │ ├── ConfigTornado.java │ │ ├── ConfigWind.java │ │ └── WeatherUtilConfig.java │ ├── data/ │ │ ├── BlockAndItemProvider.java │ │ ├── BlockLootTables.java │ │ └── WeatherRecipeProvider.java │ ├── datatypes/ │ │ ├── PrecipitationType.java │ │ ├── StormState.java │ │ └── WeatherEventType.java │ ├── energy/ │ │ └── EnergyManager.java │ ├── item/ │ │ └── WeatherItem.java │ ├── ltcompat/ │ │ ├── ClientWeatherIntegration.java │ │ └── ServerWeatherIntegration.java │ ├── mixin/ │ │ └── client/ │ │ ├── GameRendererOverride.java │ │ └── RenderParticlesOverride.java │ ├── player/ │ │ └── PlayerData.java │ ├── util/ │ │ ├── CachedNBTTagCompound.java │ │ ├── WeatherUtil.java │ │ ├── WeatherUtilBlock.java │ │ ├── WeatherUtilDim.java │ │ ├── WeatherUtilEntity.java │ │ ├── WeatherUtilParticle.java │ │ ├── WeatherUtilPhysics.java │ │ ├── WeatherUtilSound.java │ │ └── WindReader.java │ └── weathersystem/ │ ├── WeatherManager.java │ ├── WeatherManagerClient.java │ ├── WeatherManagerServer.java │ ├── fog/ │ │ ├── FogAdjuster.java │ │ └── FogProfile.java │ ├── storm/ │ │ ├── EnumWeatherObjectType.java │ │ ├── LightningBoltWeather.java │ │ ├── LightningBoltWeatherNew.java │ │ ├── StormObject.java │ │ ├── TornadoHelper.java │ │ ├── WeatherEntityConfig.java │ │ ├── WeatherObject.java │ │ ├── WeatherObjectParticleStorm.java │ │ ├── WeatherObjectSandstormOld.java │ │ └── WeatherTypes.java │ ├── tornado/ │ │ ├── ActiveTornadoConfig.java │ │ ├── CatmullRomSpline.java │ │ ├── CubicBezierCurve.java │ │ ├── Path.java │ │ ├── TornadoFunnel.java │ │ ├── TornadoManagerTodoRenameMe.java │ │ ├── Vector.java │ │ └── simple/ │ │ ├── Layer.java │ │ └── TornadoFunnelSimple.java │ └── wind/ │ ├── WindInfoCache.java │ └── WindManager.java └── resources/ ├── META-INF/ │ ├── accesstransformer.cfg │ └── mods.toml ├── assets/ │ ├── coroutil/ │ │ ├── blockstates/ │ │ │ ├── blank.json │ │ │ └── repairing_block.json │ │ ├── config/ │ │ │ ├── loot_tables/ │ │ │ │ ├── testloot.json │ │ │ │ └── testlootboss.json │ │ │ └── templates/ │ │ │ ├── actions/ │ │ │ │ ├── mob_spawns.json │ │ │ │ ├── mob_spawns_example_commented.json │ │ │ │ └── mob_spawns_testing_miners.json │ │ │ ├── cmods/ │ │ │ │ ├── all_cmods.json │ │ │ │ └── invasions_cmods.json │ │ │ └── conditions/ │ │ │ ├── all_conditions.json │ │ │ └── invasions_stages.json │ │ ├── lang/ │ │ │ └── en_us.json │ │ ├── models/ │ │ │ ├── block/ │ │ │ │ ├── blank.json │ │ │ │ └── repairing_block.json │ │ │ └── item/ │ │ │ ├── item_repairing_gel.json │ │ │ └── repairing_block.json │ │ ├── shaders/ │ │ │ ├── foliage.fs │ │ │ ├── foliage.vs │ │ │ ├── particle.fs │ │ │ └── particle.vs │ │ └── textures/ │ │ └── particles/ │ │ ├── cloud.xcf │ │ ├── cloud256.xcf │ │ ├── clouds.xcf │ │ └── hail.xcf │ └── weather2/ │ ├── blockstates/ │ │ ├── anemometer.json │ │ ├── sand_layer.json │ │ ├── tornado_sensor.json │ │ ├── tornado_sensor_new.json │ │ ├── tornado_siren.json │ │ ├── tornado_siren_manual.json │ │ ├── tornado_siren_old.json │ │ ├── weather_deflector.json │ │ ├── weather_forecast.json │ │ ├── weather_machine.json │ │ ├── wind_turbine.json │ │ └── wind_vane.json │ ├── lang/ │ │ └── en_us.json │ ├── models/ │ │ ├── block/ │ │ │ ├── anemometer.json │ │ │ ├── sand_height10.json │ │ │ ├── sand_height12.json │ │ │ ├── sand_height14.json │ │ │ ├── sand_height2.json │ │ │ ├── sand_height4.json │ │ │ ├── sand_height6.json │ │ │ ├── sand_height8.json │ │ │ ├── tornado_sensor.json │ │ │ ├── tornado_siren.json │ │ │ ├── tornado_siren_manual.json │ │ │ ├── weather_deflector.json │ │ │ ├── weather_forecast.json │ │ │ ├── weather_machine.json │ │ │ ├── wind_turbine.json │ │ │ └── wind_vane.json │ │ └── item/ │ │ ├── anemometer.json │ │ ├── pocket_sand.json │ │ ├── sand_layer.json │ │ ├── sand_layer_placeable.json │ │ ├── tornado_sensor.json │ │ ├── tornado_siren.json │ │ ├── tornado_siren_manual.json │ │ ├── weather_deflector.json │ │ ├── weather_forecast.json │ │ ├── weather_item.json │ │ ├── weather_machine.json │ │ ├── wind_turbine.json │ │ └── wind_vane.json │ ├── particles/ │ │ └── acidrain_splash.json │ ├── shaders/ │ │ ├── core/ │ │ │ ├── rendertype_clouds.fsh │ │ │ ├── rendertype_clouds.json │ │ │ ├── rendertype_clouds.vsh │ │ │ ├── rendertype_clouds_orig.vsh │ │ │ └── rendertype_clouds_wip.json │ │ └── include/ │ │ └── classicnoise3d.glsl │ ├── sounds/ │ │ ├── env/ │ │ │ ├── waterfall.ogg │ │ │ ├── wind_calm.ogg │ │ │ └── wind_calmfade.ogg │ │ └── streaming/ │ │ ├── destruction.ogg │ │ ├── destruction_0_.ogg │ │ ├── destruction_1_.ogg │ │ ├── destruction_2_.ogg │ │ ├── destruction_s.ogg │ │ ├── destructionb.ogg │ │ ├── sandstorm_high1.ogg │ │ ├── sandstorm_low1.ogg │ │ ├── sandstorm_low2.ogg │ │ ├── sandstorm_med1.ogg │ │ ├── sandstorm_med2.ogg │ │ ├── siren.ogg │ │ ├── siren_sandstorm_1.ogg │ │ ├── siren_sandstorm_2.ogg │ │ ├── siren_sandstorm_3.ogg │ │ ├── siren_sandstorm_4.ogg │ │ ├── siren_sandstorm_5_extra.ogg │ │ ├── siren_sandstorm_6_extra.ogg │ │ ├── wind_close.ogg │ │ ├── wind_close_0_.ogg │ │ ├── wind_close_1_.ogg │ │ ├── wind_close_2_.ogg │ │ ├── wind_far.ogg │ │ ├── wind_far_0_.ogg │ │ ├── wind_far_1_.ogg │ │ └── wind_far_2_.ogg │ └── sounds.json ├── pack.mcmeta └── weather2.mixins.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ # Disable autocrlf on generated files, they always generate with LF # Add any extra files or paths here to make git stop saying they # are changed when only line endings change. src/generated/**/.cache/cache text eol=lf src/generated/**/*.json text eol=lf ================================================ FILE: .github/workflows/gradle.yml ================================================ name: Java CI with Gradle on: [push] jobs: jdk17: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v3 - name: Setup JDK 17 uses: actions/setup-java@v3 with: java-version: '17' distribution: 'temurin' - name: Build with Gradle uses: gradle/gradle-build-action@v2 with: arguments: build - name: Locate built JARfile id: jar run: jarfile=$(find build/libs/ -name "*-all.jar" -not -name "*slim*" -not -name "*source*" | tr '\n' ' '); echo "jarfile=$jarfile" >> $GITHUB_OUTPUT - name: Set Artifact name id: jarname run: jarname=$(find build/libs/ -name "*-all.jar" -not -name "*slim*" -not -name "*source*" | sed 's:.*/::' | tr '\n' ' '); echo "jarname=$jarname" >> $GITHUB_OUTPUT - name: Upload artifact uses: actions/upload-artifact@v2.2.3 with: name: ${{ steps.jarname.outputs.jarname }} path: ${{ steps.jar.outputs.jarfile }} ================================================ FILE: .gitignore ================================================ # eclipse bin *.launch .settings .metadata .classpath .project # idea out *.ipr *.iws *.iml .idea # gradle build .gradle # other eclipse run run-data # Files from Forge MDK forge*changelog.txt libs_off ================================================ FILE: CONTRIBUTING.md ================================================ ## Pull Requests #### New Pull Requests are not accepted right now, and will be closed. ================================================ FILE: CREDITS.txt ================================================ Minecraft Forge: Credits/Thank You Forge is a set of tools and modifications to the Minecraft base game code to assist mod developers in creating new and exciting content. It has been in development for several years now, but I would like to take this time thank a few people who have helped it along it's way. First, the people who originally created the Forge projects way back in Minecraft alpha. Eloraam of RedPower, and SpaceToad of Buildcraft, without their acceptiance of me taking over the project, who knows what Minecraft modding would be today. Secondly, someone who has worked with me, and developed some of the core features that allow modding to be as functional, and as simple as it is, cpw. For developing FML, which stabelized the client and server modding ecosystem. As well as the base loading system that allows us to modify Minecraft's code as elegently as possible. Mezz, who has stepped up as the issue and pull request manager. Helping to keep me sane as well as guiding the community into creating better additions to Forge. Searge, Bspks, Fesh0r, ProfMobious, and all the rest over on the MCP team {of which I am a part}. For creating some of the core tools needed to make Minecraft modding both possible, and as stable as can be. On that note, here is some specific information of the MCP data we use: * Minecraft Coder Pack (MCP) * Forge Mod Loader and Minecraft Forge have permission to distribute and automatically download components of MCP and distribute MCP data files. This permission is not transitive and others wishing to redistribute the Minecraft Forge source independently should seek permission of MCP or remove the MCP data files and request their users to download MCP separately. And lastly, the countless community members who have spent time submitting bug reports, pull requests, and just helping out the community in general. Thank you. --LexManos ========================================================================= This is Forge Mod Loader. You can find the source code at all times at https://github.com/MinecraftForge/MinecraftForge/tree/1.12.x/src/main/java/net/minecraftforge/fml This minecraft mod is a clean open source implementation of a mod loader for minecraft servers and minecraft clients. The code is authored by cpw. It began by partially implementing an API defined by the client side ModLoader, authored by Risugami. http://www.minecraftforum.net/topic/75440- This support has been dropped as of Minecraft release 1.7, as Risugami no longer maintains ModLoader. It also contains suggestions and hints and generous helpings of code from LexManos, author of MinecraftForge. http://www.minecraftforge.net/ Additionally, it contains an implementation of topological sort based on that published at http://keithschwarz.com/interesting/code/?dir=topological-sort It also contains code from the Maven project for performing versioned dependency resolution. http://maven.apache.org/ It also contains a partial repackaging of the javaxdelta library from http://sourceforge.net/projects/javaxdelta/ with credit to it's authors. Forge Mod Loader downloads components from the Minecraft Coder Pack (http://mcp.ocean-labs.de/index.php/Main_Page) with kind permission from the MCP team. ================================================ FILE: LICENSE.txt ================================================ Unless noted below, Minecraft Forge, Forge Mod Loader, and all parts herein are licensed under the terms of the LGPL 2.1 found here http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt and copied below. Homepage: http://minecraftforge.net/ https://github.com/MinecraftForge/MinecraftForge A note on authorship: All source artifacts are property of their original author, with the exclusion of the contents of the patches directory and others copied from it from time to time. Authorship of the contents of the patches directory is retained by the Minecraft Forge project. This is because the patches are partially machine generated artifacts, and are changed heavily due to the way forge works. Individual attribution within them is impossible. Consent: All contributions to Forge must consent to the release of any patch content to the Forge project. A note on infectivity: The LGPL is chosen specifically so that projects may depend on Forge features without being infected with its license. That is the purpose of the LGPL. Mods and others using this code via ordinary Java mechanics for referencing libraries are specifically not bound by Forge's license for the Mod code. === MCP Data === This software includes data from the Minecraft Coder Pack (MCP), with kind permission from them. The license to MCP data is not transitive - distribution of this data by third parties requires independent licensing from the MCP team. This data is not redistributable without permission from the MCP team. === Sharing === I grant permission for some parts of FML to be redistributed outside the terms of the LGPL, for the benefit of the minecraft modding community. All contributions to these parts should be licensed under the same additional grant. -- Runtime patcher -- License is granted to redistribute the runtime patcher code (src/main/java/net/minecraftforge/fml/common/patcher and subdirectories) under any alternative open source license as classified by the OSI (http://opensource.org/licenses) -- ASM transformers -- License is granted to redistribute the ASM transformer code (src/main/java/net/minecraftforge/common/asm/ and subdirectories) under any alternative open source license as classified by the OSI (http://opensource.org/licenses) ========================================================================= This software includes portions from the Apache Maven project at http://maven.apache.org/ specifically the ComparableVersion.java code. It is included based on guidelines at http://www.softwarefreedom.org/resources/2007/gpl-non-gpl-collaboration.html with notices intact. The only change is a non-functional change of package name. This software contains a partial repackaging of javaxdelta, a BSD licensed program for generating binary differences and applying them, sourced from the subversion at http://sourceforge.net/projects/javaxdelta/ authored by genman, heikok, pivot. The only changes are to replace some Trove collection types with standard Java collections, and repackaged. ========================================================================= GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS ================================================ FILE: README.txt ================================================ Source installation information for modders ------------------------------------------- This code follows the Minecraft Forge installation methodology. It will apply some small patches to the vanilla MCP source code, giving you and it access to some of the data and functions you need to build a successful mod. Note also that the patches are built against "un-renamed" MCP source code (aka SRG Names) - this means that you will not be able to read them directly against normal code. Setup Process: ============================== Step 1: Open your command-line and browse to the folder where you extracted the zip file. Step 2: You're left with a choice. If you prefer to use Eclipse: 1. Run the following command: `gradlew genEclipseRuns` (`./gradlew genEclipseRuns` if you are on Mac/Linux) 2. Open Eclipse, Import > Existing Gradle Project > Select Folder or run `gradlew eclipse` to generate the project. If you prefer to use IntelliJ: 1. Open IDEA, and import project. 2. Select your build.gradle file and have it import. 3. Run the following command: `gradlew genIntellijRuns` (`./gradlew genIntellijRuns` if you are on Mac/Linux) 4. Refresh the Gradle Project in IDEA if required. If at any point you are missing libraries in your IDE, or you've run into problems you can run `gradlew --refresh-dependencies` to refresh the local cache. `gradlew clean` to reset everything {this does not affect your code} and then start the process again. Mapping Names: ============================= By default, the MDK is configured to use the official mapping names from Mojang for methods and fields in the Minecraft codebase. These names are covered by a specific license. All modders should be aware of this license, if you do not agree with it you can change your mapping names to other crowdsourced names in your build.gradle. For the latest license text, refer to the mapping file itself, or the reference copy here: https://github.com/MinecraftForge/MCPConfig/blob/master/Mojang.md Additional Resources: ========================= Community Documentation: http://mcforge.readthedocs.io/en/latest/gettingstarted/ LexManos' Install Video: https://www.youtube.com/watch?v=8VEdtQLuLO0 Forge Forum: https://forums.minecraftforge.net/ Forge Discord: https://discord.gg/UvedJ9m ================================================ FILE: build.gradle ================================================ buildscript { repositories { maven { name = 'sponge' url = 'https://repo.spongepowered.org/repository/maven-public/' } } dependencies { classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '5.1.+', changing: true classpath "org.spongepowered:mixingradle:0.7-SNAPSHOT" } } plugins { id 'eclipse' id 'idea' id 'maven-publish' id 'net.minecraftforge.gradle' version '[6.0,6.2)' } version = mod_version group = mod_group_id base { archivesName = mod_id } ext.buildnumber = 0 ext.projversion = 0 project.projversion = mod_version archivesBaseName = mod_id if (System.getenv('GITHUB_RUN_NUMBER')) { project.buildnumber = System.getenv('GITHUB_RUN_NUMBER') version = "${projversion}+${buildnumber}-gha" } else { version = "${projversion}" } // Mojang ships Java 17 to end users in 1.18+, so your mod should target Java 17. java.toolchain.languageVersion = JavaLanguageVersion.of(17) println "Java: ${System.getProperty 'java.version'}, JVM: ${System.getProperty 'java.vm.version'} (${System.getProperty 'java.vendor'}), Arch: ${System.getProperty 'os.arch'}" minecraft { // The mappings can be changed at any time and must be in the following format. // Channel: Version: // official MCVersion Official field/method names from Mojang mapping files // parchment YYYY.MM.DD-MCVersion Open community-sourced parameter names and javadocs layered on top of official // // You must be aware of the Mojang license when using the 'official' or 'parchment' mappings. // See more information here: https://github.com/MinecraftForge/MCPConfig/blob/master/Mojang.md // // Parchment is an unofficial project maintained by ParchmentMC, separate from MinecraftForge // Additional setup is needed to use their mappings: https://parchmentmc.org/docs/getting-started // // Use non-default mappings at your own risk. They may not always work. // Simply re-run your setup task after changing the mappings to update your workspace. mappings channel: mapping_channel, version: mapping_version // When true, this property will have all Eclipse/IntelliJ IDEA run configurations run the "prepareX" task for the given run configuration before launching the game. // In most cases, it is not necessary to enable. // enableEclipsePrepareRuns = true // enableIdeaPrepareRuns = true // This property allows configuring Gradle's ProcessResources task(s) to run on IDE output locations before launching the game. // It is REQUIRED to be set to true for this template to function. // See https://docs.gradle.org/current/dsl/org.gradle.language.jvm.tasks.ProcessResources.html copyIdeResources = true // When true, this property will add the folder name of all declared run configurations to generated IDE run configurations. // The folder name can be set on a run configuration using the "folderName" property. // By default, the folder name of a run configuration is the name of the Gradle project containing it. // generateRunFolders = true // This property enables access transformers for use in development. // They will be applied to the Minecraft artifact. // The access transformer file can be anywhere in the project. // However, it must be at "META-INF/accesstransformer.cfg" in the final mod jar to be loaded by Forge. // This default location is a best practice to automatically put the file in the right place in the final jar. // See https://docs.minecraftforge.net/en/latest/advanced/accesstransformers/ for more information. accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg') // Default run configurations. // These can be tweaked, removed, or duplicated as needed. runs { // applies to all the run configs below configureEach { workingDirectory project.file('run') arg "-mixin.config=weather2.mixins.json" // Recommended logging data for a userdev environment // The markers can be added/remove as needed separated by commas. // "SCAN": For mods scan. // "REGISTRIES": For firing of registry events. // "REGISTRYDUMP": For getting the contents of all registries. property 'forge.logging.markers', 'REGISTRIES' // Recommended logging level for the console // You can set various levels here. // Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels property 'forge.logging.console.level', 'debug' property 'mixin.env.remapRefMap', 'true' property 'mixin.env.refMapRemappingFile', "${projectDir}/build/createSrgToMcp/output.srg" mods { "${mod_id}" { source sourceSets.main } } } client { // Comma-separated list of namespaces to load gametests from. Empty = all namespaces. property 'forge.enabledGameTestNamespaces', mod_id } server { property 'forge.enabledGameTestNamespaces', mod_id args '--nogui' } // This run config launches GameTestServer and runs all registered gametests, then exits. // By default, the server will crash when no gametests are provided. // The gametest system is also enabled by default for other run configs under the /test command. gameTestServer { property 'forge.enabledGameTestNamespaces', mod_id } data { // example of overriding the workingDirectory set in configureEach above workingDirectory project.file('run-data') // Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources. args '--mod', mod_id, '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/') } } } // Include resources generated by data generators. sourceSets.main.resources { srcDir 'src/generated/resources' } apply plugin: 'org.spongepowered.mixin' mixin { add sourceSets.main, 'weather2.refmap.json' config 'weather2.mixins.json' } configurations { modImpl implementation.extendsFrom(modImpl) testImplementation.extendsFrom(modImpl) modRuntime runtimeOnly.extendsFrom(modRuntime) testRuntimeOnly.extendsFrom(modRuntime) modCompile compileOnly.extendsFrom(modCompile) testCompileOnly.extendsFrom(modCompile) } repositories { // Put repositories for dependencies here // ForgeGradle automatically adds the Forge maven and Maven Central for you maven { url = "https://maven.tterrag.com/" } maven { url = "https://api.modrinth.com/maven" } maven { name = 'sponge' url = 'https://repo.spongepowered.org/repository/maven-public/' } // If you have mod jar dependencies in ./libs, you can declare them as a repository like so. // See https://docs.gradle.org/current/userguide/declaring_repositories.html#sub:flat_dir_resolver flatDir { dir 'libs' } } dependencies { // Specify the version of Minecraft to use. // Any artifact can be supplied so long as it has a "userdev" classifier artifact and is a compatible patcher artifact. // The "userdev" classifier will be requested and setup by ForgeGradle. // If the group id is "net.minecraft" and the artifact id is one of ["client", "server", "joined"], // then special handling is done to allow a setup of a vanilla dependency without the use of an external repository. minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}" //implementation fg.deobf('com.corosus.coroutil:coroutil:1.20.1-1.3.3') implementation fg.deobf('com.corosus.coroutil:coroutil-forge:1.20.1-1.3.7') //implementation fg.deobf('com.corosus.enderio:EnderIO:1.20.1-6.0.21-alpha') //implementation fg.deobf('com.corosus.pipez:pipez:1.20.1-1.1.5') //implementation fg.deobf('oculus:oculus:mc1.20.1-1.6.9') //implementation fg.deobf('rubidium:rubidium:mc1.20.1-0.7.1') //implementation fg.deobf('spark:spark:1.10.42-forge') //implementation fg.deobf('tropicraft:tropicraft:9.6.3-beta+551-gha') annotationProcessor 'org.spongepowered:mixin:0.8.5-SNAPSHOT:processor' //implementation fg.deobf('com.lovetropics.ltweather:ltweather:1.20.1-1.0') //implementation fg.deobf('com.lovetropics.minigames:LTMinigames:0.1.0-alpha+custom') //modImpl fg.deobf('maven.modrinth:tropicraft:9.6.1-1.20.1') //implementation fg.deobf("com.tterrag.registrate:Registrate:MC1.20-${registrate_version}") //jarJar(modCompile(fg.deobf("com.tterrag.registrate:Registrate:$registrate_version"))) { //jarJar.ranged(it, "[$registrate_version,MC1.21)") //} // Example mod dependency with JEI - using fg.deobf() ensures the dependency is remapped to your development mappings // The JEI API is declared for compile time use, while the full JEI artifact is used at runtime // compileOnly fg.deobf("mezz.jei:jei-${mc_version}-common-api:${jei_version}") // compileOnly fg.deobf("mezz.jei:jei-${mc_version}-forge-api:${jei_version}") // runtimeOnly fg.deobf("mezz.jei:jei-${mc_version}-forge:${jei_version}") // Example mod dependency using a mod jar from ./libs with a flat dir repository // This maps to ./libs/coolmod-${mc_version}-${coolmod_version}.jar // The group id is ignored when searching -- in this case, it is "blank" // implementation fg.deobf("blank:coolmod-${mc_version}:${coolmod_version}") // For more info: // http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html // http://www.gradle.org/docs/current/userguide/dependency_management.html } // This block of code expands all declared replace properties in the specified resource targets. // A missing property will result in an error. Properties are expanded using ${} Groovy notation. // When "copyIdeResources" is enabled, this will also run before the game launches in IDE environments. // See https://docs.gradle.org/current/dsl/org.gradle.language.jvm.tasks.ProcessResources.html tasks.named('processResources', ProcessResources).configure { var replaceProperties = [ minecraft_version: minecraft_version, minecraft_version_range: minecraft_version_range, forge_version: forge_version, forge_version_range: forge_version_range, loader_version_range: loader_version_range, mod_id: mod_id, mod_name: mod_name, mod_license: mod_license, mod_version: mod_version, mod_authors: mod_authors, mod_description: mod_description, ] inputs.properties replaceProperties filesMatching(['META-INF/mods.toml', 'pack.mcmeta']) { expand replaceProperties + [project: project] } } // Example for how to get properties into the manifest for reading at runtime. tasks.named('jar', Jar).configure { manifest { attributes([ 'Specification-Title' : mod_id, 'Specification-Vendor' : mod_authors, 'Specification-Version' : '1', // We are version 1 of ourselves 'Implementation-Title' : project.name, 'Implementation-Version' : project.jar.archiveVersion, 'Implementation-Vendor' : mod_authors, 'Implementation-Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), 'MixinConfigs': 'weather2.mixins.json' ]) } // This is the preferred method to reobfuscate your jar file finalizedBy 'reobfJar' } // However if you are in a multi-project build, dev time needs unobfed jar files, so you can delay the obfuscation until publishing by doing: // tasks.named('publish').configure { // dependsOn 'reobfJar' // } // Example configuration to allow publishing using the maven-publish plugin publishing { publications { register('mavenJava', MavenPublication) { artifact jar } } repositories { maven { url "file://${project.projectDir}/mcmodsrepo" } } } tasks.named('jarJar').configure { archiveClassifier = '' finalizedBy 'reobfJarJar' } reobf { jarJar { } } task sourceJar(type: Jar, dependsOn: classes) { archiveClassifier = 'sources' from sourceSets.main.java } tasks.withType(JavaCompile).configureEach { options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation } ================================================ FILE: changelog.txt ================================================ Build: 1.18.1-39.0.5 - Mon Dec 13 21:58:30 GMT 2021 pupnewfster: Add RenderArmEvent to make overriding just the arm rendering not require copying nearly as much vanilla code (#8254) ========= Build: 1.18.1-39.0.4 - Mon Dec 13 21:32:20 GMT 2021 bageldotjpg: Add MobEffect tags (#8231) ========= Build: 1.18.1-39.0.3 - Mon Dec 13 19:49:00 GMT 2021 xfacthd: Log missing or unsupported dependencies (#8218) ========= Build: 1.18.1-39.0.2 - Mon Dec 13 19:33:05 GMT 2021 sciwhiz12: Fix datagen test for sounds definitions provider (#8249) ========= Build: 1.18.1-39.0.1 - Mon Dec 13 19:14:15 GMT 2021 williewillus: Fix wrong stage being declared in transition to common (#8267) ========= Build: 1.18.1-39.0.0 - Fri Dec 10 19:32:24 GMT 2021 curle: Update to 1.18.1 Co-Authored by: - Curle _ Orion ========= Build: 1.18-38.0.17 - Fri Dec 10 09:23:45 GMT 2021 oriondevelopment: [CVE-2021-44228]: Update Log4J to fix the security issue inside it. (#8268) ========= Build: 1.18-38.0.16 - Wed Dec 08 00:09:40 GMT 2021 jaredlll08: Fix KeyMappings only checking if they conflict with themselves. (#8256) ========= Build: 1.18-38.0.15 - Sun Dec 05 19:40:15 GMT 2021 xfacthd: Fix ChunkWatchEvent not being fired (#8253) ========= Build: 1.18-38.0.14 - Sat Dec 04 01:30:30 GMT 2021 git: Call handleUpdateTag for BlockEntities again (#8237) ========= Build: 1.18-38.0.13 - Fri Dec 03 22:10:25 GMT 2021 commoble: Fix test worldgen data (#8248) ========= Build: 1.18-38.0.12 - Thu Dec 02 20:16:47 GMT 2021 lexmanos: Allow Forge Registries to return key information for overridden objects. Fixes #8230 ========= Build: 1.18-38.0.11 - Thu Dec 02 19:17:12 GMT 2021 curle: Save Chunk capabilities to the chunk, rather than recursively to the capabilities. ========= Build: 1.18-38.0.10 - Thu Dec 02 15:24:47 GMT 2021 gigaherz: Make HandshakeConsumer public again. Fixes #8241 gigaherz: Fix LevelChunk capability attach crash. Fix client chunks not having capability providers attached. Add capability attach tests. ========= Build: 1.18-38.0.8 - Thu Dec 02 00:44:15 GMT 2021 curle: Add missing biomes back to the BiomeDictionary curle: Complete TODO in ShapedRecipe patch causing logspam related to minecraft:air ========= Build: 1.18-38.0.6 - Wed Dec 01 22:12:05 GMT 2021 curle: Readd Mixin 0.8.5 to fix modules issues. ========= Build: 1.18-38.0.5 - Wed Dec 01 16:56:24 GMT 2021 curle: Readd PoseStack field to RenderTooltipEvent. ========= Build: 1.18-38.0.4 - Wed Dec 01 01:29:57 GMT 2021 curle: Fix custom loot serializers using wrong registry names ========= Build: 1.18-38.0.3 - Wed Dec 01 01:15:13 GMT 2021 lexmanos: Fix DungeonHooks not returning correct values. Fixes dungeons in world spawning pigs. ========= Build: 1.18-38.0.2 - Wed Dec 01 00:23:23 GMT 2021 lexmanos: Fix dedicated server install. Closes #8226 Fix example mod Fix obf issue with records. Closes #8228 Fix dependencies beingg out of sync from vanilla. Closes #8227 Disable mixin due to module incompatibility. ========= Build: 1.18-38.0.1 - Tue Nov 30 20:56:52 GMT 2021 gigaherz: Fix mod resources not loading. Add BreakingItemParticle.java.patch which I forgot to commit during the porting. ================================================ FILE: gradle/wrapper/gradle-wrapper.properties ================================================ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists ================================================ FILE: gradle.properties ================================================ # Sets default memory used for gradle commands. Can be overridden by user or command line properties. # This is required to provide enough memory for the Minecraft decompilation process. org.gradle.jvmargs=-Xmx3G org.gradle.daemon=false ## Environment Properties #for gh action #registrate_version=1.3.3 #for dev testing registrate_version=MC1.20-1.3.3 # The Minecraft version must agree with the Forge version to get a valid artifact minecraft_version=1.20.1 # The Minecraft version range can use any release version of Minecraft as bounds. # Snapshots, pre-releases, and release candidates are not guaranteed to sort properly # as they do not follow standard versioning conventions. minecraft_version_range=[1.20.1,1.21) # The Forge version must agree with the Minecraft version to get a valid artifact forge_version=47.1.0 # The Forge version range can use any version of Forge as bounds or match the loader version range forge_version_range=[47,) # The loader version range can only use the major version of Forge/FML as bounds loader_version_range=[47,) # The mapping channel to use for mappings. # The default set of supported mapping channels are ["official", "snapshot", "snapshot_nodoc", "stable", "stable_nodoc"]. # Additional mapping channels can be registered through the "channelProviders" extension in a Gradle plugin. # # | Channel | Version | | # |-----------|----------------------|--------------------------------------------------------------------------------| # | official | MCVersion | Official field/method names from Mojang mapping files | # | parchment | YYYY.MM.DD-MCVersion | Open community-sourced parameter names and javadocs layered on top of official | # # You must be aware of the Mojang license when using the 'official' or 'parchment' mappings. # See more information here: https://github.com/MinecraftForge/MCPConfig/blob/master/Mojang.md # # Parchment is an unofficial project maintained by ParchmentMC, separate from Minecraft Forge. # Additional setup is needed to use their mappings, see https://parchmentmc.org/docs/getting-started mapping_channel=official # The mapping version to query from the mapping channel. # This must match the format required by the mapping channel. mapping_version=1.20.1 ## Mod Properties # The unique mod identifier for the mod. Must be lowercase in English locale. Must fit the regex [a-z][a-z0-9_]{1,63} # Must match the String constant located in the main mod class annotated with @Mod. mod_id=weather2 # The human-readable display name for the mod. mod_name=Weather2 # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. mod_license=All Rights Reserved # The mod version. See https://semver.org/ mod_version=1.20.1-2.8.3 # The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. # This should match the base package used for the mod sources. # See https://maven.apache.org/guides/mini/guide-naming-conventions.html mod_group_id=com.corosus.weather2 # The authors of the mod. This is a simple text string that is used for display purposes in the mod list. mod_authors=Corosus # The description of the mod. This is a simple multiline text string that is used for display purposes in the mod list. mod_description=Speeeeeeen ================================================ FILE: gradlew ================================================ #!/bin/sh # # Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ############################################################################## # # Gradle start up script for POSIX generated by Gradle. # # Important for running: # # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is # noncompliant, but you have some other compliant shell such as ksh or # bash, then to run this script, type that shell name before the whole # command line, like: # # ksh Gradle # # Busybox and similar reduced shells will NOT work, because this script # requires all of these POSIX shell features: # * functions; # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», # «${var#prefix}», «${var%suffix}», and «$( cmd )»; # * compound commands having a testable exit status, especially «case»; # * various built-in commands including «command», «set», and «ulimit». # # Important for patching: # # (2) This script targets any POSIX shell, so it avoids extensions provided # by Bash, Ksh, etc; in particular arrays are avoided. # # The "traditional" practice of packing multiple parameters into a # space-separated string is a well documented source of bugs and security # problems, so this is (mostly) avoided, by progressively accumulating # options in "$@", and eventually passing that to Java. # # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; # see the in-line comments for details. # # There are tweaks for specific operating systems such as AIX, CygWin, # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. # ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link app_path=$0 # Need this for daisy-chained symlinks. while APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path [ -h "$app_path" ] do ls=$( ls -ld "$app_path" ) link=${ls#*' -> '} case $link in #( /*) app_path=$link ;; #( *) app_path=$APP_HOME$link ;; esac done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum warn () { echo "$*" } >&2 die () { echo echo "$*" echo exit 1 } >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false case "$( uname )" in #( CYGWIN* ) cygwin=true ;; #( Darwin* ) darwin=true ;; #( MSYS* | MINGW* ) msys=true ;; #( NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD=$JAVA_HOME/jre/sh/java else JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac fi # Collect all arguments for the java command, stacking in reverse order: # * args from the command line # * the main class name # * -classpath # * -D...appname settings # * --module-path (only if needed) # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) # Now convert the arguments - kludge to limit ourselves to /bin/sh for arg do if case $arg in #( -*) false ;; # don't mess with options #( /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath [ -e "$t" ] ;; #( *) false ;; esac then arg=$( cygpath --path --ignore --mixed "$arg" ) fi # Roll the args list around exactly as many times as the number of # args, so each arg winds up back in the position where it started, but # possibly modified. # # NB: a `for` loop captures its iteration list before it begins, so # changing the positional parameters here affects neither the number of # iterations, nor the values presented in `arg`. shift # remove old arg set -- "$@" "$arg" # push replacement arg done fi # Collect all arguments for the java command; # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # shell script including quotes and variable substitutions, so put them in # double quotes to make sure that they get re-expanded; and # * put everything else in single quotes, so that it's not re-expanded. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ org.gradle.wrapper.GradleWrapperMain \ "$@" # Stop when "xargs" is not available. if ! command -v xargs >/dev/null 2>&1 then die "xargs is not available" fi # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. # # In Bash we could simply go: # # readarray ARGS < <( xargs -n1 <<<"$var" ) && # set -- "${ARGS[@]}" "$@" # # but POSIX shell has neither arrays nor command substitution, so instead we # post-process each arg (as a line of input to sed) to backslash-escape any # character that might be a shell metacharacter, then use eval to reverse # that process (while maintaining the separation between arguments), and wrap # the whole thing up as a single "set" statement. # # This will of course break if any of these variables contains a newline or # an unmatched quote. # eval "set -- $( printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | xargs -n1 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | tr '\n' ' ' )" '"$@"' exec "$JAVACMD" "$@" ================================================ FILE: gradlew.bat ================================================ @rem @rem Copyright 2015 the original author or authors. @rem @rem Licensed under the Apache License, Version 2.0 (the "License"); @rem you may not use this file except in compliance with the License. @rem You may obtain a copy of the License at @rem @rem https://www.apache.org/licenses/LICENSE-2.0 @rem @rem Unless required by applicable law or agreed to in writing, software @rem distributed under the License is distributed on an "AS IS" BASIS, @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. @rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Resolve any "." and ".." in APP_HOME to make it shorter. for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! set EXIT_CODE=%ERRORLEVEL% if %EXIT_CODE% equ 0 set EXIT_CODE=1 if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: settings.gradle ================================================ pluginManagement { repositories { gradlePluginPortal() maven { name = 'MinecraftForge' url = 'https://maven.minecraftforge.net/' } } } plugins { id 'org.gradle.toolchains.foojay-resolver-convention' version '0.5.0' } ================================================ FILE: src/generated/resources/.cache/0d0f48ca72ebc11ea3eaf67230fd125a3c581fa7 ================================================ // 1.20.1 2023-11-22T02:08:00.197677 atlases generator for coroutil 74f8b853ba67f301962a10de1f0c163cbd8a8879 assets/minecraft/atlases/particles.json ================================================ FILE: src/generated/resources/.cache/59eb3dbb5f86130e09b3c62d89b9525ee01cf52d ================================================ // 1.20.1 2023-11-23T22:56:39.0222298 Loot Tables c8c5ce560e926e54e54807bfa8292631975278c8 data/weather2/loot_tables/blocks/anemometer.json c8c3920b1604bd0d96bb0f2fe36af5bec0d17a68 data/weather2/loot_tables/blocks/sand_layer.json 110f066250df5b3b40a1ab65f5db0e42b149bfc0 data/weather2/loot_tables/blocks/tornado_sensor.json dab63dc3440645fa945cceb5bf0e90e65617307c data/weather2/loot_tables/blocks/tornado_siren.json 52d48e562562111bcbc28c4d88000e7f53be2c23 data/weather2/loot_tables/blocks/weather_deflector.json c0a4e2a903db1b6c8a509f541f413283c73e4905 data/weather2/loot_tables/blocks/weather_forecast.json 5144ca5845a17038ade0ad75085ec40b1fe313d8 data/weather2/loot_tables/blocks/wind_turbine.json 9844b43ed3b5bdcf2cb5caa511707ef6bf53a3f9 data/weather2/loot_tables/blocks/wind_vane.json ================================================ FILE: src/generated/resources/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e ================================================ // 1.20.1 2023-11-23T15:40:45.726425 Recipes a6fcf55b00f782cb61ea7ff8c36947cc71d1b6eb data/weather2/advancements/recipes/misc/anemometer.json f05e205a927078351fb102a2efb42abb0b621702 data/weather2/advancements/recipes/misc/sand_layer.json 1628c853ad5eb3f28a2183446344bc04891eb91c data/weather2/advancements/recipes/misc/tornado_sensor.json f13d83986054c3bc720075107179268a8d729079 data/weather2/advancements/recipes/misc/tornado_siren.json 1a192c2be326846c2fd212595e7c1c8336494294 data/weather2/advancements/recipes/misc/weather_deflector.json 877fab4cc1e153017cbbc49bbecdb403dd48c4b8 data/weather2/advancements/recipes/misc/weather_forecast.json 7def7a8363c66c36f79299afc71c888668c6dc82 data/weather2/advancements/recipes/misc/weather_item.json 27039723785ab5a173e8f855f777dd90ca4f1712 data/weather2/advancements/recipes/misc/wind_turbine.json 31301eabe51f450b5c713189ed8800a6e56c85af data/weather2/advancements/recipes/misc/wind_vane.json 7191a7c546fa76fda67b9eff30be60d24c66c0f9 data/weather2/recipes/anemometer.json bedb68269184b52e111d66ab1a430f50f74b2c1f data/weather2/recipes/sand_layer.json 386f9af237dac1c3ded5cb82859c8f2f409c994f data/weather2/recipes/tornado_sensor.json 8fd7a464306fd74e6d6fc771d0c918003125ade2 data/weather2/recipes/tornado_siren.json fa3fbf7a57f04cd6bafe3d294d99e0359c3c1aae data/weather2/recipes/weather_deflector.json 5465cb6ea0be946f98da47fedcd7d4328a61a1ee data/weather2/recipes/weather_forecast.json fc16ba8ec6cae16f7a3c29bba99215b13a13ef97 data/weather2/recipes/weather_item.json e79f45df5a4ad54d1d56b36885e6be58135475d3 data/weather2/recipes/wind_turbine.json fc56b6dad0bb7d6b22ae4b95d8c69cbcd964b4fa data/weather2/recipes/wind_vane.json ================================================ FILE: src/generated/resources/.cache/f991d9ad226694c30732bf45648e031ffa7d50dc ================================================ // 1.20.1 2023-11-17T01:47:33.0547988 atlases generator for weather2 8b58f99c389a93e757d0e4e05efaaa16ce22dd8c assets/minecraft/atlases/blocks.json ================================================ FILE: src/generated/resources/assets/minecraft/atlases/blocks.json ================================================ { "sources": [ { "type": "minecraft:single", "resource": "weather2:blocks/tornado_siren" }, { "type": "minecraft:single", "resource": "weather2:blocks/tornado_siren_manual" }, { "type": "minecraft:single", "resource": "weather2:blocks/tornado_siren_manual_on" }, { "type": "minecraft:single", "resource": "weather2:blocks/tornado_sensor" }, { "type": "minecraft:single", "resource": "weather2:blocks/weather_deflector" }, { "type": "minecraft:single", "resource": "weather2:blocks/weather_forecast" }, { "type": "minecraft:single", "resource": "weather2:blocks/weather_machine" }, { "type": "minecraft:single", "resource": "weather2:blocks/anemometer" }, { "type": "minecraft:single", "resource": "weather2:blocks/wind_vane" }, { "type": "minecraft:single", "resource": "weather2:blocks/wind_turbine" }, { "type": "minecraft:single", "resource": "weather2:items/weather_item" }, { "type": "minecraft:single", "resource": "weather2:items/sand_layer" }, { "type": "minecraft:single", "resource": "weather2:items/sand_layer_placeable" } ] } ================================================ FILE: src/generated/resources/assets/minecraft/atlases/particles.json ================================================ { "sources": [ { "type": "minecraft:single", "resource": "coroutil:particles/white" }, { "type": "minecraft:single", "resource": "coroutil:particles/cloud256" }, { "type": "minecraft:single", "resource": "coroutil:particles/cloud256_fire" }, { "type": "minecraft:single", "resource": "coroutil:particles/cloud256_6" }, { "type": "minecraft:single", "resource": "coroutil:particles/downfall3" }, { "type": "minecraft:single", "resource": "coroutil:particles/chicken" }, { "type": "minecraft:single", "resource": "coroutil:particles/potato" }, { "type": "minecraft:single", "resource": "coroutil:particles/leaf" }, { "type": "minecraft:single", "resource": "coroutil:particles/rain_white" }, { "type": "minecraft:single", "resource": "coroutil:particles/snow" }, { "type": "minecraft:single", "resource": "coroutil:particles/snow2" }, { "type": "minecraft:single", "resource": "coroutil:particles/tumbleweed" }, { "type": "minecraft:single", "resource": "coroutil:particles/debris_1" }, { "type": "minecraft:single", "resource": "coroutil:particles/debris_2" }, { "type": "minecraft:single", "resource": "coroutil:particles/debris_3" }, { "type": "minecraft:single", "resource": "coroutil:particles/hail" } ] } ================================================ FILE: src/generated/resources/data/weather2/advancements/recipes/misc/anemometer.json ================================================ { "parent": "minecraft:recipes/root", "criteria": { "has_the_recipe": { "conditions": { "recipe": "weather2:anemometer" }, "trigger": "minecraft:recipe_unlocked" }, "has_weather_item": { "conditions": { "items": [ { "items": [ "weather2:weather_item" ] } ] }, "trigger": "minecraft:inventory_changed" } }, "requirements": [ [ "has_weather_item", "has_the_recipe" ] ], "rewards": { "recipes": [ "weather2:anemometer" ] }, "sends_telemetry_event": false } ================================================ FILE: src/generated/resources/data/weather2/advancements/recipes/misc/sand_layer.json ================================================ { "parent": "minecraft:recipes/root", "criteria": { "has_sand": { "conditions": { "items": [ { "items": [ "minecraft:sand" ] } ] }, "trigger": "minecraft:inventory_changed" }, "has_the_recipe": { "conditions": { "recipe": "weather2:sand_layer" }, "trigger": "minecraft:recipe_unlocked" } }, "requirements": [ [ "has_sand", "has_the_recipe" ] ], "rewards": { "recipes": [ "weather2:sand_layer" ] }, "sends_telemetry_event": false } ================================================ FILE: src/generated/resources/data/weather2/advancements/recipes/misc/tornado_sensor.json ================================================ { "parent": "minecraft:recipes/root", "criteria": { "has_the_recipe": { "conditions": { "recipe": "weather2:tornado_sensor" }, "trigger": "minecraft:recipe_unlocked" }, "has_weather_item": { "conditions": { "items": [ { "items": [ "weather2:weather_item" ] } ] }, "trigger": "minecraft:inventory_changed" } }, "requirements": [ [ "has_weather_item", "has_the_recipe" ] ], "rewards": { "recipes": [ "weather2:tornado_sensor" ] }, "sends_telemetry_event": false } ================================================ FILE: src/generated/resources/data/weather2/advancements/recipes/misc/tornado_siren.json ================================================ { "parent": "minecraft:recipes/root", "criteria": { "has_sensor_item": { "conditions": { "items": [ { "items": [ "weather2:tornado_sensor" ] } ] }, "trigger": "minecraft:inventory_changed" }, "has_the_recipe": { "conditions": { "recipe": "weather2:tornado_siren" }, "trigger": "minecraft:recipe_unlocked" } }, "requirements": [ [ "has_sensor_item", "has_the_recipe" ] ], "rewards": { "recipes": [ "weather2:tornado_siren" ] }, "sends_telemetry_event": false } ================================================ FILE: src/generated/resources/data/weather2/advancements/recipes/misc/weather_deflector.json ================================================ { "parent": "minecraft:recipes/root", "criteria": { "has_the_recipe": { "conditions": { "recipe": "weather2:weather_deflector" }, "trigger": "minecraft:recipe_unlocked" }, "has_weather_item": { "conditions": { "items": [ { "items": [ "weather2:weather_item" ] } ] }, "trigger": "minecraft:inventory_changed" } }, "requirements": [ [ "has_weather_item", "has_the_recipe" ] ], "rewards": { "recipes": [ "weather2:weather_deflector" ] }, "sends_telemetry_event": false } ================================================ FILE: src/generated/resources/data/weather2/advancements/recipes/misc/weather_forecast.json ================================================ { "parent": "minecraft:recipes/root", "criteria": { "has_the_recipe": { "conditions": { "recipe": "weather2:weather_forecast" }, "trigger": "minecraft:recipe_unlocked" }, "has_weather_item": { "conditions": { "items": [ { "items": [ "weather2:weather_item" ] } ] }, "trigger": "minecraft:inventory_changed" } }, "requirements": [ [ "has_weather_item", "has_the_recipe" ] ], "rewards": { "recipes": [ "weather2:weather_forecast" ] }, "sends_telemetry_event": false } ================================================ FILE: src/generated/resources/data/weather2/advancements/recipes/misc/weather_item.json ================================================ { "parent": "minecraft:recipes/root", "criteria": { "has_redstone": { "conditions": { "items": [ { "items": [ "minecraft:redstone" ] } ] }, "trigger": "minecraft:inventory_changed" }, "has_the_recipe": { "conditions": { "recipe": "weather2:weather_item" }, "trigger": "minecraft:recipe_unlocked" } }, "requirements": [ [ "has_redstone", "has_the_recipe" ] ], "rewards": { "recipes": [ "weather2:weather_item" ] }, "sends_telemetry_event": false } ================================================ FILE: src/generated/resources/data/weather2/advancements/recipes/misc/wind_turbine.json ================================================ { "parent": "minecraft:recipes/root", "criteria": { "has_the_recipe": { "conditions": { "recipe": "weather2:wind_turbine" }, "trigger": "minecraft:recipe_unlocked" }, "has_wind_vane": { "conditions": { "items": [ { "items": [ "weather2:wind_turbine" ] } ] }, "trigger": "minecraft:inventory_changed" } }, "requirements": [ [ "has_wind_vane", "has_the_recipe" ] ], "rewards": { "recipes": [ "weather2:wind_turbine" ] }, "sends_telemetry_event": false } ================================================ FILE: src/generated/resources/data/weather2/advancements/recipes/misc/wind_vane.json ================================================ { "parent": "minecraft:recipes/root", "criteria": { "has_the_recipe": { "conditions": { "recipe": "weather2:wind_vane" }, "trigger": "minecraft:recipe_unlocked" }, "has_weather_item": { "conditions": { "items": [ { "items": [ "weather2:weather_item" ] } ] }, "trigger": "minecraft:inventory_changed" } }, "requirements": [ [ "has_weather_item", "has_the_recipe" ] ], "rewards": { "recipes": [ "weather2:wind_vane" ] }, "sends_telemetry_event": false } ================================================ FILE: src/generated/resources/data/weather2/loot_tables/blocks/anemometer.json ================================================ { "type": "minecraft:block", "pools": [ { "bonus_rolls": 0.0, "conditions": [ { "condition": "minecraft:survives_explosion" } ], "entries": [ { "type": "minecraft:item", "name": "weather2:anemometer" } ], "rolls": 1.0 } ], "random_sequence": "weather2:blocks/anemometer" } ================================================ FILE: src/generated/resources/data/weather2/loot_tables/blocks/sand_layer.json ================================================ { "type": "minecraft:block", "pools": [ { "bonus_rolls": 0.0, "conditions": [ { "condition": "minecraft:survives_explosion" } ], "entries": [ { "type": "minecraft:item", "name": "weather2:sand_layer" } ], "rolls": 1.0 } ], "random_sequence": "weather2:blocks/sand_layer" } ================================================ FILE: src/generated/resources/data/weather2/loot_tables/blocks/tornado_sensor.json ================================================ { "type": "minecraft:block", "pools": [ { "bonus_rolls": 0.0, "conditions": [ { "condition": "minecraft:survives_explosion" } ], "entries": [ { "type": "minecraft:item", "name": "weather2:tornado_sensor" } ], "rolls": 1.0 } ], "random_sequence": "weather2:blocks/tornado_sensor" } ================================================ FILE: src/generated/resources/data/weather2/loot_tables/blocks/tornado_siren.json ================================================ { "type": "minecraft:block", "pools": [ { "bonus_rolls": 0.0, "conditions": [ { "condition": "minecraft:survives_explosion" } ], "entries": [ { "type": "minecraft:item", "name": "weather2:tornado_siren" } ], "rolls": 1.0 } ], "random_sequence": "weather2:blocks/tornado_siren" } ================================================ FILE: src/generated/resources/data/weather2/loot_tables/blocks/weather_deflector.json ================================================ { "type": "minecraft:block", "pools": [ { "bonus_rolls": 0.0, "conditions": [ { "condition": "minecraft:survives_explosion" } ], "entries": [ { "type": "minecraft:item", "name": "weather2:weather_deflector" } ], "rolls": 1.0 } ], "random_sequence": "weather2:blocks/weather_deflector" } ================================================ FILE: src/generated/resources/data/weather2/loot_tables/blocks/weather_forecast.json ================================================ { "type": "minecraft:block", "pools": [ { "bonus_rolls": 0.0, "conditions": [ { "condition": "minecraft:survives_explosion" } ], "entries": [ { "type": "minecraft:item", "name": "weather2:weather_forecast" } ], "rolls": 1.0 } ], "random_sequence": "weather2:blocks/weather_forecast" } ================================================ FILE: src/generated/resources/data/weather2/loot_tables/blocks/wind_turbine.json ================================================ { "type": "minecraft:block", "pools": [ { "bonus_rolls": 0.0, "conditions": [ { "condition": "minecraft:survives_explosion" } ], "entries": [ { "type": "minecraft:item", "name": "weather2:wind_turbine" } ], "rolls": 1.0 } ], "random_sequence": "weather2:blocks/wind_turbine" } ================================================ FILE: src/generated/resources/data/weather2/loot_tables/blocks/wind_vane.json ================================================ { "type": "minecraft:block", "pools": [ { "bonus_rolls": 0.0, "conditions": [ { "condition": "minecraft:survives_explosion" } ], "entries": [ { "type": "minecraft:item", "name": "weather2:wind_vane" } ], "rolls": 1.0 } ], "random_sequence": "weather2:blocks/wind_vane" } ================================================ FILE: src/generated/resources/data/weather2/recipes/anemometer.json ================================================ { "type": "minecraft:crafting_shaped", "category": "misc", "key": { "D": { "item": "minecraft:redstone" }, "X": { "item": "weather2:weather_item" } }, "pattern": [ "X X", "XDX", "X X" ], "result": { "item": "weather2:anemometer" }, "show_notification": true } ================================================ FILE: src/generated/resources/data/weather2/recipes/sand_layer.json ================================================ { "type": "minecraft:crafting_shaped", "category": "misc", "key": { "D": { "item": "minecraft:sand" } }, "pattern": [ "DDD", "D D", "DDD" ], "result": { "count": 8, "item": "weather2:sand_layer" }, "show_notification": true } ================================================ FILE: src/generated/resources/data/weather2/recipes/tornado_sensor.json ================================================ { "type": "minecraft:crafting_shaped", "category": "misc", "key": { "D": { "item": "minecraft:redstone" }, "I": { "item": "weather2:weather_item" }, "X": { "item": "minecraft:iron_ingot" } }, "pattern": [ "X X", "DID", "X X" ], "result": { "item": "weather2:tornado_sensor" }, "show_notification": true } ================================================ FILE: src/generated/resources/data/weather2/recipes/tornado_siren.json ================================================ { "type": "minecraft:crafting_shaped", "category": "misc", "key": { "D": { "item": "minecraft:redstone" }, "I": { "item": "weather2:tornado_sensor" }, "X": { "item": "minecraft:iron_ingot" } }, "pattern": [ "XDX", "DID", "XDX" ], "result": { "item": "weather2:tornado_siren" }, "show_notification": true } ================================================ FILE: src/generated/resources/data/weather2/recipes/weather_deflector.json ================================================ { "type": "minecraft:crafting_shaped", "category": "misc", "key": { "D": { "item": "minecraft:redstone" }, "I": { "item": "weather2:weather_item" }, "X": { "item": "minecraft:iron_ingot" } }, "pattern": [ "XDX", "DID", "XDX" ], "result": { "item": "weather2:weather_deflector" }, "show_notification": true } ================================================ FILE: src/generated/resources/data/weather2/recipes/weather_forecast.json ================================================ { "type": "minecraft:crafting_shaped", "category": "misc", "key": { "D": { "item": "minecraft:redstone" }, "I": { "item": "minecraft:compass" }, "X": { "item": "weather2:weather_item" } }, "pattern": [ "XDX", "DID", "XDX" ], "result": { "item": "weather2:weather_forecast" }, "show_notification": true } ================================================ FILE: src/generated/resources/data/weather2/recipes/weather_item.json ================================================ { "type": "minecraft:crafting_shaped", "category": "misc", "key": { "D": { "item": "minecraft:redstone" }, "I": { "item": "minecraft:gold_ingot" }, "X": { "item": "minecraft:iron_ingot" } }, "pattern": [ "X X", "DID", "X X" ], "result": { "item": "weather2:weather_item" }, "show_notification": true } ================================================ FILE: src/generated/resources/data/weather2/recipes/wind_turbine.json ================================================ { "type": "minecraft:crafting_shaped", "category": "misc", "key": { "D": { "item": "minecraft:diamond" }, "G": { "item": "minecraft:gold_ingot" }, "I": { "item": "minecraft:iron_block" }, "O": { "item": "minecraft:iron_ingot" }, "R": { "item": "minecraft:redstone_block" }, "V": { "item": "weather2:wind_vane" } }, "pattern": [ "ODO", "IVI", "RGR" ], "result": { "item": "weather2:wind_turbine" }, "show_notification": true } ================================================ FILE: src/generated/resources/data/weather2/recipes/wind_vane.json ================================================ { "type": "minecraft:crafting_shaped", "category": "misc", "key": { "D": { "item": "minecraft:redstone" }, "X": { "item": "weather2:weather_item" } }, "pattern": [ "X X", "DXD", "X X" ], "result": { "item": "weather2:wind_vane" }, "show_notification": true } ================================================ FILE: src/main/java/extendedrenderer/ExtendedRenderer.java ================================================ package extendedrenderer; public class ExtendedRenderer { public static String modid = "coroutil"; } ================================================ FILE: src/main/java/extendedrenderer/ParticleManagerExtended.java ================================================ package extendedrenderer; import com.google.common.collect.*; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.Tesselator; import com.mojang.logging.LogUtils; import extendedrenderer.particle.entity.EntityRotFX; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import net.minecraft.CrashReport; import net.minecraft.CrashReportCategory; import net.minecraft.ReportedException; import net.minecraft.Util; import net.minecraft.client.Camera; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.particle.*; import net.minecraft.client.renderer.GameRenderer; import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.texture.SpriteLoader; import net.minecraft.client.renderer.texture.TextureAtlas; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureManager; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.particles.ParticleGroup; import net.minecraft.core.particles.ParticleOptions; import net.minecraft.core.particles.ParticleType; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.FileToIdConverter; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.resources.PreparableReloadListener; import net.minecraft.server.packs.resources.Resource; import net.minecraft.server.packs.resources.ResourceManager; import net.minecraft.util.GsonHelper; import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.block.RenderShape; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import org.slf4j.Logger; import javax.annotation.Nullable; import java.io.IOException; import java.io.Reader; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.stream.Collectors; @OnlyIn(Dist.CLIENT) public class ParticleManagerExtended implements PreparableReloadListener { private static final Logger LOGGER = LogUtils.getLogger(); private static final FileToIdConverter PARTICLE_LISTER = FileToIdConverter.json("particles"); private static final ResourceLocation PARTICLES_ATLAS_INFO = new ResourceLocation("particles"); private static final int MAX_PARTICLES_PER_LAYER = 16384; private static final List RENDER_ORDER = ImmutableList.of(ParticleRenderType.TERRAIN_SHEET, ParticleRenderType.PARTICLE_SHEET_OPAQUE, ParticleRenderType.PARTICLE_SHEET_LIT, ParticleRenderType.PARTICLE_SHEET_TRANSLUCENT, ParticleRenderType.CUSTOM, ParticleRenderType.CUSTOM, EntityRotFX.SORTED_OPAQUE_BLOCK, EntityRotFX.SORTED_TRANSLUCENT); protected ClientLevel level; private final Map> particles = Maps.newTreeMap(net.minecraftforge.client.ForgeHooksClient.makeParticleRenderTypeComparator(RENDER_ORDER)); private final Queue trackingEmitters = Queues.newArrayDeque(); private final TextureManager textureManager; private final RandomSource random = RandomSource.create(); private final Map> providers = new java.util.HashMap<>(); private final Queue particlesToAdd = Queues.newArrayDeque(); private final Map spriteSets = Maps.newHashMap(); private final TextureAtlas textureAtlas; private final Object2IntOpenHashMap trackedParticleCounts = new Object2IntOpenHashMap<>(); public ParticleManagerExtended(ClientLevel p_107299_, TextureManager p_107300_) { this.textureAtlas = new TextureAtlas(TextureAtlas.LOCATION_PARTICLES); //p_107300_.register(this.textureAtlas.location(), this.textureAtlas); this.level = p_107299_; this.textureManager = p_107300_; } public CompletableFuture reload(PreparableReloadListener.PreparationBarrier p_107305_, ResourceManager p_107306_, ProfilerFiller p_107307_, ProfilerFiller p_107308_, Executor p_107309_, Executor p_107310_) { @OnlyIn(Dist.CLIENT) record ParticleDefinition(ResourceLocation id, Optional> sprites) { } CompletableFuture> completablefuture = CompletableFuture.supplyAsync(() -> { return PARTICLE_LISTER.listMatchingResources(p_107306_); }, p_107309_).thenCompose((p_247914_) -> { List> list = new ArrayList<>(p_247914_.size()); p_247914_.forEach((p_247903_, p_247904_) -> { ResourceLocation resourcelocation = PARTICLE_LISTER.fileToId(p_247903_); list.add(CompletableFuture.supplyAsync(() -> { return new ParticleDefinition(resourcelocation, this.loadParticleDescription(resourcelocation, p_247904_)); }, p_107309_)); }); return Util.sequence(list); }); CompletableFuture completablefuture1 = SpriteLoader.create(this.textureAtlas).loadAndStitch(p_107306_, PARTICLES_ATLAS_INFO, 0, p_107309_).thenCompose(SpriteLoader.Preparations::waitForUpload); return CompletableFuture.allOf(completablefuture1, completablefuture).thenCompose(p_107305_::wait).thenAcceptAsync((p_247900_) -> { this.clearParticles(); p_107308_.startTick(); p_107308_.push("upload"); SpriteLoader.Preparations spriteloader$preparations = completablefuture1.join(); this.textureAtlas.upload(spriteloader$preparations); p_107308_.popPush("bindSpriteSets"); Set set = new HashSet<>(); TextureAtlasSprite textureatlassprite = spriteloader$preparations.missing(); completablefuture.join().forEach((p_247911_) -> { Optional> optional = p_247911_.sprites(); if (!optional.isEmpty()) { List list = new ArrayList<>(); for(ResourceLocation resourcelocation : optional.get()) { TextureAtlasSprite textureatlassprite1 = spriteloader$preparations.regions().get(resourcelocation); if (textureatlassprite1 == null) { set.add(resourcelocation); list.add(textureatlassprite); } else { list.add(textureatlassprite1); } } if (list.isEmpty()) { list.add(textureatlassprite); } this.spriteSets.get(p_247911_.id()).rebind(list); } }); if (!set.isEmpty()) { LOGGER.warn("Missing particle sprites: {}", set.stream().sorted().map(ResourceLocation::toString).collect(Collectors.joining(","))); } p_107308_.pop(); p_107308_.endTick(); }, p_107310_); } public void close() { this.textureAtlas.clearTextureData(); } private Optional> loadParticleDescription(ResourceLocation p_250648_, Resource p_248793_) { if (!this.spriteSets.containsKey(p_250648_)) { LOGGER.debug("Redundant texture list for particle: {}", (Object)p_250648_); return Optional.empty(); } else { try (Reader reader = p_248793_.openAsReader()) { ParticleDescription particledescription = ParticleDescription.fromJson(GsonHelper.parse(reader)); return Optional.of(particledescription.getTextures()); } catch (IOException ioexception) { throw new IllegalStateException("Failed to load description for particle " + p_250648_, ioexception); } } } @Nullable private Particle makeParticle(T p_107396_, double p_107397_, double p_107398_, double p_107399_, double p_107400_, double p_107401_, double p_107402_) { ParticleProvider particleprovider = (ParticleProvider)this.providers.get(BuiltInRegistries.PARTICLE_TYPE.getKey(p_107396_.getType())); return particleprovider == null ? null : particleprovider.createParticle(p_107396_, this.level, p_107397_, p_107398_, p_107399_, p_107400_, p_107401_, p_107402_); } public void add(Particle p_107345_) { Optional optional = p_107345_.getParticleGroup(); if (optional.isPresent()) { if (this.hasSpaceInParticleLimit(optional.get())) { this.particlesToAdd.add(p_107345_); this.updateCount(optional.get(), 1); } } else { this.particlesToAdd.add(p_107345_); } } public void tick() { this.level.getProfiler().push("weather2_particle_tick"); this.particles.forEach((p_288249_, p_288250_) -> { this.level.getProfiler().push("weather2_particle_tick_" + p_288249_.toString()); this.tickParticleList(p_288250_); this.level.getProfiler().pop(); }); if (!this.trackingEmitters.isEmpty()) { List list = Lists.newArrayList(); for(TrackingEmitter trackingemitter : this.trackingEmitters) { trackingemitter.tick(); if (!trackingemitter.isAlive()) { list.add(trackingemitter); } } this.trackingEmitters.removeAll(list); } Particle particle; if (!this.particlesToAdd.isEmpty()) { while((particle = this.particlesToAdd.poll()) != null) { this.particles.computeIfAbsent(particle.getRenderType(), (p_107347_) -> { return EvictingQueue.create(16384 * 2); }).add(particle); } } this.level.getProfiler().pop(); } private void tickParticleList(Collection p_107385_) { if (!p_107385_.isEmpty()) { Iterator iterator = p_107385_.iterator(); while(iterator.hasNext()) { Particle particle = iterator.next(); this.tickParticle(particle); if (!particle.isAlive()) { particle.getParticleGroup().ifPresent((p_172289_) -> { this.updateCount(p_172289_, -1); }); iterator.remove(); } } } } private void updateCount(ParticleGroup p_172282_, int p_172283_) { this.trackedParticleCounts.addTo(p_172282_, p_172283_); } private void tickParticle(Particle p_107394_) { try { p_107394_.tick(); } catch (Throwable throwable) { CrashReport crashreport = CrashReport.forThrowable(throwable, "Ticking Particle"); CrashReportCategory crashreportcategory = crashreport.addCategory("Particle being ticked"); crashreportcategory.setDetail("Particle", p_107394_::toString); crashreportcategory.setDetail("Particle Type", p_107394_.getRenderType()::toString); throw new ReportedException(crashreport); } } /**@deprecated Forge: use {@link #render(PoseStack, MultiBufferSource.BufferSource, LightTexture, Camera, float, net.minecraft.client.renderer.culling.Frustum)} with Frustum as additional parameter*/ @Deprecated public void render(PoseStack p_107337_, MultiBufferSource.BufferSource p_107338_, LightTexture p_107339_, Camera p_107340_, float p_107341_) { render(p_107337_, p_107338_, p_107339_, p_107340_, p_107341_, null); } public void render(PoseStack p_107337_, MultiBufferSource.BufferSource p_107338_, LightTexture p_107339_, Camera p_107340_, float p_107341_, @Nullable net.minecraft.client.renderer.culling.Frustum clippingHelper) { this.level.getProfiler().push("weather2_particle_render"); //if (true) return; float fogStart = RenderSystem.getShaderFogStart(); float fogEnd = RenderSystem.getShaderFogEnd(); RenderSystem.setShaderFogStart(fogStart * 4); RenderSystem.setShaderFogEnd(fogEnd * 4); p_107339_.turnOnLightLayer(); RenderSystem.enableDepthTest(); //these didnt exist in our 1.18 modification, why? RenderSystem.activeTexture(org.lwjgl.opengl.GL13.GL_TEXTURE2); RenderSystem.activeTexture(org.lwjgl.opengl.GL13.GL_TEXTURE0); PoseStack posestack = RenderSystem.getModelViewStack(); posestack.pushPose(); posestack.mulPoseMatrix(p_107337_.last().pose()); RenderSystem.applyModelViewMatrix(); RenderSystem.disableCull(); int particleCount = 0; for(ParticleRenderType particlerendertype : this.particles.keySet()) { // Forge: allow custom IParticleRenderType's this.level.getProfiler().push(particlerendertype.toString()); if (particlerendertype == ParticleRenderType.NO_RENDER) continue; Iterable iterable = this.particles.get(particlerendertype); if (iterable != null) { RenderSystem.setShader(GameRenderer::getParticleShader); Tesselator tesselator = Tesselator.getInstance(); BufferBuilder bufferbuilder = tesselator.getBuilder(); particlerendertype.begin(bufferbuilder, this.textureManager); for(Particle particle : iterable) { if (particle instanceof EntityRotFX) { if (clippingHelper != null && particle.shouldCull() && !clippingHelper.isVisible(((EntityRotFX)particle).getBoundingBoxForRender(p_107341_))) continue; } else { if (clippingHelper != null && particle.shouldCull() && !clippingHelper.isVisible(particle.getBoundingBox())) continue; } try { particle.render(bufferbuilder, p_107340_, p_107341_); } catch (Throwable throwable) { CrashReport crashreport = CrashReport.forThrowable(throwable, "Rendering Particle"); CrashReportCategory crashreportcategory = crashreport.addCategory("Particle being rendered"); crashreportcategory.setDetail("Particle", particle::toString); crashreportcategory.setDetail("Particle Type", particlerendertype::toString); throw new ReportedException(crashreport); } } particlerendertype.end(tesselator); } this.level.getProfiler().pop(); } posestack.popPose(); RenderSystem.applyModelViewMatrix(); RenderSystem.depthMask(true); RenderSystem.disableBlend(); p_107339_.turnOffLightLayer(); RenderSystem.setShaderFogStart(fogStart); RenderSystem.setShaderFogEnd(fogEnd); this.level.getProfiler().pop(); } public void setLevel(@Nullable ClientLevel p_107343_) { this.level = p_107343_; this.clearParticles(); this.trackingEmitters.clear(); } public String countParticles() { return String.valueOf(this.particles.values().stream().mapToInt(Collection::size).sum()); } private boolean hasSpaceInParticleLimit(ParticleGroup p_172280_) { return this.trackedParticleCounts.getInt(p_172280_) < p_172280_.getLimit(); } public void clearParticles() { this.particles.clear(); this.particlesToAdd.clear(); this.trackingEmitters.clear(); this.trackedParticleCounts.clear(); } @OnlyIn(Dist.CLIENT) static class MutableSpriteSet implements SpriteSet { private List sprites; public TextureAtlasSprite get(int p_107413_, int p_107414_) { return this.sprites.get(p_107413_ * (this.sprites.size() - 1) / p_107414_); } public TextureAtlasSprite get(RandomSource p_233889_) { return this.sprites.get(p_233889_.nextInt(this.sprites.size())); } public void rebind(List p_107416_) { this.sprites = ImmutableList.copyOf(p_107416_); } } public Map> getParticles() { return particles; } } ================================================ FILE: src/main/java/extendedrenderer/ParticleRegistry2ElectricBubbleoo.java ================================================ package extendedrenderer; import weather2.DeferredHelper; import net.minecraft.client.Minecraft; import net.minecraft.client.particle.WaterDropParticle; import net.minecraft.core.particles.SimpleParticleType; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.client.event.RegisterParticleProvidersEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.registries.RegistryObject; import weather2.Weather; //@ObjectHolder(Weather.MODID) @Mod.EventBusSubscriber(modid = Weather.MODID, bus = Mod.EventBusSubscriber.Bus.MOD) public class ParticleRegistry2ElectricBubbleoo { //@ObjectHolder("acidrain_splash") //public static SimpleParticleType ACIDRAIN_SPLASH; public static final RegistryObject ACIDRAIN_SPLASH = Weather.R.particle("acidrain_splash", () -> new SimpleParticleType(false)); /*@SubscribeEvent public static void registerParticles(RegistryEvent.Register> evt){ SimpleParticleType acidrain_splash = new SimpleParticleType(false); acidrain_splash.setRegistryName(Weather.MODID, "acidrain_splash"); evt.getRegistry().register(acidrain_splash); }*/ /*@OnlyIn(Dist.CLIENT) @SubscribeEvent public static void registerParticleFactory(ParticleFactoryRegisterEvent evt){ Minecraft.getInstance().particleEngine.register(ParticleRegistry2ElectricBubbleoo.ACIDRAIN_SPLASH.get(), WaterDropParticle.Provider::new); }*/ @SubscribeEvent @OnlyIn(Dist.CLIENT) public static void factories(RegisterParticleProvidersEvent event) { //event.registerSprite(ACIDRAIN_SPLASH.get(), WaterDropParticleImpl::new); Minecraft.getInstance().particleEngine.register(new SimpleParticleType(false), WaterDropParticle.Provider::new); //event.registerSpecial(new SimpleParticleType(false), WaterDropParticle.Provider::new); } public static void bootstrap() {} } ================================================ FILE: src/main/java/extendedrenderer/WeatherSpriteSourceProvider.java ================================================ /* * Copyright (c) Forge Development LLC and contributors * SPDX-License-Identifier: LGPL-2.1-only */ package extendedrenderer; import net.minecraft.client.renderer.texture.atlas.sources.SingleFile; import net.minecraft.data.PackOutput; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.common.data.ExistingFileHelper; import net.minecraftforge.common.data.SpriteSourceProvider; import weather2.Weather; import java.util.Optional; public class WeatherSpriteSourceProvider extends SpriteSourceProvider { public WeatherSpriteSourceProvider(PackOutput output, ExistingFileHelper fileHelper) { super(output, fileHelper, Weather.MODID); } @Override protected void addSources() { atlas(SpriteSourceProvider.PARTICLES_ATLAS).addSource(new SingleFile(new ResourceLocation(Weather.MODID + "white"), Optional.empty())); } } ================================================ FILE: src/main/java/extendedrenderer/particle/ParticleRegistry.java ================================================ package extendedrenderer.particle; import extendedrenderer.ExtendedRenderer; import net.minecraft.client.renderer.texture.TextureAtlas; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.atlas.sources.SingleFile; import net.minecraft.data.PackOutput; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.client.event.TextureStitchEvent; import net.minecraftforge.common.data.ExistingFileHelper; import net.minecraftforge.common.data.SpriteSourceProvider; import net.minecraftforge.eventbus.api.SubscribeEvent; import weather2.Weather; import java.util.ArrayList; import java.util.List; import java.util.Optional; public class ParticleRegistry extends SpriteSourceProvider { public static TextureAtlasSprite squareGrey; public static TextureAtlasSprite smoke; //public static TextureAtlasSprite smokeTest; public static TextureAtlasSprite cloud; public static TextureAtlasSprite cloud256; public static TextureAtlasSprite cloud256_fire; public static TextureAtlasSprite cloud256_test; //public static TextureAtlasSprite cloud256_2; public static TextureAtlasSprite groundSplash; //public static TextureAtlasSprite downfall2; public static TextureAtlasSprite downfall3; //public static TextureAtlasSprite downfall4; //public static TextureAtlasSprite cloud256_7; public static TextureAtlasSprite chicken; public static TextureAtlasSprite potato; public static TextureAtlasSprite leaf; public static TextureAtlasSprite rain; public static TextureAtlasSprite rain_white; //public static TextureAtlasSprite rain_white_trans; //public static TextureAtlasSprite rain_white_2; //public static TextureAtlasSprite rain_10; //public static TextureAtlasSprite rain_vanilla; //public static TextureAtlasSprite snow_vanilla; public static TextureAtlasSprite snow; public static TextureAtlasSprite snow2; //public static TextureAtlasSprite test; //public static TextureAtlasSprite cloud256dark; //public static TextureAtlasSprite cloudDownfall; public static TextureAtlasSprite tumbleweed; public static TextureAtlasSprite debris_1; public static TextureAtlasSprite debris_2; public static TextureAtlasSprite debris_3; public static TextureAtlasSprite test_texture; public static TextureAtlasSprite white_square; public static List listFish = new ArrayList<>(); //public static List listSeaweed = new ArrayList<>(); public static TextureAtlasSprite grass; public static TextureAtlasSprite hail; public static TextureAtlasSprite cloudNew; public static TextureAtlasSprite cloud_square; public static TextureAtlasSprite square16; public static TextureAtlasSprite square64; public ParticleRegistry(PackOutput output, ExistingFileHelper fileHelper) { super(output, fileHelper, ExtendedRenderer.modid); } @Override protected void addSources() { //atlas(SpriteSourceProvider.PARTICLES_ATLAS).addSource(new SingleFile(new ResourceLocation(Weather.MODID + ":white"), Optional.empty())); addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/white")); //addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/smoke_00")); //smokeTest = event.addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/smoke_2")); //cloud = event.addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/cloud64")); addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/cloud256")); addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/cloud256_fire")); //addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/cloud256_test")); //cloud256_2 = addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/cloud256_5")); //ground splash addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/cloud256_6")); //cloud256_7 = addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/cloud256_7")); //downfall2 = addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/downfall2")); addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/downfall3")); //downfall4 = addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/downfall4")); if (!Weather.isLoveTropicsInstalled()) { addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/chicken")); addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/potato")); } addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/leaf")); //rain = addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/rain")); //addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/test_texture")); //addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/white_square")); addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/rain_white")); //rain_white_trans = addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/rain_white_trans")); //rain_white_2 = addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/rain_white_2")); //rain_10 = addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/rain_10")); //rain_vanilla = addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/vanilla/rain")); //snow_vanilla = addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/vanilla/snow")); addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/snow")); addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/snow2")); //cloud256dark = addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/cloud256dark")); //cloudDownfall = addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/downfall")); addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/tumbleweed")); addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/debris_1")); addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/debris_2")); addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/debris_3")); /*for (int i = 1; i <= 9; i++) { listFish.add(addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/fish_" + i))); } for (int i = 1; i <= 7; i++) { listSeaweed.add(addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/seaweed_section_" + i))); }*/ //used indirectly not via reference //addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/grass")); addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/hail")); //addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/cloud")); //addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/cloud_square")); //addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/white16")); //addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/white64")); //TODO: 1.14 uncomment /*MeshBufferManagerParticle.cleanup(); MeshBufferManagerFoliage.cleanup();*/ } public void addSprite(ResourceLocation res) { atlas(SpriteSourceProvider.PARTICLES_ATLAS).addSource(new SingleFile(res, Optional.empty())); } @SubscribeEvent public static void getRegisteredParticles(TextureStitchEvent.Post event) { if (!event.getAtlas().location().equals(TextureAtlas.LOCATION_PARTICLES)) { return; } squareGrey = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/white")); //smoke = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/smoke_00")); //smokeTest = event.addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/smoke_2")); //cloud = event.addSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/cloud64")); cloud256 = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/cloud256")); cloud256_fire = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/cloud256_fire")); //cloud256_test = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/cloud256_test")); //cloud256_2 = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/cloud256_5")); //ground splash groundSplash = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/cloud256_6")); //cloud256_7 = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/cloud256_7")); //downfall2 = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/downfall2")); downfall3 = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/downfall3")); //downfall4 = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/downfall4")); if (!Weather.isLoveTropicsInstalled()) { chicken = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/chicken")); potato = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/potato")); } leaf = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/leaf")); //rain = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/rain")); //test_texture = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/test_texture")); //white_square = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/white_square")); rain_white = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/rain_white")); //rain_white_trans = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/rain_white_trans")); //rain_white_2 = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/rain_white_2")); //rain_10 = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/rain_10")); //rain_vanilla = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/vanilla/rain")); //snow_vanilla = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/vanilla/snow")); snow = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/snow")); snow2 = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/snow2")); //cloud256dark = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/cloud256dark")); //cloudDownfall = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/downfall")); tumbleweed = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/tumbleweed")); debris_1 = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/debris_1")); debris_2 = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/debris_2")); debris_3 = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/debris_3")); /*for (int i = 1; i <= 9; i++) { listFish.add(event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/fish_" + i))); } for (int i = 1; i <= 7; i++) { listSeaweed.add(event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/seaweed_section_" + i))); }*/ //used indirectly not via reference //grass = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/grass")); hail = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/hail")); //cloudNew = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/cloud")); //cloud_square = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/cloud_square")); //square16 = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/white16")); //square64 = event.getAtlas().getSprite(new ResourceLocation(ExtendedRenderer.modid + ":particles/white64")); //TODO: 1.14 uncomment /*if (RotatingParticleManager.useShaders) { RotatingParticleManager.forceShaderReset = true; }*/ } /*public static TextureAtlasSprite addSprite(TextureStitchEvent.Pre event, ResourceLocation resourceLocation) { event.addSprite(resourceLocation); return event.getAtlas().getSprite(resourceLocation); }*/ } ================================================ FILE: src/main/java/extendedrenderer/particle/behavior/ParticleBehaviorFog.java ================================================ package extendedrenderer.particle.behavior; import extendedrenderer.particle.entity.EntityRotFX; import net.minecraft.world.phys.Vec3; public class ParticleBehaviorFog extends ParticleBehaviors { //Externally updated variables, adjusting how templated behavior works public int curTick = 0; public int ticksMax = 1; //TODO: temp, for comparing until done //public static boolean newCloudWay = false; public ParticleBehaviorFog(Vec3 source) { super(source); } public EntityRotFX initParticle(EntityRotFX particle) { super.initParticle(particle); //particle.particleGravity = 0.5F; //fog particle.rotationYaw = rand.nextInt(360); particle.rotationPitch = rand.nextInt(50)-rand.nextInt(50); //cloud particle.rotationYaw = rand.nextInt(360); particle.rotationPitch = -90+rand.nextInt(50)-rand.nextInt(50); particle.setMaxAge(650+rand.nextInt(10)); particle.setGravity(0.01F); float randFloat = (rand.nextFloat() * 0.6F); float baseBright = 0.7F; float finalBright = Math.min(1F, baseBright+randFloat); particle.setColor(finalBright, finalBright, finalBright); //particle.setColor(72F/255F, 239F/255F, 8F/255F); //sand //particle.setColor(204F/255F, 198F/255F, 120F/255F); //red //particle.setColor(0.6F + (rand.nextFloat() * 0.4F), 0.2F + (rand.nextFloat() * 0.7F), 0); //green //particle.setColor(0, 0.4F + (rand.nextFloat() * 0.4F), 0); //tealy blue //particle.setColor(0, 0.4F + (rand.nextFloat() * 0.4F), 0.4F + (rand.nextFloat() * 0.4F)); //particle.setColor(0.4F + (rand.nextFloat() * 0.4F), 0.4F + (rand.nextFloat() * 0.4F), 0.4F + (rand.nextFloat() * 0.4F)); //location based color shift //particle.setColor((float) (0.4F + (Math.abs(particle.posX / 300D) * 0.6D)), 0.4F, (float) (0.4F + (Math.abs(particle.posZ / 300D) * 0.6D))); particle.setUseCustomBBForRenderCulling(true); particle.setScale(0.25F + 0.2F * rand.nextFloat()); particle.brightness = 1F; particle.setAlphaF(0); float sizeBase = (float) (500+(rand.nextDouble()*40)); sizeBase *= 0.15F; particle.setScale(sizeBase); //particle.spawnY = (float) particle.posY; //particle.noClip = false; particle.setCanCollide(true); //entityfx.spawnAsWeatherEffect(); particle.renderRange = 2048; return particle; } @Override public void tickUpdateAct(EntityRotFX particle) { //particle.particleScale = 900; //particle.rotationPitch = 30; //for (int i = 0; i < particles.size(); i++) { //EntityRotFX particle = particles.get(i); if (!particle.isAlive()) { particles.remove(particle); } else { if (particle.getEntityId() % 2 == 0) { particle.rotationYaw -= 0.02; } else { particle.rotationYaw += 0.02; } float ticksFadeInMax = 50; float ticksFadeOutMax = 50; if (particle.getAge() < ticksFadeInMax) { //System.out.println("particle.getAge(): " + particle.getAge()); particle.setAlphaF(particle.getAge() / ticksFadeInMax); //particle.setAlphaF(1); } else if (particle.getAge() > particle.getMaxAge() - ticksFadeOutMax) { float count = particle.getAge() - (particle.getMaxAge() - ticksFadeOutMax); float val = (ticksFadeOutMax - (count)) / ticksFadeOutMax; //System.out.println(val); particle.setAlphaF(val); } else { /*if (particle.getAlphaF() > 0) { particle.setAlphaF(particle.getAlphaF() - rateAlpha*1.3F); } else { particle.remove(); }*/ } double moveSpeed = 0.001D; //1.10.2 no /*if (particle.onGround) { moveSpeed = 0.012D; particle.setMotionY(particle.getMotionY() + 0.01D); }*/ //if (particle.isCollidedHorizontally) { if (particle.isCollided()) { particle.rotationYaw += 0.1; } particle.setMotionX(particle.getMotionX() - Math.sin(Math.toRadians((particle.rotationYaw + particle.getEntityId()) % 360)) * moveSpeed); particle.setMotionZ(particle.getMotionZ() + Math.cos(Math.toRadians((particle.rotationYaw + particle.getEntityId()) % 360)) * moveSpeed); double moveSpeedRand = 0.005D; particle.setMotionX(particle.getMotionX() + (rand.nextDouble() * moveSpeedRand - rand.nextDouble() * moveSpeedRand)); particle.setMotionZ(particle.getMotionZ() + (rand.nextDouble() * moveSpeedRand - rand.nextDouble() * moveSpeedRand)); particle.setScale(particle.getScale() - 0.1F); if (particle.spawnY != -1) { particle.setPosition(particle.getPosX(), particle.spawnY, particle.getPosZ()); //particle.posY = particle.spawnY; } //particle.remove(); } //} } } ================================================ FILE: src/main/java/extendedrenderer/particle/behavior/ParticleBehaviorSandstorm.java ================================================ package extendedrenderer.particle.behavior; import com.corosus.coroutil.util.CoroUtilBlock; import extendedrenderer.particle.entity.EntityRotFX; import net.minecraft.client.Minecraft; import net.minecraft.util.Mth; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.core.BlockPos; import net.minecraft.world.phys.Vec3; import weather2.ClientTickHandler; import weather2.ClientWeatherProxy; import weather2.datatypes.PrecipitationType; public class ParticleBehaviorSandstorm extends ParticleBehaviors { //Externally updated variables, adjusting how templated behavior works public int curTick = 0; public int ticksMax = 1; public ParticleBehaviorSandstorm(Vec3 source) { super(source); } public EntityRotFX initParticle(EntityRotFX particle) { super.initParticle(particle); //particle.particleGravity = 0.5F; //fog particle.rotationYaw = rand.nextInt(360); particle.rotationPitch = rand.nextInt(50)-rand.nextInt(50); //cloud //particle.rotationYaw = rand.nextInt(360); //particle.rotationPitch = -90+rand.nextInt(50)-rand.nextInt(50); particle.setLifetime(450+rand.nextInt(10)); float randFloat = (rand.nextFloat() * 0.6F); float baseBright = 0.7F; float finalBright = Math.min(1F, baseBright+randFloat); particle.setColor(finalBright, finalBright, finalBright); //particle.setColor(72F/255F, 239F/255F, 8F/255F); //sand //particle.setColor(204F/255F, 198F/255F, 120F/255F); //red //particle.setColor(0.6F + (rand.nextFloat() * 0.4F), 0.2F + (rand.nextFloat() * 0.7F), 0); //green //particle.setColor(0, 0.4F + (rand.nextFloat() * 0.4F), 0); //tealy blue //particle.setColor(0, 0.4F + (rand.nextFloat() * 0.4F), 0.4F + (rand.nextFloat() * 0.4F)); //particle.setColor(0.4F + (rand.nextFloat() * 0.4F), 0.4F + (rand.nextFloat() * 0.4F), 0.4F + (rand.nextFloat() * 0.4F)); //location based color shift //particle.setColor((float) (0.4F + (Math.abs(particle.posX / 300D) * 0.6D)), 0.4F, (float) (0.4F + (Math.abs(particle.posZ / 300D) * 0.6D))); //particle.setScale(0.25F + 0.2F * rand.nextFloat()); particle.brightness = 1F; particle.setAlpha(1F); float sizeBase = (float) (30+(rand.nextDouble()*4)); particle.setScale(sizeBase); //particle.spawnY = (float) particle.posY; //particle.noClip = false; particle.setCanCollide(true); //entityfx.spawnAsWeatherEffect(); particle.renderRange = 2048; particle.setFacePlayer(true); particle.setGravity(0.03F); return particle; } @Override public void tickUpdateAct(EntityRotFX particle) { //particle.particleScale = 900; //particle.rotationPitch = 30; //for (int i = 0; i < particles.size(); i++) { //EntityRotFX particle = particles.get(i); if (!particle.isAlive()) { particles.remove(particle); } else { //random rotation yaw adjustment if (particle.getEntityId() % 2 == 0) { particle.rotationYaw -= 0.1; } else { particle.rotationYaw += 0.1; } float ticksFadeInMax = 10; float ticksFadeOutMax = 10; //fade in and fade out near age edges if (particle.getAge() < ticksFadeInMax) { //System.out.println("particle.getAge(): " + particle.getAge()); particle.setAlpha(Math.min(1F, particle.getAge() / ticksFadeInMax)); //System.out.println(particle.getAge() / ticksFadeInMax); //particle.setAlphaF(1); } else if (particle.getAge() > particle.getLifetime() - ticksFadeOutMax) { float count = particle.getAge() - (particle.getLifetime() - ticksFadeOutMax); float val = (ticksFadeOutMax - (count)) / ticksFadeOutMax; //System.out.println(val); particle.setAlpha(val); } else { /*if (particle.getAlphaF() > 0) { particle.setAlphaF(particle.getAlphaF() - rateAlpha*1.3F); } else { particle.remove(); }*/ //particle.setAlphaF(1F); } //TEMP //particle.setAlphaF(1F); double moveSpeed = 0.001D; //1.10.2 no /*if (particle.onGround) { moveSpeed = 0.012D; particle.setMotionY(particle.getMotionY() + 0.01D); }*/ //get pos a bit under particle BlockPos pos = CoroUtilBlock.blockPos(particle.getPosX(), particle.getPosY() - particle.aboveGroundHeight, particle.getPosZ()); BlockState state = particle.getWorld().getBlockState(pos); //if particle is near ground, push it up to keep from landing if (!state.isAir()) { if (particle.getMotionY() < particle.bounceSpeedMax) { particle.setMotionY(particle.getMotionY() + particle.bounceSpeed); } //check ahead for better flowing over large cliffs } else { double aheadMultiplier = 20D; BlockPos posAhead = CoroUtilBlock.blockPos((particle.getPosX() + (particle.getMotionX() * aheadMultiplier)), particle.getPosY() - particle.aboveGroundHeight, particle.getPosZ() + (particle.getMotionZ() * aheadMultiplier)); BlockState stateAhead = particle.getWorld().getBlockState(posAhead); if (!stateAhead.isAir()) { if (particle.getMotionY() < particle.bounceSpeedMaxAhead) { particle.setMotionY(particle.getMotionY() + particle.bounceSpeedAhead); } } } //if (particle.isCollidedHorizontally) { /*if (particle.isCollided()) { particle.rotationYaw += 0.1; }*/ /*particle.setMotionX(particle.getMotionX() - Math.sin(Math.toRadians((particle.rotationYaw + particle.getEntityId()) % 360)) * moveSpeed); particle.setMotionZ(particle.getMotionZ() + Math.cos(Math.toRadians((particle.rotationYaw + particle.getEntityId()) % 360)) * moveSpeed);*/ double moveSpeedRand = 0.005D; particle.setMotionX(particle.getMotionX() + (rand.nextDouble() * moveSpeedRand - rand.nextDouble() * moveSpeedRand)); particle.setMotionZ(particle.getMotionZ() + (rand.nextDouble() * moveSpeedRand - rand.nextDouble() * moveSpeedRand)); //TEMPOFF? //particle.setScale(particle.getScale() - 0.1F); if (particle.spawnY != -1) { particle.setPos(particle.getPosX(), particle.spawnY, particle.getPosZ()); //particle.posY = particle.spawnY; } //particle.remove(); } //} } } ================================================ FILE: src/main/java/extendedrenderer/particle/behavior/ParticleBehaviors.java ================================================ package extendedrenderer.particle.behavior; import java.util.ArrayList; import java.util.List; import java.util.Random; import com.corosus.coroutil.util.CoroUtilBlock; import com.corosus.coroutil.util.CoroUtilMisc; import extendedrenderer.particle.entity.EntityRotFX; import extendedrenderer.particle.entity.ParticleTexExtraRender; import extendedrenderer.particle.entity.ParticleTexFX; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.core.BlockPos; import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.Level; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.phys.Vec3; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import weather2.ClientTickHandler; import weather2.ClientWeatherProxy; import weather2.client.SceneEnhancer; import weather2.datatypes.PrecipitationType; import weather2.util.WeatherUtilParticle; @OnlyIn(Dist.CLIENT) public class ParticleBehaviors { public List particles = new ArrayList(); public Vec3 coordSource; public Entity sourceEntity = null; public Random rand = new Random(); //Visual tweaks public float rateDarken = 0.025F; public float rateBrighten = 0.010F; public float rateBrightenSlower = 0.003F; public float rateAlpha = 0.002F; public float rateScale = 0.1F; public int tickSmokifyTrigger = 40; float acidRainRed = 0.5F; float acidRainGreen = 1F; float acidRainBlue = 0.5F; float vanillaRainRed = 0.7F; float vanillaRainGreen = 0.7F; float vanillaRainBlue = 1F; public ParticleBehaviors(Vec3 source) { coordSource = source; } public void tickUpdateList() { //shouldnt be used, particles tick their own method, who removes it though? for (int i = 0; i < particles.size(); i++) { EntityRotFX particle = particles.get(i); if (!particle.isAlive()) { particles.remove(particle); } else { tickUpdate(particle); } } } public void tickUpdate(EntityRotFX particle) { if (sourceEntity != null) { coordSource = sourceEntity.position(); } tickUpdateAct(particle); } //default is smoke effect, override for custom public void tickUpdateAct(EntityRotFX particle) { double centerX = particle.getPosX(); //double centerY = particle.posY; double centerZ = particle.getPosZ(); if (coordSource != null) { centerX = coordSource.x/* + 0.5D*/; //centerY = coordSource.yCoord/* + 0.5D*/; centerZ = coordSource.z/* + 0.5D*/; } double vecX = centerX - particle.getPosX(); double vecZ = centerZ - particle.getPosZ(); double distToCenter = Math.sqrt(vecX * vecX + vecZ * vecZ); double rotYaw = (float)(Math.atan2(vecZ, vecX) * 180.0D / Math.PI); double adjYaw = Math.min(360, 45+particle.getAge()); rotYaw -= adjYaw; //rotYaw -= 90D; //rotYaw += 20D; double speed = 0.1D; if (particle.getAge() < 25 && distToCenter > 0.05D) { particle.setMotionX(Math.cos(rotYaw * 0.017453D) * speed); particle.setMotionZ(Math.sin(rotYaw * 0.017453D) * speed); } else { double speed2 = 0.008D; double pSpeed = Math.sqrt(particle.getMotionX() * particle.getMotionX() + particle.getMotionZ() * particle.getMotionZ()); //cheap air search code if (pSpeed < 0.2 && particle.getMotionY() < 0.01) { speed2 = 0.08D; } if (pSpeed < 0.002 && Math.abs(particle.getMotionY()) < 0.02) { particle.setMotionY(particle.getMotionY() - 0.15D); } particle.setMotionX(particle.getMotionX() + (rand.nextDouble() - rand.nextDouble()) * speed2); particle.setMotionZ(particle.getMotionZ() + (rand.nextDouble() - rand.nextDouble()) * speed2); } float brightnessShiftRate = rateDarken; int stateChangeTick = tickSmokifyTrigger; if (particle.getAge() < stateChangeTick) { particle.setGravity(-0.2F); particle.setColor(particle.rCol - brightnessShiftRate, particle.gCol - brightnessShiftRate, particle.bCol - brightnessShiftRate); } else if (particle.getAge() == stateChangeTick) { particle.setColor(0,0,0); } else { brightnessShiftRate = rateBrighten; particle.setGravity(-0.05F); //particle.motionY *= 0.99F; if (particle.rCol < 0.3F) { } else { brightnessShiftRate = rateBrightenSlower; } particle.setColor(particle.rCol + brightnessShiftRate, particle.gCol + brightnessShiftRate, particle.bCol + brightnessShiftRate); if (particle.getAlphaF() > 0) { particle.setAlpha(particle.getAlphaF() - rateAlpha); } else { particle.remove(); } } if (particle.getScale() < 8F) particle.setScale(particle.getScale() + rateScale); /*if (particle.getAge() % cycle < cycle/2) { particle.setGravity(-0.02F); } else {*/ //} } public void tickUpdateCloud(EntityRotFX particle) { particle.rotationYaw -= 0.1; int ticksFadeInMax = 100; if (particle.getAge() < ticksFadeInMax) { //System.out.println("particle.getAge(): " + particle.getAge()); particle.setAlpha(particle.getAge() * 0.01F); } else { if (particle.getAlphaF() > 0) { particle.setAlpha(particle.getAlphaF() - rateAlpha*1.3F); } else { particle.remove(); } } } public EntityRotFX spawnNewParticleIconFX(Level world, TextureAtlasSprite icon, double x, double y, double z, double vecX, double vecY, double vecZ) { return spawnNewParticleIconFX(world, icon, x, y, z, vecX, vecY, vecZ, 0); } public EntityRotFX spawnNewParticleIconFX(Level world, TextureAtlasSprite icon, double x, double y, double z, double vecX, double vecY, double vecZ, int renderOrder) { EntityRotFX entityfx = new ParticleTexFX((ClientLevel) world, x, y, z, vecX, vecY, vecZ, icon); entityfx.pb = this; entityfx.renderOrder = renderOrder; return entityfx; } public EntityRotFX initParticle(EntityRotFX particle) { particle.setPrevPosX(particle.getPosX()); particle.setPrevPosY(particle.getPosY()); particle.setPrevPosZ(particle.getPosZ()); /*particle.prevPosX = particle.getPosX(); particle.prevPosY = particle.getPosY(); particle.prevPosZ = particle.getPosZ();*/ //keep AABB small, very important to performance particle.setSize(0.01F, 0.01F); return particle; } public void initParticleRain(EntityRotFX particle, int extraRenderCount) { particle.setKillWhenUnderTopmostBlock(true); particle.setCanCollide(false); particle.killWhenUnderCameraAtLeast = 5; particle.setDontRenderUnderTopmostBlock(true); if (particle instanceof ParticleTexExtraRender) { ((ParticleTexExtraRender)particle).setExtraParticlesBaseAmount(extraRenderCount); } particle.fastLight = true; particle.setSlantParticleToWind(true); particle.windWeight = 5F; particle.setFacePlayer(false); particle.setScale(2F * 0.15F); particle.isTransparent = true; particle.setGravity(1.8F); particle.setLifetime(50); particle.setTicksFadeInMax(5); particle.setTicksFadeOutMax(5); particle.setTicksFadeOutMaxOnDeath(3); particle.setFullAlphaTarget(0.6F); particle.setAlpha(0); particle.rotationYaw = CoroUtilMisc.random.nextInt(360) - 180F; particle.setMotionY(-0.5D); ClientTickHandler.getClientWeather().getWindManager().applyWindForceNew(particle, 10F, 0.5F); Player entP = Minecraft.getInstance().player; Biome biome = entP.level().getBiome(new BlockPos(Mth.floor(entP.getX()), (int)Math.floor(entP.getY()), Mth.floor(entP.getZ()))).value(); if (ClientWeatherProxy.get().getPrecipitationType(biome) == PrecipitationType.ACID) { particle.rCol = acidRainRed; particle.gCol = acidRainGreen; particle.bCol = acidRainBlue; } else { particle.setFullAlphaTarget(0.8F); particle.rCol = vanillaRainRed; particle.gCol = vanillaRainGreen; particle.bCol = vanillaRainBlue; } particle.spawnAsWeatherEffect(); } public void initParticleGroundSplash(EntityRotFX particle) { particle.setKillWhenUnderTopmostBlock(true); particle.setCanCollide(false); particle.killWhenUnderCameraAtLeast = 5; boolean upward = rand.nextBoolean(); particle.windWeight = 20F; particle.setFacePlayer(upward); particle.setScale(0.2F + (rand.nextFloat() * 0.05F)); particle.setLifetime(15); particle.setGravity(-0.0F); particle.setTicksFadeInMax(3); particle.setFullAlphaTarget(0.6F); particle.setAlpha(0); particle.setTicksFadeOutMax(4); particle.renderOrder = 2; particle.rotationYaw = CoroUtilMisc.random.nextInt(360) - 180F; particle.rotationPitch = 90; particle.setMotionY(0D); particle.setMotionX((rand.nextFloat() - 0.5F) * 0.01F); particle.setMotionZ((rand.nextFloat() - 0.5F) * 0.01F); //ClientTickHandler.getClientWeather().getWindManager().applyWindForceNew(particle, 1F / 5F, 0.5F); Player entP = Minecraft.getInstance().player; Biome biome = entP.level().getBiome(new BlockPos(Mth.floor(entP.getX()), Mth.floor(entP.getY()), Mth.floor(entP.getZ()))).value(); if (ClientWeatherProxy.get().getPrecipitationType(biome) == PrecipitationType.ACID) { particle.rCol = acidRainRed; particle.gCol = acidRainGreen; particle.bCol = acidRainBlue; } else { particle.rCol = vanillaRainRed; particle.gCol = vanillaRainGreen; particle.bCol = vanillaRainBlue; } } public void initParticleRainDownfall(EntityRotFX particle) { particle.setCanCollide(false); particle.killWhenUnderCameraAtLeast = 15; particle.setKillWhenUnderTopmostBlock(true); particle.setKillWhenUnderTopmostBlock_ScanAheadRange(3); particle.setTicksFadeOutMaxOnDeath(10); particle.setDontRenderUnderTopmostBlock(false); particle.windWeight = 5F; particle.setFacePlayer(false); particle.facePlayerYaw = true; particle.setScale(12F + (rand.nextFloat() * 0.3F)); particle.setSize(10, 50); particle.setLifetime(120); particle.setGravity(0.35F); particle.setTicksFadeInMax(20); particle.setFullAlphaTarget(1F); particle.setAlpha(0); particle.setTicksFadeOutMax(10); particle.rotationYaw = CoroUtilMisc.random.nextInt(360) - 180F; particle.rotationPitch = 0; particle.setMotionY(-0.3D); particle.setMotionX((rand.nextFloat() - 0.5F) * 0.01F); particle.setMotionZ((rand.nextFloat() - 0.5F) * 0.01F); Player entP = Minecraft.getInstance().player; Biome biome = entP.level().getBiome(CoroUtilBlock.blockPos(entP.getX(), entP.getY(), entP.getZ())).get(); if (ClientWeatherProxy.get().getPrecipitationType(biome) == PrecipitationType.ACID) { particle.rCol = acidRainRed; particle.gCol = acidRainGreen; particle.bCol = acidRainBlue; } else { particle.rCol = vanillaRainRed; particle.gCol = vanillaRainGreen; particle.bCol = vanillaRainBlue; } } public void initParticleSnow(EntityRotFX particle, int extraRenderCount, float windSpeed) { float windScale = Math.max(0.1F, 1F - windSpeed); particle.setCanCollide(false); //particle.setKillWhenUnderTopmostBlock(true); particle.setTicksFadeOutMaxOnDeath(5); particle.setDontRenderUnderTopmostBlock(true); particle.setKillWhenUnderTopmostBlock(true); if (particle instanceof ParticleTexExtraRender) { ((ParticleTexExtraRender)particle).setExtraParticlesBaseAmount(extraRenderCount); } particle.killWhenFarFromCameraAtLeast = 25; particle.setMotionX(0); particle.setMotionZ(0); particle.setMotionY(0); particle.setScale(1.3F * 0.15F); particle.setGravity(0.05F); particle.windWeight = 5F; particle.setMaxAge((int) (120F * 12F * windScale)); particle.setFacePlayer(true); particle.setTicksFadeInMax(40 * windScale); particle.setAlphaF(0); particle.setTicksFadeOutMax(40 * windScale); particle.setTicksFadeOutMaxOnDeath(10); //particle.setTicksFadeOutMax(5); particle.rotationYaw = CoroUtilMisc.random.nextInt(360) - 180F; ClientTickHandler.getClientWeather().getWindManager().applyWindForceNew(particle, 1F, 0.5F); } public void initParticleSnowstorm(EntityRotFX particle, int extraRenderCount) { particle.setCanCollide(false); //particle.setKillWhenUnderTopmostBlock(true); particle.setTicksFadeOutMaxOnDeath(5); particle.setDontRenderUnderTopmostBlock(true); if (particle instanceof ParticleTexExtraRender) { ((ParticleTexExtraRender)particle).setExtraParticlesBaseAmount(extraRenderCount); } particle.killWhenFarFromCameraAtLeast = 15; particle.setMotionX(0); particle.setMotionZ(0); particle.setMotionY(0D); particle.setScale(1.3F * 0.15F); particle.setGravity(0.05F); particle.windWeight = 5F; particle.setMaxAge(120); particle.setFacePlayer(false); particle.setTicksFadeInMax(5); particle.setAlphaF(0); particle.setTicksFadeOutMax(20); particle.rotationYaw = CoroUtilMisc.random.nextInt(360) - 180F; ClientTickHandler.getClientWeather().getWindManager().applyWindForceNew(particle, 1F, 0.5F); } public void initParticleHail(EntityRotFX particle) { particle.setKillWhenUnderTopmostBlock(false); particle.setCanCollide(true); particle.setKillOnCollide(true); particle.killWhenUnderCameraAtLeast = 5; particle.setDontRenderUnderTopmostBlock(true); particle.rotationYaw = rand.nextInt(360); particle.rotationPitch = rand.nextInt(360); particle.fastLight = true; particle.setSlantParticleToWind(true); particle.windWeight = 5F; particle.spinFast = true; particle.spinFastRate = 10F; particle.setFacePlayer(false); particle.setScale(0.7F * 0.15F); //particle.setScale(2F * 0.15F); particle.isTransparent = true; particle.setGravity(3.5F); particle.setLifetime(70); particle.setTicksFadeInMax(5); particle.setTicksFadeOutMax(5); particle.setTicksFadeOutMaxOnDeath(50); particle.setFullAlphaTarget(1F); particle.setAlpha(0); particle.rotationYaw = CoroUtilMisc.random.nextInt(360) - 180F; particle.setMotionY(-0.5D); ClientTickHandler.getClientWeather().getWindManager().applyWindForceNew(particle, 1F, 0.5F); particle.rCol = 0.9F; particle.gCol = 0.9F; particle.bCol = 0.9F; particle.bounceOnVerticalImpact = true; particle.bounceOnVerticalImpactEnergy = 0.2F; } public void initParticleCube(EntityRotFX particle) { particle.setKillWhenUnderTopmostBlock(false); particle.setCanCollide(true); particle.setKillOnCollide(true); particle.setKillOnCollideActivateAtAge(30); particle.killWhenUnderCameraAtLeast = 0; particle.setDontRenderUnderTopmostBlock(true); particle.rotationYaw = rand.nextInt(360); particle.rotationPitch = rand.nextInt(360); particle.fastLight = true; particle.windWeight = 5 + ((float)((Math.random() * 0.3) - (Math.random() * 0.3))); particle.spinFast = true; particle.spinFastRate = 1F; particle.setFacePlayer(false); particle.setScale(3F * 0.15F); particle.isTransparent = false; particle.setGravity(4F); particle.setLifetime(20*20); particle.setTicksFadeInMax(5); particle.setTicksFadeOutMax(5); particle.setTicksFadeOutMaxOnDeath(20); particle.setFullAlphaTarget(1F); particle.setAlpha(0); particle.rotationYaw = CoroUtilMisc.random.nextInt(360) - 180F; //particle.setMotionY(-0.5D); //ClientTickHandler.getClientWeather().getWindManager().applyWindForceNew(particle, 1F, 0.5F); /*float tempBrightness = 0.5F; particle.rCol = 0.5F * tempBrightness; particle.gCol = 0.9F * tempBrightness; particle.bCol = 0.5F * tempBrightness;*/ particle.setVanillaMotionDampen(true); particle.bounceOnVerticalImpact = true; particle.bounceOnVerticalImpactEnergy = 0.2F; } public void initParticleDustAir(EntityRotFX particle) { particle.setKillWhenUnderTopmostBlock(false); particle.setCanCollide(false); particle.killWhenUnderCameraAtLeast = 5; particle.setTicksFadeOutMaxOnDeath(5); particle.setDontRenderUnderTopmostBlock(true); if (particle instanceof ParticleTexExtraRender) { ((ParticleTexExtraRender)particle).setExtraParticlesBaseAmount(0); } particle.setMotionX(0); particle.setMotionZ(0); particle.setMotionY(0); particle.fastLight = true; particle.windWeight = 10F; particle.setFacePlayer(true); particle.setScale(0.1F * 0.15F); particle.isTransparent = true; particle.setGravity(0F); particle.setLifetime(80); particle.setTicksFadeInMax(20); particle.setTicksFadeOutMax(20); particle.setTicksFadeOutMaxOnDeath(20); particle.setFullAlphaTarget(0.6F); particle.setAlpha(0); float brightness = 0.5F + (rand.nextFloat() * 0.5F); particle.setColor(particle.rCol * brightness, particle.gCol * brightness, particle.bCol * brightness); particle.rotationYaw = CoroUtilMisc.random.nextInt(360) - 180F; //ClientTickHandler.getClientWeather().getWindManager().applyWindForceNew(particle, 10F, 0.5F); } public void initParticleDustGround(EntityRotFX particle, boolean spawnInside, boolean spawnAboveSnow) { particle.setKillOnCollide(false); particle.setKillWhenUnderTopmostBlock(false); particle.killWhenUnderCameraAtLeast = 5; particle.setDontRenderUnderTopmostBlock(false); particle.setMotionX(0); particle.setMotionZ(0); particle.setMotionY(0); particle.fastLight = true; particle.windWeight = 1F; particle.setFacePlayer(true); particle.setScale(0.15F * 0.15F); particle.isTransparent = true; particle.setGravity(0.06F); particle.setCanCollide(false); particle.setCanCollide(true); particle.collisionSpeedDampen = false; /*if (spawnInside) { particle.setGravity(0.05F); particle.setCanCollide(true); }*/ particle.setLifetime(30); particle.setTicksFadeInMax(5); particle.setTicksFadeOutMax(5); particle.setTicksFadeOutMaxOnDeath(5); particle.setFullAlphaTarget(0.6F); particle.setAlpha(0); if (spawnAboveSnow || !spawnInside) { float brightness = 0.5F; particle.setColor(particle.rCol * brightness, particle.gCol * brightness, particle.bCol * brightness); } particle.rotationYaw = CoroUtilMisc.random.nextInt(360) - 180F; } public void initParticleLeaf(EntityRotFX particle, float particleAABB) { Vec3 windForce = ClientTickHandler.getClientWeather().getWindManager().getWindForce(WeatherUtilParticle.getPos(particle)); particle.setMotionX(windForce.x / 2); particle.setMotionZ(windForce.z / 2); particle.setMotionY(windForce.y / 2); particle.setSize(particleAABB, particleAABB); particle.setGravity(0.05F); particle.setCanCollide(true); particle.setKillOnCollide(false); particle.collisionSpeedDampen = false; particle.killWhenUnderCameraAtLeast = 20; particle.killWhenFarFromCameraAtLeast = 20; particle.isTransparent = false; particle.rotationYaw = rand.nextInt(360); particle.rotationPitch = rand.nextInt(360); } public void initParticleSnowstormCloudDust(EntityRotFX particle) { boolean farSpawn = Minecraft.getInstance().player.isSpectator() || !SceneEnhancer.isPlayerOutside; Vec3 windForce = ClientTickHandler.getClientWeather().getWindManager().getWindForce(null); particle.setMotionX(windForce.x * 0.3); particle.setMotionZ(windForce.z * 0.3); particle.setFacePlayer(false); particle.isTransparent = true; particle.rotationYaw = (float)rand.nextInt(360); particle.rotationPitch = (float)rand.nextInt(360); particle.setLifetime(farSpawn ? 30 : 10); particle.setLifetime(20); particle.setGravity(0.09F); particle.setAlpha(0F); float brightnessMulti = 1F - (rand.nextFloat() * 0.4F); particle.setColor(1F * brightnessMulti, 1F * brightnessMulti, 1F * brightnessMulti); particle.setScale(30 * 0.15F); particle.aboveGroundHeight = 0.2D; particle.setKillOnCollide(true); particle.killWhenFarFromCameraAtLeast = 15; particle.windWeight = 1F; particle.setTicksFadeInMax(5); particle.setTicksFadeOutMax(3); particle.setTicksFadeOutMaxOnDeath(3); ClientTickHandler.getClientWeather().getWindManager().applyWindForceNew(particle, 1F / 5F, 0.5F); } public void initParticleSandstormDust(EntityRotFX particle) { Vec3 windForce = ClientTickHandler.getClientWeather().getWindManager().getWindForce(null); particle.setMotionX(windForce.x); particle.setMotionZ(windForce.z); particle.setFacePlayer(false); particle.isTransparent = true; particle.rotationYaw = (float)rand.nextInt(360); particle.rotationPitch = (float)rand.nextInt(360); particle.setLifetime(40); particle.setGravity(0.09F); particle.setAlpha(0F); float brightnessMulti = 1F - (rand.nextFloat() * 0.5F); particle.setColor(0.65F * brightnessMulti, 0.6F * brightnessMulti, 0.3F * brightnessMulti); particle.setScale(40 * 0.15F); particle.aboveGroundHeight = 0.2D; particle.setKillOnCollide(true); particle.killWhenFarFromCameraAtLeast = 15; particle.setTicksFadeInMax(5); particle.setTicksFadeOutMax(5); particle.setTicksFadeOutMaxOnDeath(5); particle.windWeight = 1F; } public void initParticleSandstormTumbleweed(EntityRotFX particle) { Vec3 windForce = ClientTickHandler.getClientWeather().getWindManager().getWindForce(null); particle.setMotionX(windForce.x); particle.setMotionZ(windForce.z); particle.setFacePlayer(false); particle.facePlayerYaw = false; particle.spinTowardsMotionDirection = true; particle.isTransparent = true; particle.rotationYaw = (float)rand.nextInt(360); particle.rotationPitch = (float)rand.nextInt(360); particle.setLifetime(80); particle.setGravity(0.3F); particle.setAlpha(0F); float brightnessMulti = 1F - (rand.nextFloat() * 0.2F); particle.setColor(1F * brightnessMulti, 1F * brightnessMulti, 1F * brightnessMulti); particle.setScale(8 * 0.15F); particle.aboveGroundHeight = 0.5D; particle.collisionSpeedDampen = false; particle.bounceSpeed = 0.03D; particle.bounceSpeedAhead = 0.03D; particle.setKillOnCollide(false); particle.killWhenFarFromCameraAtLeast = 30; particle.setTicksFadeInMax(5); particle.setTicksFadeOutMax(5); particle.setTicksFadeOutMaxOnDeath(5); particle.windWeight = 1F; } public void initParticleSandstormDebris(EntityRotFX particle) { Vec3 windForce = ClientTickHandler.getClientWeather().getWindManager().getWindForce(null); particle.setMotionX(windForce.x); particle.setMotionZ(windForce.z); particle.setFacePlayer(false); particle.spinFast = true; particle.spinFastRate = 2F; particle.isTransparent = true; particle.rotationYaw = (float)rand.nextInt(360); particle.rotationPitch = (float)rand.nextInt(360); particle.setLifetime(80); particle.setGravity(0.3F); particle.setAlpha(0F); float brightnessMulti = 1F - (rand.nextFloat() * 0.5F); particle.setColor(1F * brightnessMulti, 1F * brightnessMulti, 1F * brightnessMulti); particle.setScale(8 * 0.15F); particle.aboveGroundHeight = 0.5D; particle.collisionSpeedDampen = false; particle.bounceSpeed = 0.03D; particle.bounceSpeedAhead = 0.03D; particle.setKillOnCollide(false); particle.killWhenFarFromCameraAtLeast = 30; particle.setTicksFadeInMax(5); particle.setTicksFadeOutMax(5); particle.setTicksFadeOutMaxOnDeath(5); particle.windWeight = 1F; } public static EntityRotFX setParticleRandoms(EntityRotFX particle, boolean yaw, boolean pitch) { Random rand = new Random(); if (yaw) particle.rotationYaw = rand.nextInt(360); if (pitch) particle.rotationPitch = rand.nextInt(360); return particle; } public static EntityRotFX setParticleFire(EntityRotFX particle) { Random rand = new Random(); particle.setColor(0.6F + (rand.nextFloat() * 0.4F), 0.2F + (rand.nextFloat() * 0.2F), 0); particle.setScale(0.25F + 0.2F * rand.nextFloat()); particle.brightness = 1F; particle.setSize(0.1F, 0.1F); particle.setAlpha(0.6F); return particle; } public static EntityRotFX setParticleCloud(EntityRotFX particle, float freezeY) { particle.spawnY = freezeY; particle.rotationPitch = 90F; //particle.renderDistanceWeight = 999D; //1.10.2 no known replacement for above //particle.noClip = true; particle.setCanCollide(false); particle.setSize(0.25F, 0.25F); particle.setScale(500F); //particle.particleScale = 200F; particle.callUpdateSuper = false; particle.callUpdatePB = false; particle.setLifetime(500); particle.setColor(1F, 1F, 1F); particle.brightness = 0.3F;//- ((200F - particle.spawnY) * 0.05F); particle.renderRange = 999F; particle.setAlpha(0F); return particle; } public void cleanup() { } } ================================================ FILE: src/main/java/extendedrenderer/particle/entity/DustEmitter.java ================================================ package extendedrenderer.particle.entity; import extendedrenderer.particle.ParticleRegistry; import net.minecraft.client.multiplayer.ClientLevel; import weather2.client.SceneEnhancer; public class DustEmitter extends ParticleEmitter { public DustEmitter(ClientLevel par1World, double par2, double par4, double par6, double par8, double par10, double par12) { super(par1World, par2, par4, par6, par8, par10, par12); } @Override public void tick() { super.tick(); ParticleTexExtraRender dust = new ParticleTexExtraRender(level, x, y, z, 0D, 0D, 0D, ParticleRegistry.squareGrey); SceneEnhancer.particleBehavior.initParticleDustAir(dust); dust.spawnAsWeatherEffect(); } } ================================================ FILE: src/main/java/extendedrenderer/particle/entity/EntityRotFX.java ================================================ package extendedrenderer.particle.entity; import com.corosus.coroutil.util.CoroUtilBlock; import com.corosus.coroutil.util.CoroUtilMisc; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.*; import com.mojang.math.Axis; import extendedrenderer.particle.behavior.ParticleBehaviors; import net.minecraft.client.Minecraft; import net.minecraft.client.particle.ParticleRenderType; import net.minecraft.client.particle.TextureSheetParticle; import net.minecraft.client.Camera; import net.minecraft.client.renderer.GameRenderer; import net.minecraft.client.renderer.texture.TextureAtlas; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureManager; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.world.entity.Entity; import net.minecraft.core.BlockPos; import net.minecraft.util.Mth; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; import net.minecraft.world.level.Level; import net.minecraft.world.level.levelgen.Heightmap; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import org.joml.Quaternionf; import org.joml.Vector3f; import weather2.ClientTickHandler; import weather2.IWindHandler; import weather2.config.ConfigParticle; import weather2.weathersystem.WeatherManagerClient; import weather2.weathersystem.wind.WindManager; import java.util.List; import java.util.stream.Stream; @OnlyIn(Dist.CLIENT) public class EntityRotFX extends TextureSheetParticle implements IWindHandler { public static final ParticleRenderType SORTED_TRANSLUCENT = new ParticleRenderType() { @Override public void begin(BufferBuilder p_217600_1_, TextureManager p_217600_2_) { RenderSystem.disableCull(); ParticleRenderType.PARTICLE_SHEET_TRANSLUCENT.begin(p_217600_1_, p_217600_2_); } @Override public void end(Tesselator p_217599_1_) { //TODO: not possible in 1.20 now i guess, cant remember why this line was important //p_217599_1_.getBuilder().setQuadSortOrigin(0, 0, 0); ParticleRenderType.PARTICLE_SHEET_TRANSLUCENT.end(p_217599_1_); } @Override public String toString() { return "PARTICLE_SHEET_SORTED_TRANSLUCENT"; } }; public static final ParticleRenderType SORTED_OPAQUE_BLOCK = new ParticleRenderType() { @Override public void begin(BufferBuilder p_217600_1_, TextureManager p_217600_2_) { RenderSystem.disableBlend(); RenderSystem.depthMask(true); RenderSystem.setShader(GameRenderer::getParticleShader); RenderSystem.setShaderTexture(0, TextureAtlas.LOCATION_BLOCKS); p_217600_1_.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.PARTICLE); } @Override public void end(Tesselator p_217599_1_) { //TODO: not possible in 1.20 now i guess, cant remember why this line was important //p_217599_1_.getBuilder().setQuadSortOrigin(0, 0, 0); ParticleRenderType.PARTICLE_SHEET_OPAQUE.end(p_217599_1_); } @Override public String toString() { return "PARTICLE_BLOCK_SHEET_SORTED_OPAQUE"; } }; public boolean weatherEffect = false; public float spawnY = -1; //this field and 2 methods below are for backwards compatibility with old particle system from the new icon based system public int particleTextureIndexInt = 0; public float brightness = 0.7F; public boolean callUpdateSuper = true; public boolean callUpdatePB = true; public float renderRange = 128F; //used in RotatingEffectRenderer to assist in solving some transparency ordering issues, eg, tornado funnel before clouds public int renderOrder = 0; //not a real entity ID now, just used for making rendering of entities slightly unique private int entityID = 0; public int debugID = 0; public float prevRotationYaw; public float rotationYaw; public float prevRotationPitch; public float rotationPitch; public float windWeight = 5; public boolean isTransparent = true; public boolean killOnCollide = false; public int killOnCollideActivateAtAge = 0; public boolean facePlayer = false; //facePlayer will override this public boolean facePlayerYaw = false; public boolean vanillaMotionDampen = true; //for particle behaviors public double aboveGroundHeight = 4.5D; public boolean checkAheadToBounce = true; public boolean collisionSpeedDampen = true; public double bounceSpeed = 0.05D; public double bounceSpeedMax = 0.15D; public double bounceSpeedAhead = 0.35D; public double bounceSpeedMaxAhead = 0.25D; public boolean bounceOnVerticalImpact = false; public double bounceOnVerticalImpactEnergy = 0.3F; public boolean spinFast = false; public float spinFastRate = 10F; public boolean spinTowardsMotionDirection = false; private float ticksFadeInMax = 0; private float ticksFadeOutMax = 0; private float fullAlphaTarget = 1F; private boolean dontRenderUnderTopmostBlock = false; private boolean killWhenUnderTopmostBlock = false; private int killWhenUnderTopmostBlock_ScanAheadRange = 0; public int killWhenUnderCameraAtLeast = 0; public int killWhenFarFromCameraAtLeast = 0; private float ticksFadeOutMaxOnDeath = -1; private float ticksFadeOutCurOnDeath = 0; protected boolean fadingOut = false; public float avoidTerrainAngle = 0; //this is for yaw only public boolean useRotationAroundCenter = false; public float rotationAroundCenter = 0; public float rotationAroundCenterPrev = 0; public float rotationSpeedAroundCenter = 0; public float rotationDistAroundCenter = 0; private boolean slantParticleToWind = false; /*public Quaternion rotation; public Quaternion rotationPrev;*/ //set to true for direct quaternion control, not EULER conversion helper public boolean quatControl = false; public boolean fastLight = false; public float brightnessCache = 0.5F; public boolean rotateOrderXY = false; public float extraYRotation = 0; public boolean markCollided = false; public boolean isCollidedHorizontally = false; public boolean isCollidedVerticallyDownwards = false; public boolean isCollidedVerticallyUpwards = false; //used for translational rotation around a point public Vector3f rotationAround = new Vector3f(); //workaround for particles that are fading out while partially in the ground, keeps them rendering at previous brightness instead of 0 protected int lastNonZeroBrightness = 15728640; public ParticleBehaviors pb = null; //designed to be a reference to the central objects particle behavior //workaround for avoiding using vanilla bb which causes huge performance issues for large sizes private boolean useCustomBBForRenderCulling = false; private static final AABB INITIAL_AABB = new AABB(0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D); private AABB bbRender = INITIAL_AABB; private float renderDistanceCull = -1; private boolean useDynamicWindSpeed = true; public EntityRotFX(ClientLevel par1World, double par2, double par4, double par6, double par8, double par10, double par12) { super(par1World, par2, par4, par6, par8, par10, par12); setSize(0.3F, 0.3F); //this.isImmuneToFire = true; //this.setMaxAge(100); this.entityID = CoroUtilMisc.random.nextInt(100000); //rotation = new Quaternion(); //TODO: 1.14 uncomment for shaders //brightnessCache = CoroUtilBlockLightCache.getBrightnessCached(world, (float)posX, (float)posY, (float)posZ); } public boolean isSlantParticleToWind() { return slantParticleToWind; } public void setSlantParticleToWind(boolean slantParticleToWind) { this.slantParticleToWind = slantParticleToWind; } public float getTicksFadeOutMaxOnDeath() { return ticksFadeOutMaxOnDeath; } public void setTicksFadeOutMaxOnDeath(float ticksFadeOutMaxOnDeath) { this.ticksFadeOutMaxOnDeath = ticksFadeOutMaxOnDeath; } public boolean isKillWhenUnderTopmostBlock() { return killWhenUnderTopmostBlock; } public void setKillWhenUnderTopmostBlock(boolean killWhenUnderTopmostBlock) { this.killWhenUnderTopmostBlock = killWhenUnderTopmostBlock; } public boolean isDontRenderUnderTopmostBlock() { return dontRenderUnderTopmostBlock; } public void setDontRenderUnderTopmostBlock(boolean dontRenderUnderTopmostBlock) { this.dontRenderUnderTopmostBlock = dontRenderUnderTopmostBlock; } public float getTicksFadeInMax() { return ticksFadeInMax; } public void setTicksFadeInMax(float ticksFadeInMax) { this.ticksFadeInMax = ticksFadeInMax; } public float getTicksFadeOutMax() { return ticksFadeOutMax; } public void setTicksFadeOutMax(float ticksFadeOutMax) { this.ticksFadeOutMax = ticksFadeOutMax; } public int getParticleTextureIndex() { return this.particleTextureIndexInt; } public void setLifetime(int par) { lifetime = par; } public float getAlphaF() { return this.alpha; } @Override public void remove() { if (pb != null) pb.particles.remove(this); super.remove(); } @Override public void tick() { super.tick(); this.prevRotationPitch = this.rotationPitch; if (!(this instanceof PivotingParticle)) { this.prevRotationYaw = this.rotationYaw; } Entity ent = Minecraft.getInstance().getCameraEntity(); //if (this.entityID % 400 == 0) System.out.println("tick time: " + this.worldObj.getGameTime()); if (!isVanillaMotionDampen()) { //cancel motion dampening (which is basically air resistance) //keep this up to date with the inverse of whatever Particle.tick uses this.xd /= 0.9800000190734863D; this.yd /= 0.9800000190734863D; this.zd /= 0.9800000190734863D; } if (!this.removed && !fadingOut) { if (killOnCollide && (killOnCollideActivateAtAge == 0 || age >= killOnCollideActivateAtAge)) { if (this.isCollided()) { startDeath(); } } BlockPos pos = CoroUtilBlock.blockPos(this.x, this.y, this.z); if (killWhenUnderTopmostBlock) { //int height = this.world.getPrecipitationHeight(new BlockPos(this.posX, this.posY, this.posZ)).getY(); int height = level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, pos).getY(); if (this.y - killWhenUnderTopmostBlock_ScanAheadRange <= height) { startDeath(); } } //case: when on high pillar and rain is falling far below you, start killing it / fading it out if (killWhenUnderCameraAtLeast != 0) { if (this.y < ent.getY() - killWhenUnderCameraAtLeast) { startDeath(); } } if (killWhenFarFromCameraAtLeast != 0) { if (getAge() > 20 && getAge() % 5 == 0) { if (ent.distanceToSqr(this.x, this.y, this.z) > killWhenFarFromCameraAtLeast * killWhenFarFromCameraAtLeast) { //System.out.println("far kill"); startDeath(); } } } } if (!collisionSpeedDampen) { //if (this.isCollided()) { if (this.onGround) { this.xd /= 0.699999988079071D; this.zd /= 0.699999988079071D; } } double speedXZ = Math.sqrt(getMotionX() * getMotionX() + /*getMotionY() * getMotionY() + */getMotionZ() * getMotionZ()); double spinFastRateAdj = spinFastRate * speedXZ * 10F; //spinFastRateAdj = 0; if (spinFast) { this.rotationPitch += this.entityID % 2 == 0 ? spinFastRateAdj : -spinFastRateAdj; this.rotationYaw += this.entityID % 2 == 0 ? -spinFastRateAdj : spinFastRateAdj; } float angleToMovement = (float) (Math.toDegrees(Math.atan2(xd, zd))); if (spinTowardsMotionDirection) { this.rotationYaw = angleToMovement; this.rotationPitch += spinFastRate; } if (!fadingOut) { if (ticksFadeInMax > 0 && this.getAge() < ticksFadeInMax) { //System.out.println("this.getAge() / ticksFadeInMax: " + this.getAge() / ticksFadeInMax); this.setAlpha((float)this.getAge() / ticksFadeInMax * getFullAlphaTarget()); //this.setAlphaF(0.15F); } else if (ticksFadeOutMax > 0 && this.getAge() > this.getLifetime() - ticksFadeOutMax) { float count = this.getAge() - (this.getLifetime() - ticksFadeOutMax); float val = (ticksFadeOutMax - (count)) / ticksFadeOutMax; //System.out.println(val); this.setAlpha(val * getFullAlphaTarget()); //make sure fully visible otherwise } else if (ticksFadeInMax > 0 || ticksFadeOutMax > 0) { this.setAlpha(getFullAlphaTarget()); } } else { if (ticksFadeOutCurOnDeath < ticksFadeOutMaxOnDeath) { ticksFadeOutCurOnDeath++; } else { this.remove(); } float val = 1F - (ticksFadeOutCurOnDeath / ticksFadeOutMaxOnDeath); //System.out.println(val); this.setAlpha(val * getFullAlphaTarget()); } if (level.getGameTime() % 5 == 0) { //TODO: 1.14 uncomment //brightnessCache = CoroUtilBlockLightCache.getBrightnessCached(world, (float)posX, (float)posY, (float)posZ); } rotationAroundCenter += rotationSpeedAroundCenter; rotationAroundCenter %= 360; /*while (rotationAroundCenter >= 360) { System.out.println(rotationAroundCenter); rotationAroundCenter -= 360; }*/ tickExtraRotations(); } public void tickExtraRotations() { if (slantParticleToWind) { double motionXZ = Math.sqrt(xd * xd + zd * zd); rotationPitch = (float)Math.atan2(yd, motionXZ); } WeatherManagerClient weatherMan = ClientTickHandler.weatherManager; if (weatherMan == null) return; WindManager windMan = weatherMan.getWindManager(); if (windMan == null) return; if (this instanceof PivotingParticle) return; //particles on ground shouldnt get blown as hard (idea for hail) if (onGround) { windMan.applyWindForceNew(this, (1F / 20F) * 0.3F, 0.5F, useDynamicWindSpeed); } else { windMan.applyWindForceNew(this, 1F / 20F, 0.5F, useDynamicWindSpeed); } /*if (!quatControl) { rotationPrev = new Quaternion(rotation); Entity ent = Minecraft.getInstance().getRenderViewEntity(); updateQuaternion(ent); }*/ } public void startDeath() { if (ticksFadeOutMaxOnDeath > 0) { ticksFadeOutCurOnDeath = 0;//ticksFadeOutMaxOnDeath; fadingOut = true; } else { this.remove(); } } /*public void setParticleTextureIndex(int par1) { this.particleTextureIndexInt = par1; if (this.getFXLayer() == 0) super.setParticleTextureIndex(par1); }*/ /*@Override public int getFXLayer() { return 5; }*/ public void spawnAsWeatherEffect() { weatherEffect = true; if (ConfigParticle.Particle_engine_weather2) { ClientTickHandler.particleManagerExtended().add(this); } else { Minecraft.getInstance().particleEngine.add(this); } } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public int getLifetime() { return lifetime; } public void setSize(float par1, float par2) { super.setSize(par1, par2); // MC-12269 - fix particle being offset to the NW this.setPos(x, y, z); } public void setGravity(float par) { gravity = par; } public float maxRenderRange() { return renderRange; } public void setScale(float parScale) { //dont set the AABB as big as the render scale, otherwise huge performance losses, we'll just use 0.3 in constructor for now //super.setSize(parScale, parScale); this.setSizeForRenderCulling(parScale, parScale); quadSize = parScale; } public Vector3f getPosition() { return new Vector3f((float)x, (float)y, (float)z); } /*@Override public Quaternion getQuaternion() { return this.rotation; } @Override public Quaternion getQuaternionPrev() { return this.rotationPrev; }*/ public float getScale() { return quadSize; } public Vec3 getPos() { return new Vec3(x, y, z); } public double getPosX() { return x; } public void setPosX(double posX) { this.x = posX; } public double getPosY() { return y; } public void setPosY(double posY) { this.y = posY; } public double getPosZ() { return z; } public void setPosZ(double posZ) { this.z = posZ; } public double getMotionX() { return xd; } public void setMotionX(double motionX) { this.xd = motionX; } public double getMotionY() { return yd; } public void setMotionY(double motionY) { this.yd = motionY; } public double getMotionZ() { return zd; } public void setMotionZ(double motionZ) { this.zd = motionZ; } public double getPrevPosX() { return xo; } public void setPrevPosX(double prevPosX) { this.xo = prevPosX; } public double getPrevPosY() { return yo; } public void setPrevPosY(double prevPosY) { this.yo = prevPosY; } public double getPrevPosZ() { return zo; } public void setPrevPosZ(double prevPosZ) { this.zo = prevPosZ; } public int getEntityId() { return entityID; } public Level getWorld() { return this.level; } public void setCanCollide(boolean val) { this.hasPhysics = val; } public boolean getCanCollide() { return this.hasPhysics; } public boolean isCollided() { return this.onGround || isCollidedHorizontally; } public double getDistance(double x, double y, double z) { double d0 = this.x - x; double d1 = this.y - y; double d2 = this.z - z; return Mth.sqrt((float) (d0 * d0 + d1 * d1 + d2 * d2)); } @Override public float getQuadSize(float scaleFactor) { return this.quadSize; } @Override public void render(VertexConsumer buffer, Camera renderInfo, float partialTicks) { Vec3 Vector3d = renderInfo.getPosition(); Vec3 pivotedPosition = getPivotedPosition(partialTicks); float f = (float)(Mth.lerp(partialTicks, this.xo, this.x) + pivotedPosition.x - Vector3d.x()); float f1 = (float)(Mth.lerp(partialTicks, this.yo, this.y) + pivotedPosition.y - Vector3d.y()); float f2 = (float)(Mth.lerp(partialTicks, this.zo, this.z) + pivotedPosition.z - Vector3d.z()); Quaternionf quaternion; if (this.facePlayer || (this.rotationPitch == 0 && this.rotationYaw == 0)) { quaternion = renderInfo.rotation(); } else { // override rotations quaternion = new Quaternionf(0, 0, 0, 1); if (facePlayerYaw) { quaternion.mul(Axis.YP.rotationDegrees(-renderInfo.getYRot())); } else { quaternion.mul(Axis.YP.rotationDegrees(Mth.lerp(partialTicks, this.prevRotationYaw, rotationYaw))); } quaternion.mul(Axis.XP.rotationDegrees(Mth.lerp(partialTicks, this.prevRotationPitch, rotationPitch))); } Quaternionf quaternionf; if (this.roll == 0.0F) { quaternionf = renderInfo.rotation(); } else { quaternionf = new Quaternionf(renderInfo.rotation()); quaternionf.rotateZ(Mth.lerp(partialTicks, this.oRoll, this.roll)); } Vector3f[] avector3f = new Vector3f[]{new Vector3f(-1.0F, -1.0F, 0.0F), new Vector3f(-1.0F, 1.0F, 0.0F), new Vector3f(1.0F, 1.0F, 0.0F), new Vector3f(1.0F, -1.0F, 0.0F)}; float f4 = this.getQuadSize(partialTicks); for(int i = 0; i < 4; ++i) { Vector3f vector3f = avector3f[i]; vector3f.rotate(quaternion); vector3f.mul(f4); vector3f.add(f, f1, f2); } float f7 = this.getU0(); float f8 = this.getU1(); float f5 = this.getV0(); float f6 = this.getV1(); int j = this.getLightColor(partialTicks); //int j = 15728800; if (j > 0) { lastNonZeroBrightness = j; } else { j = lastNonZeroBrightness; } buffer.vertex(avector3f[0].x(), avector3f[0].y(), avector3f[0].z()).uv(f8, f6).color(this.rCol, this.gCol, this.bCol, this.alpha).uv2(j).endVertex(); buffer.vertex(avector3f[1].x(), avector3f[1].y(), avector3f[1].z()).uv(f8, f5).color(this.rCol, this.gCol, this.bCol, this.alpha).uv2(j).endVertex(); buffer.vertex(avector3f[2].x(), avector3f[2].y(), avector3f[2].z()).uv(f7, f5).color(this.rCol, this.gCol, this.bCol, this.alpha).uv2(j).endVertex(); buffer.vertex(avector3f[3].x(), avector3f[3].y(), avector3f[3].z()).uv(f7, f6).color(this.rCol, this.gCol, this.bCol, this.alpha).uv2(j).endVertex(); } //TODO: 1.14 uncomment /*public void renderParticleForShader(InstancedMeshParticle mesh, Transformation transformation, Matrix4fe viewMatrix, Entity entityIn, float partialTicks, float rotationX, float rotationZ, float rotationYZ, float rotationXY, float rotationXZ) { if (mesh.curBufferPos >= mesh.numInstances) return; //camera relative positions, for world position, remove the interpPos values float posX = (float) (this.prevPosX + (this.posX - this.prevPosX) * (double) partialTicks - this.interpPosX); float posY = (float) (this.prevPosY + (this.posY - this.prevPosY) * (double) partialTicks - this.interpPosY); float posZ = (float) (this.prevPosZ + (this.posZ - this.prevPosZ) * (double) partialTicks - this.interpPosZ); //Vector3f pos = new Vector3f((float) (entityIn.posX - particle.posX), (float) (entityIn.posY - particle.posY), (float) (entityIn.posZ - particle.posZ)); Vector3f pos = new Vector3f(posX, posY, posZ); Matrix4fe modelMatrix = transformation.buildModelMatrix(this, pos, partialTicks); //adjust to perspective and camera //Matrix4fe modelViewMatrix = transformation.buildModelViewMatrix(modelMatrix, viewMatrix); //upload to buffer modelMatrix.get(mesh.INSTANCE_SIZE_FLOATS * (mesh.curBufferPos), mesh.instanceDataBuffer); //brightness float brightness; //brightness = CoroUtilBlockLightCache.getBrightnessCached(world, (float)this.posX, (float)this.posY, (float)this.posZ); brightness = brightnessCache; //brightness = -1F; //brightness = CoroUtilBlockLightCache.brightnessPlayer; mesh.instanceDataBuffer.put(mesh.INSTANCE_SIZE_FLOATS * (mesh.curBufferPos) + mesh.MATRIX_SIZE_FLOATS, brightness); int rgbaIndex = 0; mesh.instanceDataBuffer.put(mesh.INSTANCE_SIZE_FLOATS * (mesh.curBufferPos) + mesh.MATRIX_SIZE_FLOATS + 1 + (rgbaIndex++), this.particleRed); mesh.instanceDataBuffer.put(mesh.INSTANCE_SIZE_FLOATS * (mesh.curBufferPos) + mesh.MATRIX_SIZE_FLOATS + 1 + (rgbaIndex++), this.particleGreen); mesh.instanceDataBuffer.put(mesh.INSTANCE_SIZE_FLOATS * (mesh.curBufferPos) + mesh.MATRIX_SIZE_FLOATS + 1 + (rgbaIndex++), this.particleBlue); mesh.instanceDataBuffer.put(mesh.INSTANCE_SIZE_FLOATS * (mesh.curBufferPos) + mesh.MATRIX_SIZE_FLOATS + 1 + (rgbaIndex++), this.getAlphaF()); mesh.curBufferPos++; }*/ /*public void renderParticleForShaderTest(InstancedMeshParticle mesh, Transformation transformation, Matrix4fe viewMatrix, Entity entityIn, float partialTicks, float rotationX, float rotationZ, float rotationYZ, float rotationXY, float rotationXZ) { if (mesh.curBufferPos >= mesh.numInstances) return; int rgbaIndex = 0; mesh.instanceDataBufferTest.put(mesh.INSTANCE_SIZE_FLOATS_TEST * (mesh.curBufferPos) + (rgbaIndex++), this.getRedColorF()); mesh.instanceDataBufferTest.put(mesh.INSTANCE_SIZE_FLOATS_TEST * (mesh.curBufferPos) + (rgbaIndex++), this.getGreenColorF()); mesh.instanceDataBufferTest.put(mesh.INSTANCE_SIZE_FLOATS_TEST * (mesh.curBufferPos) + (rgbaIndex++), this.getBlueColorF()); mesh.instanceDataBufferTest.put(mesh.INSTANCE_SIZE_FLOATS_TEST * (mesh.curBufferPos) + (rgbaIndex++), this.getAlphaF()); mesh.curBufferPos++; }*/ public void setKillOnCollide(boolean val) { this.killOnCollide = val; } //override for extra isCollided types @Override public void move(double x, double y, double z) { double xx = x; double yy = y; double zz = z; if (this.hasPhysics && (x != 0.0D || y != 0.0D || z != 0.0D)) { Vec3 Vector3d = Entity.collideBoundingBox(null, new Vec3(x, y, z), this.getBoundingBox(), this.level, List.of()); x = Vector3d.x; y = Vector3d.y; z = Vector3d.z; } if (x != 0.0D || y != 0.0D || z != 0.0D) { this.setBoundingBox(this.getBoundingBox().move(x, y, z)); if (isUseCustomBBForRenderCulling()) { this.setBoundingBoxForRender(this.getBoundingBoxForRender(1F).move(x, y, z)); } /*Vec3 pivotedPosition = getPivotedPosition(0); if (pivotedPosition != Vec3.ZERO) { this.setBoundingBox(this.getBoundingBox().move(x + pivotedPosition.x, y + pivotedPosition.y, z + pivotedPosition.z)); } else { this.setBoundingBox(this.getBoundingBox().move(x, y, z)); }*/ this.setLocationFromBoundingbox(); } this.onGround = yy != y && yy < 0.0D; this.isCollidedHorizontally = xx != x || zz != z; this.isCollidedVerticallyDownwards = yy < y; this.isCollidedVerticallyUpwards = yy > y; if (xx != x) { this.xd = 0.0D; } if (zz != z) { this.zd = 0.0D; } if (!markCollided) { if (onGround || isCollidedVerticallyDownwards || isCollidedHorizontally || isCollidedVerticallyUpwards) { onHit(); markCollided = true; } if (bounceOnVerticalImpact && (onGround || isCollidedVerticallyDownwards)) { setMotionY(-getMotionY() * bounceOnVerticalImpactEnergy); } } } public void setFacePlayer(boolean val) { this.facePlayer = val; } public TextureAtlasSprite getParticleTexture() { return this.sprite; } public boolean isVanillaMotionDampen() { return vanillaMotionDampen; } public void setVanillaMotionDampen(boolean motionDampen) { this.vanillaMotionDampen = motionDampen; } @Override public int getLightColor(float p_189214_1_) { return super.getLightColor(p_189214_1_);//(int)((float)super.getBrightnessForRender(p_189214_1_))/* * this.world.getSunBrightness(1F))*/; } /*public void updateQuaternion(Entity camera) { if (camera != null) { if (this.facePlayer) { this.rotationYaw = camera.rotationYaw; this.rotationPitch = camera.rotationPitch; } else if (facePlayerYaw) { this.rotationYaw = camera.rotationYaw; } } Quaternion qY = new Quaternion(); Quaternion qX = new Quaternion(); qY.setFromAxisAngle(new Vector4f(0, 1, 0, (float)Math.toRadians(-this.rotationYaw - 180F))); qX.setFromAxisAngle(new Vector4f(1, 0, 0, (float)Math.toRadians(-this.rotationPitch))); if (this.rotateOrderXY) { Quaternion.mul(qX, qY, this.rotation); } else { Quaternion.mul(qY, qX, this.rotation); } }*/ @Override public void setColor(float particleRedIn, float particleGreenIn, float particleBlueIn) { super.setColor(particleRedIn, particleGreenIn, particleBlueIn); //TODO: 1.14 uncomment /*RotatingParticleManager.markDirtyVBO2();*/ } @Override public void setAlpha(float alpha) { super.setAlpha(alpha); //TODO: 1.14 uncomment /*RotatingParticleManager.markDirtyVBO2();*/ } public int getKillWhenUnderTopmostBlock_ScanAheadRange() { return killWhenUnderTopmostBlock_ScanAheadRange; } public void setKillWhenUnderTopmostBlock_ScanAheadRange(int killWhenUnderTopmostBlock_ScanAheadRange) { this.killWhenUnderTopmostBlock_ScanAheadRange = killWhenUnderTopmostBlock_ScanAheadRange; } public boolean isCollidedVertically() { return isCollidedVerticallyDownwards || isCollidedVerticallyUpwards; } @Override public ParticleRenderType getRenderType() { return SORTED_TRANSLUCENT; } @Override public void setSprite(TextureAtlasSprite sprite) { super.setSprite(sprite); } public TextureAtlasSprite getSprite() { return sprite; } public float getFullAlphaTarget() { return fullAlphaTarget; } public void setFullAlphaTarget(float fullAlphaTarget) { this.fullAlphaTarget = fullAlphaTarget; } public int getLastNonZeroBrightness() { return lastNonZeroBrightness; } public void setLastNonZeroBrightness(int lastNonZeroBrightness) { this.lastNonZeroBrightness = lastNonZeroBrightness; } public void onHit() { } public void setMaxAge(int par) { setLifetime(par); } public int getMaxAge() { return getLifetime(); } public void setAlphaF(float val) { setAlpha(val); } public void setPosition(double posX, double posY, double posZ) { this.setPos(posX, posY, posZ); } public Vec3 getPivotedPosition(float partialTicks) { return Vec3.ZERO; } public void setBoundingBoxForRender(AABB p_107260_) { this.bbRender = p_107260_; } public AABB getBoundingBoxForRender(float partialTicks) { if (isUseCustomBBForRenderCulling()) { return bbRender; } else { return this.getBoundingBox(); } } public void setSizeForRenderCulling(float p_107251_, float p_107252_) { if (p_107251_ != this.bbWidth || p_107252_ != this.bbHeight) { this.bbWidth = p_107251_; this.bbHeight = p_107252_; AABB aabb = this.getBoundingBox(); double d0 = (aabb.minX + aabb.maxX - (double)p_107251_) / 2.0D; double d1 = (aabb.minZ + aabb.maxZ - (double)p_107251_) / 2.0D; this.setBoundingBoxForRender(new AABB(d0, aabb.minY, d1, d0 + (double)this.bbWidth, aabb.minY + (double)this.bbHeight, d1 + (double)this.bbWidth)); } } public boolean isUseCustomBBForRenderCulling() { return useCustomBBForRenderCulling; } public void setUseCustomBBForRenderCulling(boolean useCustomBBForRenderCulling) { this.useCustomBBForRenderCulling = useCustomBBForRenderCulling; } @Override public float getWindWeight() { return windWeight; } @Override public int getParticleDecayExtra() { return 0; } public int getKillOnCollideActivateAtAge() { return killOnCollideActivateAtAge; } public void setKillOnCollideActivateAtAge(int killOnCollideActivateAtAge) { this.killOnCollideActivateAtAge = killOnCollideActivateAtAge; } public float getRenderDistanceCull() { return renderDistanceCull; } public void setRenderDistanceCull(float renderDistanceCull) { this.renderDistanceCull = renderDistanceCull; } public boolean isUseDynamicWindSpeed() { return useDynamicWindSpeed; } public void setUseDynamicWindSpeed(boolean useDynamicWindSpeed) { this.useDynamicWindSpeed = useDynamicWindSpeed; } } ================================================ FILE: src/main/java/extendedrenderer/particle/entity/ParticleCrossSection.java ================================================ package extendedrenderer.particle.entity; import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.math.Axis; import net.minecraft.client.Camera; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.util.Mth; import net.minecraft.world.phys.Vec3; import net.minecraft.world.level.Level; import org.joml.Quaternionf; import org.joml.Vector3f; public class ParticleCrossSection extends ParticleTexFX { public ParticleCrossSection(Level worldIn, double posXIn, double posYIn, double posZIn, double mX, double mY, double mZ, TextureAtlasSprite par8Item) { super((ClientLevel) worldIn, posXIn, posYIn, posZIn, mX, mY, mZ, par8Item); } @Override public void render(VertexConsumer buffer, Camera renderInfo, float partialTicks) { Vec3 Vector3d = renderInfo.getPosition(); float f = (float)(Mth.lerp(partialTicks, this.xo, this.x) - Vector3d.x()); float f1 = (float)(Mth.lerp(partialTicks, this.yo, this.y) - Vector3d.y()); float f2 = (float)(Mth.lerp(partialTicks, this.zo, this.z) - Vector3d.z()); Quaternionf quaternion; if (this.facePlayer || (this.rotationPitch == 0 && this.rotationYaw == 0)) { quaternion = renderInfo.rotation(); } else { // override rotations quaternion = new Quaternionf(0, 0, 0, 1); if (facePlayerYaw) { quaternion.mul(Axis.YP.rotationDegrees(-renderInfo.getYRot())); } else { quaternion.mul(Axis.YP.rotationDegrees(Mth.lerp(partialTicks, this.prevRotationYaw, rotationYaw))); } quaternion.mul(Axis.XP.rotationDegrees(Mth.lerp(partialTicks, this.prevRotationPitch, rotationPitch))); } Vector3f[] avector3f = new Vector3f[]{ new Vector3f(-1.0F, -1.0F, 0.0F), new Vector3f(-1.0F, 1.0F, 0.0F), new Vector3f(1.0F, 1.0F, 0.0F), new Vector3f(1.0F, -1.0F, 0.0F)}; Vector3f[] avector3f2 = new Vector3f[]{ new Vector3f(0.0F, -1.0F, -1.0F), new Vector3f(0.0F, 1.0F, -1.0F), new Vector3f(0.0F, 1.0F, 1.0F), new Vector3f(0.0F, -1.0F, 1.0F)}; Vector3f[] avector3f3 = new Vector3f[]{ new Vector3f(-1.0F, 0.0F, -1.0F), new Vector3f(-1.0F, 0.0F, 1.0F), new Vector3f(1.0F, 0.0F, 1.0F), new Vector3f(1.0F, 0.0F, -1.0F)}; float f4 = this.getQuadSize(partialTicks); for(int i = 0; i < 4; ++i) { Vector3f vector3f = avector3f[i]; vector3f.rotate(quaternion); vector3f.mul(f4); vector3f.add(f, f1, f2); } for(int i = 0; i < 4; ++i) { Vector3f vector3f = avector3f2[i]; vector3f.rotate(quaternion); vector3f.mul(f4); vector3f.add(f, f1, f2); } for(int i = 0; i < 4; ++i) { Vector3f vector3f = avector3f3[i]; vector3f.rotate(quaternion); vector3f.mul(f4); vector3f.add(f, f1, f2); } float f7 = this.getU0(); float f8 = this.getU1(); float f5 = this.getV0(); float f6 = this.getV1(); int j = this.getLightColor(partialTicks); if (j > 0) { lastNonZeroBrightness = j; } else { j = lastNonZeroBrightness; } buffer.vertex(avector3f[0].x(), avector3f[0].y(), avector3f[0].z()).uv(f8, f6).color(this.rCol, this.gCol, this.bCol, this.alpha).uv2(j).endVertex(); buffer.vertex(avector3f[1].x(), avector3f[1].y(), avector3f[1].z()).uv(f8, f5).color(this.rCol, this.gCol, this.bCol, this.alpha).uv2(j).endVertex(); buffer.vertex(avector3f[2].x(), avector3f[2].y(), avector3f[2].z()).uv(f7, f5).color(this.rCol, this.gCol, this.bCol, this.alpha).uv2(j).endVertex(); buffer.vertex(avector3f[3].x(), avector3f[3].y(), avector3f[3].z()).uv(f7, f6).color(this.rCol, this.gCol, this.bCol, this.alpha).uv2(j).endVertex(); buffer.vertex(avector3f2[0].x(), avector3f2[0].y(), avector3f2[0].z()).uv(f8, f6).color(this.rCol, this.gCol, this.bCol, this.alpha).uv2(j).endVertex(); buffer.vertex(avector3f2[1].x(), avector3f2[1].y(), avector3f2[1].z()).uv(f8, f5).color(this.rCol, this.gCol, this.bCol, this.alpha).uv2(j).endVertex(); buffer.vertex(avector3f2[2].x(), avector3f2[2].y(), avector3f2[2].z()).uv(f7, f5).color(this.rCol, this.gCol, this.bCol, this.alpha).uv2(j).endVertex(); buffer.vertex(avector3f2[3].x(), avector3f2[3].y(), avector3f2[3].z()).uv(f7, f6).color(this.rCol, this.gCol, this.bCol, this.alpha).uv2(j).endVertex(); buffer.vertex(avector3f3[0].x(), avector3f3[0].y(), avector3f3[0].z()).uv(f8, f6).color(this.rCol, this.gCol, this.bCol, this.alpha).uv2(j).endVertex(); buffer.vertex(avector3f3[1].x(), avector3f3[1].y(), avector3f3[1].z()).uv(f8, f5).color(this.rCol, this.gCol, this.bCol, this.alpha).uv2(j).endVertex(); buffer.vertex(avector3f3[2].x(), avector3f3[2].y(), avector3f3[2].z()).uv(f7, f5).color(this.rCol, this.gCol, this.bCol, this.alpha).uv2(j).endVertex(); buffer.vertex(avector3f3[3].x(), avector3f3[3].y(), avector3f3[3].z()).uv(f7, f6).color(this.rCol, this.gCol, this.bCol, this.alpha).uv2(j).endVertex(); } } ================================================ FILE: src/main/java/extendedrenderer/particle/entity/ParticleCube.java ================================================ package extendedrenderer.particle.entity; import com.corosus.coroutil.util.CULog; import com.corosus.coroutil.util.CoroUtilBlock; import com.corosus.coroutil.util.CoroUtilMisc; import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.math.Axis; import extendedrenderer.particle.ParticleRegistry; import net.minecraft.client.Camera; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.particle.ParticleRenderType; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.block.BlockRenderDispatcher; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec3; import org.joml.Quaternionf; import org.joml.Vector3f; import java.util.ArrayList; import java.util.List; import java.util.Random; public class ParticleCube extends ParticleTexFX { public ParticleCube(Level worldIn, double posXIn, double posYIn, double posZIn, double mX, double mY, double mZ, BlockState state) { super((ClientLevel) worldIn, posXIn, posYIn, posZIn, mX, mY, mZ, ParticleRegistry.potato); /** * really basic way to get a sprite from a blockstate, could easily get the wrong one if multiple quads are used per direction * should do fine for most blocks that have the same texture on every side */ TextureAtlasSprite sprite = getSpriteFromState(state); if (sprite != null) { setSprite(sprite); } else { CULog.dbg("unable to find sprite to use from block: " + state); sprite = getSpriteFromState(Blocks.DIRT.defaultBlockState()); //if (CoroUtilMisc.random().nextBoolean()) sprite = getSpriteFromState(Blocks.GRASS.defaultBlockState()); if (sprite != null) { setSprite(sprite); } } int multiplier = Minecraft.getInstance().getBlockColors().getColor(state, this.level, CoroUtilBlock.blockPos(posXIn, posYIn, posZIn), 0); float mr = ((multiplier >>> 16) & 0xFF) / 255f; float mg = ((multiplier >>> 8) & 0xFF) / 255f; float mb = (multiplier & 0xFF) / 255f; setColor(mr, mg, mb); } public TextureAtlasSprite getSpriteFromState(BlockState state) { BlockRenderDispatcher blockrenderdispatcher = Minecraft.getInstance().getBlockRenderer(); BakedModel model = blockrenderdispatcher.getBlockModel(state); for(Direction direction : Direction.values()) { List list = model.getQuads(state, direction, RandomSource.create()); if (list.size() > 0) { return list.get(0).getSprite(); } //plan b if (model.getParticleIcon() != null) { return model.getParticleIcon(); } } return null; } @Override public void render(VertexConsumer buffer, Camera renderInfo, float partialTicks) { //if (true) return; Vec3 Vector3d = renderInfo.getPosition(); float f = (float)(Mth.lerp(partialTicks, this.xo, this.x) - Vector3d.x()); float f1 = (float)(Mth.lerp(partialTicks, this.yo, this.y) - Vector3d.y()); float f2 = (float)(Mth.lerp(partialTicks, this.zo, this.z) - Vector3d.z()); Quaternionf quaternion; if (this.facePlayer || (this.rotationPitch == 0 && this.rotationYaw == 0)) { quaternion = renderInfo.rotation(); } else { // override rotations quaternion = new Quaternionf(0, 0, 0, 1); if (facePlayerYaw) { quaternion.mul(Axis.YP.rotationDegrees(-renderInfo.getYRot())); } else { quaternion.mul(Axis.YP.rotationDegrees(Mth.lerp(partialTicks, this.prevRotationYaw, rotationYaw))); } quaternion.mul(Axis.XP.rotationDegrees(Mth.lerp(partialTicks, this.prevRotationPitch, rotationPitch))); } TextureAtlasSprite sprite = null; List faces = new ArrayList<>(); Vector3f[] face; //xy -z face = new Vector3f[]{ new Vector3f(-1.0F, -1.0F, -1.0F), new Vector3f(-1.0F, 1.0F, -1.0F), new Vector3f(1.0F, 1.0F, -1.0F), new Vector3f(1.0F, -1.0F, -1.0F)}; faces.add(face); //xy +z face = new Vector3f[]{ new Vector3f(-1.0F, -1.0F, 1.0F), new Vector3f(-1.0F, 1.0F, 1.0F), new Vector3f(1.0F, 1.0F, 1.0F), new Vector3f(1.0F, -1.0F, 1.0F)}; faces.add(face); //yz -x face = new Vector3f[]{ new Vector3f(-1.0F, -1.0F, -1.0F), new Vector3f(-1.0F, 1.0F, -1.0F), new Vector3f(-1.0F, 1.0F, 1.0F), new Vector3f(-1.0F, -1.0F, 1.0F)}; faces.add(face); //yz +x face = new Vector3f[]{ new Vector3f(1.0F, -1.0F, -1.0F), new Vector3f(1.0F, 1.0F, -1.0F), new Vector3f(1.0F, 1.0F, 1.0F), new Vector3f(1.0F, -1.0F, 1.0F)}; faces.add(face); //xz -y face = new Vector3f[]{ new Vector3f(-1.0F, -1.0F, -1.0F), new Vector3f(-1.0F, -1.0F, 1.0F), new Vector3f(1.0F, -1.0F, 1.0F), new Vector3f(1.0F, -1.0F, -1.0F)}; faces.add(face); //xz +y face = new Vector3f[]{ new Vector3f(-1.0F, 1.0F, -1.0F), new Vector3f(-1.0F, 1.0F, 1.0F), new Vector3f(1.0F, 1.0F, 1.0F), new Vector3f(1.0F, 1.0F, -1.0F)}; faces.add(face); float f4 = this.getQuadSize(partialTicks); for (Vector3f[] entryFace : faces) { for(int i = 0; i < 4; ++i) { entryFace[i].rotate(quaternion); entryFace[i].mul(f4); entryFace[i].add(f, f1, f2); } } float f7 = this.getU0(); float f8 = this.getU1(); float f5 = this.getV0(); float f6 = this.getV1(); if (sprite != null) { f7 = sprite.getU0(); f8 = sprite.getU1(); f5 = sprite.getV0(); f6 = sprite.getV1(); } int j = this.getLightColor(partialTicks); if (j > 0) { lastNonZeroBrightness = j; } else { j = lastNonZeroBrightness; } for (Vector3f[] entryFace : faces) { buffer.vertex(entryFace[0].x(), entryFace[0].y(), entryFace[0].z()).uv(f8, f6).color(this.rCol, this.gCol, this.bCol, this.alpha).uv2(j).endVertex(); buffer.vertex(entryFace[1].x(), entryFace[1].y(), entryFace[1].z()).uv(f8, f5).color(this.rCol, this.gCol, this.bCol, this.alpha).uv2(j).endVertex(); buffer.vertex(entryFace[2].x(), entryFace[2].y(), entryFace[2].z()).uv(f7, f5).color(this.rCol, this.gCol, this.bCol, this.alpha).uv2(j).endVertex(); buffer.vertex(entryFace[3].x(), entryFace[3].y(), entryFace[3].z()).uv(f7, f6).color(this.rCol, this.gCol, this.bCol, this.alpha).uv2(j).endVertex(); } } @Override public ParticleRenderType getRenderType() { return SORTED_OPAQUE_BLOCK; //return super.getRenderType(); } } ================================================ FILE: src/main/java/extendedrenderer/particle/entity/ParticleEmitter.java ================================================ package extendedrenderer.particle.entity; import com.mojang.blaze3d.vertex.VertexConsumer; import net.minecraft.client.Camera; import net.minecraft.client.multiplayer.ClientLevel; public class ParticleEmitter extends EntityRotFX { public ParticleEmitter(ClientLevel par1World, double par2, double par4, double par6, double par8, double par10, double par12) { super(par1World, par2, par4, par6, par8, par10, par12); } @Override public void tick() { //super.tick(); if (this.age++ >= this.lifetime) { this.remove(); } } @Override public void render(VertexConsumer buffer, Camera renderInfo, float partialTicks) { //super.render(buffer, renderInfo, partialTicks); } } ================================================ FILE: src/main/java/extendedrenderer/particle/entity/ParticleTexExtraRender.java ================================================ package extendedrenderer.particle.entity; import com.corosus.coroutil.util.CoroUtilBlock; import com.corosus.coroutil.util.CoroUtilParticle; import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.math.Axis; import net.minecraft.client.Camera; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.core.BlockPos; import net.minecraft.util.Mth; import net.minecraft.world.phys.Vec3; import net.minecraft.world.level.levelgen.Heightmap; import org.joml.Quaternionf; import org.joml.Vector3f; import weather2.ClientTickHandler; import weather2.weathersystem.WeatherManagerClient; import weather2.weathersystem.wind.WindManager; public class ParticleTexExtraRender extends ParticleTexFX { private int severityOfRainRate = 2; private int extraParticlesBaseAmount = 5; public boolean noExtraParticles = false; private float extraRandomSecondaryYawRotation = 360; //public float[] cachedLight; public ParticleTexExtraRender(ClientLevel worldIn, double posXIn, double posYIn, double posZIn, double mX, double mY, double mZ, TextureAtlasSprite par8Item) { super(worldIn, posXIn, posYIn, posZIn, mX, mY, mZ, par8Item); /*cachedLight = new float[CoroUtilParticle.rainPositions.length]; if (worldObj.getGameTime() % 5 == 0) { for (int i = 0; i < cachedLight.length; i++) { Vector3 vec = CoroUtilParticle.rainPositions[i]; cachedLight[i] = getBrightnessNonLightmap(new BlockPos(posX+vec.xCoord, posY+vec.yCoord, posZ+vec.zCoord), 1F); } }*/ } public int getSeverityOfRainRate() { return severityOfRainRate; } public void setSeverityOfRainRate(int severityOfRainRate) { this.severityOfRainRate = severityOfRainRate; } public int getExtraParticlesBaseAmount() { return extraParticlesBaseAmount; } public void setExtraParticlesBaseAmount(int extraParticlesBaseAmount) { this.extraParticlesBaseAmount = extraParticlesBaseAmount; } @Override public void tickExtraRotations() { //super.tickExtraRotations(); WeatherManagerClient weatherMan = ClientTickHandler.weatherManager; if (weatherMan == null) return; WindManager windMan = weatherMan.getWindManager(); if (windMan == null) return; if (isSlantParticleToWind()) { double speed = xd * xd + zd * zd; rotationYaw = -(float)Math.toDegrees(Math.atan2(zd, xd)) - 90; rotationPitch = Math.min(45, (float)(speed * 120)); rotationPitch += (this.getEntityId() % 10) - 5; } windMan.applyWindForceNew(this, 1F / 2F, 0.5F); } /** * make sure extra renderings arent culled out * * @return */ /*@Override public boolean shouldCull() { return false; }*/ @Override public void render(VertexConsumer buffer, Camera renderInfo, float partialTicks) { //override rotations Vec3 Vector3d = renderInfo.getPosition(); Quaternionf quaternion; if (this.facePlayer || (this.rotationPitch == 0 && this.rotationYaw == 0)) { quaternion = renderInfo.rotation(); } else { // override rotations quaternion = new Quaternionf(0, 0, 0, 1); quaternion.mul(Axis.YP.rotationDegrees(this.rotationYaw)); quaternion.mul(Axis.XP.rotationDegrees(this.rotationPitch)); if (extraRandomSecondaryYawRotation > 0) { quaternion.mul(Axis.YP.rotationDegrees(getEntityId() % extraRandomSecondaryYawRotation)); } } float posX = (float)(Mth.lerp((double)partialTicks, this.xo, this.x) - Vector3d.x()); float posY = (float)(Mth.lerp((double)partialTicks, this.yo, this.y) - Vector3d.y()); float posZ = (float)(Mth.lerp((double)partialTicks, this.zo, this.z) - Vector3d.z()); float f = this.getU0(); float f1 = this.getU1(); float f2 = this.getV0(); float f3 = this.getV1(); float fixY = 0; float part = 16F / 3F; float offset = 0; float posBottom = (float)(this.y - 10D); float height = level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, CoroUtilBlock.blockPos(this.x, this.y, this.z)).getY(); if (posBottom < height) { float diff = height - posBottom; offset = diff; fixY = 0;//diff * 1.0F; if (offset > part) offset = part; } int renderAmount = 0; if (noExtraParticles) { renderAmount = 1; } else { //renderAmount = Math.min(extraParticlesBaseAmount + ((Math.max(0, severityOfRainRate-1)) * 5), CoroUtilParticle.maxRainDrops); renderAmount = Math.min(1 + extraParticlesBaseAmount, CoroUtilParticle.maxRainDrops); } //catch code hotload crash, doesnt help much anyways try { for (int ii = 0; ii < renderAmount; ii++) { double xx = 0; double zz = 0; double yy = 0; if (ii != 0) { xx = CoroUtilParticle.rainPositions[ii].x; zz = CoroUtilParticle.rainPositions[ii].z; //yy = CoroUtilParticle.rainPositions[ii].y; yy = CoroUtilParticle.rainPositions[ii].y; } //prevent precip under overhangs/inside for extra render if (this.isDontRenderUnderTopmostBlock()) { int height2 = level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, CoroUtilBlock.blockPos(this.x + xx, this.y, this.z + zz)).getY(); if (this.y + yy < height2) continue; } //TODO: 1.14 uncomment /*if (ii != 0) { RotatingParticleManager.debugParticleRenderCount++; }*/ /*int height = entityIn.world.getPrecipitationHeight(new BlockPos(ActiveRenderInfo.getPosition().xCoord + f5, this.posY + f6, ActiveRenderInfo.getPosition().zCoord + f7)).getY(); if (ActiveRenderInfo.getPosition().yCoord + f6 <= height) continue;*/ int i = this.getLightColor(partialTicks); if (i > 0) { setLastNonZeroBrightness(i); } else { i = getLastNonZeroBrightness(); } Vector3f[] avector3f = new Vector3f[]{new Vector3f(-1.0F, -1.0F, 0.0F), new Vector3f(-1.0F, 1.0F, 0.0F), new Vector3f(1.0F, 1.0F, 0.0F), new Vector3f(1.0F, -1.0F, 0.0F)}; float scale = this.getQuadSize(partialTicks); for(int v = 0; v < 4; ++v) { Vector3f vector3f = avector3f[v]; vector3f.rotate(quaternion); vector3f.mul(scale); vector3f.add(posX, posY, posZ); } buffer.vertex(xx + avector3f[0].x(), yy + avector3f[0].y(), zz + avector3f[0].z()).uv(f1, f3).color(this.rCol, this.gCol, this.bCol, this.alpha).uv2(i).endVertex(); buffer.vertex(xx + avector3f[1].x(), yy + avector3f[1].y(), zz + avector3f[1].z()).uv(f1, f2).color(this.rCol, this.gCol, this.bCol, this.alpha).uv2(i).endVertex(); buffer.vertex(xx + avector3f[2].x(), yy + avector3f[2].y(), zz + avector3f[2].z()).uv(f, f2).color(this.rCol, this.gCol, this.bCol, this.alpha).uv2(i).endVertex(); buffer.vertex(xx + avector3f[3].x(), yy + avector3f[3].y(), zz + avector3f[3].z()).uv(f, f3).color(this.rCol, this.gCol, this.bCol, this.alpha).uv2(i).endVertex(); } } catch (Throwable ex) { ex.printStackTrace(); } } //TODO: 1.14 uncomment /*public void renderParticleForShader(InstancedMeshParticle mesh, Transformation transformation, Matrix4fe viewMatrix, Entity entityIn, float partialTicks, float rotationX, float rotationZ, float rotationYZ, float rotationXY, float rotationXZ) { float posX = (float) (this.prevPosX + (this.posX - this.prevPosX) * (double) partialTicks); float posY = (float) (this.prevPosY + (this.posY - this.prevPosY) * (double) partialTicks); float posZ = (float) (this.prevPosZ + (this.posZ - this.prevPosZ) * (double) partialTicks); //Vector3f pos = new Vector3f((float) (entityIn.posX - particle.posX), (float) (entityIn.posY - particle.posY), (float) (entityIn.posZ - particle.posZ)); int renderAmount = 0; if (noExtraParticles) { renderAmount = 1; } else { renderAmount = Math.min(extraParticlesBaseAmount + ((Math.max(0, severityOfRainRate-1)) * 5), CoroUtilParticle.maxRainDrops); } for (int iii = 0; iii < renderAmount; iii++) { if (mesh.curBufferPos >= mesh.numInstances) return; Vector3f pos; if (iii != 0) { pos = new Vector3f(posX + (float) CoroUtilParticle.rainPositions[iii].xCoord, posY + (float) CoroUtilParticle.rainPositions[iii].yCoord, posZ + (float) CoroUtilParticle.rainPositions[iii].zCoord); } else { pos = new Vector3f(posX, posY, posZ); } if (false && useRotationAroundCenter) { float deltaRot = rotationAroundCenterPrev + (rotationAroundCenter - rotationAroundCenterPrev) * partialTicks; float rotX = (float) Math.sin(Math.toRadians(deltaRot)); float rotZ = (float) Math.cos(Math.toRadians(deltaRot)); pos.x += rotX * rotationDistAroundCenter; pos.z += rotZ * rotationDistAroundCenter; } if (this.isDontRenderUnderTopmostBlock()) { int height = world.getHeight(Heightmap.Type.MOTION_BLOCKING, new BlockPos(pos.x, this.posY, pos.z)).getY(); if (pos.y <= height) continue; } //adjust to relative to camera positions finally pos.x -= interpPosX; pos.y -= interpPosY; pos.z -= interpPosZ; Matrix4fe modelMatrix = transformation.buildModelMatrix(this, pos, partialTicks); //adjust to perspective and camera //Matrix4fe modelViewMatrix = transformation.buildModelViewMatrix(modelMatrix, viewMatrix); //upload to buffer modelMatrix.get(mesh.INSTANCE_SIZE_FLOATS * (mesh.curBufferPos), mesh.instanceDataBuffer); //brightness float brightness; //brightness = CoroUtilBlockLightCache.getBrightnessCached(worldObj, pos.x, pos.y, pos.z); //brightness = this.brightnessCache; if (fastLight) { brightness = CoroUtilBlockLightCache.brightnessPlayer; } else { brightness = CoroUtilBlockLightCache.getBrightnessCached(world, (float)this.posX, (float)this.posY, (float)this.posZ); } //brightness to buffer mesh.instanceDataBuffer.put(mesh.INSTANCE_SIZE_FLOATS * (mesh.curBufferPos) + mesh.MATRIX_SIZE_FLOATS, brightness); //rgba to buffer int rgbaIndex = 0; mesh.instanceDataBuffer.put(mesh.INSTANCE_SIZE_FLOATS * (mesh.curBufferPos) + mesh.MATRIX_SIZE_FLOATS + 1 + (rgbaIndex++), this.particleRed); mesh.instanceDataBuffer.put(mesh.INSTANCE_SIZE_FLOATS * (mesh.curBufferPos) + mesh.MATRIX_SIZE_FLOATS + 1 + (rgbaIndex++), this.particleGreen); mesh.instanceDataBuffer.put(mesh.INSTANCE_SIZE_FLOATS * (mesh.curBufferPos) + mesh.MATRIX_SIZE_FLOATS + 1 + (rgbaIndex++), this.particleBlue); mesh.instanceDataBuffer.put(mesh.INSTANCE_SIZE_FLOATS * (mesh.curBufferPos) + mesh.MATRIX_SIZE_FLOATS + 1 + (rgbaIndex++), this.getAlphaF()); mesh.curBufferPos++; } }*/ /*@Override public void renderParticleForShaderTest(InstancedMeshParticle mesh, Transformation transformation, Matrix4fe viewMatrix, Entity entityIn, float partialTicks, float rotationX, float rotationZ, float rotationYZ, float rotationXY, float rotationXZ) { float posX = (float) (this.prevPosX + (this.posX - this.prevPosX) * (double) partialTicks); float posY = (float) (this.prevPosY + (this.posY - this.prevPosY) * (double) partialTicks); float posZ = (float) (this.prevPosZ + (this.posZ - this.prevPosZ) * (double) partialTicks); int renderAmount = 0; if (noExtraParticles) { renderAmount = 1; } else { renderAmount = Math.min(extraParticlesBaseAmount + ((Math.max(0, severityOfRainRate-1)) * 5), CoroUtilParticle.maxRainDrops); } for (int iii = 0; iii < renderAmount; iii++) { if (mesh.curBufferPos >= mesh.numInstances) return; Vector3f pos; if (iii != 0) { pos = new Vector3f(posX + (float) CoroUtilParticle.rainPositions[iii].xCoord, posY + (float) CoroUtilParticle.rainPositions[iii].yCoord, posZ + (float) CoroUtilParticle.rainPositions[iii].zCoord); } else { pos = new Vector3f(posX, posY, posZ); } if (this.isDontRenderUnderTopmostBlock()) { int height = this.world.getPrecipitationHeight(new BlockPos(pos.x, this.posY, pos.z)).getY(); if (pos.y <= height) continue; } //adjust to relative to camera positions finally pos.x -= interpPosX; pos.y -= interpPosY; pos.z -= interpPosZ; int rgbaIndex = 0; mesh.instanceDataBufferTest.put(mesh.INSTANCE_SIZE_FLOATS_TEST * (mesh.curBufferPos) + (rgbaIndex++), this.getRedColorF()); mesh.instanceDataBufferTest.put(mesh.INSTANCE_SIZE_FLOATS_TEST * (mesh.curBufferPos) + (rgbaIndex++), this.getGreenColorF()); mesh.instanceDataBufferTest.put(mesh.INSTANCE_SIZE_FLOATS_TEST * (mesh.curBufferPos) + (rgbaIndex++), this.getBlueColorF()); mesh.instanceDataBufferTest.put(mesh.INSTANCE_SIZE_FLOATS_TEST * (mesh.curBufferPos) + (rgbaIndex++), this.getAlphaF()); mesh.curBufferPos++; } }*/ /*@Override public void updateQuaternion(Entity camera) { if (camera != null) { if (this.facePlayer) { this.rotationYaw = camera.rotationYaw; this.rotationPitch = camera.rotationPitch; } else if (facePlayerYaw) { this.rotationYaw = camera.rotationYaw; } } Quaternion qY = new Quaternion(); Quaternion qX = new Quaternion(); qY.setFromAxisAngle(new Vector4f(0, 1, 0, (float)Math.toRadians(-this.rotationYaw - 180F))); qX.setFromAxisAngle(new Vector4f(1, 0, 0, (float)Math.toRadians(-this.rotationPitch))); if (this.rotateOrderXY) { Quaternion.mul(qX, qY, this.rotation); } else { Quaternion.mul(qY, qX, this.rotation); if (extraYRotation != 0) { //float rot = (new Random()).nextFloat() * 360F; qY = new Quaternion(); qY.setFromAxisAngle(new Vector4f(0, 1, 0, extraYRotation)); Quaternion.mul(this.rotation, qY, this.rotation); } } }*/ } ================================================ FILE: src/main/java/extendedrenderer/particle/entity/ParticleTexFX.java ================================================ package extendedrenderer.particle.entity; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; @OnlyIn(Dist.CLIENT) public class ParticleTexFX extends EntityRotFX { public ParticleTexFX(ClientLevel worldIn, double posXIn, double posYIn, double posZIn, double mX, double mY, double mZ, TextureAtlasSprite par8Item) { super(worldIn, posXIn, posYIn, posZIn, mX, mY-0.5, mZ); this.setSprite(par8Item); //this.setParticleTexture(Minecraft.getInstance().getItemRenderer().getItemModelMesher().getParticleIcon(Items.IRON_AXE, 0)); this.rCol = 1.0F; this.gCol = 1.0F; this.bCol = 1.0F; this.gravity = 1F; this.quadSize = 0.15F; this.setLifetime(100); this.setCanCollide(false); } public float getParticleGravity() { return this.gravity; } /*@Override public int getFXLayer() { return 1; }*/ } ================================================ FILE: src/main/java/extendedrenderer/particle/entity/ParticleTexLeafColor.java ================================================ package extendedrenderer.particle.entity; import com.corosus.coroutil.util.CoroUtilColor; import com.corosus.coroutil.util.CoroUtilMisc; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.client.Minecraft; import net.minecraft.client.color.block.BlockColors; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.core.BlockPos; import org.apache.commons.lang3.ArrayUtils; import java.util.concurrent.ConcurrentHashMap; public class ParticleTexLeafColor extends ParticleTexFX { // Save a few stack depth by caching this private static BlockColors colors; //TODO: see forge note on field to use registry delegate //private static final Field _blockColorMap = null;//BlockColors.blockColors; //private static final Field _blockColorMap = ObfuscationReflectionHelper.findField(BlockColors.class, "blockColors"); //private static Map, BlockColor> blockColorMap; private static ConcurrentHashMap colorCache = new ConcurrentHashMap<>(); /*static { ((SimpleReloadableResourceManager) Minecraft.getInstance().getResourceManager()).registerReloadListener(rm -> colorCache.clear()); }*/ //only use positives for now public float rotationYawMomentum; public float rotationPitchMomentum; public ParticleTexLeafColor(ClientLevel worldIn, double posXIn, double posYIn, double posZIn, double mX, double mY, double mZ, TextureAtlasSprite par8Item) { super(worldIn, posXIn, posYIn, posZIn, mX, mY, mZ, par8Item); if (colors == null) { colors = Minecraft.getInstance().getBlockColors(); /*try { blockColorMap = (Map, BlockColor>) _blockColorMap.get(colors); } catch (IllegalArgumentException | IllegalAccessException e) { throw new RuntimeException(e); }*/ } BlockPos pos = new BlockPos((int)Math.floor(posXIn), (int)Math.floor(posYIn), (int)Math.floor(posZIn)); BlockState state = worldIn.getBlockState(pos); // top of double plants doesn't have variant property //TODO: 1.14 uncomment /*if (state.getBlock() instanceof DoublePlantBlock && state.get(DoublePlantBlock.HALF) == EnumBlockHalf.UP) { state = state.with(DoublePlantBlock.name, worldIn.getBlockState(pos.down()).get(DoublePlantBlock.name)); }*/ int multiplier = this.colors.getColor(state, this.level, pos, 0); //was this supposed to be temp?! //colorCache.clear(); int[] colors = colorCache.get(state); if (colors == null) { // colors = IntArrays.EMPTY_ARRAY; colors = CoroUtilColor.getColors(state); if (colors.length == 0) { //if there is no color to use AND theres no multiplier, fallback to good ol green if (!hasColor(state) || (multiplier & 0xFFFFFF) == 0xFFFFFF) { multiplier = 5811761; //color for vanilla leaf in forest biome } //add just white that will get colormultiplied //TODO: adjusted for LT19 //colors = new int[] { 0xFFFFFF }; colors = new int[] { 0x00FF00 }; } // Remove duplicate colors from end of array, this will skew the random choice later if (colors.length > 1) { while (colors[colors.length - 1] == colors[colors.length - 2]) { colors = ArrayUtils.remove(colors, colors.length - 1); } } colorCache.put(state, colors); } // Randomize the color with exponential decrease in likelihood. That is, the first color has a 50% chance, then 25%, etc. int randMax = 1 << (colors.length - 1); int choice = 32 - Integer.numberOfLeadingZeros(CoroUtilMisc.random.nextInt(randMax)); int color = colors[choice]; float mr = ((multiplier >>> 16) & 0xFF) / 255f; float mg = ((multiplier >>> 8) & 0xFF) / 255f; float mb = (multiplier & 0xFF) / 255f; this.rCol *= (float) (color >> 16 & 255) / 255.0F * mr; this.gCol *= (float) (color >> 8 & 255) / 255.0F * mg; this.bCol *= (float) (color & 255) / 255.0F * mb; } @Override public void tick() { super.tick(); //make leafs catch on the ground and cause them to bounce up and slow a bit for effect if (isCollidedVerticallyDownwards && random.nextInt(10) == 0) { double speed = Math.sqrt(this.xd * this.xd + this.zd * this.zd); if (speed > 0.07) { this.yd = 0.02D + random.nextDouble() * 0.03D; this.xd *= 0.6D; this.zd *= 0.6D; rotationYawMomentum = 30; rotationPitchMomentum = 30; } } if (rotationYawMomentum > 0) { this.rotationYaw += rotationYawMomentum; rotationYawMomentum -= 1.5F; if (rotationYawMomentum < 0) { rotationYawMomentum = 0; } } else { rotationYawMomentum += random.nextDouble() * 30; } if (rotationPitchMomentum > 0) { this.rotationPitch += rotationPitchMomentum; rotationPitchMomentum -= 1.5F; if (rotationPitchMomentum < 0) { rotationPitchMomentum = 0; } } else { rotationPitchMomentum += random.nextDouble() * 30; } } private final boolean hasColor(BlockState state) { return false;//blockColorMap.containsKey(state.getBlock().delegate); } } ================================================ FILE: src/main/java/extendedrenderer/particle/entity/PivotingParticle.java ================================================ package extendedrenderer.particle.entity; import com.mojang.blaze3d.vertex.VertexConsumer; import net.minecraft.client.Camera; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; /** * Particle that has a secondary rotation with a 1 dimensional pivot point. * For rotating particles around a point with nice rotational interpolation. */ public class PivotingParticle extends ParticleTexFX { private Vec3 pivot = new Vec3(0, 0, 0); private Vec3 pivotPrev = new Vec3(0, 0, 0); //in degrees private Vec3 pivotRot = new Vec3(0, 0, 0); private Vec3 pivotRotPrev = new Vec3(0, 0, 0); public PivotingParticle(ClientLevel worldIn, double posXIn, double posYIn, double posZIn, double mX, double mY, double mZ, TextureAtlasSprite par8Item) { super(worldIn, posXIn, posYIn, posZIn, mX, mY, mZ, par8Item); } @Override public void tick() { super.tick(); } /** * Get coordinates for pivoted rotation * For now we'll just do a rotation around the y axis * if we need to do full 3d pivoting, use a rotation matrix with quaternion and make use of the full vectors * * @param partialTicks * @return */ @Override public Vec3 getPivotedPosition(float partialTicks) { Vec3 pivotLerped = pivotPrev.lerp(pivot, partialTicks); Vec3 pivotRotLerped = pivotRotPrev.lerp(pivotRot, partialTicks); float x = (float) (-Math.sin(Math.toRadians(pivotRotLerped.y)) * pivotLerped.y); float z = (float) (Math.cos(Math.toRadians(pivotRotLerped.y)) * pivotLerped.y); return new Vec3(x, 0, z); } public Vec3 getPivot() { return pivot; } public void setPivot(Vec3 pivot) { this.pivot = pivot; } public Vec3 getPivotPrev() { return pivotPrev; } public void setPivotPrev(Vec3 pivotPrev) { this.pivotPrev = pivotPrev; } public Vec3 getPivotRot() { return pivotRot; } public void setPivotRot(Vec3 pivotRot) { this.pivotRot = pivotRot; } public Vec3 getPivotRotPrev() { return pivotRotPrev; } public void setPivotRotPrev(Vec3 pivotRotPrev) { this.pivotRotPrev = pivotRotPrev; } @Override public AABB getBoundingBoxForRender(float partialTicks) { return getBoundingBox().move(getPivotedPosition(partialTicks)); } @Override public void render(VertexConsumer buffer, Camera renderInfo, float partialTicks) { super.render(buffer, renderInfo, partialTicks); } } ================================================ FILE: src/main/java/extendedrenderer/particle/entity/WaterDropParticleImpl.java ================================================ package extendedrenderer.particle.entity; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.particle.WaterDropParticle; import net.minecraft.core.particles.SimpleParticleType; import net.minecraft.world.level.Level; public class WaterDropParticleImpl extends WaterDropParticle { public WaterDropParticleImpl(SimpleParticleType simpleParticleType, ClientLevel p_108484_, double p_108485_, double p_108486_, double p_108487_, double v3, double v4, double v5) { super(p_108484_, p_108485_, p_108486_, p_108487_); } } ================================================ FILE: src/main/java/weather2/ClientRegistry.java ================================================ package weather2; import net.minecraft.client.renderer.entity.EntityRenderers; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.client.event.EntityRenderersEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import weather2.client.tile.AnemometerEntityRenderer; import weather2.client.tile.WindTurbineEntityRenderer; import weather2.client.tile.WindVaneEntityRenderer; import weather2.client.entity.model.AnemometerModel; import weather2.client.entity.model.WindTurbineModel; import weather2.client.entity.model.WindVaneModel; import weather2.client.entity.render.LightningBoltWeatherNewRenderer; @Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) public class ClientRegistry { @OnlyIn(Dist.CLIENT) @SubscribeEvent public static void clientSetup(FMLClientSetupEvent event) { EntityRenderers.register(EntityRegistry.LIGHTNING_BOLT.get(), render -> new LightningBoltWeatherNewRenderer(render)); } @OnlyIn(Dist.CLIENT) @SubscribeEvent public static void registerRenderers(EntityRenderersEvent.RegisterRenderers e) { e.registerBlockEntityRenderer(WeatherBlocks.BLOCK_ENTITY_ANEMOMETER.get(), AnemometerEntityRenderer::new); e.registerBlockEntityRenderer(WeatherBlocks.BLOCK_ENTITY_WIND_VANE.get(), WindVaneEntityRenderer::new); e.registerBlockEntityRenderer(WeatherBlocks.BLOCK_ENTITY_WIND_TURBINE.get(), WindTurbineEntityRenderer::new); } @OnlyIn(Dist.CLIENT) @SubscribeEvent public static void registerLayerDefinitions(EntityRenderersEvent.RegisterLayerDefinitions event) { event.registerLayerDefinition(WindVaneModel.LAYER_LOCATION, WindVaneModel::createBodyLayer); event.registerLayerDefinition(AnemometerModel.LAYER_LOCATION, AnemometerModel::createBodyLayer); event.registerLayerDefinition(WindTurbineModel.LAYER_LOCATION, WindTurbineModel::createBodyLayer); } } ================================================ FILE: src/main/java/weather2/ClientTickHandler.java ================================================ package weather2; import com.corosus.coroutil.util.CULog; import extendedrenderer.ParticleManagerExtended; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.BackupConfirmScreen; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.nbt.CompoundTag; import net.minecraft.util.Mth; import net.minecraft.world.level.Level; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.event.TickEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.network.NetworkDirection; import weather2.client.SceneEnhancer; import weather2.config.ClientConfigData; import weather2.config.ConfigDebug; import weather2.util.WeatherUtil; import weather2.util.WindReader; import weather2.weathersystem.WeatherManagerClient; @Mod.EventBusSubscriber(modid = Weather.MODID, value = Dist.CLIENT) public class ClientTickHandler { public static final ClientTickHandler INSTANCE = new ClientTickHandler(); public static Level lastWorld; public static WeatherManagerClient weatherManager; public static SceneEnhancer sceneEnhancer; public static ClientConfigData clientConfigData; public float smoothAngle = 0; public float smoothAngleRotationalVelAccel = 0; public float smoothAngleAdj = 0.1F; public int prevDir = 0; public long lastParticleResetTime = 0; private static ParticleManagerExtended particleManagerExtended; private ClientTickHandler() { //this constructor gets called multiple times when created from proxy, this prevents multiple inits if (sceneEnhancer == null) { sceneEnhancer = new SceneEnhancer(); (new Thread(sceneEnhancer, "Weather2 Scene Enhancer")).start(); } clientConfigData = new ClientConfigData(); } @SubscribeEvent public static void tick(TickEvent.ClientTickEvent event) { if (event.phase == TickEvent.Phase.START) { INSTANCE.onTickInGame(); } } public void onTickInGame() { Minecraft mc = Minecraft.getInstance(); Level world = mc.level; if (world != null) { getClientWeather(); weatherManager.tick(); sceneEnhancer.tickClient(); if (!WeatherUtil.isPausedForClient()) { if (ConfigDebug.Particle_engine_tick) { particleManagerExtended().tick(); } } if (ConfigDebug.Particle_Reset_Frequency > 0 && lastParticleResetTime + ConfigDebug.Particle_Reset_Frequency < world.getGameTime()) { CULog.log("clearing vanilla particles, set Weather2 Debug Particle_Reset_Frequency to 0 to disable"); lastParticleResetTime = world.getGameTime(); mc.particleEngine.clearParticles(); } //TODO: evaluate if best here float windDir = WindReader.getWindAngle(world); float windSpeed = WindReader.getWindSpeed(world, mc.player != null ? mc.player.blockPosition() : null); //windDir = 0; //TODO: ???????????? what is all this even affecting now float diff = Math.abs(windDir - smoothAngle)/* - 180*/; if (true && diff > 10/* && (smoothAngle > windDir - give || smoothAngle < windDir + give)*/) { if (smoothAngle > 180) smoothAngle -= 360; if (smoothAngle < -180) smoothAngle += 360; float bestMove = Mth.wrapDegrees(windDir - smoothAngle); smoothAngleAdj = windSpeed;//0.2F; if (Math.abs(bestMove) < 180/* - (angleAdjust * 2)*/) { float realAdj = smoothAngleAdj;//Math.max(smoothAngleAdj, Math.abs(bestMove)); if (realAdj * 2 > windSpeed) { if (bestMove > 0) { smoothAngleRotationalVelAccel -= realAdj; if (prevDir < 0) { smoothAngleRotationalVelAccel = 0; } prevDir = 1; } else if (bestMove < 0) { smoothAngleRotationalVelAccel += realAdj; if (prevDir > 0) { smoothAngleRotationalVelAccel = 0; } prevDir = -1; } } if (smoothAngleRotationalVelAccel > 0.3 || smoothAngleRotationalVelAccel < -0.3) { smoothAngle += smoothAngleRotationalVelAccel * 0.3F; } else { //smoothAngleRotationalVelAccel *= 0.9F; } smoothAngleRotationalVelAccel *= 0.80F; } } } else { resetClientWeather(); } } public static void resetClientWeather() { weatherManager = null; ClientWeatherProxy.get().reset(); ClientWeatherHelper.get().reset(); } public static WeatherManagerClient getClientWeather() { try { Level world = Minecraft.getInstance().level; if (weatherManager == null || world != lastWorld) { init(world); } } catch (Exception ex) { Weather.dbg("Weather2: Warning, client received packet before it was ready to use, and failed to init client weather due to null world"); } return weatherManager; } public static void init(Level world) { Weather.dbg("Weather2: Initializing WeatherManagerClient for client world and requesting full sync"); lastWorld = world; weatherManager = new WeatherManagerClient(world.dimension()); Minecraft mc = Minecraft.getInstance(); if (particleManagerExtended == null) { particleManagerExtended = new ParticleManagerExtended(mc.level, mc.textureManager); } else { particleManagerExtended.setLevel((ClientLevel) world); } //((IReloadableResourceManager)mc.getResourceManager()).addReloadListener(particleManagerExtended); CompoundTag data = new CompoundTag(); data.putString("command", "syncFull"); data.putString("packetCommand", "WeatherData"); //Weather.eventChannel.sendToServer(PacketHelper.getNBTPacket(data, Weather.eventChannelName)); WeatherNetworking.HANDLER.sendTo(new PacketNBTFromClient(data), mc.player.connection.getConnection(), NetworkDirection.PLAY_TO_SERVER); } public static ParticleManagerExtended particleManagerExtended() { return particleManagerExtended; } } ================================================ FILE: src/main/java/weather2/ClientWeatherHelper.java ================================================ package weather2; import com.corosus.coroutil.util.CULog; import net.minecraft.client.Minecraft; import net.minecraft.world.entity.player.Player; import net.minecraft.world.phys.Vec3; import weather2.config.ConfigMisc; import weather2.config.ConfigStorm; import weather2.weathersystem.storm.StormObject; /** * Moving client weather logic to here from scene enhancer. * Also when LT isnt in control of the weather, logic redirects to here for weather2s more special rules */ public final class ClientWeatherHelper { private static ClientWeatherHelper instance; private float curPrecipStr = 0F; private float curPrecipStrTarget = 0F; private float curOvercastStr = 0F; private float curOvercastStrTarget = 0F; private ClientWeatherHelper() { } public static ClientWeatherHelper get() { if (instance == null) { instance = new ClientWeatherHelper(); } return instance; } public void reset() { instance.curPrecipStr = 0F; instance.curPrecipStrTarget = 0F; instance.curOvercastStr = 0F; instance.curOvercastStrTarget = 0F; } public void tick() { tickRainRates(); } public float getPrecipitationStrength(Player entP) { return getPrecipitationStrength(entP, false); } /** * returns 0 to 1 of storm strength * * @param entP * @param forOvercast * @return */ public float getPrecipitationStrength(Player entP, boolean forOvercast) { if (entP == null) return 0; double maxStormDist = 512 / 4 * 3; Vec3 plPos = new Vec3(entP.getX(), StormObject.static_YPos_layer0, entP.getZ()); StormObject storm; ClientTickHandler.getClientWeather(); storm = ClientTickHandler.weatherManager.getClosestStorm(plPos, maxStormDist, StormObject.STATE_FORMING, -1, true); boolean closeEnough = false; double stormDist = 9999; float tempAdj = 1F; float sizeToUse = 0; float overcastModeMinPrecip = 0.23F; //overcastModeMinPrecip = 0.16F; //overcastModeMinPrecip = (float) ConfigStorm.Storm_Rain_Overcast_Amount; overcastModeMinPrecip = ClientTickHandler.weatherManager.vanillaRainAmountOnServer; //evaluate if storms size is big enough to be over player if (storm != null) { sizeToUse = storm.size; //extend overcast effect, using x2 for now since we cant cancel sound and ground particles, originally was 4x, then 3x, change to that for 1.7 if lex made change if (forOvercast) { sizeToUse *= 1F; } stormDist = storm.pos.distanceTo(plPos); //System.out.println("storm dist: " + stormDist); if (sizeToUse > stormDist) { closeEnough = true; } } if (closeEnough) { //max of 1 if at center of storm, subtract player xz distance out of the size to act like its a weaker storm double stormIntensity = (sizeToUse - stormDist) / sizeToUse; //why is this not a -1 or 1 anymore?! //tempAdj = storm.levelTemperature/* > 0 ? 1F : -1F*/; tempAdj = 1F;//storm.levelTemperature/* > 0 ? 1F : -1F*/; //limit plain rain clouds to light intensity if (storm.levelCurIntensityStage == StormObject.STATE_NORMAL) { if (stormIntensity > 0.3) stormIntensity = 0.3; } if (ConfigStorm.Storm_NoRainVisual) { stormIntensity = 0; } //TODO: verify this if statement was added correctly if (forOvercast) { if (stormIntensity < overcastModeMinPrecip) { stormIntensity = overcastModeMinPrecip; } } if (forOvercast) { curOvercastStrTarget = (float) stormIntensity; } else { curPrecipStrTarget = (float) stormIntensity; } } else { if (!ClientTickHandler.clientConfigData.overcastMode) { if (forOvercast) { curOvercastStrTarget = 0; } else { curPrecipStrTarget = 0; } } else { if (ClientTickHandler.weatherManager.isVanillaRainActiveOnServer) { if (forOvercast) { curOvercastStrTarget = overcastModeMinPrecip; } else { curPrecipStrTarget = overcastModeMinPrecip; } } else { if (forOvercast) { curOvercastStrTarget = 0; } else { curPrecipStrTarget = 0; } } } } if (forOvercast) { if (curOvercastStr < 0.002 && curOvercastStr > -0.002F) { return 0; } else { return curOvercastStr * tempAdj; } } else { if (curPrecipStr < 0.002 && curPrecipStr > -0.002F) { return 0; } else { return curPrecipStr * tempAdj; } } } public void controlVisuals(boolean precipitating) { Minecraft mc = Minecraft.getInstance(); ClientTickHandler.getClientWeather(); ClientWeatherProxy weather = ClientWeatherProxy.get(); float rainAmount = weather.getVanillaRainAmount(); float visualDarknessAmplifier = 0.5F; //using 1F to make shaders happy visualDarknessAmplifier = 1F; //CULog.dbg("rainAmount: " + rainAmount); if (!ConfigMisc.Aesthetic_Only_Mode) { if (precipitating) { mc.level.getLevelData().setRaining(rainAmount > 0); mc.level.setRainLevel(rainAmount * visualDarknessAmplifier); mc.level.setThunderLevel(rainAmount * visualDarknessAmplifier); } else { //TODO: i think these glitch out and trigger on world load if it was already raining, will think its false for a sec and lock sky visual to off if (!ClientTickHandler.clientConfigData.overcastMode) { mc.level.getLevelData().setRaining(false); mc.level.setRainLevel(0); mc.level.setThunderLevel(0); } else { if (ClientTickHandler.weatherManager.isVanillaRainActiveOnServer) { mc.level.getLevelData().setRaining(true); mc.level.setRainLevel(rainAmount * visualDarknessAmplifier); mc.level.setThunderLevel(rainAmount * visualDarknessAmplifier); } else { } } } } //TESTING /*mc.level.getLevelData().setRaining(true); mc.level.setRainLevel(1); mc.level.setThunderLevel(1);*/ } public void tickRainRates() { float rateChange = 0.0015F; if (curOvercastStr > curOvercastStrTarget) { curOvercastStr -= rateChange; } else if (curOvercastStr < curOvercastStrTarget) { curOvercastStr += rateChange; } if (curPrecipStr > curPrecipStrTarget) { curPrecipStr -= rateChange; } else if (curPrecipStr < curPrecipStrTarget) { curPrecipStr += rateChange; } } } ================================================ FILE: src/main/java/weather2/ClientWeatherProxy.java ================================================ package weather2; import net.minecraft.world.level.biome.Biome; import weather2.datatypes.PrecipitationType; import net.minecraft.client.Minecraft; import net.minecraft.world.entity.player.Player; import net.minecraft.world.phys.Vec3; import weather2.client.SceneEnhancer; import weather2.ltcompat.ClientWeatherIntegration; import weather2.weathersystem.storm.StormObject; import weather2.weathersystem.storm.WeatherObjectParticleStorm; import javax.annotation.Nullable; public final class ClientWeatherProxy { private static ClientWeatherProxy instance; private static boolean cacheIsSnowstorm = false; private static boolean cacheIsSandstorm = false; private static boolean cacheIsHail = false; private static int cacheRate = 40; private ClientWeatherProxy() { } public static ClientWeatherProxy get() { if (instance == null) { instance = new ClientWeatherProxy(); } return instance; } public void reset() { cacheIsSnowstorm = false; cacheIsSandstorm = false; cacheIsHail = false; } public float getRainAmount() { if (isWeatherEffectsServerSideControlled()) { return ClientWeatherIntegration.get().getRainAmount(); /*} else if (ClientTickHandler.clientConfigData.overcastMode) { if (Minecraft.getInstance().level == null) return 0; return Math.max(0, Minecraft.getInstance().level.rainLevel * SceneEnhancer.downfallSheetThreshold - 0.02F); //enough rain without the downfall */} else { return ClientWeatherHelper.get().getPrecipitationStrength(Minecraft.getInstance().player); } } public float getVanillaRainAmount() { if (Weather.isLoveTropicsInstalled()) { return ClientWeatherIntegration.get().getVanillaRainAmount(); /*} else if (ClientTickHandler.clientConfigData.overcastMode) { if (Minecraft.getInstance().level == null) return 0; return Minecraft.getInstance().level.rainLevel * 1F; */} else { return ClientWeatherHelper.get().getPrecipitationStrength(Minecraft.getInstance().player); } } @Nullable public PrecipitationType getPrecipitationType(Biome biome) { if (Weather.isLoveTropicsInstalled()) { return ClientWeatherIntegration.get().getPrecipitationType(); } else { if (biome == null) return null; if (biome.hasPrecipitation() && biome.getModifiedClimateSettings().temperatureModifier() == Biome.TemperatureModifier.NONE) return PrecipitationType.NORMAL; if (biome.hasPrecipitation() && biome.getModifiedClimateSettings().temperatureModifier() == Biome.TemperatureModifier.FROZEN) return PrecipitationType.SNOW; if (!biome.hasPrecipitation()) return null; } return null; } public float getWindSpeed() { return ClientWeatherIntegration.get().getWindSpeed(); } public boolean isHeatwave() { return ClientWeatherIntegration.get().isHeatwave(); } public boolean isSandstorm() { if (isWeatherEffectsServerSideControlled()) { return ClientWeatherIntegration.get().isSandstorm(); } else { Minecraft client = Minecraft.getInstance(); Player player = client.player; if (player == null) return false; if (player.level().getGameTime() % cacheRate == 0) { Vec3 posPlayer = new Vec3(client.player.getX(), 0, client.player.getZ()); WeatherObjectParticleStorm storm = ClientTickHandler.weatherManager.getClosestParticleStormByIntensity(posPlayer, WeatherObjectParticleStorm.StormType.SANDSTORM); cacheIsSandstorm = storm != null && posPlayer.distanceTo(storm.pos) < storm.getSize(); } return cacheIsSandstorm; } } public boolean isSnowstorm() { //return ClientWeatherIntegration.get().isSnowstorm(); if (isWeatherEffectsServerSideControlled()) { return ClientWeatherIntegration.get().isSnowstorm(); } else { Minecraft client = Minecraft.getInstance(); Player player = client.player; if (player == null) return false; if (player.level().getGameTime() % cacheRate == 0) { Vec3 posPlayer = new Vec3(client.player.getX(), 0, client.player.getZ()); WeatherObjectParticleStorm storm = ClientTickHandler.weatherManager.getClosestParticleStormByIntensity(posPlayer, WeatherObjectParticleStorm.StormType.SNOWSTORM); cacheIsSnowstorm = storm != null && posPlayer.distanceTo(storm.pos) < storm.getSize(); } return cacheIsSnowstorm; } } public boolean isHail() { Minecraft client = Minecraft.getInstance(); Player player = client.player; if (player == null) return false; if (player.level().getGameTime() % cacheRate == 0) { Vec3 posPlayer = new Vec3(client.player.getX(), 0, client.player.getZ()); double maxStormDist = 512 / 4 * 3; StormObject storm = ClientTickHandler.weatherManager.getClosestStorm(posPlayer, maxStormDist, StormObject.STATE_HAIL, StormObject.STATE_HAIL, false); cacheIsHail = storm != null && posPlayer.distanceTo(storm.posGround) < storm.getSize(); } return cacheIsHail; } public boolean hasWeather() { if (SceneEnhancer.FORCE_ON_DEBUG_TESTING) return true; return ClientWeatherIntegration.get().hasWeather(); } public boolean isWeatherEffectsServerSideControlled() { return Weather.isLoveTropicsInstalled(); } } ================================================ FILE: src/main/java/weather2/DeferredHelper.java ================================================ package weather2; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.function.Supplier; import net.minecraft.core.Registry; import net.minecraft.core.particles.ParticleOptions; import net.minecraft.core.particles.ParticleType; import net.minecraft.core.registries.Registries; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.sounds.SoundEvent; import net.minecraft.stats.StatFormatter; import net.minecraft.stats.StatType; import net.minecraft.stats.Stats; import net.minecraft.world.Container; import net.minecraft.world.effect.MobEffect; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.ai.attributes.Attribute; import net.minecraft.world.entity.decoration.PaintingVariant; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.MenuType; import net.minecraft.world.item.CreativeModeTab; import net.minecraft.world.item.Item; import net.minecraft.world.item.alchemy.Potion; import net.minecraft.world.item.crafting.Recipe; import net.minecraft.world.item.crafting.RecipeSerializer; import net.minecraft.world.item.crafting.RecipeType; import net.minecraft.world.item.enchantment.Enchantment; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.levelgen.feature.Feature; import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration; import net.minecraft.world.level.material.Fluid; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import net.minecraftforge.registries.RegisterEvent; import net.minecraftforge.registries.RegistryObject; public class DeferredHelper { protected final String modid; protected final Map>, List>> objects; /** * Creates a new DeferredHelper and registers it to the mod event bus. * * @param modid The modid of the owning mod. * @return A new DeferredHelper. */ public static DeferredHelper create(String modid) { DeferredHelper helper = new DeferredHelper(modid); FMLJavaModLoadingContext.get().getModEventBus().register(helper); return helper; } protected DeferredHelper(String modid) { this.modid = modid; this.objects = new IdentityHashMap<>(); } public RegistryObject block(String path, Supplier factory) { return this.create(path, Registries.BLOCK, factory); } public RegistryObject fluid(String path, Supplier factory) { return this.create(path, Registries.FLUID, factory); } public RegistryObject item(String path, Supplier factory) { return this.create(path, Registries.ITEM, factory); } public RegistryObject effect(String path, Supplier factory) { return this.create(path, Registries.MOB_EFFECT, factory); } public RegistryObject sound(String path, Supplier factory) { return this.create(path, Registries.SOUND_EVENT, factory); } public RegistryObject sound(String path) { return sound(path, () -> SoundEvent.createVariableRangeEvent(new ResourceLocation(modid, path))); } public RegistryObject potion(String path, Supplier factory) { return this.create(path, Registries.POTION, factory); } public RegistryObject enchant(String path, Supplier factory) { return this.create(path, Registries.ENCHANTMENT, factory); } public > RegistryObject entity(String path, Supplier factory) { return this.create(path, Registries.ENTITY_TYPE, factory); } public > RegistryObject blockEntity(String path, Supplier factory) { return this.create(path, Registries.BLOCK_ENTITY_TYPE, factory); } public > RegistryObject particle(String path, Supplier factory) { return this.create(path, Registries.PARTICLE_TYPE, factory); } public > RegistryObject menu(String path, Supplier factory) { return this.create(path, Registries.MENU, factory); } public RegistryObject painting(String path, Supplier factory) { return this.create(path, Registries.PAINTING_VARIANT, factory); } public , T extends RecipeType> RegistryObject recipe(String path, Supplier factory) { return this.create(path, Registries.RECIPE_TYPE, factory); } public , T extends RecipeSerializer> RegistryObject recipeSerializer(String path, Supplier factory) { return this.create(path, Registries.RECIPE_SERIALIZER, factory); } public RegistryObject attribute(String path, Supplier factory) { return this.create(path, Registries.ATTRIBUTE, factory); } public , T extends StatType> RegistryObject stat(String path, Supplier factory) { return this.create(path, Registries.STAT_TYPE, factory); } /** * Creates a custom stat with the given path and formatter.
* Calling {@link StatType#get} on {@link Stats#CUSTOM} is required for full registration, for some reason. * * @see Stats#makeCustomStat */ public RegistryObject customStat(String path, StatFormatter formatter) { return this.create(path, Registries.CUSTOM_STAT, () -> { ResourceLocation id = new ResourceLocation(this.modid, path); Stats.CUSTOM.get(id, formatter); return id; }); } public > RegistryObject feature(String path, Supplier factory) { return this.create(path, Registries.FEATURE, factory); } public RegistryObject tab(String path, Supplier factory) { return this.create(path, Registries.CREATIVE_MODE_TAB, factory); } public RegistryObject custom(String path, ResourceKey> registry, Supplier factory) { return this.create(path, registry, factory); } protected RegistryObject create(String path, ResourceKey> regKey, Supplier factory) { List> registrars = this.objects.computeIfAbsent(regKey, k -> new ArrayList<>()); ResourceLocation id = new ResourceLocation(this.modid, path); RegistryObject obj = RegistryObject.create(id, regKey, this.modid); registrars.add(new Registrar<>(id, obj, factory)); return obj; } private static MethodHandle RO_updateReference; static { try { Method m = RegistryObject.class.getDeclaredMethod("updateReference", RegisterEvent.class); m.setAccessible(true); RO_updateReference = MethodHandles.lookup().unreflect(m); } catch (Exception ex) { // Failing means we're using Neo, and RO has been replaced with DH, so this is unnecessary anyway. } } @SubscribeEvent @SuppressWarnings({ "rawtypes", "unchecked" }) public void register(RegisterEvent e) { this.objects.getOrDefault(e.getRegistryKey(), Collections.emptyList()).forEach(registrar -> { e.register((ResourceKey) e.getRegistryKey(), registrar.id, (Supplier) registrar.factory); if (RO_updateReference != null) { try { RO_updateReference.invoke(registrar.obj, e); } catch (Throwable t) { throw new RuntimeException(t); } } }); } protected static record Registrar(ResourceLocation id, RegistryObject obj, Supplier factory) { } } ================================================ FILE: src/main/java/weather2/EntityRegistry.java ================================================ package weather2; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.MobCategory; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.registries.RegistryObject; import weather2.weathersystem.storm.LightningBoltWeatherNew; @Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) public class EntityRegistry { /*@ObjectHolder(Weather.MODID + ":lightning_bolt") public static EntityType lightning_bolt;*/ public static final RegistryObject> LIGHTNING_BOLT = Weather.R.entity("lightning_bolt", () -> EntityType.Builder .of(LightningBoltWeatherNew::new, MobCategory.MISC) .noSave() .sized(0.0F, 0.0F) .clientTrackingRange(16) .updateInterval(Integer.MAX_VALUE) .build("gateway")); /* @SubscribeEvent public static void registerEntity(RegistryEvent.Register> e) { IForgeRegistry> r = e.getRegistry(); r.register( EntityType.Builder.of(LightningBoltWeatherNew::new, MobCategory.MISC) .noSave() .sized(0.0F, 0.0F) .clientTrackingRange(16) .updateInterval(Integer.MAX_VALUE) .build("lightning_bolt") .setRegistryName("lightning_bolt")); }*/ } ================================================ FILE: src/main/java/weather2/EventHandlerForge.java ================================================ package weather2; import com.corosus.coroutil.util.CULog; import com.corosus.coroutil.util.CoroUtilCompatibility; import net.minecraft.client.Minecraft; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.Level; import net.minecraft.world.level.biome.Biome; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.client.event.RegisterClientCommandsEvent; import net.minecraftforge.client.event.RenderLevelStageEvent; import net.minecraftforge.client.event.ViewportEvent; import net.minecraftforge.event.TickEvent; import net.minecraftforge.event.entity.living.LivingEvent; import net.minecraftforge.event.entity.player.PlayerEvent; import net.minecraftforge.eventbus.api.EventPriority; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import weather2.client.SceneEnhancer; import weather2.command.CommandWeather2Client; import weather2.config.ConfigDebug; import weather2.util.WeatherUtilBlock; import weather2.util.WeatherUtilEntity; import weather2.weathersystem.WeatherManagerClient; import weather2.weathersystem.wind.WindManager; @Mod.EventBusSubscriber(modid = Weather.MODID, bus = Mod.EventBusSubscriber.Bus.MOD) public class EventHandlerForge { @SubscribeEvent @OnlyIn(Dist.CLIENT) public void worldRender(RenderLevelStageEvent event) { if (event.getStage() == RenderLevelStageEvent.Stage.AFTER_WEATHER) { ClientTickHandler.getClientWeather(); } else if (event.getStage() == RenderLevelStageEvent.Stage.AFTER_PARTICLES) { if (ConfigDebug.Particle_engine_render) { ClientTickHandler.particleManagerExtended().render(event.getPoseStack(), null, Minecraft.getInstance().gameRenderer.lightTexture(), event.getCamera(), event.getPartialTick(), event.getFrustum()); } } } @SubscribeEvent(priority = EventPriority.LOWEST) @OnlyIn(Dist.CLIENT) public void onFogColors(ViewportEvent.ComputeFogColor event) { SceneEnhancer.getFogAdjuster().onFogColors(event); } @SubscribeEvent(priority = EventPriority.LOWEST) @OnlyIn(Dist.CLIENT) public void onFogRender(ViewportEvent.RenderFog event) { SceneEnhancer.getFogAdjuster().onFogRender(event); } @SubscribeEvent @OnlyIn(Dist.CLIENT) public void onRenderTick(TickEvent.RenderTickEvent event) { SceneEnhancer.renderTick(event); } @SubscribeEvent public void onEntityLivingUpdate(LivingEvent.LivingTickEvent event) { Entity ent = event.getEntity(); if (ent.level().isClientSide && (ent instanceof Player && ((Player) ent).isLocalPlayer())) { onClientPlayerUpdate(event); } /*if (!ent.level.isClientSide && ent instanceof Player) { onServerPlayerUpdate(event); }*/ } @SubscribeEvent public void onPlayerClone(PlayerEvent.Clone event) { CompoundTag tag = event.getOriginal().getPersistentData(); CompoundTag tag2 = event.getEntity().getPersistentData(); tag2.putLong("lastSandstormTime", tag.getLong("lastSandstormTime")); tag2.putLong("lastStormDeadlyTime", tag.getLong("lastStormDeadlyTime")); } public void onServerPlayerUpdate(LivingEvent.LivingTickEvent event) { Level level = event.getEntity().level(); if (level.getGameTime() % 40 == 0) { Entity ent = event.getEntity(); Biome bgb = level.getBiome(WeatherUtilBlock.getPrecipitationHeightSafe(level, new BlockPos(Mth.floor(ent.position().x), 0, Mth.floor(ent.position().z)))).get(); float biomeTemp = CoroUtilCompatibility.getAdjustedTemperature(ent.level(), bgb, new BlockPos(Mth.floor(ent.position().x), Mth.floor(ent.position().y), Mth.floor(ent.position().z))); CULog.dbg("biomeTemp: " + biomeTemp); } } @SubscribeEvent @OnlyIn(Dist.CLIENT) public void onClientPlayerUpdate(LivingEvent.LivingTickEvent event) { Entity ent = event.getEntity(); WeatherManagerClient weatherMan = ClientTickHandler.weatherManager; if (weatherMan == null) return; WindManager windMan = weatherMan.getWindManager(); if (windMan == null) return; ClientWeatherProxy weather = ClientWeatherProxy.get(); if (weather.isSnowstorm() || weather.isSandstorm()) { if (ent.onGround() && !ent.isSpectator() && !WeatherUtilEntity.isPlayerSheltered(ent)/* && ent.world.getGameTime() % 20 == 0*/) { float playerSpeed = (float) Math.sqrt(ent.getDeltaMovement().x * ent.getDeltaMovement().x + ent.getDeltaMovement().z * ent.getDeltaMovement().z); if (playerSpeed > 0.02F && playerSpeed < 0.3F) { //System.out.println("playerSpeed: " + playerSpeed); /** * Calculate the players angle from motion, compare it against wind * under 90 means theyre moving with the wind, above 90 means against the wind, 90 means perpendicular to it * scale wind assistance / resistance to wind based on dist from 0 to 90 or 90 to 180 */ float playerAngle = -(float) (Math.toDegrees(Math.atan2(ent.getDeltaMovement().x, ent.getDeltaMovement().z))); int phi = (int) (Math.abs(windMan.getWindAngle(ent.position()) - playerAngle) % 360); float diffAngle = phi > 180 ? 360 - phi : phi; //System.out.println("diffAngle: " + diffAngle); if (diffAngle < 90) { float assistRate = 1F - (diffAngle / 90F); float assist = 1F + (0.12F * assistRate); //System.out.println("assist: " + assist); ent.setDeltaMovement(ent.getDeltaMovement().x * assist, ent.getDeltaMovement().y, ent.getDeltaMovement().z * assist); } else if (diffAngle >= 90) { float dampenRate = ((diffAngle - 90F) / 90F); float dampen = 1F - (0.12F * dampenRate); //System.out.println("dampen: " + dampen); if (dampen != 0) { ent.setDeltaMovement(ent.getDeltaMovement().x * dampen, ent.getDeltaMovement().y, ent.getDeltaMovement().z * dampen); } } } } } } @SubscribeEvent public void registerCommandsClient(RegisterClientCommandsEvent event) { CommandWeather2Client.register(event.getDispatcher()); } } ================================================ FILE: src/main/java/weather2/IWindHandler.java ================================================ package weather2; public interface IWindHandler { float getWindWeight(); int getParticleDecayExtra(); } ================================================ FILE: src/main/java/weather2/IWorldData.java ================================================ package weather2; import net.minecraft.nbt.CompoundTag; public interface IWorldData { CompoundTag save(CompoundTag data); } ================================================ FILE: src/main/java/weather2/PacketNBTFromClient.java ================================================ package weather2; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.server.level.ServerPlayer; import net.minecraftforge.network.NetworkEvent; import java.util.function.Supplier; public class PacketNBTFromClient { private final CompoundTag nbt; public PacketNBTFromClient(CompoundTag nbt) { this.nbt = nbt; } public static void encode(PacketNBTFromClient msg, FriendlyByteBuf buffer) { buffer.writeNbt(msg.nbt); } public static PacketNBTFromClient decode(FriendlyByteBuf buffer) { return new PacketNBTFromClient(buffer.readNbt()); } public static class Handler { public static void handle(final PacketNBTFromClient msg, Supplier ctx) { ServerPlayer playerEntity = ctx.get().getSender(); if( playerEntity == null ) { ctx.get().setPacketHandled(true); return; } ctx.get().enqueueWork(() -> { try { CompoundTag nbt = msg.nbt; String packetCommand = nbt.getString("packetCommand"); String command = nbt.getString("command"); Weather.dbg("Weather2 packet command from client: " + packetCommand + " - " + command); if (packetCommand.equals("WeatherData")) { if (command.equals("syncFull")) { ServerTickHandler.playerClientRequestsFullSync(playerEntity); } } } catch (Exception ex) { ex.printStackTrace(); } /*ItemStack heldItem = GadgetCopyPaste.getGadget(playerEntity); if (heldItem.isEmpty()) return; BlockPos startPos = msg.start; BlockPos endPos = msg.end; if (startPos.equals(BlockPos.ZERO) && endPos.equals(BlockPos.ZERO)) { GadgetCopyPaste.setSelectedRegion(heldItem, null); playerEntity.sendStatusMessage(MessageTranslation.AREA_RESET.componentTranslation().setStyle(Styles.AQUA), true); } else { GadgetCopyPaste.setSelectedRegion(heldItem, new Region(startPos, endPos)); }*/ }); ctx.get().setPacketHandled(true); } } } ================================================ FILE: src/main/java/weather2/PacketNBTFromServer.java ================================================ package weather2; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.FriendlyByteBuf; import net.minecraftforge.network.NetworkEvent; import java.util.function.Supplier; public class PacketNBTFromServer { private final CompoundTag nbt; public PacketNBTFromServer(CompoundTag nbt) { this.nbt = nbt; } public static void encode(PacketNBTFromServer msg, FriendlyByteBuf buffer) { buffer.writeNbt(msg.nbt); } public static PacketNBTFromServer decode(FriendlyByteBuf buffer) { return new PacketNBTFromServer(buffer.readNbt()); } public static class Handler { public static void handle(final PacketNBTFromServer msg, Supplier ctx) { /*ServerPlayerEntity playerEntity = ctx.get().getSender(); if( playerEntity == null ) { ctx.get().setPacketHandled(true); return; }*/ ctx.get().enqueueWork(() -> { try { CompoundTag nbt = msg.nbt; String packetCommand = nbt.getString("packetCommand"); String command = nbt.getString("command"); //System.out.println("Weather2 packet command from server: " + packetCommand); if (packetCommand.equals("WeatherData")) { ClientTickHandler.getClientWeather(); //this line still gets NPE's despite it checking if its null right before it, wtf ClientTickHandler.weatherManager.nbtSyncFromServer(nbt); } else if (packetCommand.equals("ClientConfigData")) { if (command.equals("syncUpdate")) { ClientTickHandler.clientConfigData.readNBT(nbt); } } } catch (Exception ex) { ex.printStackTrace(); } }); ctx.get().setPacketHandled(true); } } } ================================================ FILE: src/main/java/weather2/PerlinNoiseHelper.java ================================================ package weather2; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import it.unimi.dsi.fastutil.doubles.DoubleArrayList; import it.unimi.dsi.fastutil.doubles.DoubleList; import net.minecraft.world.level.levelgen.LegacyRandomSource; import net.minecraft.world.level.levelgen.synth.NormalNoise; import net.minecraft.world.level.levelgen.synth.PerlinNoise; import net.minecraft.world.level.levelgen.synth.SimplexNoise; import java.util.List; import java.util.Random; public class PerlinNoiseHelper { public static class NoiseParameters { private final int firstOctave; private final DoubleList amplitudes; public static final Codec CODEC = RecordCodecBuilder.create((p_48510_) -> { return p_48510_.group(Codec.INT.fieldOf("firstOctave").forGetter(NoiseParameters::firstOctave), Codec.DOUBLE.listOf().fieldOf("amplitudes").forGetter(NoiseParameters::amplitudes)).apply(p_48510_, NoiseParameters::new); }); public NoiseParameters(int p_48506_, List p_48507_) { this.firstOctave = p_48506_; this.amplitudes = new DoubleArrayList(p_48507_); } public NoiseParameters(int p_151854_, double... p_151855_) { this.firstOctave = p_151854_; this.amplitudes = new DoubleArrayList(p_151855_); } public int firstOctave() { return this.firstOctave; } public DoubleList amplitudes() { return this.amplitudes; } } private SimplexNoise simplexNoise; private PerlinNoise perlinNoise; private NormalNoise normalNoise; public SimplexNoise getSimplexNoise() { return simplexNoise; } public PerlinNoise getPerlinNoise() { return perlinNoise; } public NormalNoise getNormalNoise() { return normalNoise; } private static PerlinNoiseHelper instance = null; public static PerlinNoiseHelper get() { if (instance == null) instance = new PerlinNoiseHelper(); return instance; } public PerlinNoiseHelper() { Random random = new Random(5); simplexNoise = new SimplexNoise(new LegacyRandomSource(random.nextLong())); NoiseParameters noiseParameters = new NoiseParameters(-9, 1.0D, 0.0D, 3.0D, 3.0D, 3.0D, 3.0D); this.perlinNoise = PerlinNoise.create(new LegacyRandomSource(random.nextLong()), noiseParameters.firstOctave(), noiseParameters.amplitudes()); } } ================================================ FILE: src/main/java/weather2/ServerTickHandler.java ================================================ package weather2; import com.corosus.coroutil.util.CULog; import com.corosus.modconfig.ConfigMod; import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; import net.minecraft.nbt.CompoundTag; import net.minecraft.server.level.ServerPlayer; import net.minecraft.resources.ResourceKey; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.Level; import net.minecraft.server.level.ServerLevel; import net.minecraftforge.event.TickEvent; import net.minecraftforge.event.entity.player.PlayerEvent; import net.minecraftforge.event.level.LevelEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.InterModComms; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.network.NetworkDirection; import net.minecraftforge.network.PacketDistributor; import weather2.config.ClientConfigData; import weather2.config.ConfigMisc; import weather2.config.WeatherUtilConfig; import weather2.weathersystem.WeatherManagerServer; import weather2.weathersystem.storm.StormObject; import weather2.weathersystem.wind.WindManager; import java.util.HashMap; import java.util.Map; import java.util.UUID; @Mod.EventBusSubscriber(modid = Weather.MODID) public class ServerTickHandler { private static final Map, WeatherManagerServer> MANAGERS = new Reference2ObjectOpenHashMap<>(); private static final HashMap MANAGERSLOOKUP = new HashMap<>(); @SubscribeEvent public static void onWorldLoad(LevelEvent.Load event) { LevelAccessor world = event.getLevel(); if (!world.isClientSide() && world instanceof ServerLevel) { ServerLevel serverWorld = (ServerLevel) world; ResourceKey dimension = serverWorld.dimension(); WeatherManagerServer weatherManagerServer = new WeatherManagerServer(serverWorld); if (WeatherUtilConfig.listDimensionsWeather.contains(weatherManagerServer.getWorld().dimension().location().toString())) { weatherManagerServer.read(); } MANAGERS.put(dimension, weatherManagerServer); MANAGERSLOOKUP.put(dimension.location().toString(), weatherManagerServer); } } @SubscribeEvent public static void onWorldUnload(LevelEvent.Unload event) { LevelAccessor world = event.getLevel(); if (!world.isClientSide() && world instanceof ServerLevel) { ServerLevel serverWorld = (ServerLevel) world; MANAGERS.remove(serverWorld.dimension()); MANAGERSLOOKUP.remove(serverWorld.dimension().toString()); } } @SubscribeEvent public static void tickServer(TickEvent.ServerTickEvent event) { if (event.phase == TickEvent.Phase.START) { for (WeatherManagerServer manager : MANAGERS.values()) { //for non whitelisted dimensions i chose to still tick the manager, and also register it, so it can get cleaned up if people spawn stuff or change config //if (WeatherUtilConfig.listDimensionsWeather.contains(manager.getWorld().dimension().location().toString())) { manager.tick(); //} } processIMCMessages(); } } @SubscribeEvent public static void tickServer(TickEvent.LevelTickEvent event) { //TODO: TEMPPPPPPPPPPPP //ConfigMisc.Aesthetic_Only_Mode = true; //ConfigMisc.overcastMode = true; if (event.level.dimension() == Level.OVERWORLD && event.phase == TickEvent.Phase.END && !event.level.isClientSide()) { if (ConfigMisc.Aesthetic_Only_Mode) { if (!ConfigMisc.overcastMode) { ConfigMisc.overcastMode = true; CULog.dbg("detected Aesthetic_Only_Mode on, setting overcast mode on"); //WeatherUtilConfig.setOvercastModeServerSide(ConfigMisc.overcastMode); ConfigMod.forceSaveAllFilesFromRuntimeSettings(); syncServerConfigToClient(null); } } //TODO: only sync when things change? is now sent via PlayerLoggedInEvent at least /*if (event.level.getGameTime() % 200 == 0) { syncServerConfigToClient(null); }*/ } } public static void processIMCMessages() { InterModComms.getMessages("weather2").forEach((msg) -> { CompoundTag tag = (CompoundTag) msg.messageSupplier().get(); String dimResource = tag.getString("dimension"); WeatherManagerServer wm = MANAGERSLOOKUP.get(dimResource); if (wm != null) { if (msg.method().equals("player_tornado")) { int timeTicks = tag.getInt("time_ticks"); boolean baby = tag.getBoolean("baby"); //boolean sharknado = tag.getBoolean("sharknado"); String uuid = tag.getString("uuid"); Player player = wm.getWorld().getPlayerByUUID(UUID.fromString(uuid)); if (player != null) { StormObject stormObject = new StormObject(wm); stormObject.setupStorm(player); stormObject.levelCurIntensityStage = StormObject.STATE_STAGE1; stormObject.levelStormIntensityMax = StormObject.STATE_STAGE1; stormObject.setupPlayerControlledTornado(player); stormObject.setPlayerControlledTimeLeft(timeTicks); stormObject.setBaby(baby); //stormObject.setSharknado(sharknado); wm.addStormObject(stormObject); wm.syncStormNew(stormObject); CULog.dbg("processed imc message: " + tag); } else { CULog.err("error cant find player in dimension " + dimResource + " for uuid " + uuid + " via IMC"); } } else if (msg.method().equals("sharknado")) { StormObject stormObject = new StormObject(wm); stormObject.setupStorm(null); stormObject.levelCurIntensityStage = StormObject.STATE_STAGE1; stormObject.levelStormIntensityMax = StormObject.STATE_STAGE4; stormObject.setSharknado(true); stormObject.setupTornadoAwayFromPlayersAimAtPlayers(); wm.addStormObject(stormObject); wm.syncStormNew(stormObject); CULog.dbg("processed imc message: " + tag); } else if (msg.method().equals("firenado")) { StormObject stormObject = new StormObject(wm); stormObject.setupStorm(null); stormObject.levelCurIntensityStage = StormObject.STATE_STAGE1; stormObject.levelStormIntensityMax = StormObject.STATE_STAGE4; stormObject.isFirenado = true; stormObject.setupTornadoAwayFromPlayersAimAtPlayers(); wm.addStormObject(stormObject); wm.syncStormNew(stormObject); CULog.dbg("processed imc message: " + tag); } else if (msg.method().equals("tornado")) { StormObject stormObject = new StormObject(wm); stormObject.setupStorm(null); stormObject.levelCurIntensityStage = StormObject.STATE_STAGE1; stormObject.levelStormIntensityMax = StormObject.STATE_STAGE4; stormObject.setSharknado(false); stormObject.setupTornadoAwayFromPlayersAimAtPlayers(); wm.addStormObject(stormObject); wm.syncStormNew(stormObject); CULog.dbg("processed imc message: " + tag); } } else { CULog.err("error cant find WeatherManagerServer for dimension " + dimResource + " via IMC"); } }); } @SubscribeEvent public static void tickPlayer(TickEvent.PlayerTickEvent event) { if (!event.player.level().isClientSide()) { syncServerConfigToClient(event.player); } } public static void joinPlayer(PlayerEvent.PlayerLoggedInEvent event) { } public static WeatherManagerServer getWeatherManagerFor(ResourceKey dimension) { return MANAGERS.get(dimension); } public static WeatherManagerServer getWeatherManagerFor(Level level) { return MANAGERS.get(level.dimension()); } public static void playerClientRequestsFullSync(ServerPlayer entP) { WeatherManagerServer wm = MANAGERS.get(entP.level().dimension()); if (wm != null) { wm.playerJoinedWorldSyncFull(entP); } } public static void syncServerConfigToClient(Player player) { CompoundTag data = new CompoundTag(); data.putString("packetCommand", "ClientConfigData"); data.putString("command", "syncUpdate"); ClientConfigData.writeNBT(data); if (player != null) { WeatherNetworking.HANDLER.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new PacketNBTFromServer(data)); } else { WeatherNetworking.HANDLER.send(PacketDistributor.ALL.noArg(), new PacketNBTFromServer(data)); } } } ================================================ FILE: src/main/java/weather2/ServerWeatherProxy.java ================================================ package weather2; import net.minecraft.server.level.ServerLevel; import weather2.datatypes.StormState; import weather2.ltcompat.ServerWeatherIntegration; public class ServerWeatherProxy { public static float getWindSpeed(ServerLevel level) { if (isWeatherEffectsServerSideControlled()) { return ServerWeatherIntegration.getWindSpeed(level); } else { return -1; } } public static StormState getSandstormForEverywhere(ServerLevel level) { if (isWeatherEffectsServerSideControlled()) { return ServerWeatherIntegration.getSandstormForEverywhere(level); } else { return null; } } public static StormState getSnowstormForEverywhere(ServerLevel level) { if (isWeatherEffectsServerSideControlled()) { return ServerWeatherIntegration.getSnowstormForEverywhere(level); } else { return null; } } public static boolean isWeatherEffectsServerSideControlled() { return Weather.isLoveTropicsInstalled(); } } ================================================ FILE: src/main/java/weather2/SoundRegistry.java ================================================ package weather2; import java.util.HashMap; import net.minecraft.resources.ResourceLocation; import net.minecraft.sounds.SoundEvent; import net.minecraftforge.registries.ForgeRegistries; public class SoundRegistry { private static HashMap lookupStringToEvent = new HashMap(); public static void init() { register("env.waterfall"); register("env.wind_calm"); register("env.wind_calmfade"); register("streaming.destruction"); register("streaming.destruction_0_"); register("streaming.destruction_1_"); register("streaming.destruction_2_"); register("streaming.destruction_s"); register("streaming.destructionb"); register("streaming.siren"); register("streaming.wind_close"); register("streaming.wind_close_0_"); register("streaming.wind_close_1_"); register("streaming.wind_close_2_"); register("streaming.wind_far"); register("streaming.wind_far_0_"); register("streaming.wind_far_1_"); register("streaming.wind_far_2_"); register("streaming.sandstorm_high1"); register("streaming.sandstorm_med1"); register("streaming.sandstorm_med2"); register("streaming.sandstorm_low1"); register("streaming.sandstorm_low2"); register("streaming.siren_sandstorm_1"); register("streaming.siren_sandstorm_2"); register("streaming.siren_sandstorm_3"); register("streaming.siren_sandstorm_4"); register("streaming.siren_sandstorm_5_extra"); register("streaming.siren_sandstorm_6_extra"); } public static void register(String soundPath) { ResourceLocation resLoc = new ResourceLocation(Weather.MODID, soundPath); //SoundEvent event = new SoundEvent(resLoc).setRegistryName(resLoc); SoundEvent event = SoundEvent.createVariableRangeEvent(resLoc); //TODO: WIP SoundEvent event = SoundEvent.createVariableRangeEvent(resLoc).setRegistryName(resLoc); ForgeRegistries.SOUND_EVENTS.register(resLoc, event); if (lookupStringToEvent.containsKey(soundPath)) { System.out.println("WEATHER SOUNDS WARNING: duplicate sound registration for " + soundPath); } lookupStringToEvent.put(soundPath, event); } public static SoundEvent get(String soundPath) { return lookupStringToEvent.get(soundPath); } } ================================================ FILE: src/main/java/weather2/Weather.java ================================================ package weather2; import com.corosus.coroutil.util.CULog; import com.corosus.modconfig.ConfigMod; import com.corosus.modconfig.IConfigCategory; import com.mojang.brigadier.CommandDispatcher; import extendedrenderer.ParticleRegistry2ElectricBubbleoo; import extendedrenderer.particle.ParticleRegistry; import net.minecraft.commands.CommandSourceStack; import net.minecraft.core.registries.Registries; import net.minecraft.data.DataGenerator; import net.minecraft.data.PackOutput; import net.minecraft.data.loot.LootTableProvider; import net.minecraft.network.chat.Component; import net.minecraft.world.item.CreativeModeTab; import net.minecraft.world.item.CreativeModeTabs; import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.data.ExistingFileHelper; import net.minecraftforge.data.event.GatherDataEvent; import net.minecraftforge.event.BuildCreativeModeTabContentsEvent; import net.minecraftforge.event.RegisterCommandsEvent; import net.minecraftforge.event.server.ServerStartedEvent; import net.minecraftforge.event.server.ServerStoppedEvent; import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.ModList; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.event.lifecycle.InterModProcessEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import net.minecraftforge.fml.loading.FMLEnvironment; import net.minecraftforge.registries.DeferredRegister; import net.minecraftforge.registries.RegistryObject; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import weather2.command.WeatherCommand; import weather2.config.*; import weather2.data.BlockAndItemProvider; import weather2.data.BlockLootTables; import weather2.data.WeatherRecipeProvider; import weather2.util.WeatherUtilSound; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; // The value here should match an entry in the META-INF/mods.toml file @Mod(Weather.MODID) public class Weather { // Directly reference a log4j logger. public static final Logger LOGGER = LogManager.getLogger(); public static final DeferredHelper R = DeferredHelper.create(Weather.MODID); public static final String MODID = "weather2"; public static boolean initProperNeededForWorld = true; public static List listConfigs = new ArrayList<>(); public static ConfigMisc configMisc = null; //public static final CreativeModeTab CREATIVE_TAB = new WeatherTab(); public static final DeferredRegister CREATIVE_MODE_TABS = DeferredRegister.create(Registries.CREATIVE_MODE_TAB, MODID); public static final RegistryObject WEATHER_TAB = CREATIVE_MODE_TABS.register("weather_tab", () -> CreativeModeTab.builder() .withTabsBefore(CreativeModeTabs.COMBAT) .title(Component.translatable("itemGroup.weather2")) .icon(() -> WeatherItems.WEATHER_ITEM.get().getDefaultInstance()) .displayItems((parameters, output) -> { output.accept(WeatherItems.WEATHER_ITEM.get()); output.accept(WeatherItems.BLOCK_TORNADO_SIREN_ITEM.get()); output.accept(WeatherItems.BLOCK_TORNADO_SENSOR_ITEM.get()); output.accept(WeatherItems.BLOCK_DEFLECTOR_ITEM.get()); output.accept(WeatherItems.BLOCK_FORECAST_ITEM.get()); output.accept(WeatherItems.BLOCK_SAND_LAYER_ITEM.get()); output.accept(WeatherItems.BLOCK_ANEMOMETER_ITEM.get()); output.accept(WeatherItems.BLOCK_WIND_VANE_ITEM.get()); output.accept(WeatherItems.BLOCK_WIND_TURBINE_ITEM.get()); }).build()); public Weather() { ParticleRegistry2ElectricBubbleoo.bootstrap(); IEventBus modBus = FMLJavaModLoadingContext.get().getModEventBus(); modBus.addListener(this::setup); MinecraftForge.EVENT_BUS.addListener(this::serverStop); MinecraftForge.EVENT_BUS.addListener(this::serverStart); CREATIVE_MODE_TABS.register(modBus); modBus.addListener(this::clientSetup); modBus.addListener(this::gatherData); modBus.addListener(this::processIMC); modBus.addListener(this::addCreative); MinecraftForge.EVENT_BUS.register(this); WeatherBlocks.registerHandlers(modBus); WeatherItems.registerHandlers(modBus); MinecraftForge.EVENT_BUS.register(new EventHandlerForge()); MinecraftForge.EVENT_BUS.addListener(this::registerCommands); MinecraftForge.EVENT_BUS.register(new WeatherBlocks()); new File("./config/Weather2").mkdirs(); configMisc = new ConfigMisc(); ConfigMod.addConfigFile(MODID, addConfig(configMisc)); ConfigMod.addConfigFile(MODID, addConfig(new ConfigWind())); ConfigMod.addConfigFile(MODID, addConfig(new ConfigSand())); ConfigMod.addConfigFile(MODID, addConfig(new ConfigSnow())); ConfigMod.addConfigFile(MODID, addConfig(new ConfigStorm())); ConfigMod.addConfigFile(MODID, addConfig(new ConfigTornado())); ConfigMod.addConfigFile(MODID, addConfig(new ConfigParticle())); ConfigMod.addConfigFile(MODID, addConfig(new ConfigDebug())); ConfigMod.addConfigFile(MODID, addConfig(new ConfigSound())); //ConfigMod.addConfigFile(MODID, addConfig(new ConfigFoliage())); //WeatherUtilConfig.nbtLoadDataAll(); SoundRegistry.init(); if (FMLEnvironment.dist.isClient()) { modBus.addListener(ParticleRegistry::getRegisteredParticles); modBus.addListener(ClientRegistry::registerLayerDefinitions); } } private void addCreative(BuildCreativeModeTabContentsEvent event) { if (event.getTabKey() == CreativeModeTabs.BUILDING_BLOCKS) event.accept(WeatherItems.WEATHER_ITEM); } public static IConfigCategory addConfig(IConfigCategory config) { listConfigs.add(config); return config; } private void setup(final FMLCommonSetupEvent event) { WeatherNetworking.register(); } private void clientSetup(FMLClientSetupEvent event) { WeatherUtilSound.init(); } private void processIMC(final InterModProcessEvent event) { LOGGER.info("Got IMC {}", event.getIMCStream(). map(m->m.getMessageSupplier().get()). collect(Collectors.toList())); } @SubscribeEvent public void serverStart(ServerStartedEvent event) { //initProperNeededForWorld = true; //WeatherUtil.testAllBlocks(); } @SubscribeEvent public void serverStop(ServerStoppedEvent event) { initProperNeededForWorld = true; } public static void dbg(Object obj) { CULog.dbg("" + obj); } public static boolean isLoveTropicsInstalled() { return ModList.get().isLoaded("ltminigames"); } private void registerCommands(RegisterCommandsEvent event) { CommandDispatcher dispatcher = event.getDispatcher(); WeatherCommand.register(dispatcher); } /** * * run runData for me * * @param event */ private void gatherData(GatherDataEvent event) { DataGenerator gen = event.getGenerator(); if (event.includeServer()) { gen.addProvider(event.includeServer(), new WeatherRecipeProvider(gen.getPackOutput())); gen.addProvider(event.includeServer(), new LootTableProvider(gen.getPackOutput(), Collections.emptySet(), List.of(new LootTableProvider.SubProviderEntry(BlockLootTables::new, LootContextParamSets.BLOCK)))); } if (event.includeClient()) { gatherClientData(event); } } /** * * run runData for me * * @param event */ @OnlyIn(Dist.CLIENT) private void gatherClientData(GatherDataEvent event) { DataGenerator gen = event.getGenerator(); PackOutput packOutput = gen.getPackOutput(); ExistingFileHelper existingFileHelper = event.getExistingFileHelper(); gen.addProvider(event.includeClient(), new ParticleRegistry(packOutput, existingFileHelper)); gen.addProvider(event.includeClient(), new BlockAndItemProvider(packOutput, existingFileHelper)); } } ================================================ FILE: src/main/java/weather2/WeatherBlocks.java ================================================ package weather2; import net.minecraft.world.entity.ai.village.poi.PoiType; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Item; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.SoundType; import net.minecraft.world.level.material.MapColor; import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.registries.DeferredRegister; import net.minecraftforge.registries.ForgeRegistries; import net.minecraftforge.registries.RegistryObject; import weather2.block.*; import weather2.blockentity.*; import weather2.item.WeatherItem; @Mod.EventBusSubscriber(modid = Weather.MODID, bus = Mod.EventBusSubscriber.Bus.MOD) public class WeatherBlocks { public static final String SAND_LAYER = "sand_layer"; public static final String DEFLECTOR = "weather_deflector"; public static final String TORNADO_SENSOR = "tornado_sensor"; public static final String TORNADO_SIREN = "tornado_siren"; public static final String WEATHER_MACHINE = "weather_machine"; public static final String WEATHER_FORECAST = "weather_forecast"; public static final String WIND_VANE = "wind_vane"; public static final String ANEMOMETER = "anemometer"; public static final String TORNADO_SIREN_MANUAL = "tornado_siren_manual"; public static final String SAND_LAYER_PLACEABLE = "sand_layer_placeable"; public static final String WEATHER_ITEM = "weather_item"; public static final String POCKET_SAND = "pocket_sand"; public static final String WIND_TURBINE = "wind_turbine"; private static final DeferredRegister BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, Weather.MODID); private static final DeferredRegister> BLOCK_ENTITIES = DeferredRegister.create(ForgeRegistries.BLOCK_ENTITY_TYPES, Weather.MODID); public static final RegistryObject BLOCK_SAND_LAYER = BLOCKS.register(SAND_LAYER, () -> new SandLayerBlock(BlockBehaviour.Properties.copy(Blocks.DIRT).mapColor(MapColor.SAND).strength(0.1F).sound(SoundType.SAND))); public static final RegistryObject BLOCK_DEFLECTOR = BLOCKS.register(DEFLECTOR, () -> new DeflectorBlock(BlockBehaviour.Properties.copy(Blocks.DIRT).mapColor(MapColor.STONE).strength(0.5F, 6F).sound(SoundType.STONE))); public static final RegistryObject BLOCK_FORECAST = BLOCKS.register(WEATHER_FORECAST, () -> new ForecastBlock(BlockBehaviour.Properties.of().mapColor(MapColor.STONE).strength(0.5F, 6F).sound(SoundType.STONE))); public static final RegistryObject BLOCK_TORNADO_SENSOR = BLOCKS.register(TORNADO_SENSOR, () -> new SensorBlock(BlockBehaviour.Properties.of().mapColor(MapColor.STONE).strength(0.5F, 6F).sound(SoundType.STONE))); public static final RegistryObject BLOCK_ANEMOMETER = BLOCKS.register(ANEMOMETER, () -> new AnemometerBlock(BlockBehaviour.Properties.of().mapColor(MapColor.STONE).strength(0.5F, 6F).sound(SoundType.STONE))); public static final RegistryObject BLOCK_WIND_VANE = BLOCKS.register(WIND_VANE, () -> new WindVaneBlock(BlockBehaviour.Properties.of().mapColor(MapColor.STONE).strength(0.5F, 6F).sound(SoundType.STONE))); public static final RegistryObject BLOCK_TORNADO_SIREN = BLOCKS.register(TORNADO_SIREN, () -> new SirenBlock(BlockBehaviour.Properties.of().mapColor(MapColor.STONE).strength(0.5F, 6F).sound(SoundType.STONE))); public static final RegistryObject BLOCK_WIND_TURBINE = BLOCKS.register(WIND_TURBINE, () -> new WindTurbineBlock(BlockBehaviour.Properties.of().mapColor(MapColor.STONE).strength(0.5F, 6F).sound(SoundType.STONE))); //public static final RegistryObject BLOCK_WEATHER_MACHINE = BLOCKS.register(WEATHER_MACHINE, () -> new WeatherMachineBlock(BlockBehaviour.Properties.of(Material.STONE).strength(0.5F, 6F).sound(SoundType.STONE))); @SuppressWarnings("ConstantConditions") public static final RegistryObject> BLOCK_ENTITY_DEFLECTOR = BLOCK_ENTITIES.register(DEFLECTOR, () -> BlockEntityType.Builder.of(DeflectorBlockEntity::new, BLOCK_DEFLECTOR.get()).build(null)); @SuppressWarnings("ConstantConditions") public static final RegistryObject> BLOCK_ENTITY_TORNADO_SIREN = BLOCK_ENTITIES.register(TORNADO_SIREN, () -> BlockEntityType.Builder.of(SirenBlockEntity::new, BLOCK_TORNADO_SIREN.get()).build(null)); public static final RegistryObject> BLOCK_ENTITY_TORNADO_SENSOR = BLOCK_ENTITIES.register(TORNADO_SENSOR, () -> BlockEntityType.Builder.of(SensorBlockEntity::new, BLOCK_TORNADO_SENSOR.get()).build(null)); public static final RegistryObject> BLOCK_ENTITY_ANEMOMETER = BLOCK_ENTITIES.register(ANEMOMETER, () -> BlockEntityType.Builder.of(AnemometerBlockEntity::new, BLOCK_ANEMOMETER.get()).build(null)); public static final RegistryObject> BLOCK_ENTITY_WIND_VANE = BLOCK_ENTITIES.register(WIND_VANE, () -> BlockEntityType.Builder.of(WindVaneBlockEntity::new, BLOCK_WIND_VANE.get()).build(null)); public static final RegistryObject> BLOCK_ENTITY_WIND_TURBINE = BLOCK_ENTITIES.register(WIND_TURBINE, () -> BlockEntityType.Builder.of(WindTurbineBlockEntity::new, BLOCK_WIND_TURBINE.get()).build(null)); /*public static final RegistryObject> BLOCK_ENTITY_WEATHER_MACHINE = BLOCK_ENTITIES.register(WEATHER_MACHINE, () -> BlockEntityType.Builder.of(WeatherMachineBlockEntity::new, BLOCK_WEATHER_MACHINE.get()).build(null));*/ public static void registerHandlers(IEventBus modBus) { BLOCKS.register(modBus); BLOCK_ENTITIES.register(modBus); } } ================================================ FILE: src/main/java/weather2/WeatherItems.java ================================================ package weather2; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Item; import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.registries.DeferredRegister; import net.minecraftforge.registries.ForgeRegistries; import net.minecraftforge.registries.RegistryObject; import weather2.item.WeatherItem; public class WeatherItems { private static final DeferredRegister ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, Weather.MODID); public static final RegistryObject WEATHER_ITEM = ITEMS.register(WeatherBlocks.WEATHER_ITEM, () -> new WeatherItem(new Item.Properties().stacksTo(64))); public static final RegistryObject BLOCK_DEFLECTOR_ITEM = WeatherItems.ITEMS.register(WeatherBlocks.DEFLECTOR, () -> new BlockItem(WeatherBlocks.BLOCK_DEFLECTOR.get(), new Item.Properties())); public static final RegistryObject BLOCK_TORNADO_SIREN_ITEM = ITEMS.register(WeatherBlocks.TORNADO_SIREN, () -> new BlockItem(WeatherBlocks.BLOCK_TORNADO_SIREN.get(), new Item.Properties())); public static final RegistryObject BLOCK_TORNADO_SENSOR_ITEM = ITEMS.register(WeatherBlocks.TORNADO_SENSOR, () -> new BlockItem(WeatherBlocks.BLOCK_TORNADO_SENSOR.get(), new Item.Properties())); public static final RegistryObject BLOCK_SAND_LAYER_ITEM = ITEMS.register(WeatherBlocks.SAND_LAYER, () -> new BlockItem(WeatherBlocks.BLOCK_SAND_LAYER.get(), new Item.Properties())); public static final RegistryObject BLOCK_FORECAST_ITEM = ITEMS.register(WeatherBlocks.WEATHER_FORECAST, () -> new BlockItem(WeatherBlocks.BLOCK_FORECAST.get(), new Item.Properties())); public static final RegistryObject BLOCK_ANEMOMETER_ITEM = ITEMS.register(WeatherBlocks.ANEMOMETER, () -> new BlockItem(WeatherBlocks.BLOCK_ANEMOMETER.get(), new Item.Properties())); public static final RegistryObject BLOCK_WIND_VANE_ITEM = ITEMS.register(WeatherBlocks.WIND_VANE, () -> new BlockItem(WeatherBlocks.BLOCK_WIND_VANE.get(), new Item.Properties())); public static final RegistryObject BLOCK_WIND_TURBINE_ITEM = ITEMS.register(WeatherBlocks.WIND_TURBINE, () -> new BlockItem(WeatherBlocks.BLOCK_WIND_TURBINE.get(), new Item.Properties())); public static void registerHandlers(IEventBus modBus) { ITEMS.register(modBus); } } ================================================ FILE: src/main/java/weather2/WeatherNetworking.java ================================================ package weather2; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.network.NetworkDirection; import net.minecraftforge.network.NetworkEvent; import net.minecraftforge.network.NetworkRegistry; import net.minecraftforge.network.simple.SimpleChannel; import java.util.Optional; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.Supplier; public class WeatherNetworking { private static final String PROTOCOL_VERSION = Integer.toString(4); private static short lastID = 0; public static final ResourceLocation NETWORK_CHANNEL_ID_MAIN = new ResourceLocation(Weather.MODID, "main"); public static final SimpleChannel HANDLER = NetworkRegistry.ChannelBuilder .named(NETWORK_CHANNEL_ID_MAIN) .clientAcceptedVersions(PROTOCOL_VERSION::equals) .serverAcceptedVersions(PROTOCOL_VERSION::equals) .networkProtocolVersion(() -> PROTOCOL_VERSION) .simpleChannel(); public static void register() { registerMessage(PacketNBTFromServer.class, PacketNBTFromServer::encode, PacketNBTFromServer::decode, PacketNBTFromServer.Handler::handle, NetworkDirection.PLAY_TO_CLIENT); registerMessage(PacketNBTFromClient.class, PacketNBTFromClient::encode, PacketNBTFromClient::decode, PacketNBTFromClient.Handler::handle, NetworkDirection.PLAY_TO_SERVER); } private static void registerMessage(Class messageType, BiConsumer encoder, Function decoder, BiConsumer> messageConsumer, NetworkDirection networkDirection) { HANDLER.registerMessage(lastID, messageType, encoder, decoder, messageConsumer, Optional.ofNullable(networkDirection)); lastID++; if (lastID > 0xFF) throw new RuntimeException("Too many messages!"); } } ================================================ FILE: src/main/java/weather2/WeatherTab.java ================================================ package weather2; import net.minecraft.world.item.CreativeModeTab; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.state.properties.WoodType; public class WeatherTab extends CreativeModeTab { private ItemStack tabIcon; public WeatherTab(Builder builder, ItemStack tabIcon) { super(builder); this.tabIcon = tabIcon; } /*WeatherTab() { super(Weather.MODID); } @Override public ItemStack makeIcon() { if (tabIcon == null) { tabIcon = new ItemStack(WeatherItems.WEATHER_ITEM.get()); } return tabIcon; }*/ } ================================================ FILE: src/main/java/weather2/WorldNBTData.java ================================================ package weather2; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.level.saveddata.SavedData; public class WorldNBTData extends SavedData { private CompoundTag data; private IWorldData dataHandler; public WorldNBTData() { this.data = new CompoundTag(); } public WorldNBTData(CompoundTag data) { this.data = data; } public void setDataHandler(IWorldData dataHandler) { this.dataHandler = dataHandler; } public static WorldNBTData load(CompoundTag p_151484_) { return new WorldNBTData(p_151484_); } @Override public CompoundTag save(CompoundTag p_77763_) { dataHandler.save(p_77763_); return p_77763_; } public CompoundTag getData() { return data; } @Override //backwards compat, the data is always changing public boolean isDirty() { return true; } } ================================================ FILE: src/main/java/weather2/block/AnemometerBlock.java ================================================ package weather2.block; import net.minecraft.ChatFormatting; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.network.chat.Component; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.BaseEntityBlock; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.RenderShape; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityTicker; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.StateDefinition; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.BooleanProperty; import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import org.jetbrains.annotations.Nullable; import weather2.WeatherBlocks; import weather2.blockentity.AnemometerBlockEntity; import weather2.blockentity.SensorBlockEntity; import java.util.List; public class AnemometerBlock extends BaseEntityBlock { public static final VoxelShape SHAPE = box(6.0, 0.0, 6.0, 10.0, 16.0, 10.0); public static final void register() {} public AnemometerBlock(Properties properties) { super(properties); } @Override protected void createBlockStateDefinition(StateDefinition.Builder pBuilder) { } @Override public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) { return SHAPE; } @Override public RenderShape getRenderShape(BlockState p_49232_) { return RenderShape.INVISIBLE; } @Override @OnlyIn(Dist.CLIENT) public void appendHoverText(ItemStack stack, BlockGetter worldIn, List tooltip, TooltipFlag flagIn) { super.appendHoverText(stack, worldIn, tooltip, flagIn); //tooltip.add(Component.translatable(this.getDescriptionId() + ".desc").withStyle(ChatFormatting.GRAY)); } @Nullable @Override public BlockEntity newBlockEntity(BlockPos p_153215_, BlockState p_153216_) { return new AnemometerBlockEntity(p_153215_, p_153216_); } @Nullable @Override public BlockEntityTicker getTicker(Level p_153212_, BlockState p_153213_, BlockEntityType p_153214_) { return createTickerHelper(p_153214_, WeatherBlocks.BLOCK_ENTITY_ANEMOMETER.get(), AnemometerBlockEntity::tick); } @Nullable @SuppressWarnings("unchecked") private static BlockEntityTicker createTicker(final BlockEntityType type, final BlockEntityType tickerType, final BlockEntityTicker ticker) { return tickerType == type ? (BlockEntityTicker) ticker : null; } } ================================================ FILE: src/main/java/weather2/block/DeflectorBlock.java ================================================ package weather2.block; import net.minecraft.core.BlockPos; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.BaseEntityBlock; import net.minecraft.world.level.block.RenderShape; import net.minecraft.world.level.block.entity.BellBlockEntity; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityTicker; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import org.jetbrains.annotations.Nullable; import weather2.WeatherBlocks; import weather2.blockentity.DeflectorBlockEntity; public class DeflectorBlock extends BaseEntityBlock { public DeflectorBlock(Properties p_49224_) { super(p_49224_); } @Nullable @Override public BlockEntity newBlockEntity(BlockPos p_153215_, BlockState p_153216_) { return new DeflectorBlockEntity(p_153215_, p_153216_); } @Override public RenderShape getRenderShape(BlockState p_49232_) { return RenderShape.MODEL; } @Nullable @Override public BlockEntityTicker getTicker(Level p_153212_, BlockState p_153213_, BlockEntityType p_153214_) { return createTickerHelper(p_153214_, WeatherBlocks.BLOCK_ENTITY_DEFLECTOR.get(), DeflectorBlockEntity::tickHelper); } @Override public void onRemove(BlockState p_60515_, Level level, BlockPos pos, BlockState p_60518_, boolean p_60519_) { //System.out.println("removed deflector block"); if (level.getBlockEntity(pos) instanceof DeflectorBlockEntity deflectorBlockEntity) { deflectorBlockEntity.blockBroken(); } super.onRemove(p_60515_, level, pos, p_60518_, p_60519_); } } ================================================ FILE: src/main/java/weather2/block/ForecastBlock.java ================================================ package weather2.block; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.RenderShape; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; import weather2.ServerTickHandler; import weather2.config.ConfigStorm; import weather2.weathersystem.WeatherManagerServer; public class ForecastBlock extends Block { public ForecastBlock(Properties p_49224_) { super(p_49224_); } @Override public RenderShape getRenderShape(BlockState p_49232_) { return RenderShape.MODEL; } @Override public InteractionResult use(BlockState p_60503_, Level p_60504_, BlockPos p_60505_, Player p_60506_, InteractionHand p_60507_, BlockHitResult p_60508_) { if (!p_60504_.isClientSide) { WeatherManagerServer wm = ServerTickHandler.getWeatherManagerFor(p_60506_.level().dimension()); float chance = wm.getBiomeBasedStormSpawnChanceInArea(new BlockPos(p_60506_.blockPosition())); float chanceEvery10Days = 0; int rateOften; int rateLessOften; int day = 20*60*20; int diceRollRate = ConfigStorm.Storm_AllTypes_TickRateDelay; if (ConfigStorm.Server_Storm_Deadly_UseGlobalRate) { rateOften = ConfigStorm.Server_Storm_Deadly_TimeBetweenInTicks / day; rateLessOften = ConfigStorm.Server_Storm_Deadly_TimeBetweenInTicks_Land_Based / day; chanceEvery10Days = ConfigStorm.Server_Storm_Deadly_OddsTo1_Land_Based; } else { rateOften = ConfigStorm.Player_Storm_Deadly_TimeBetweenInTicks / day; rateLessOften = ConfigStorm.Player_Storm_Deadly_TimeBetweenInTicks_Land_Based / day; chanceEvery10Days = ConfigStorm.Player_Storm_Deadly_OddsTo1_Land_Based; } if (chanceEvery10Days > 0 && diceRollRate > 0) { chanceEvery10Days = ((float)day / (float)diceRollRate) / chanceEvery10Days; } p_60506_.sendSystemMessage(Component.literal(String.format("Chance of a deadly storm here every:"))); p_60506_.sendSystemMessage(Component.literal(String.format("%d days: %.2f", rateOften, (chance * 100F)) + "% from nearby biome temperature differences")); p_60506_.sendSystemMessage(Component.literal(String.format("%d days: %.2f", rateLessOften, (chanceEvery10Days * 100F)) + "% from randomly trying once a day")); int count = 0; for (int i = 0; i < 100000; i++) { if (p_60504_.random.nextInt(1000) == 0) { count++; } } //System.out.println(count); } return InteractionResult.CONSUME; } } ================================================ FILE: src/main/java/weather2/block/SandLayerBlock.java ================================================ package weather2.block; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.pathfinder.PathComputationType; import net.minecraft.world.level.block.state.properties.IntegerProperty; import net.minecraft.world.level.block.state.StateDefinition; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.core.Direction; import net.minecraft.core.BlockPos; import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.LightLayer; import net.minecraft.server.level.ServerLevel; import javax.annotation.Nullable; import java.util.Random; import net.minecraft.world.level.block.state.BlockBehaviour.Properties; public class SandLayerBlock extends Block { public static final IntegerProperty LAYERS = BlockStateProperties.LAYERS; protected static final VoxelShape[] SHAPES = new VoxelShape[]{Shapes.empty(), Block.box(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D), Block.box(0.0D, 0.0D, 0.0D, 16.0D, 4.0D, 16.0D), Block.box(0.0D, 0.0D, 0.0D, 16.0D, 6.0D, 16.0D), Block.box(0.0D, 0.0D, 0.0D, 16.0D, 8.0D, 16.0D), Block.box(0.0D, 0.0D, 0.0D, 16.0D, 10.0D, 16.0D), Block.box(0.0D, 0.0D, 0.0D, 16.0D, 12.0D, 16.0D), Block.box(0.0D, 0.0D, 0.0D, 16.0D, 14.0D, 16.0D), Block.box(0.0D, 0.0D, 0.0D, 16.0D, 16.0D, 16.0D)}; public SandLayerBlock(Properties properties) { super(properties); this.registerDefaultState(this.stateDefinition.any().setValue(LAYERS, Integer.valueOf(1))); } public boolean isPathfindable(BlockState state, BlockGetter worldIn, BlockPos pos, PathComputationType type) { switch(type) { case LAND: return state.getValue(LAYERS) < 5; case WATER: return false; case AIR: return false; default: return false; } } public VoxelShape getShape(BlockState state, BlockGetter worldIn, BlockPos pos, CollisionContext context) { return SHAPES[state.getValue(LAYERS)]; } public VoxelShape getCollisionShape(BlockState state, BlockGetter worldIn, BlockPos pos, CollisionContext context) { return SHAPES[state.getValue(LAYERS) - 1]; } public VoxelShape getBlockSupportShape(BlockState state, BlockGetter reader, BlockPos pos) { return SHAPES[state.getValue(LAYERS)]; } public VoxelShape getVisualShape(BlockState state, BlockGetter reader, BlockPos pos, CollisionContext context) { return SHAPES[state.getValue(LAYERS)]; } public boolean useShapeForLightOcclusion(BlockState state) { return true; } public boolean canSurvive(BlockState state, LevelReader worldIn, BlockPos pos) { BlockState blockstate = worldIn.getBlockState(pos.below()); if (!blockstate.is(Blocks.ICE) && !blockstate.is(Blocks.PACKED_ICE) && !blockstate.is(Blocks.BARRIER)) { if (!blockstate.is(Blocks.HONEY_BLOCK) && !blockstate.is(Blocks.SOUL_SAND)) { return Block.isFaceFull(blockstate.getCollisionShape(worldIn, pos.below()), Direction.UP) || blockstate.getBlock() == this && blockstate.getValue(LAYERS) == 8; } else { return true; } } else { return false; } } /** * Update the provided state given the provided neighbor facing and neighbor state, returning a new state. * For example, fences make their connections to the passed in state if possible, and wet concrete powder immediately * returns its solidified counterpart. * Note that this method should ideally consider only the specific face passed in. */ public BlockState updateShape(BlockState stateIn, Direction facing, BlockState facingState, LevelAccessor worldIn, BlockPos currentPos, BlockPos facingPos) { return !stateIn.canSurvive(worldIn, currentPos) ? Blocks.AIR.defaultBlockState() : super.updateShape(stateIn, facing, facingState, worldIn, currentPos, facingPos); } /** * Performs a random tick on a block. */ public void randomTick(BlockState state, ServerLevel worldIn, BlockPos pos, Random random) { if (worldIn.getBrightness(LightLayer.BLOCK, pos) > 11) { dropResources(state, worldIn, pos); worldIn.removeBlock(pos, false); } } public boolean canBeReplaced(BlockState state, BlockPlaceContext useContext) { int i = state.getValue(LAYERS); return i == 1; } @Nullable public BlockState getStateForPlacement(BlockPlaceContext context) { BlockState blockstate = context.getLevel().getBlockState(context.getClickedPos()); if (blockstate.is(this)) { int i = blockstate.getValue(LAYERS); return blockstate.setValue(LAYERS, Integer.valueOf(Math.min(8, i + 1))); } else { return super.getStateForPlacement(context); } } protected void createBlockStateDefinition(StateDefinition.Builder builder) { builder.add(LAYERS); } } ================================================ FILE: src/main/java/weather2/block/SensorBlock.java ================================================ package weather2.block; import net.minecraft.ChatFormatting; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.network.chat.Component; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.BaseEntityBlock; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.RenderShape; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityTicker; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.StateDefinition; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.BooleanProperty; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import org.jetbrains.annotations.Nullable; import weather2.WeatherBlocks; import weather2.blockentity.SensorBlockEntity; import java.util.List; public class SensorBlock extends BaseEntityBlock { public static final BooleanProperty POWERED = BlockStateProperties.POWERED; public static final void register() {} public SensorBlock(Properties properties) { super(properties); this.registerDefaultState(this.stateDefinition.any().setValue(POWERED, Boolean.valueOf(false))); } @Override protected void createBlockStateDefinition(StateDefinition.Builder pBuilder) { pBuilder.add(POWERED); } @Override public RenderShape getRenderShape(BlockState p_49232_) { return RenderShape.MODEL; } @Override @OnlyIn(Dist.CLIENT) public void appendHoverText(ItemStack stack, BlockGetter worldIn, List tooltip, TooltipFlag flagIn) { super.appendHoverText(stack, worldIn, tooltip, flagIn); //tooltip.add(Component.translatable(this.getDescriptionId() + ".desc").withStyle(ChatFormatting.GRAY)); } @Nullable @Override public BlockEntity newBlockEntity(BlockPos p_153215_, BlockState p_153216_) { return new SensorBlockEntity(p_153215_, p_153216_); } @Nullable @Override public BlockEntityTicker getTicker(Level p_153212_, BlockState p_153213_, BlockEntityType p_153214_) { return createTickerHelper(p_153214_, WeatherBlocks.BLOCK_ENTITY_TORNADO_SENSOR.get(), SensorBlockEntity::tick); } @Nullable @SuppressWarnings("unchecked") private static BlockEntityTicker createTicker(final BlockEntityType type, final BlockEntityType tickerType, final BlockEntityTicker ticker) { return tickerType == type ? (BlockEntityTicker) ticker : null; } @Override public int getSignal(BlockState pState, BlockGetter pLevel, BlockPos pPos, Direction pDirection) { return pState.getValue(POWERED) ? 15 : 0; } @Override public int getDirectSignal(BlockState pBlockState, BlockGetter pBlockAccess, BlockPos pPos, Direction pSide) { return pBlockState.getValue(POWERED) ? 15 : 0; } @Override public boolean isSignalSource(BlockState pState) { return true; } public BlockState setPoweredState(BlockState pState, Level pLevel, BlockPos pPos, boolean state) { pLevel.setBlock(pPos, pState.setValue(POWERED, Boolean.valueOf(state)), 3); pLevel.updateNeighborsAt(pPos, this); return pState; } public BlockState toggle(BlockState pState, Level pLevel, BlockPos pPos) { pState = pState.cycle(POWERED); pLevel.setBlock(pPos, pState, 3); pLevel.updateNeighborsAt(pPos, this); return pState; } } ================================================ FILE: src/main/java/weather2/block/SirenBlock.java ================================================ package weather2.block; import net.minecraft.core.BlockPos; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.BaseEntityBlock; import net.minecraft.world.level.block.RenderShape; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityTicker; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import org.jetbrains.annotations.Nullable; import weather2.WeatherBlocks; import weather2.blockentity.SirenBlockEntity; public class SirenBlock extends BaseEntityBlock { public SirenBlock(Properties p_49224_) { super(p_49224_); } @Nullable @Override public BlockEntity newBlockEntity(BlockPos p_153215_, BlockState p_153216_) { return new SirenBlockEntity(p_153215_, p_153216_); } @Override public RenderShape getRenderShape(BlockState p_49232_) { return RenderShape.MODEL; } @Nullable @Override public BlockEntityTicker getTicker(Level p_153212_, BlockState p_153213_, BlockEntityType p_153214_) { return createTickerHelper(p_153214_, WeatherBlocks.BLOCK_ENTITY_TORNADO_SIREN.get(), SirenBlockEntity::tickHelper); } } ================================================ FILE: src/main/java/weather2/block/WeatherMachineBlock.java ================================================ package weather2.block; import net.minecraft.core.BlockPos; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.BaseEntityBlock; import net.minecraft.world.level.block.RenderShape; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityTicker; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import org.jetbrains.annotations.Nullable; import weather2.WeatherBlocks; import weather2.blockentity.DeflectorBlockEntity; import weather2.blockentity.WeatherMachineBlockEntity; public class WeatherMachineBlock extends BaseEntityBlock { public WeatherMachineBlock(Properties p_49224_) { super(p_49224_); } @Nullable @Override public BlockEntity newBlockEntity(BlockPos p_153215_, BlockState p_153216_) { return new WeatherMachineBlockEntity(p_153215_, p_153216_); } @Override public RenderShape getRenderShape(BlockState p_49232_) { return RenderShape.MODEL; } /*@Nullable @Override public BlockEntityTicker getTicker(Level p_153212_, BlockState p_153213_, BlockEntityType p_153214_) { return createTickerHelper(p_153214_, WeatherBlocks.BLOCK_ENTITY_WEATHER_MACHINE.get(), WeatherMachineBlockEntity::tickHelper); }*/ } ================================================ FILE: src/main/java/weather2/block/WindTurbineBlock.java ================================================ package weather2.block; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.BaseEntityBlock; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.RenderShape; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityTicker; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.StateDefinition; import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import org.jetbrains.annotations.Nullable; import weather2.WeatherBlocks; import weather2.blockentity.WindTurbineBlockEntity; import weather2.blockentity.WindVaneBlockEntity; import java.util.List; public class WindTurbineBlock extends BaseEntityBlock { public static final VoxelShape SHAPE = box(2.0, 0.0, 2.0, 14.0, 32.0, 14.0); public static final void register() {} public WindTurbineBlock(Properties properties) { super(properties); } @Override protected void createBlockStateDefinition(StateDefinition.Builder pBuilder) { } @Override public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) { return SHAPE; } @Override public RenderShape getRenderShape(BlockState p_49232_) { return RenderShape.INVISIBLE; } @Override @OnlyIn(Dist.CLIENT) public void appendHoverText(ItemStack stack, BlockGetter worldIn, List tooltip, TooltipFlag flagIn) { super.appendHoverText(stack, worldIn, tooltip, flagIn); //tooltip.add(Component.translatable(this.getDescriptionId() + ".desc").withStyle(ChatFormatting.GRAY)); } @Nullable @Override public BlockEntity newBlockEntity(BlockPos p_153215_, BlockState p_153216_) { return new WindTurbineBlockEntity(p_153215_, p_153216_); } @Nullable @Override public BlockEntityTicker getTicker(Level p_153212_, BlockState p_153213_, BlockEntityType p_153214_) { return createTickerHelper(p_153214_, WeatherBlocks.BLOCK_ENTITY_WIND_TURBINE.get(), WindTurbineBlockEntity::tick); } @Nullable @SuppressWarnings("unchecked") private static BlockEntityTicker createTicker(final BlockEntityType type, final BlockEntityType tickerType, final BlockEntityTicker ticker) { return tickerType == type ? (BlockEntityTicker) ticker : null; } } ================================================ FILE: src/main/java/weather2/block/WindVaneBlock.java ================================================ package weather2.block; import net.minecraft.ChatFormatting; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.network.chat.Component; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.BaseEntityBlock; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.RenderShape; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityTicker; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.StateDefinition; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.BooleanProperty; import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import org.jetbrains.annotations.Nullable; import weather2.WeatherBlocks; import weather2.blockentity.WindVaneBlockEntity; import java.util.List; public class WindVaneBlock extends BaseEntityBlock { public static final VoxelShape SHAPE = box(6.0, 0.0, 6.0, 10.0, 16.0, 10.0); public static final void register() {} public WindVaneBlock(Properties properties) { super(properties); } @Override protected void createBlockStateDefinition(StateDefinition.Builder pBuilder) { } @Override public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) { return SHAPE; } @Override public RenderShape getRenderShape(BlockState p_49232_) { return RenderShape.INVISIBLE; } @Override @OnlyIn(Dist.CLIENT) public void appendHoverText(ItemStack stack, BlockGetter worldIn, List tooltip, TooltipFlag flagIn) { super.appendHoverText(stack, worldIn, tooltip, flagIn); //tooltip.add(Component.translatable(this.getDescriptionId() + ".desc").withStyle(ChatFormatting.GRAY)); } @Nullable @Override public BlockEntity newBlockEntity(BlockPos p_153215_, BlockState p_153216_) { return new WindVaneBlockEntity(p_153215_, p_153216_); } @Nullable @Override public BlockEntityTicker getTicker(Level p_153212_, BlockState p_153213_, BlockEntityType p_153214_) { return createTickerHelper(p_153214_, WeatherBlocks.BLOCK_ENTITY_WIND_VANE.get(), WindVaneBlockEntity::tick); } @Nullable @SuppressWarnings("unchecked") private static BlockEntityTicker createTicker(final BlockEntityType type, final BlockEntityType tickerType, final BlockEntityTicker ticker) { return tickerType == type ? (BlockEntityTicker) ticker : null; } } ================================================ FILE: src/main/java/weather2/blockentity/AnemometerBlockEntity.java ================================================ package weather2.blockentity; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.util.Mth; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec3; import weather2.WeatherBlocks; import weather2.block.AnemometerBlock; import weather2.util.WeatherUtilEntity; import weather2.util.WindReader; public class AnemometerBlockEntity extends BlockEntity { public float smoothAngle = 0; public float smoothAnglePrev = 0; public float smoothAngleRotationalVel = 0; public boolean isOutsideCached = false; public AnemometerBlockEntity(BlockPos p_155229_, BlockState p_155230_) { super(WeatherBlocks.BLOCK_ENTITY_ANEMOMETER.get(), p_155229_, p_155230_); } @Override public void setLevel(final Level level) { super.setLevel(level); } public static void tick(Level level, BlockPos pos, BlockState state, AnemometerBlockEntity entity) { entity.tick(level, pos, state); } public void tick(Level level, BlockPos pos, BlockState state) { if (!level.isClientSide) { } else { if (level.getGameTime() % 40 == 0) { isOutsideCached = WeatherUtilEntity.isPosOutside(level, new Vec3(getBlockPos().getX()+0.5F, getBlockPos().getY()+0.5F, getBlockPos().getZ()+0.5F), false, true); } if (isOutsideCached) { //do not cache for this, the amp value used will mess with the turbines amp, design oversight float windSpeed = WindReader.getWindSpeed(level, pos, 1); float rotMax = 50F; float maxSpeed = (windSpeed / 1.2F) * rotMax; if (smoothAngleRotationalVel < maxSpeed) { smoothAngleRotationalVel += windSpeed * 0.3F; } if (smoothAngleRotationalVel > rotMax) smoothAngleRotationalVel = rotMax; if (smoothAngle >= 180) smoothAngle -= 360; } smoothAnglePrev = smoothAngle; smoothAngle += smoothAngleRotationalVel; smoothAngleRotationalVel -= 0.01F; smoothAngleRotationalVel *= 0.99F; if (smoothAngleRotationalVel <= 0) smoothAngleRotationalVel = 0; } } @Override public void load(final CompoundTag tag) { super.load(tag); } @Override protected void saveAdditional(final CompoundTag tag) { super.saveAdditional(tag); } } ================================================ FILE: src/main/java/weather2/blockentity/DeflectorBlockEntity.java ================================================ package weather2.blockentity; import com.corosus.coroutil.util.CULog; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import weather2.ServerTickHandler; import weather2.WeatherBlocks; import weather2.weathersystem.WeatherManagerServer; public class DeflectorBlockEntity extends BlockEntity { private boolean needsInit = true; public DeflectorBlockEntity(BlockPos p_155229_, BlockState p_155230_) { super(WeatherBlocks.BLOCK_ENTITY_DEFLECTOR.get(), p_155229_, p_155230_); } public static void tickHelper(Level level, BlockPos pos, BlockState state, BlockEntity blockEntity) { ((DeflectorBlockEntity)blockEntity).tick(); } public void tick() { if (needsInit) { needsInit = false; init(); } } public void init() { if (!level.isClientSide()) { ServerTickHandler.getWeatherManagerFor(level).registerDeflector(getBlockPos()); } } public void blockBroken() { WeatherManagerServer weatherManagerServer = ServerTickHandler.getWeatherManagerFor(level); if (weatherManagerServer != null) { //CULog.dbg("removing weather deflector poi at " + getBlockPos()); weatherManagerServer.removeDeflector(getBlockPos()); } } @Override public void setRemoved() { super.setRemoved(); } } ================================================ FILE: src/main/java/weather2/blockentity/SensorBlockEntity.java ================================================ package weather2.blockentity; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec3; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import weather2.ClientTickHandler; import weather2.ServerTickHandler; import weather2.WeatherBlocks; import weather2.block.SensorBlock; import weather2.config.ConfigMisc; import weather2.weathersystem.WeatherManagerServer; import weather2.weathersystem.storm.StormObject; public class SensorBlockEntity extends BlockEntity { public SensorBlockEntity(BlockPos p_155229_, BlockState p_155230_) { super(WeatherBlocks.BLOCK_ENTITY_TORNADO_SENSOR.get(), p_155229_, p_155230_); } @Override public void setLevel(final Level level) { super.setLevel(level); } public static void tick(Level level, BlockPos pos2, BlockState state, SensorBlockEntity entity) { if (!level.isClientSide && level.getGameTime() % 100 == 0) { WeatherManagerServer wm = ServerTickHandler.getWeatherManagerFor(level); if (wm != null) { Vec3 pos = new Vec3(pos2.getX(), pos2.getY(), pos2.getZ()); StormObject so = wm.getClosestStorm(pos, ConfigMisc.sirenActivateDistance, StormObject.STATE_FORMING); if (so != null) { entity.setPoweredState(true); } else { entity.setPoweredState(false); } } } } public void setPoweredState(boolean state) { BlockState blockState = level.getBlockState(this.getBlockPos()); if (blockState.getBlock() instanceof SensorBlock) { ((SensorBlock) blockState.getBlock()).setPoweredState(this.getBlockState(), level, this.getBlockPos(), state); } } @Override public void load(final CompoundTag tag) { super.load(tag); } @Override protected void saveAdditional(final CompoundTag tag) { super.saveAdditional(tag); } } ================================================ FILE: src/main/java/weather2/blockentity/SirenBlockEntity.java ================================================ package weather2.blockentity; import com.corosus.coroutil.util.CoroUtilMisc; import net.minecraft.core.BlockPos; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec3; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import weather2.ClientTickHandler; import weather2.WeatherBlocks; import weather2.config.ConfigMisc; import weather2.config.ConfigSand; import weather2.config.ConfigSound; import weather2.util.WeatherUtilSound; import weather2.weathersystem.storm.StormObject; import weather2.weathersystem.storm.WeatherObjectParticleStorm; import java.util.List; public class SirenBlockEntity extends BlockEntity { public long lastPlayTime = 0L; public SirenBlockEntity(BlockPos p_155229_, BlockState p_155230_) { super(WeatherBlocks.BLOCK_ENTITY_TORNADO_SIREN.get(), p_155229_, p_155230_); } public static void tickHelper(Level level, BlockPos pos, BlockState state, BlockEntity blockEntity) { ((SirenBlockEntity)blockEntity).tick(); } public void tick() { if (level.isClientSide()) { tickClient(); } } @OnlyIn(Dist.CLIENT) public void tickClient() { if (this.lastPlayTime < System.currentTimeMillis()) { Vec3 pos = new Vec3(getBlockPos().getX(), getBlockPos().getY(), getBlockPos().getZ()); StormObject so = ClientTickHandler.weatherManager.getClosestStorm(pos, ConfigMisc.sirenActivateDistance, StormObject.STATE_FORMING); if (so != null) { this.lastPlayTime = System.currentTimeMillis() + 13000L; WeatherUtilSound.playNonMovingSound(pos, "streaming.siren", (float) ConfigSound.sirenVolume, 1.0F, 120); } else { if (!ConfigSand.Sandstorm_Siren_PleaseNoDarude) { WeatherObjectParticleStorm storm = ClientTickHandler.weatherManager.getClosestParticleStormByIntensity(pos, WeatherObjectParticleStorm.StormType.SANDSTORM); if (storm == null) { storm = ClientTickHandler.weatherManager.getClosestParticleStormByIntensity(pos, WeatherObjectParticleStorm.StormType.SNOWSTORM); } if (storm != null) { if (pos.distanceTo(storm.pos) < storm.getSize()) { String soundToPlay = "siren_sandstorm_5_extra"; if (CoroUtilMisc.random.nextBoolean()) { soundToPlay = "siren_sandstorm_6_extra"; } float distScaleFunnyPitchChangeHaha = Math.max(0.1F, 1F - (float) ((pos.distanceTo(storm.pos)) / storm.getSize())); this.lastPlayTime = System.currentTimeMillis() + 15000L;//WeatherUtilSound.soundToLength.get(soundToPlay) - 500L; WeatherUtilSound.playNonMovingSound(pos, "streaming." + soundToPlay, (float) ConfigSound.sirenVolume, distScaleFunnyPitchChangeHaha, storm.getSize()); } } } } } } } ================================================ FILE: src/main/java/weather2/blockentity/WeatherMachineBlockEntity.java ================================================ package weather2.blockentity; import net.minecraft.core.BlockPos; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import weather2.WeatherBlocks; public class WeatherMachineBlockEntity extends BlockEntity { public WeatherMachineBlockEntity(BlockPos p_155229_, BlockState p_155230_) { super(WeatherBlocks.BLOCK_ENTITY_DEFLECTOR.get(), p_155229_, p_155230_); } public static void tickHelper(Level level, BlockPos pos, BlockState state, BlockEntity blockEntity) { } } ================================================ FILE: src/main/java/weather2/blockentity/WindTurbineBlockEntity.java ================================================ package weather2.blockentity; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec3; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.ForgeCapabilities; import net.minecraftforge.common.util.LazyOptional; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import weather2.WeatherBlocks; import weather2.config.ConfigWind; import weather2.energy.EnergyManager; import weather2.util.WeatherUtilEntity; import weather2.util.WindReader; import weather2.weathersystem.WeatherManager; public class WindTurbineBlockEntity extends BlockEntity { public float smoothAngle = 0; public float smoothAnglePrev = 0; public float smoothAngleRotationalVel = 0; public boolean isOutsideCached = false; private boolean needsInit = true; //private final EnergyManager energyManager; private LazyOptional energy; private EnergyManager energyManager; //amount generated at windspeed of 1, theoretical max windspeed is 2 when tornado right on top of it private int maxNormalGenerated = ConfigWind.Wind_Turbine_FE_Generated_Per_Tick; private int capacity = maxNormalGenerated * 2; private int maxTransfer = capacity; private float lastWindSpeed = 0; public WindTurbineBlockEntity(BlockPos p_155229_, BlockState p_155230_) { super(WeatherBlocks.BLOCK_ENTITY_WIND_TURBINE.get(), p_155229_, p_155230_); this.energyManager = new EnergyManager(maxTransfer, capacity); this.energy = LazyOptional.of(() -> this.energyManager); } @Override public void setLevel(final Level level) { super.setLevel(level); } public static void tick(Level level, BlockPos pos, BlockState state, WindTurbineBlockEntity entity) { entity.tick(level, pos, state); } public void tick(Level level, BlockPos pos, BlockState state) { if (needsInit) { needsInit = false; updateIsOutside(); } if (level.getGameTime() % 100 == 0) { updateIsOutside(); } if (isOutsideCached) { if (level.getGameTime() % 20 == 0) { WeatherManager weatherManager = WindReader.getWeatherManagerFor(level); if (weatherManager != null) { lastWindSpeed = weatherManager.getWindManager().getWindSpeedPositional(getBlockPos(), 2, false); } } } if (!level.isClientSide) { if (isOutsideCached) { this.energyManager.addEnergy((int) (maxNormalGenerated * lastWindSpeed)); outputEnergy(); } } else { if (isOutsideCached) { float windSpeed = lastWindSpeed;//WindReader.getWindSpeed(level); float rotMax = 100F; float maxSpeed = (windSpeed / 2F) * rotMax; if (smoothAngleRotationalVel < maxSpeed) { smoothAngleRotationalVel += windSpeed * 0.3F; } if (smoothAngleRotationalVel > rotMax) smoothAngleRotationalVel = rotMax; if (smoothAngle >= 180) smoothAngle -= 360; } smoothAnglePrev = smoothAngle; smoothAngle += smoothAngleRotationalVel; smoothAngleRotationalVel -= 0.01F; smoothAngleRotationalVel *= 0.99F; if (smoothAngleRotationalVel <= 0) smoothAngleRotationalVel = 0; } } public void updateIsOutside() { isOutsideCached = WeatherUtilEntity.isPosOutside(level, new Vec3(getBlockPos().getX()+0.5F, getBlockPos().getY()+0.5F, getBlockPos().getZ()+0.5F), false, true); } public void outputEnergy() { //System.out.println(this.energyManager.getEnergyStored()); if (this.energyManager.getEnergyStored() >= this.energyManager.getMaxExtract() && this.energyManager.canExtract()) { for (final var direction : Direction.values()) { final BlockEntity be = this.level.getBlockEntity(this.worldPosition.relative(direction)); if (be == null) { continue; } be.getCapability(ForgeCapabilities.ENERGY, direction.getOpposite()).ifPresent(storage -> { if (be != this && storage.getEnergyStored() < storage.getMaxEnergyStored()) { this.energyManager.drainEnergy(this.energyManager.getMaxExtract()); //Weather.LOGGER.info("Send: {}", this.energyManager.getMaxExtract()); final int received = storage.receiveEnergy(this.energyManager.getMaxExtract(), false); //Weather.LOGGER.info("Final Received: {}", received); } }); } } } @Override public void load(final CompoundTag tag) { super.load(tag); } @Override protected void saveAdditional(final CompoundTag tag) { super.saveAdditional(tag); } @Override public @NotNull LazyOptional getCapability(@NotNull Capability cap, @Nullable Direction side) { LazyOptional energyCapability = energyManager.getCapability(cap); if (energyCapability.isPresent()) { return energyCapability; } return super.getCapability(cap, side); } @Override public void invalidateCaps() { super.invalidateCaps(); this.energy.invalidate(); } } ================================================ FILE: src/main/java/weather2/blockentity/WindVaneBlockEntity.java ================================================ package weather2.blockentity; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.util.Mth; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec3; import weather2.WeatherBlocks; import weather2.block.AnemometerBlock; import weather2.util.WeatherUtilEntity; import weather2.util.WindReader; public class WindVaneBlockEntity extends BlockEntity { public float smoothAngle = 0; public float smoothAnglePrev = 0; public float smoothAngleRotationalVelAccel = 0; public boolean isOutsideCached = false; public WindVaneBlockEntity(BlockPos p_155229_, BlockState p_155230_) { super(WeatherBlocks.BLOCK_ENTITY_WIND_VANE.get(), p_155229_, p_155230_); } @Override public void setLevel(final Level level) { super.setLevel(level); } public static void tick(Level level, BlockPos pos, BlockState state, WindVaneBlockEntity entity) { entity.tick(level, pos, state); } public void tick(Level level, BlockPos pos, BlockState state) { if (!level.isClientSide) { } else { if (level.getGameTime() % 40 == 0) { isOutsideCached = WeatherUtilEntity.isPosOutside(level, new Vec3(getBlockPos().getX()+0.5F, getBlockPos().getY()+0.5F, getBlockPos().getZ()+0.5F), false, true); } if (isOutsideCached) { float targetAngle = WindReader.getWindAngle(level, new Vec3(getBlockPos().getX(), getBlockPos().getY(), getBlockPos().getZ())); //do not cache for this, the amp value used will mess with the turbines amp, design oversight float windSpeed = WindReader.getWindSpeed(level, pos, 1); if (smoothAngle > 180) smoothAngle -= 360; if (smoothAngle < -180) smoothAngle += 360; if (smoothAnglePrev > 180) smoothAnglePrev -= 360; if (smoothAnglePrev < -180) smoothAnglePrev += 360; smoothAnglePrev = smoothAngle; float bestMove = Mth.wrapDegrees(targetAngle - smoothAngle); if (Math.abs(bestMove) < 180) { if (bestMove > 0) smoothAngleRotationalVelAccel -= windSpeed * 0.4; if (bestMove < 0) smoothAngleRotationalVelAccel += windSpeed * 0.4; if (smoothAngleRotationalVelAccel > 0.3 || smoothAngleRotationalVelAccel < -0.3) { smoothAngle += smoothAngleRotationalVelAccel; } smoothAngleRotationalVelAccel *= 0.96F; } } } } @Override public void load(final CompoundTag tag) { super.load(tag); } @Override protected void saveAdditional(final CompoundTag tag) { super.saveAdditional(tag); } } ================================================ FILE: src/main/java/weather2/client/MovingSoundStreamingSource.java ================================================ package weather2.client; import com.corosus.coroutil.util.CULog; import net.minecraft.client.Minecraft; import net.minecraft.client.resources.sounds.AbstractTickableSoundInstance; import net.minecraft.client.resources.sounds.SoundInstance; import net.minecraft.world.entity.player.Player; import net.minecraft.sounds.SoundSource; import net.minecraft.sounds.SoundEvent; import net.minecraft.util.Mth; import net.minecraft.world.phys.Vec3; import weather2.weathersystem.storm.StormObject; public class MovingSoundStreamingSource extends AbstractTickableSoundInstance { private StormObject storm = null; public float cutOffRange = 128; public Vec3 realSource = null; public boolean lockToPlayer = false; private float extraVolumeAdjForDistScale = 1F; public MovingSoundStreamingSource(Vec3 parPos, SoundEvent event, SoundSource category, float parVolume, float parPitch, boolean lockToPlayer) { super(event, category, SoundInstance.createUnseededRandom()); this.looping = false; this.volume = parVolume; this.extraVolumeAdjForDistScale = parVolume; this.pitch = parPitch; this.realSource = parPos; this.lockToPlayer = lockToPlayer; tick(); } //constructor for non moving sounds public MovingSoundStreamingSource(Vec3 parPos, SoundEvent event, SoundSource category, float parVolume, float parPitch, float parCutOffRange) { super(event, category, SoundInstance.createUnseededRandom()); this.looping = false; this.volume = parVolume; this.extraVolumeAdjForDistScale = parVolume; this.pitch = parPitch; cutOffRange = parCutOffRange; realSource = parPos; //sync position tick(); } //constructor for moving sounds public MovingSoundStreamingSource(StormObject parStorm, SoundEvent event, SoundSource category, float parVolume, float parPitch, float parCutOffRange) { super(event, category, SoundInstance.createUnseededRandom()); this.storm = parStorm; this.looping = false; this.volume = parVolume; this.extraVolumeAdjForDistScale = parVolume; this.pitch = parPitch; cutOffRange = parCutOffRange; //sync position tick(); } public void tick() { Player entP = Minecraft.getInstance().player; if (entP != null) { this.x = (float) entP.getX(); this.y = (float) entP.getY(); this.z = (float) entP.getZ(); } if (storm != null) { realSource = this.storm.posGround; } //if locked to player, don't dynamically adjust volume if (!lockToPlayer) { double dist = getDistanceFrom(realSource, entP.position()); if (dist > cutOffRange) { volume = 0; } else { volume = (float) (1F - (dist / cutOffRange)) * extraVolumeAdjForDistScale; } //CULog.dbg("sound: " + this.location + " vol: " + volume + " cutOffRange: " + cutOffRange + " dist: " + dist); } } public double getDistanceFrom(Vec3 source, Vec3 targ) { return source.distanceTo(targ); } } ================================================ FILE: src/main/java/weather2/client/SceneEnhancer.java ================================================ package weather2.client; import com.corosus.coroutil.config.ConfigCoroUtil; import com.corosus.coroutil.util.*; import net.minecraft.core.Direction; import net.minecraft.core.particles.ParticleOptions; import net.minecraft.sounds.SoundEvents; import net.minecraft.tags.FluidTags; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.block.*; import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.material.MapColor; import weather2.config.ConfigMisc; import weather2.config.ConfigSound; import weather2.datatypes.PrecipitationType; import weather2.datatypes.WeatherEventType; import extendedrenderer.particle.ParticleRegistry; import extendedrenderer.particle.behavior.ParticleBehaviorSandstorm; import extendedrenderer.particle.entity.*; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.client.Minecraft; import net.minecraft.client.particle.FlameParticle; import net.minecraft.client.particle.Particle; import net.minecraft.client.particle.SuspendedParticle; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.ParticleStatus; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.resources.ResourceLocation; import net.minecraft.sounds.SoundSource; import net.minecraft.core.BlockPos; import net.minecraft.util.Mth; import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraft.world.phys.Vec3; import net.minecraft.core.Vec3i; import net.minecraft.world.level.Level; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.levelgen.Heightmap; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.event.TickEvent; import weather2.*; import weather2.client.entity.particle.ParticleHail; import weather2.client.entity.particle.ParticleSandstorm; import weather2.config.ConfigParticle; import weather2.config.ConfigSand; import weather2.util.*; import weather2.weathersystem.WeatherManagerClient; import weather2.weathersystem.fog.FogAdjuster; import weather2.weathersystem.storm.StormObject; import weather2.weathersystem.storm.WeatherObjectParticleStorm; import weather2.weathersystem.tornado.TornadoManagerTodoRenameMe; import weather2.weathersystem.wind.WindManager; import java.util.*; @OnlyIn(Dist.CLIENT) public class SceneEnhancer implements Runnable { private static final double PRECIPITATION_PARTICLE_EFFECT_RATE = 0.7; //this is for the thread we make public ClientLevel lastWorldDetected = null; public static List spawnQueueNormal = new ArrayList<>(); public static List spawnQueue = new ArrayList<>(); public static long threadLastWorldTickTime; public static int lastTickFoundBlocks; public static long lastTickAmbient; public static long lastTickAmbientThreaded; public static ArrayList soundLocations = new ArrayList<>(); public static HashMap soundTimeLocations = new HashMap<>(); public static List LEAVES_BLOCKS = new ArrayList<>(); private static final List listPosRandom = new ArrayList<>(); public static final ResourceLocation RAIN_TEXTURES_GREEN = new ResourceLocation(Weather.MODID, "textures/environment/rain_green.png"); public static final ResourceLocation RAIN_TEXTURES = new ResourceLocation("textures/environment/rain.png"); public static boolean FORCE_ON_DEBUG_TESTING = false; /*public static int fadeInTimer = 0; public static int fadeInTimerMax = 400;*/ public static ParticleBehaviorSandstorm particleBehavior; private static FogAdjuster fogAdjuster; public static boolean isPlayerOutside = true; public static boolean isPlayerNearTornadoCached = false; public static WeatherEventType lastWeatherType = null; public static int particleRateLerp = 0; public static int particleRateLerpMax = 100; public static TornadoManagerTodoRenameMe playerManagerClient; private static Biome lastBiomeIn = null; public static float downfallSheetThreshold = 0.32F; public SceneEnhancer() { listPosRandom.clear(); listPosRandom.add(new BlockPos(0, -1, 0)); listPosRandom.add(new BlockPos(1, 0, 0)); listPosRandom.add(new BlockPos(-1, 0, 0)); listPosRandom.add(new BlockPos(0, 0, 1)); listPosRandom.add(new BlockPos(0, 0, -1)); //TODO: tags bruh Collections.addAll(LEAVES_BLOCKS, Blocks.OAK_LEAVES, Blocks.SPRUCE_LEAVES, Blocks.BIRCH_LEAVES, Blocks.JUNGLE_LEAVES, Blocks.ACACIA_LEAVES, Blocks.CHERRY_LEAVES, Blocks.DARK_OAK_LEAVES, Blocks.MANGROVE_LEAVES, Blocks.AZALEA_LEAVES, Blocks.FLOWERING_AZALEA_LEAVES); } @Override public void run() { while (true) { try { tickClientThreaded(); Thread.sleep(400); } catch (Throwable throwable) { throwable.printStackTrace(); } } } //run from client side _client_ thread public void tickClient() { if (!Minecraft.getInstance().isPaused()) { Minecraft client = Minecraft.getInstance(); if (client.level != null && lastWorldDetected != client.level) { lastWorldDetected = client.level; reset(); } boolean testTornadoTech = false; if (testTornadoTech) { if (playerManagerClient == null) { playerManagerClient = new TornadoManagerTodoRenameMe(); } playerManagerClient.tick(client.level); } WeatherManagerClient weatherMan = ClientTickHandler.weatherManager; if (weatherMan == null) return; WindManager windMan = weatherMan.getWindManager(); if (windMan == null) return; ClientTickHandler.getClientWeather(); ClientWeatherProxy weather = ClientWeatherProxy.get(); WeatherEventType curWeather = getWeatherState(); if (curWeather != lastWeatherType) { //System.out.println("new weather changed to: " + curWeather); particleRateLerp = 0; } lastWeatherType = getWeatherState(); if (particleRateLerp < particleRateLerpMax) { particleRateLerp++; } if (weather.hasWeather() || windMan.cachedWindSpeedClient > 0) { tryParticleSpawning(); } FORCE_ON_DEBUG_TESTING = false; if (weather.hasWeather() || !Weather.isLoveTropicsInstalled()) { ClientWeatherHelper.get().tick(); tickEnvironmentalParticleSpawning(); trySoundPlaying(); tryWind(client.level); } tickMisc(); getFogAdjuster().tickGame(weather); checkParticleBehavior(); particleBehavior.tickUpdateList(); if (client.player != null && client.level != null && client.level.getGameTime() % 10 == 0) { isPlayerOutside = WeatherUtilEntity.isEntityOutside(client.player); } } } //run from our newly created thread public void tickClientThreaded() { Minecraft client = Minecraft.getInstance(); if (client != null && client.level != null && client.player != null) { profileSurroundings(); if (!Weather.isLoveTropicsInstalled() || ClientWeatherProxy.get().hasWeather()) { tryAmbientSounds(); } } } public synchronized void trySoundPlaying() { try { Minecraft client = Minecraft.getInstance(); tickRainSound(); if (lastTickAmbient < System.currentTimeMillis()) { lastTickAmbient = System.currentTimeMillis() + 500; Level worldRef = client.level; Player player = client.player; int size = 32; int hsize = size / 2; BlockPos cur = player.blockPosition(); Random rand = new Random(); //trim out distant sound locations, also tick last time played for (int i = 0; i < soundLocations.size(); i++) { ChunkCoordinatesBlock cCor = soundLocations.get(i); if (Math.sqrt(cCor.distSqr(cur)) > size) { soundLocations.remove(i--); soundTimeLocations.remove(cCor); //System.out.println("trim out soundlocation"); } else { Block block = getBlock(worldRef, cCor.getX(), cCor.getY(), cCor.getZ());//Block.blocksList[id]; //if (block == null || (block.defaultBlockState().getMaterial() != Material.WATER && block.defaultBlockState().getMaterial() != Material.LEAVES)) { if (block == null || (block.defaultMapColor() != MapColor.WATER && block.defaultMapColor() != MapColor.PLANT)) { soundLocations.remove(i); soundTimeLocations.remove(cCor); } else { long lastPlayTime = 0; float soundMuffle = 0.6F; if (getWeatherState() == WeatherEventType.SANDSTORM || getWeatherState() == WeatherEventType.SNOWSTORM) { soundMuffle = 0.15F; } if (soundTimeLocations.containsKey(cCor)) { lastPlayTime = soundTimeLocations.get(cCor); } float maxLeavesVolume = 1; soundMuffle *= ConfigSound.leavesVolume; //System.out.println(Math.sqrt(cCor.getDistanceSquared(curX, curY, curZ))); if (lastPlayTime < System.currentTimeMillis()) { if (LEAVES_BLOCKS.contains(cCor.block)) { float windSpeed = WindReader.getWindSpeed(client.level, cur); if (windSpeed > 0.2F) { soundTimeLocations.put(cCor, System.currentTimeMillis() + 12000 + rand.nextInt(50)); //client.getSoundHandler().playSound(Weather.modID + ":wind_calmfade", cCor.getPosX(), cCor.getPosY(), cCor.getPosZ(), (float)(windSpeed * 4F * ConfigMisc.volWindTreesScale), 0.70F + (rand.nextFloat() * 0.1F)); //client.world.playSound(cCor.getPosX(), cCor.getPosY(), cCor.getPosZ(), Weather.modID + ":env.wind_calmfade", (float)(windSpeed * 4F * ConfigMisc.volWindTreesScale), 0.70F + (rand.nextFloat() * 0.1F), false); client.level.playLocalSound(cCor, SoundRegistry.get("env.wind_calmfade"), SoundSource.AMBIENT, (float)Math.min(maxLeavesVolume, (windSpeed * 2F) * soundMuffle), 0.70F + (rand.nextFloat() * 0.1F), false); //System.out.println("play leaves sound at: " + cCor.getPosX() + " - " + cCor.getPosY() + " - " + cCor.getPosZ() + " - windSpeed: " + windSpeed); } else { windSpeed = WindReader.getWindSpeed(client.level, cur); //if (windSpeed > 0.3F) { if (CoroUtilMisc.random.nextInt(15) == 0) { soundTimeLocations.put(cCor, System.currentTimeMillis() + 12000 + rand.nextInt(50)); //client.getSoundHandler().playSound(Weather.modID + ":wind_calmfade", cCor.getPosX(), cCor.getPosY(), cCor.getPosZ(), (float)(windSpeed * 2F * ConfigMisc.volWindTreesScale), 0.70F + (rand.nextFloat() * 0.1F)); //client.world.playSound(cCor.getPosX(), cCor.getPosY(), cCor.getPosZ(), Weather.modID + ":env.wind_calmfade", (float)(windSpeed * 2F * ConfigMisc.volWindTreesScale), 0.70F + (rand.nextFloat() * 0.1F), false); client.level.playLocalSound(cCor, SoundRegistry.get("env.wind_calmfade"), SoundSource.AMBIENT, Math.min(maxLeavesVolume, windSpeed * soundMuffle), 0.70F + (rand.nextFloat() * 0.1F), false); } //System.out.println("play leaves sound at: " + cCor.getPosX() + " - " + cCor.getPosY() + " - " + cCor.getPosZ() + " - windSpeed: " + windSpeed); //} } } } } } } } } catch (Exception ex) { System.out.println("Weather2: Error handling sound play queue: "); ex.printStackTrace(); } } /** * This method is meant to keep playing rain sound past the point where vanilla cuts off its own rain sounds * edit: used to, now we just fully override * edit2: it also now prevents rain sounds when its actually snowing * Modified copy of LevelRenderer.tickRain * @param p_109694_ */ private int rainSoundTime; public void tickRainSound() { Minecraft minecraft = Minecraft.getInstance(); Player player = Minecraft.getInstance().player; float precipitationStrength = ClientWeatherHelper.get().getPrecipitationStrength(player); if (!(precipitationStrength <= 0.0F) && !shouldSnowHere(player.level(), player.level().getBiome(player.blockPosition()).get(), player.blockPosition())) { Random random = new Random(player.level().getGameTime() * 312987231L); LevelReader levelreader = minecraft.level; BlockPos blockpos = player.blockPosition(); BlockPos blockpos1 = null; int i = (int)(100.0F * precipitationStrength * precipitationStrength) / (minecraft.options.particles.get() == ParticleStatus.DECREASED ? 2 : 1); for(int j = 0; j < i; ++j) { int k = random.nextInt(21) - 10; int l = random.nextInt(21) - 10; BlockPos blockpos2 = levelreader.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, blockpos.offset(k, 0, l)); Biome biome = levelreader.getBiome(blockpos2).get(); if (blockpos2.getY() > levelreader.getMinBuildHeight() && blockpos2.getY() <= blockpos.getY() + 10 && blockpos2.getY() >= blockpos.getY() - 10 && biome.getPrecipitationAt(blockpos2) == Biome.Precipitation.RAIN && biome.warmEnoughToRain(blockpos2)) { blockpos1 = blockpos2.below(); if (minecraft.options.particles.get() == ParticleStatus.MINIMAL) { break; } double d0 = random.nextDouble(); double d1 = random.nextDouble(); BlockState blockstate = levelreader.getBlockState(blockpos1); FluidState fluidstate = levelreader.getFluidState(blockpos1); VoxelShape voxelshape = blockstate.getCollisionShape(levelreader, blockpos1); double d2 = voxelshape.max(Direction.Axis.Y, d0, d1); double d3 = (double)fluidstate.getHeight(levelreader, blockpos1); double d4 = Math.max(d2, d3); ParticleOptions particleoptions = !fluidstate.is(FluidTags.LAVA) && !blockstate.is(Blocks.MAGMA_BLOCK) && !CampfireBlock.isLitCampfire(blockstate) ? ParticleTypes.RAIN : ParticleTypes.SMOKE; minecraft.level.addParticle(particleoptions, (double)blockpos1.getX() + d0, (double)blockpos1.getY() + d4, (double)blockpos1.getZ() + d1, 0.0D, 0.0D, 0.0D); } } if (blockpos1 != null && random.nextInt(3) < this.rainSoundTime++) { this.rainSoundTime = 0; if (blockpos1.getY() > blockpos.getY() + 1 && levelreader.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, blockpos).getY() > Mth.floor((float)blockpos.getY())) { minecraft.level.playLocalSound(blockpos1, SoundEvents.WEATHER_RAIN_ABOVE, SoundSource.WEATHER, 0.1F + (0.4F * precipitationStrength), 0.5F, false); } else { minecraft.level.playLocalSound(blockpos1, SoundEvents.WEATHER_RAIN, SoundSource.WEATHER, 0.2F + (0.6F * precipitationStrength), 1.0F, false); } } } } //Threaded function @OnlyIn(Dist.CLIENT) public static void tryAmbientSounds() { Minecraft client = Minecraft.getInstance(); Level worldRef = client.level; Player player = client.player; //this is currently only used for leaves and its default off now if (lastTickAmbientThreaded < System.currentTimeMillis() && ConfigSound.leavesVolume > 0) { lastTickAmbientThreaded = System.currentTimeMillis() + 500; int size = 32; int hsize = size / 2; int curX = (int)player.getX(); int curY = (int)player.getY(); int curZ = (int)player.getZ(); //soundLocations.clear(); for (int xx = curX - hsize; xx < curX + hsize; xx++) { for (int yy = curY - (hsize / 2); yy < curY + hsize; yy++) { for (int zz = curZ - hsize; zz < curZ + hsize; zz++) { Block block = getBlock(worldRef, xx, yy, zz); if (block != null) { if (((block.defaultMapColor() == MapColor.PLANT))) { boolean proxFail = false; for (ChunkCoordinatesBlock soundLocation : soundLocations) { if (Math.sqrt(soundLocation.distSqr(new Vec3i(xx, yy, zz))) < 15) { proxFail = true; break; } } if (!proxFail) { soundLocations.add(new ChunkCoordinatesBlock(xx, yy, zz, block)); } } } } } } } } public void reset() { if (WeatherUtilParticle.fxLayers == null) { WeatherUtilParticle.getFXLayers(); } } private static void tickHeatwave(ClientWeatherProxy weather) { Minecraft client = Minecraft.getInstance(); /*if (weather.isHeatwave() || true) { heatwaveIntensityTarget = 0.7F; } else { heatwaveIntensityTarget = 0.0F; } heatwaveIntensity = CoroUtilMisc.adjVal(heatwaveIntensity, heatwaveIntensityTarget, 0.01F);*/ /*if (fogAdjuster.getActiveIntensity() > 0) { if (fogAdjuster.getActiveIntensity() < 0.33F) { tryPlayPlayerLockedSound(WeatherUtilSound.snd_sandstorm_low, 5, client.player, 1F); } else if (fogAdjuster.getActiveIntensity() < 0.66F) { tryPlayPlayerLockedSound(WeatherUtilSound.snd_sandstorm_med, 4, client.player, 1F); } else { tryPlayPlayerLockedSound(WeatherUtilSound.snd_sandstorm_high, 3, client.player, 1F); } }*/ } public static boolean tryPlayPlayerLockedSound(String[] sound, int arrIndex, Entity source, float vol) { Random rand = new Random(); if (WeatherUtilSound.soundTimer[arrIndex] <= System.currentTimeMillis()) { String soundStr = sound[WeatherUtilSound.snd_rand[arrIndex]]; WeatherUtilSound.playPlayerLockedSound(source.position(), new StringBuilder().append("streaming." + soundStr).toString(), vol, 1.0F); int length = WeatherUtilSound.soundToLength.get(soundStr); //-500L, for blending WeatherUtilSound.soundTimer[arrIndex] = System.currentTimeMillis() + length - 500L; WeatherUtilSound.snd_rand[arrIndex] = rand.nextInt(sound.length); } return false; } public void tickMisc() { /*ClientWeatherProxy weather = ClientWeatherProxy.get(); if (weather.getPrecipitationType() == RainType.ACID) { if (LevelRenderer.RAIN_LOCATION != RAIN_TEXTURES_GREEN) { LevelRenderer.RAIN_LOCATION = RAIN_TEXTURES_GREEN; } } else { if (LevelRenderer.RAIN_LOCATION != RAIN_TEXTURES) { LevelRenderer.RAIN_LOCATION = RAIN_TEXTURES; } }*/ } public void tickEnvironmentalParticleSpawning() { FORCE_ON_DEBUG_TESTING = false; Player entP = Minecraft.getInstance().player; WeatherManagerClient weatherMan = ClientTickHandler.weatherManager; if (weatherMan == null) return; WindManager windMan = weatherMan.getWindManager(); if (windMan == null) return; if (particleBehavior == null) return; ClientWeatherProxy weather = ClientWeatherProxy.get(); //returns 0 to 1 now float curPrecipVal = weather.getRainAmount(); //System.out.println("curPrecipVal: " + curPrecipVal); if (FORCE_ON_DEBUG_TESTING) { curPrecipVal = 0.3F; //curPrecipVal = (float)((entP.getLevel().getGameTime() / 10) % 100) / 100F; //curPrecipVal = 0F; } //workaround until i clean up logic that is flickering the state between heavy rain and null if (Weather.isLoveTropicsInstalled()) { if (curPrecipVal < 0.0001F) { curPrecipVal = 0; } } float maxPrecip = 1F; BlockPos posPlayer = CoroUtilBlock.blockPos(entP.getX(), entP.getY(), entP.getZ()); Biome biome = entP.level().getBiome(posPlayer).get(); lastBiomeIn = biome; Level world = entP.level(); Random rand = CoroUtilMisc.random(); float windSpeed = windMan.getWindSpeed(posPlayer); //funnel.tickGame(); //now absolute it for ez math //off cause ltminigames can give 1 tick under 0 worth of rain value change //curPrecipVal = Math.min(maxPrecip, Math.abs(curPrecipVal)); if (Weather.isLoveTropicsInstalled()) { curPrecipVal = Math.min(maxPrecip, Math.max(0, curPrecipVal)); } else { curPrecipVal = Math.min(maxPrecip, Math.abs(curPrecipVal)); } float particleSettingsAmplifier = 1F; if (Minecraft.getInstance().options.particles.get() == ParticleStatus.DECREASED) { particleSettingsAmplifier = 0.5F; } else if (Minecraft.getInstance().options.particles.get() == ParticleStatus.MINIMAL) { particleSettingsAmplifier = 0.2F; } particleSettingsAmplifier *= ConfigParticle.Particle_effect_rate; /** * we set the spawn amount for actual particles up to 0.5 intensity * then after 0.5, we up the extra render amount * ensures super low precip isnt patchy, and stops increasing rate of real particles at higher precip */ float curPrecipValMaxNoExtraRender = 0.3F; int extraRenderCountMax = 10; int extraRenderCount = 0; if (curPrecipVal > curPrecipValMaxNoExtraRender) { float precipValForExtraRenders = curPrecipVal - curPrecipValMaxNoExtraRender; float precipValExtraRenderRange = 1F - curPrecipValMaxNoExtraRender; extraRenderCount = Math.min((int) (extraRenderCountMax * (precipValForExtraRenders / precipValExtraRenderRange)), extraRenderCountMax); } //cap at amount before extra rendering starts float curPrecipCappedForSpawnNeed = Math.min(curPrecipVal, curPrecipValMaxNoExtraRender); int spawnCount; double spawnNeedBase = curPrecipCappedForSpawnNeed * ConfigParticle.Precipitation_Particle_effect_rate * particleSettingsAmplifier; int safetyCutout = 100; if (world.getGameTime() % 20 == 0) { StormObject stormObject = ClientTickHandler.weatherManager.getClosestStorm(entP.position(), ConfigMisc.sirenActivateDistance, StormObject.STATE_FORMING); if (stormObject != null && entP.position().distanceTo(stormObject.pos) < stormObject.getSize()) { isPlayerNearTornadoCached = true; } else { isPlayerNearTornadoCached = false; } } //adjusted to this way to make it work with serene seasons boolean canPrecip = weather.getPrecipitationType(biome) == PrecipitationType.NORMAL || weather.getPrecipitationType(biome) == PrecipitationType.SNOW; boolean isRain = canPrecip && shouldRainHere(world, biome, posPlayer); if (Weather.isLoveTropicsInstalled() && ClientWeatherProxy.get().getPrecipitationType(biome) == PrecipitationType.ACID) isRain = true; boolean isHail = weather.isHail(); if (Weather.isLoveTropicsInstalled() && ClientWeatherProxy.get().getPrecipitationType(biome) == PrecipitationType.HAIL) isHail = true; boolean isSnowstorm = weather.isSnowstorm(); boolean isSandstorm = weather.isSandstorm(); boolean isRain_WaterParticle = true; boolean isRain_GroundSplash = true; boolean isRain_DownfallSheet = true; if (isHail) { isRain_WaterParticle = false; isRain_GroundSplash = false; isRain_DownfallSheet = false; } //wind should be so rediculous that sheets of rain isnt gonna be happening in your face if (isPlayerNearTornadoCached) { isRain_DownfallSheet = false; } boolean farSpawn = Minecraft.getInstance().player.isSpectator() || !isPlayerOutside; float particleStormIntensity = 0; if (isSandstorm) { if (Weather.isLoveTropicsInstalled()) { if (getWeatherState() == WeatherEventType.SANDSTORM) { particleStormIntensity = 1; } } else { WeatherObjectParticleStorm storm = ClientTickHandler.weatherManager.getClosestParticleStormByIntensity(entP.position(), WeatherObjectParticleStorm.StormType.SANDSTORM); if (storm != null) { particleStormIntensity = storm.getIntensity(); } } } if (isSnowstorm) { if (Weather.isLoveTropicsInstalled()) { if (getWeatherState() == WeatherEventType.SNOWSTORM) { particleStormIntensity = 1; } } else { WeatherObjectParticleStorm storm = ClientTickHandler.weatherManager.getClosestParticleStormByIntensity(entP.position(), WeatherObjectParticleStorm.StormType.SNOWSTORM); if (storm != null) { particleStormIntensity = storm.getIntensity(); } } } if (Weather.isLoveTropicsInstalled() && farSpawn) { particleStormIntensity *= 0.5F; } //let snowstorm buildup a bit before turning off regular snow boolean isSnow = canPrecip && (!weather.isSnowstorm() || particleStormIntensity < 0.1) && !isHail && shouldSnowHere(world, biome, posPlayer); //System.out.println("particleStormIntensity: " + particleStormIntensity); //dev testing boolean devTest = false; if (devTest) { /*isRain = false; isSnow = false; isSnowstorm = false; isSandstorm = true; particleStormIntensity = 1F; */ //isRain = false; //isRain_DownfallSheet = false; if (entP.level().getGameTime() % 40 == 0) { if (ConfigCoroUtil.useLoggingDebug) { System.out.printf("curPrecipVal: %.2f", curPrecipVal); System.out.println(""); } CULog.dbg("spawnNeedBase: " + spawnNeedBase); CULog.dbg("extraRenderCount: " + extraRenderCount); CULog.dbg("particleStormIntensity: " + particleStormIntensity); } } //check rules same way vanilla texture precip does if (biome != null && (biome.getPrecipitationAt(posPlayer) != Biome.Precipitation.NONE)) { if (curPrecipVal > 0) { if (isRain) { spawnCount = 0; int spawnAreaSize = 30; int spawnNeed = (int) (spawnNeedBase * 300); if (entP.level().getGameTime() % 40 == 0) { //CULog.dbg("rain spawnNeed: " + spawnNeed); } if (isRain_WaterParticle && spawnNeed > 0) { for (int i = 0; i < safetyCutout; i++) { BlockPos pos = CoroUtilBlock.blockPos( entP.getX() + rand.nextInt(spawnAreaSize) - (spawnAreaSize / 2), entP.getY() - 5 + rand.nextInt(25), entP.getZ() + rand.nextInt(spawnAreaSize) - (spawnAreaSize / 2)); if (canPrecipitateAt(world, pos)) { ParticleTexExtraRender rain = new ParticleTexExtraRender((ClientLevel) entP.level(), pos.getX(), pos.getY(), pos.getZ(), 0D, 0D, 0D, ParticleRegistry.rain_white); particleBehavior.initParticleRain(rain, extraRenderCount); spawnCount++; if (spawnCount >= spawnNeed) { break; } } } } //TODO: make ground splash and downfall use spawnNeed var style design spawnAreaSize = 40; if (isRain_GroundSplash && curPrecipVal > 0.15) { for (int i = 0; i < 30F * curPrecipVal * PRECIPITATION_PARTICLE_EFFECT_RATE * 8F * particleSettingsAmplifier; i++) { BlockPos pos = CoroUtilBlock.blockPos( entP.getX() + rand.nextInt(spawnAreaSize) - (spawnAreaSize / 2), entP.getY() - 5 + rand.nextInt(15), entP.getZ() + rand.nextInt(spawnAreaSize) - (spawnAreaSize / 2)); //get the block on the topmost ground pos = world.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, pos).below(); BlockState state = world.getBlockState(pos); double maxY = 0; double minY = 0; VoxelShape shape = state.getShape(world, pos); if (!shape.isEmpty()) { minY = shape.bounds().minY; maxY = shape.bounds().maxY; } if (pos.distSqr(entP.blockPosition()) > (spawnAreaSize / 2) * (spawnAreaSize / 2)) continue; //block above topmost ground if (canPrecipitateAt(world, pos.above())) { //fix for splash spawning invisibly 1 block underwater if (world.getBlockState(pos).getBlock().defaultMapColor() == MapColor.WATER) { pos = pos.offset(0,1,0); } ParticleTexFX rain = new ParticleTexFX((ClientLevel) entP.level(), pos.getX() + rand.nextFloat(), pos.getY() + 0.01D + maxY, pos.getZ() + rand.nextFloat(), 0D, 0D, 0D, ParticleRegistry.groundSplash); particleBehavior.initParticleGroundSplash(rain); rain.spawnAsWeatherEffect(); } } } spawnAreaSize = 30; //downfall - at just above 0.3 cause rainstorms lock at 0.3 but flicker a bit above and below if (isRain_DownfallSheet && curPrecipVal > downfallSheetThreshold) { int scanAheadRange = 0; //quick is outside check, prevent them spawning right near ground //and especially right above the roof so they have enough space to fade out //results in not seeing them through roofs if (WeatherUtilDim.canBlockSeeSky(world, entP.blockPosition())) { scanAheadRange = 3; } else { scanAheadRange = 10; } double closeDistCutoff = 10D; for (int i = 0; i < 2F * curPrecipVal * PRECIPITATION_PARTICLE_EFFECT_RATE * particleSettingsAmplifier * 0.5F; i++) { BlockPos pos = CoroUtilBlock.blockPos( entP.getX() + rand.nextInt(spawnAreaSize) - (spawnAreaSize / 2), entP.getY() + 5 + rand.nextInt(15), entP.getZ() + rand.nextInt(spawnAreaSize) - (spawnAreaSize / 2)); if (WeatherUtilEntity.getDistanceSqEntToPos(entP, pos) < closeDistCutoff * closeDistCutoff) continue; if (canPrecipitateAt(world, pos.above(-scanAheadRange))/*world.isRainingAt(pos)*/) { ParticleTexFX rain = new ParticleTexFX((ClientLevel) entP.level(), pos.getX() + rand.nextFloat(), pos.getY() - 1 + 0.01D, pos.getZ() + rand.nextFloat(), 0D, 0D, 0D, ParticleRegistry.downfall3); particleBehavior.initParticleRainDownfall(rain); rain.spawnAsWeatherEffect(); } } } //snow } else if (isSnow) { spawnCount = 0; int spawnAreaSize = 50; int spawnNeed = (int) (spawnNeedBase * 80); if (entP.level().getGameTime() % 40 == 0) { //CULog.dbg("snow spawnNeed: " + spawnNeed); } if (spawnNeed > 0) { for (int i = 0; i < safetyCutout; i++) { BlockPos pos = CoroUtilBlock.blockPos( entP.getX() + rand.nextInt(spawnAreaSize) - (spawnAreaSize / 2), entP.getY() - 5 + rand.nextInt(25), entP.getZ() + rand.nextInt(spawnAreaSize) - (spawnAreaSize / 2)); if (canPrecipitateAt(world, pos)) { ParticleTexExtraRender snow = new ParticleTexExtraRender((ClientLevel) entP.level(), pos.getX(), pos.getY(), pos.getZ(), 0D, 0D, 0D, ParticleRegistry.snow2); particleBehavior.initParticleSnow(snow, extraRenderCount, windSpeed); snow.spawnAsWeatherEffect(); spawnCount++; if (spawnCount >= spawnNeed) { break; } } } } } int spawnAreaSize = 30; spawnCount = 0; int spawnNeed = (int) (spawnNeedBase * 80); if ((getWeatherState() == WeatherEventType.HAIL || isHail) && spawnNeed > 0) { for (int i = 0; i < safetyCutout / 4; i++) { BlockPos pos = CoroUtilBlock.blockPos( entP.getX() + rand.nextInt(spawnAreaSize) - (spawnAreaSize / 2), entP.getY() - 5 + rand.nextInt(25), entP.getZ() + rand.nextInt(spawnAreaSize) - (spawnAreaSize / 2)); if (canPrecipitateAt(world, pos)) { ParticleHail hail = new ParticleHail((ClientLevel) entP.level(), pos.getX(), pos.getY(), pos.getZ(), 0D, 0D, 0D, ParticleRegistry.hail); /*ParticleCube hail = new ParticleCube((ClientLevel) entP.level, pos.getX(), pos.getY(), pos.getZ(), 0D, 0D, 0D, ParticleRegistry.hail);*/ particleBehavior.initParticleHail(hail); hail.spawnAsWeatherEffect(); spawnCount++; if (spawnCount >= spawnNeed) { break; } } } } } boolean groundFire = ClientWeatherProxy.get().isHeatwave(); int spawnAreaSize = 40; if (groundFire) { for (int i = 0; i < 10F * PRECIPITATION_PARTICLE_EFFECT_RATE * 1F * particleSettingsAmplifier; i++) { BlockPos pos = CoroUtilBlock.blockPos( entP.getX() + rand.nextInt(spawnAreaSize) - (spawnAreaSize / 2), entP.getY() - 5 + rand.nextInt(15), entP.getZ() + rand.nextInt(spawnAreaSize) - (spawnAreaSize / 2)); //get the block on the topmost ground pos = world.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, pos).below(); BlockState state = world.getBlockState(pos); double maxY = 0; double minY = 0; VoxelShape shape = state.getShape(world, pos); if (!shape.isEmpty()) { minY = shape.bounds().minY; maxY = shape.bounds().maxY; } if (pos.distSqr(entP.blockPosition()) > (spawnAreaSize / 2) * (spawnAreaSize / 2)) continue; //block above topmost ground if (canPrecipitateAt(world, pos.above()) && world.getBlockState(pos).getBlock().defaultMapColor() != MapColor.WATER) { world.addParticle(ParticleTypes.SMOKE, pos.getX() + rand.nextFloat(), pos.getY() + 0.01D + maxY, pos.getZ() + rand.nextFloat(), 0.0D, 0.0D, 0.0D); world.addParticle(ParticleTypes.FLAME, pos.getX() + rand.nextFloat(), pos.getY() + 0.01D + maxY, pos.getZ() + rand.nextFloat(), 0.0D, 0.0D, 0.0D); } } } } //extra dust in the air { int spawnAreaSize = 25; int spawnNeed = (int) (particleSettingsAmplifier * 5 * windSpeed); spawnCount = 0; for (int i = 0; i < safetyCutout; i++) { if (spawnCount >= spawnNeed) { break; } if (windSpeed >= 0.1F/* && rand.nextInt(1) == 0*/) { BlockPos pos = CoroUtilBlock.blockPos( entP.getX() + rand.nextInt(spawnAreaSize) - (spawnAreaSize / 2), entP.getY() - 5 + rand.nextInt(25), entP.getZ() + rand.nextInt(spawnAreaSize) - (spawnAreaSize / 2)); if (canPrecipitateAt(world, pos)) { ParticleTexExtraRender dust = new ParticleTexExtraRender((ClientLevel) entP.level(), pos.getX(), pos.getY(), pos.getZ(), 0D, 0D, 0D, ParticleRegistry.squareGrey); particleBehavior.initParticleDustAir(dust); dust.spawnAsWeatherEffect(); spawnCount++; } } } } if (isSnowstorm) { spawnCount = 0; //less for snow, since it falls slower so more is on screen longer float particleSettingsAmplifierExtra = particleSettingsAmplifier; //spawnNeedBase = Math.max(1, ConfigParticle.Precipitation_Particle_effect_rate * particleSettingsAmplifierExtra); spawnNeedBase = ConfigParticle.Precipitation_Particle_effect_rate * particleSettingsAmplifierExtra; int spawnNeed = (int) Math.max(0, spawnNeedBase * 5); safetyCutout = 60; int spawnAreaSize = 20; double closeDistCutoff = 7D; float yetAnotherRateNumber = 120 * getParticleFadeInLerpForNewWeatherState(); if (farSpawn) { safetyCutout = 20; spawnAreaSize = 100; yetAnotherRateNumber = 40 * getParticleFadeInLerpForNewWeatherState(); } if (particleStormIntensity >= 0.01F) { if (spawnNeed > 0) { if (getParticleFadeInLerpForNewWeatherState() > 0.5F) { particleSettingsAmplifierExtra *= (getParticleFadeInLerpForNewWeatherState() - 0.5F) * 2F; } else { particleSettingsAmplifierExtra = 0; } //System.out.println("particleSettingsAmplifierExtra: " + particleSettingsAmplifierExtra); //snow for (int i = 0; i < Math.max(1, safetyCutout * particleSettingsAmplifierExtra)/*curPrecipVal * 20F * PRECIPITATION_PARTICLE_EFFECT_RATE*/; i++) { BlockPos pos = CoroUtilBlock.blockPos( entP.getX() + rand.nextInt(spawnAreaSize) - (spawnAreaSize / 2), entP.getY() - 5 + rand.nextInt(20), entP.getZ() + rand.nextInt(spawnAreaSize) - (spawnAreaSize / 2)); Vec3 windForce = ClientTickHandler.getClientWeather().getWindManager().getWindForce(null); double upwindDistAdjust = -10D; windForce = windForce.multiply(upwindDistAdjust, upwindDistAdjust, upwindDistAdjust); pos = pos.offset(Mth.floor(windForce.x), Mth.floor(windForce.y), Mth.floor(windForce.z)); if (WeatherUtilEntity.getDistanceSqEntToPos(entP, pos) < closeDistCutoff * closeDistCutoff) continue; if (canPrecipitateAt(world, pos)) { ParticleTexExtraRender snow = new ParticleTexExtraRender((ClientLevel) entP.level(), pos.getX(), pos.getY(), pos.getZ(), 0D, 0D, 0D, ParticleRegistry.snow); particleBehavior.initParticleSnowstorm(snow, (int)(10 * particleStormIntensity)); snow.spawnAsWeatherEffect(); } } double sandstormParticleRateDust = ConfigSand.Sandstorm_Particle_Dust_effect_rate; Minecraft client = Minecraft.getInstance(); Player player = client.player; //float adjustAmountSmooth75 = (particleStormIntensity * 8F) - 7F; float adjustAmountSmooth75 = particleStormIntensity; //extra snow cloud dust for (int i = 0; i < (particleSettingsAmplifier * yetAnotherRateNumber * adjustAmountSmooth75 * sandstormParticleRateDust)/*adjustAmountSmooth * 20F * ConfigMisc.Particle_Precipitation_effect_rate*/; i++) { BlockPos pos = CoroUtilBlock.blockPos( player.getX() + rand.nextInt(spawnAreaSize) - (spawnAreaSize / 2), player.getY() - 2 + rand.nextInt(10), player.getZ() + rand.nextInt(spawnAreaSize) - (spawnAreaSize / 2)); if (WeatherUtilEntity.getDistanceSqEntToPos(entP, pos) < closeDistCutoff * closeDistCutoff) continue; if (canPrecipitateAt(world, pos)) { TextureAtlasSprite sprite = ParticleRegistry.cloud256; ParticleSandstorm part = new ParticleSandstorm(world, pos.getX(), pos.getY(), pos.getZ(), 0, 0, 0, sprite); particleBehavior.initParticle(part); particleBehavior.initParticleSnowstormCloudDust(part); particleBehavior.particles.add(part); part.spawnAsWeatherEffect(); } } } } //works for snowstorms too tickSandstormSound(); } if (isSandstorm) { Minecraft client = Minecraft.getInstance(); Player player = client.player; ClientTickHandler.getClientWeather(); //enhance the scene further with particles around player, check for sandstorm to account for pocket sand modifying adjustAmountTarget if (particleStormIntensity >= 0.01F) { rand = CoroUtilMisc.random(); int spawnAreaSize = 60; double sandstormParticleRateDebris = ConfigSand.Sandstorm_Particle_Debris_effect_rate; double sandstormParticleRateDust = ConfigSand.Sandstorm_Particle_Dust_effect_rate; //float adjustAmountSmooth75 = (particleStormIntensity * 8F) - 7F; float adjustAmountSmooth75 = particleStormIntensity; if (farSpawn) { adjustAmountSmooth75 *= 0.3F; } /*if (Minecraft.getInstance().options.particles == ParticleStatus.DECREASED) { adjustAmountSmooth75 *= 0.5F; } else if (Minecraft.getInstance().options.particles == ParticleStatus.MINIMAL) { adjustAmountSmooth75 *= 0.25F; }*/ adjustAmountSmooth75 *= particleSettingsAmplifier; adjustAmountSmooth75 *= getParticleFadeInLerpForNewWeatherState(); //extra dust for (int i = 0; i < ((float) 60 * adjustAmountSmooth75 * sandstormParticleRateDust)/*adjustAmountSmooth * 20F * ConfigMisc.Particle_Precipitation_effect_rate*/; i++) { BlockPos pos = CoroUtilBlock.blockPos( player.getX() + rand.nextInt(spawnAreaSize) - (spawnAreaSize / 2), player.getY() - 2 + rand.nextInt(10), player.getZ() + rand.nextInt(spawnAreaSize) - (spawnAreaSize / 2)); if (canPrecipitateAt(world, pos)) { TextureAtlasSprite sprite = ParticleRegistry.cloud256; ParticleSandstorm part = new ParticleSandstorm(world, pos.getX(), pos.getY(), pos.getZ(), 0, 0, 0, sprite); particleBehavior.initParticle(part); particleBehavior.initParticleSandstormDust(part); particleBehavior.particles.add(part); part.spawnAsWeatherEffect(); } } //tumbleweed for (int i = 0; i < ((float) 1 * adjustAmountSmooth75 * sandstormParticleRateDebris)/*adjustAmountSmooth * 20F * ConfigMisc.Particle_Precipitation_effect_rate*/; i++) { BlockPos pos = CoroUtilBlock.blockPos( player.getX() + rand.nextInt(spawnAreaSize) - (spawnAreaSize / 2), player.getY() - 2 + rand.nextInt(10), player.getZ() + rand.nextInt(spawnAreaSize) - (spawnAreaSize / 2)); if (canPrecipitateAt(world, pos)) { TextureAtlasSprite sprite = ParticleRegistry.tumbleweed; ParticleCrossSection part = new ParticleCrossSection(world, pos.getX(), pos.getY(), pos.getZ(), 0, 0, 0, sprite); particleBehavior.initParticle(part); particleBehavior.initParticleSandstormTumbleweed(part); particleBehavior.particles.add(part); part.spawnAsWeatherEffect(); } } //debris for (int i = 0; i < ((float) 8 * adjustAmountSmooth75 * sandstormParticleRateDebris)/*adjustAmountSmooth * 20F * ConfigMisc.Particle_Precipitation_effect_rate*/; i++) { BlockPos pos = CoroUtilBlock.blockPos( player.getX() + rand.nextInt(spawnAreaSize) - (spawnAreaSize / 2), player.getY() - 2 + rand.nextInt(10), player.getZ() + rand.nextInt(spawnAreaSize) - (spawnAreaSize / 2)); if (canPrecipitateAt(world, pos)) { TextureAtlasSprite sprite = null; int tex = rand.nextInt(3); if (tex == 0) { sprite = ParticleRegistry.debris_1; } else if (tex == 1) { sprite = ParticleRegistry.debris_2; } else if (tex == 2) { sprite = ParticleRegistry.debris_3; } ParticleSandstorm part = new ParticleSandstorm(world, pos.getX(), pos.getY(), pos.getZ(), 0, 0, 0, sprite); particleBehavior.initParticle(part); particleBehavior.initParticleSandstormDebris(part); particleBehavior.particles.add(part); part.spawnAsWeatherEffect(); } } } tickSandstormSound(); } } public static boolean canPrecipitateAt(Level world, BlockPos strikePosition) { return world.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, strikePosition).getY() <= strikePosition.getY(); } public synchronized void tryParticleSpawning() { try { for (Particle ent : spawnQueue) { if (ent != null/* && ent.world != null*/) { if (ent instanceof EntityRotFX) { ((EntityRotFX) ent).spawnAsWeatherEffect(); } } } for (Particle ent : spawnQueueNormal) { if (ent != null/* && ent.world != null*/) { Minecraft.getInstance().particleEngine.add(ent); } } } catch (Exception ex) { //CMEs occur, its fine //ex.printStackTrace(); } spawnQueue.clear(); spawnQueueNormal.clear(); } public void profileSurroundings() { //tryClouds(); Minecraft client = Minecraft.getInstance(); ClientLevel worldRef = lastWorldDetected; Player player = Minecraft.getInstance().player; WeatherManagerClient manager = ClientTickHandler.weatherManager; if (worldRef == null || player == null || manager == null || manager.getWindManager() == null || (manager.getWindManager() != null && manager.getWindManager().cachedWindSpeedClient == 0)) { try { Thread.sleep(1000L); } catch (Exception ex) { ex.printStackTrace(); } return; } if (threadLastWorldTickTime == worldRef.getGameTime()) { return; } threadLastWorldTickTime = worldRef.getGameTime(); Random rand = new Random(); //mining a tree causes leaves to fall int size = 40; int hsize = size / 2; int curX = (int)player.getX(); int curY = (int)player.getY(); int curZ = (int)player.getZ(); float windStr = manager.getWindManager().cachedWindSpeedClient; //System.out.println("windStr " + windStr); //Wind requiring code goes below int spawnRateRandChanceOdds = (int)(30 / (windStr + 0.001)); float lastBlockCount = lastTickFoundBlocks; float particleCreationRate = 0.7F; float maxScaleSample = 15000; if (lastBlockCount > maxScaleSample) lastBlockCount = maxScaleSample-1; float scaleRate = (maxScaleSample - lastBlockCount) / maxScaleSample; spawnRateRandChanceOdds = (int) ((spawnRateRandChanceOdds / (scaleRate + 0.001F)) / (particleCreationRate + 0.001F)); float particleSettingsAmplifier = 1F; if (Minecraft.getInstance().options.particles.get() == ParticleStatus.DECREASED) { particleSettingsAmplifier = 0.5F; } else if (Minecraft.getInstance().options.particles.get() == ParticleStatus.MINIMAL) { particleSettingsAmplifier = 0.2F; } particleSettingsAmplifier *= ConfigParticle.Particle_effect_rate; //spawnRate *= (client.options.particles.getId()+1); spawnRateRandChanceOdds /= particleSettingsAmplifier; //since reducing threaded ticking to 200ms sleep, 1/4 rate, must decrease rand size spawnRateRandChanceOdds /= 2; //performance fix if (spawnRateRandChanceOdds < 40) { spawnRateRandChanceOdds = 40; } lastTickFoundBlocks = 0; double particleAmp = 1F; spawnRateRandChanceOdds = (int)((double)spawnRateRandChanceOdds / particleAmp); //Weather.dbg("spawnRate: " + spawnRate); for (int xx = curX - hsize; xx < curX + hsize; xx++) { for (int yy = curY - (hsize / 2); yy < curY + hsize; yy++) { for (int zz = curZ - hsize; zz < curZ + hsize; zz++) { Block block = getBlock(worldRef, xx, yy, zz); if (block != null) { //leaf particle spawning if ((block.defaultMapColor() == MapColor.PLANT)) { lastTickFoundBlocks++; if (CoroUtilMisc.random.nextInt(spawnRateRandChanceOdds) == 0) { //bottom of tree check || air beside vine check //far out enough to avoid having the AABB already inside the block letting it phase through more //close in as much as we can to make it look like it came from the block double relAdj = 0.70D; BlockPos pos = getRandomWorkingPos(worldRef, new BlockPos(xx, yy, zz)); double xRand = 0; double yRand = 0; double zRand = 0; if (pos != null) { //further limit the spawn position along the face side to prevent it clipping into perpendicular blocks float particleAABB = 0.1F; float particleAABBAndBuffer = particleAABB + 0.05F; float invert = 1F - (particleAABBAndBuffer * 2F); if (pos.getY() != 0) { xRand = particleAABBAndBuffer + (rand.nextDouble() - 0.5D) * invert; zRand = particleAABBAndBuffer + (rand.nextDouble() - 0.5D) * invert; } else if (pos.getX() != 0) { yRand = particleAABBAndBuffer + (rand.nextDouble() - 0.5D) * invert; zRand = particleAABBAndBuffer + (rand.nextDouble() - 0.5D) * invert; } else if (pos.getZ() != 0) { yRand = particleAABBAndBuffer + (rand.nextDouble() - 0.5D) * invert; xRand = particleAABBAndBuffer + (rand.nextDouble() - 0.5D) * invert; } EntityRotFX particle = new ParticleTexLeafColor(worldRef, xx, yy, zz, 0D, 0D, 0D, ParticleRegistry.leaf); particle.setPos(xx + 0.5D + (pos.getX() * relAdj) + xRand, yy + 0.5D + (pos.getY() * relAdj) + yRand, zz + 0.5D + (pos.getZ() * relAdj) + zRand); particle.setPrevPosX(particle.getPosX()); particle.setPrevPosY(particle.getPosY()); particle.setPrevPosZ(particle.getPosZ()); particleBehavior.initParticleLeaf(particle, particleAABB); spawnQueue.add(particle); } } } if (windStr >= 0.1F) { if (block instanceof GrassBlock || block.defaultMapColor() == MapColor.DIRT || block.defaultMapColor() == MapColor.SAND || block.defaultMapColor() == MapColor.PLANT) { lastTickFoundBlocks++; boolean spawnInside = false; boolean spawnAbove = false; boolean spawnAboveSnow = false; //boolean placeAbove = false; if (block instanceof GrassBlock || block.defaultMapColor() == MapColor.DIRT || block.defaultMapColor() == MapColor.SAND) { spawnAbove = true; } int oddsTo1 = spawnRateRandChanceOdds; if (block.defaultMapColor() == MapColor.PLANT) { oddsTo1 = spawnRateRandChanceOdds / 3; spawnInside = true; } //oddsTo1 = (int) (oddsTo1 * (5F * windStr)); if (CoroUtilMisc.random.nextInt(oddsTo1) == 0) { BlockPos pos = new BlockPos(xx, yy, zz); BlockPos posAbove = new BlockPos(xx, yy + 1, zz); BlockState blockStateAbove = getBlockState(worldRef, posAbove); if (blockStateAbove != null && (blockStateAbove.isAir() || blockStateAbove.getBlock() instanceof SnowLayerBlock)) { spawnAboveSnow = blockStateAbove.getBlock() instanceof SnowLayerBlock; boolean test = false; if (!test) { ParticleTexLeafColor dust = new ParticleTexLeafColor(worldRef, pos.getX(), spawnAboveSnow ? posAbove.getY() : pos.getY(), pos.getZ(), 0D, 0D, 0D, ParticleRegistry.squareGrey); if (spawnAbove) { if (spawnAboveSnow) { dust.setPosition(posAbove.getX(), posAbove.getY() + 0.4F, posAbove.getZ()); } else { dust.setPosition(posAbove.getX(), posAbove.getY() + 0.1F, posAbove.getZ()); } } else if (spawnInside) { dust.setPosition(pos.getX() + rand.nextFloat(), pos.getY() + rand.nextFloat(), pos.getZ() + rand.nextFloat()); } dust.setPrevPosX(dust.getPosX()); dust.setPrevPosY(dust.getPosY()); dust.setPrevPosZ(dust.getPosZ()); particleBehavior.initParticleDustGround(dust, spawnInside, spawnAboveSnow); spawnQueue.add(dust); } else { DustEmitter dustEmitter = new DustEmitter(worldRef, pos.getX(), spawnAboveSnow ? posAbove.getY() : pos.getY(), pos.getZ(), 0D, 0D, 0D); dustEmitter.setLifetime(20); spawnQueue.add(dustEmitter); } } } } } } } } } } /** * Returns the successful relative position * * @param world * @param posOrigin * @return */ public static BlockPos getRandomWorkingPos(Level world, BlockPos posOrigin) { Collections.shuffle(listPosRandom); for (BlockPos posRel : listPosRandom) { Block blockCheck = getBlock(world, posOrigin.offset(posRel)); if (blockCheck != null && CoroUtilBlock.isAir(blockCheck)) { return posRel; } } return null; } @OnlyIn(Dist.CLIENT) public static void tryWind(Level world) { Minecraft client = Minecraft.getInstance(); Player player = client.player; if (player == null) { return; } WeatherManagerClient weatherMan = ClientTickHandler.weatherManager; if (weatherMan == null) return; WindManager windMan = weatherMan.getWindManager(); if (windMan == null) return; //Weather Effects //Built in particles if (WeatherUtilParticle.fxLayers != null && windMan.getWindSpeed(player.blockPosition()) >= 0.10) { for (Queue type : WeatherUtilParticle.fxLayers.values()) { for (Particle particle : type) { if (particle instanceof SuspendedParticle) { continue; } if ((WeatherUtilBlock.getPrecipitationHeightSafe(world, WeatherUtilParticle.getPos(particle)).getY() - 1 < (int)Mth.floor(particle.y) + 1) || (particle instanceof ParticleTexFX)) { if ((particle instanceof FlameParticle)) { if (windMan.getWindSpeed(player.blockPosition()) >= 0.20) { particle.age += 1; } } windMan.applyWindForceNew(particle, 1F/20F, 0.5F, true); } } } } } //Thread safe functions @OnlyIn(Dist.CLIENT) private static Block getBlock(Level parWorld, BlockPos pos) { return getBlock(parWorld, pos.getX(), pos.getY(), pos.getZ()); } @OnlyIn(Dist.CLIENT) private static Block getBlock(Level parWorld, int x, int y, int z) { try { if (!parWorld.hasChunkAt(new BlockPos(x, 0, z))) { return null; } return parWorld.getBlockState(new BlockPos(x, y, z)).getBlock(); } catch (Exception ex) { return null; } } @OnlyIn(Dist.CLIENT) private static BlockState getBlockState(Level parWorld, BlockPos pos) { return getBlockState(parWorld, pos.getX(), pos.getY(), pos.getZ()); } @OnlyIn(Dist.CLIENT) private static BlockState getBlockState(Level parWorld, int x, int y, int z) { try { if (!parWorld.hasChunkAt(new BlockPos(x, 0, z))) { return null; } return parWorld.getBlockState(new BlockPos(x, y, z)); } catch (Exception ex) { return null; } } public static boolean isFogOverridding() { Minecraft client = Minecraft.getInstance(); BlockState blockAtCamera = client.gameRenderer.getMainCamera().getBlockAtCamera(); if (blockAtCamera.getBlock().defaultMapColor() == MapColor.WATER) return false; //return heatwaveIntensity > 0; //return true; return fogAdjuster.isFogOverriding(); } public static void renderTick(TickEvent.RenderTickEvent event) { Minecraft client = Minecraft.getInstance(); ClientWeatherProxy weather = ClientWeatherProxy.get(); if (client.level != null) { ClientWeatherHelper.get().controlVisuals(weather.getVanillaRainAmount() > 0); } } public static void tickSandstorm() { Minecraft client = Minecraft.getInstance(); Player player = client.player; Level world = client.level; WindManager windMan = ClientTickHandler.weatherManager.getWindManager(); ClientTickHandler.getClientWeather(); boolean farSpawn = Minecraft.getInstance().player.isSpectator() || !isPlayerOutside; float adjustAmountSmooth = 0; WeatherObjectParticleStorm sandstorm = ClientTickHandler.weatherManager.getClosestParticleStormByIntensity(player.position(), WeatherObjectParticleStorm.StormType.SANDSTORM); if (sandstorm != null) { adjustAmountSmooth = sandstorm.getIntensity(); //CULog.dbg("sandstorm: " + adjustAmountSmooth); } //enhance the scene further with particles around player, check for sandstorm to account for pocket sand modifying adjustAmountTarget if (adjustAmountSmooth >= 0.25F/* && sandstorm != null*/) { //porting whee adjustAmountSmooth += 0.5F; Random rand = CoroUtilMisc.random(); int spawnAreaSize = 80; double sandstormParticleRateDebris = ConfigSand.Sandstorm_Particle_Debris_effect_rate; double sandstormParticleRateDust = ConfigSand.Sandstorm_Particle_Dust_effect_rate; float adjustAmountSmooth75 = (adjustAmountSmooth * 8F) - 7F; if (farSpawn) { adjustAmountSmooth75 *= 0.3F; } if (Minecraft.getInstance().options.particles.get() == ParticleStatus.DECREASED) { adjustAmountSmooth75 *= 0.5F; } else if (Minecraft.getInstance().options.particles.get() == ParticleStatus.MINIMAL) { adjustAmountSmooth75 *= 0.25F; } adjustAmountSmooth75 *= getParticleFadeInLerpForNewWeatherState(); //extra dust for (int i = 0; i < ((float)60 * adjustAmountSmooth75 * sandstormParticleRateDust)/*adjustAmountSmooth * 20F * ConfigMisc.Particle_Precipitation_effect_rate*/; i++) { BlockPos pos = CoroUtilBlock.blockPos( player.getX() + rand.nextInt(spawnAreaSize) - (spawnAreaSize / 2), player.getY() - 2 + rand.nextInt(10), player.getZ() + rand.nextInt(spawnAreaSize) - (spawnAreaSize / 2)); if (canPrecipitateAt(world, pos)) { TextureAtlasSprite sprite = ParticleRegistry.cloud256; ParticleSandstorm part = new ParticleSandstorm(world, pos.getX(), pos.getY(), pos.getZ(), 0, 0, 0, sprite); particleBehavior.initParticle(part); particleBehavior.initParticleSandstormDust(part); particleBehavior.particles.add(part); part.spawnAsWeatherEffect(); } } //tumbleweed for (int i = 0; i < ((float)1 * adjustAmountSmooth75 * sandstormParticleRateDebris)/*adjustAmountSmooth * 20F * ConfigMisc.Particle_Precipitation_effect_rate*/; i++) { BlockPos pos = CoroUtilBlock.blockPos( player.getX() + rand.nextInt(spawnAreaSize) - (spawnAreaSize / 2), player.getY() - 2 + rand.nextInt(10), player.getZ() + rand.nextInt(spawnAreaSize) - (spawnAreaSize / 2)); if (canPrecipitateAt(world, pos)) { TextureAtlasSprite sprite = ParticleRegistry.tumbleweed; ParticleCrossSection part = new ParticleCrossSection(world, pos.getX(), pos.getY(), pos.getZ(), 0, 0, 0, sprite); particleBehavior.initParticle(part); particleBehavior.initParticleSandstormTumbleweed(part); particleBehavior.particles.add(part); part.spawnAsWeatherEffect(); } } //debris for (int i = 0; i < ((float)8 * adjustAmountSmooth75 * sandstormParticleRateDebris)/*adjustAmountSmooth * 20F * ConfigMisc.Particle_Precipitation_effect_rate*/; i++) { BlockPos pos = CoroUtilBlock.blockPos( player.getX() + rand.nextInt(spawnAreaSize) - (spawnAreaSize / 2), player.getY() - 2 + rand.nextInt(10), player.getZ() + rand.nextInt(spawnAreaSize) - (spawnAreaSize / 2)); if (canPrecipitateAt(world, pos)) { TextureAtlasSprite sprite = null; int tex = rand.nextInt(3); if (tex == 0) { sprite = ParticleRegistry.debris_1; } else if (tex == 1) { sprite = ParticleRegistry.debris_2; } else if (tex == 2) { sprite = ParticleRegistry.debris_3; } ParticleSandstorm part = new ParticleSandstorm(world, pos.getX(), pos.getY(), pos.getZ(), 0, 0, 0, sprite); particleBehavior.initParticle(part); particleBehavior.initParticleSandstormDebris(part); particleBehavior.particles.add(part); part.spawnAsWeatherEffect(); } } } tickSandstormSound(); } public static void tickSandstormSound() { /** * dist + storm intensity * 0F - 1F * * 0 = low * 0.33 = med * 0.66 = high * * static sound volume, keep at player */ Minecraft mc = Minecraft.getInstance(); if (particleRateLerp > 0) { if (particleRateLerp < 0.66F) { tryPlayPlayerLockedSound(WeatherUtilSound.snd_sandstorm_low, 5, mc.player, 0.6F); } else if (particleRateLerp < 0.85F) { tryPlayPlayerLockedSound(WeatherUtilSound.snd_sandstorm_med, 4, mc.player, 0.6F); } else { tryPlayPlayerLockedSound(WeatherUtilSound.snd_sandstorm_high, 3, mc.player, 0.6F); } } } public static FogAdjuster getFogAdjuster() { if (fogAdjuster == null) { fogAdjuster = new FogAdjuster(); } return fogAdjuster; } public static WeatherEventType getWeatherState() { ClientWeatherProxy clientWeather = ClientWeatherProxy.get(); if (clientWeather.isSandstorm()) { return WeatherEventType.SANDSTORM; } else if (clientWeather.isSnowstorm()) { return WeatherEventType.SNOWSTORM; } else if (clientWeather.isHeatwave()) { return WeatherEventType.HEATWAVE; } else if (clientWeather.getRainAmount() > 0 && clientWeather.getPrecipitationType(lastBiomeIn) == PrecipitationType.ACID) { return WeatherEventType.ACID_RAIN; } else if (clientWeather.getRainAmount() > 0 && clientWeather.getPrecipitationType(lastBiomeIn) == PrecipitationType.NORMAL) { return WeatherEventType.HEAVY_RAIN; } else if (clientWeather.getRainAmount() > 0 && clientWeather.getPrecipitationType(lastBiomeIn) == PrecipitationType.HAIL) { return WeatherEventType.HAIL; } else { return null; } } public static float getParticleFadeInLerpForNewWeatherState() { return (float)particleRateLerp / (float)particleRateLerpMax; } /** * Needed for serene seasons compat * * @param level * @param biome * @param pos * @return */ public static boolean shouldRainHere(Level level, Biome biome, BlockPos pos) { return CoroUtilCompatibility.warmEnoughToRain(biome, pos, level); } /** * Needed for serene seasons compat * * @param level * @param biome * @param pos * @return */ public static boolean shouldSnowHere(Level level, Biome biome, BlockPos pos) { return CoroUtilCompatibility.coldEnoughToSnow(biome, pos, level); } public static void checkParticleBehavior() { if (particleBehavior == null) { particleBehavior = new ParticleBehaviorSandstorm(null); } } } ================================================ FILE: src/main/java/weather2/client/entity/model/AnemometerModel.java ================================================ package weather2.client.entity.model;// Made with Blockbench 4.8.3 // Exported for Minecraft version 1.17 or later with Mojang mappings // Paste this class into your mod and generate all required imports import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import net.minecraft.client.model.EntityModel; import net.minecraft.client.model.HierarchicalModel; import net.minecraft.client.model.geom.ModelLayerLocation; import net.minecraft.client.model.geom.ModelPart; import net.minecraft.client.model.geom.PartPose; import net.minecraft.client.model.geom.builders.*; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.Entity; import weather2.Weather; public class AnemometerModel extends HierarchicalModel { // This layer location should be baked with EntityRendererProvider.Context in the entity renderer and passed into this model's constructor public static final ModelLayerLocation LAYER_LOCATION = new ModelLayerLocation(new ResourceLocation(Weather.MODID, "anemometer"), "main"); private final ModelPart root; public AnemometerModel(ModelPart root) { this.root = root.getChild("root"); } @Override public ModelPart root() { return this.root; } public static LayerDefinition createBodyLayer() { MeshDefinition meshdefinition = new MeshDefinition(); PartDefinition partdefinition = meshdefinition.getRoot(); PartDefinition root = partdefinition.addOrReplaceChild("root", CubeListBuilder.create().texOffs(0, 0).addBox(-1.5F, -1.0F, -1.5F, 3.0F, 1.0F, 3.0F, new CubeDeformation(0.0F)), PartPose.offset(0.0F, 24.0F, 0.0F)); PartDefinition base = root.addOrReplaceChild("base", CubeListBuilder.create().texOffs(1, 1).addBox(-0.5F, -11.0F, -0.5F, 1.0F, 11.0F, 1.0F, new CubeDeformation(0.0F)), PartPose.offset(0.0F, 0.0F, 0.0F)); PartDefinition top = base.addOrReplaceChild("top", CubeListBuilder.create().texOffs(0, 0).addBox(-1.0F, -1.0F, -1.0F, 2.0F, 2.0F, 2.0F, new CubeDeformation(0.0F)), PartPose.offset(0.0F, -11.5F, 0.0F)); PartDefinition arm1 = top.addOrReplaceChild("arm1", CubeListBuilder.create().texOffs(-5, -5).addBox(-0.5F, -0.5F, -8.0F, 1.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)), PartPose.offset(0.0F, 0.0F, 0.0F)); PartDefinition cup1 = arm1.addOrReplaceChild("cup1", CubeListBuilder.create().texOffs(1, 1).addBox(0.5F, -12.0F, -8.5F, 1.0F, 1.0F, 1.0F, new CubeDeformation(0.0F)) .texOffs(-1, -1).addBox(0.5F, -13.0F, -8.5F, 1.0F, 1.0F, 3.0F, new CubeDeformation(0.0F)) .texOffs(1, 1).addBox(0.5F, -12.0F, -6.5F, 1.0F, 1.0F, 1.0F, new CubeDeformation(0.0F)) .texOffs(-1, -1).addBox(0.5F, -11.0F, -8.5F, 1.0F, 1.0F, 3.0F, new CubeDeformation(0.0F)), PartPose.offset(0.0F, 11.5F, 0.0F)); PartDefinition arm2 = top.addOrReplaceChild("arm2", CubeListBuilder.create().texOffs(-5, -5).addBox(-0.5F, -0.5F, -8.0F, 1.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -1.5708F, 0.0F)); PartDefinition cup2 = arm2.addOrReplaceChild("cup2", CubeListBuilder.create().texOffs(1, 1).addBox(0.5F, -12.0F, -8.5F, 1.0F, 1.0F, 1.0F, new CubeDeformation(0.0F)) .texOffs(-1, -1).addBox(0.5F, -13.0F, -8.5F, 1.0F, 1.0F, 3.0F, new CubeDeformation(0.0F)) .texOffs(1, 1).addBox(0.5F, -12.0F, -6.5F, 1.0F, 1.0F, 1.0F, new CubeDeformation(0.0F)) .texOffs(-1, -1).addBox(0.5F, -11.0F, -8.5F, 1.0F, 1.0F, 3.0F, new CubeDeformation(0.0F)), PartPose.offset(0.0F, 11.5F, 0.0F)); PartDefinition arm3 = top.addOrReplaceChild("arm3", CubeListBuilder.create().texOffs(-5, -5).addBox(-0.5F, -0.5F, -8.0F, 1.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 3.1416F, 0.0F)); PartDefinition cup3 = arm3.addOrReplaceChild("cup3", CubeListBuilder.create().texOffs(1, 1).addBox(0.5F, -12.0F, -8.5F, 1.0F, 1.0F, 1.0F, new CubeDeformation(0.0F)) .texOffs(-1, -1).addBox(0.5F, -13.0F, -8.5F, 1.0F, 1.0F, 3.0F, new CubeDeformation(0.0F)) .texOffs(1, 1).addBox(0.5F, -12.0F, -6.5F, 1.0F, 1.0F, 1.0F, new CubeDeformation(0.0F)) .texOffs(-1, -1).addBox(0.5F, -11.0F, -8.5F, 1.0F, 1.0F, 3.0F, new CubeDeformation(0.0F)), PartPose.offset(0.0F, 11.5F, 0.0F)); PartDefinition arm4 = top.addOrReplaceChild("arm4", CubeListBuilder.create().texOffs(-5, -5).addBox(-0.5F, -0.5F, -8.0F, 1.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 1.5708F, 0.0F)); PartDefinition cup4 = arm4.addOrReplaceChild("cup4", CubeListBuilder.create().texOffs(1, 1).addBox(0.5F, -12.0F, -8.5F, 1.0F, 1.0F, 1.0F, new CubeDeformation(0.0F)) .texOffs(-1, -1).addBox(0.5F, -13.0F, -8.5F, 1.0F, 1.0F, 3.0F, new CubeDeformation(0.0F)) .texOffs(1, 1).addBox(0.5F, -12.0F, -6.5F, 1.0F, 1.0F, 1.0F, new CubeDeformation(0.0F)) .texOffs(-1, -1).addBox(0.5F, -11.0F, -8.5F, 1.0F, 1.0F, 3.0F, new CubeDeformation(0.0F)), PartPose.offset(0.0F, 11.5F, 0.0F)); return LayerDefinition.create(meshdefinition, 16, 16); } @Override public void setupAnim(T entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { } @Override public void renderToBuffer(PoseStack poseStack, VertexConsumer vertexConsumer, int packedLight, int packedOverlay, float red, float green, float blue, float alpha) { root.render(poseStack, vertexConsumer, packedLight, packedOverlay, red, green, blue, alpha); } } ================================================ FILE: src/main/java/weather2/client/entity/model/WindTurbineModel.java ================================================ package weather2.client.entity.model;// Made with Blockbench 4.8.3 // Exported for Minecraft version 1.17 or later with Mojang mappings // Paste this class into your mod and generate all required imports import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import net.minecraft.client.model.HierarchicalModel; import net.minecraft.client.model.geom.ModelLayerLocation; import net.minecraft.client.model.geom.ModelPart; import net.minecraft.client.model.geom.PartPose; import net.minecraft.client.model.geom.builders.*; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.Entity; import weather2.Weather; public class WindTurbineModel extends HierarchicalModel { // This layer location should be baked with EntityRendererProvider.Context in the entity renderer and passed into this model's constructor public static final ModelLayerLocation LAYER_LOCATION = new ModelLayerLocation(new ResourceLocation(Weather.MODID, "wind_turbine"), "main"); private final ModelPart root; public WindTurbineModel(ModelPart root) { this.root = root; } @Override public ModelPart root() { return this.root; } public static LayerDefinition createBodyLayer() { MeshDefinition meshdefinition = new MeshDefinition(); PartDefinition partdefinition = meshdefinition.getRoot(); PartDefinition root = partdefinition.addOrReplaceChild("root", CubeListBuilder.create().texOffs(0, 0).addBox(-6.0F, -2.0F, -6.0F, 12.0F, 2.0F, 12.0F, new CubeDeformation(0.0F)) .texOffs(36, 2).addBox(-3.0F, -4.0F, -3.0F, 6.0F, 2.0F, 6.0F, new CubeDeformation(0.0F)), PartPose.offset(0.0F, 24.0F, 0.0F)); PartDefinition shaft = root.addOrReplaceChild("shaft", CubeListBuilder.create().texOffs(0, 35).addBox(-1.0F, -31.0F, -1.0F, 2.0F, 29.0F, 2.0F, new CubeDeformation(0.0F)) .texOffs(0, 20).addBox(-2.0F, -30.0F, -2.0F, 4.0F, 2.0F, 4.0F, new CubeDeformation(0.0F)) .texOffs(0, 33).addBox(-9.0F, -29.5F, -0.5F, 18.0F, 1.0F, 1.0F, new CubeDeformation(0.0F)) .texOffs(0, 14).addBox(-0.5F, -29.5F, -9.0F, 1.0F, 1.0F, 18.0F, new CubeDeformation(0.0F)) .texOffs(2, 16).addBox(-0.5F, -13.5F, -8.0F, 1.0F, 1.0F, 16.0F, new CubeDeformation(0.0F)) .texOffs(36, 0).addBox(-8.0F, -13.5F, -0.5F, 16.0F, 1.0F, 1.0F, new CubeDeformation(0.0F)) .texOffs(2, 16).addBox(-0.5F, -13.5F, -8.0F, 1.0F, 1.0F, 16.0F, new CubeDeformation(0.0F)) .texOffs(0, 14).addBox(-2.0F, -14.0F, -2.0F, 4.0F, 2.0F, 4.0F, new CubeDeformation(0.0F)), PartPose.offset(0.0F, 0.0F, 0.0F)); PartDefinition fin_r1 = shaft.addOrReplaceChild("fin_r1", CubeListBuilder.create().texOffs(8, 35).addBox(-2.0F, -21.0F, -1.0F, 4.0F, 22.0F, 2.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, -13.0F, -8.0F, -0.4162F, 0.1863F, 0.3969F)); PartDefinition fin_r2 = shaft.addOrReplaceChild("fin_r2", CubeListBuilder.create().texOffs(8, 35).addBox(-2.0F, -21.0F, -1.0F, 4.0F, 22.0F, 2.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, -13.0F, 8.0F, 0.4164F, 0.2075F, -0.436F)); PartDefinition fin_r3 = shaft.addOrReplaceChild("fin_r3", CubeListBuilder.create().texOffs(34, 31).addBox(-1.0F, -21.0F, -2.0F, 2.0F, 22.0F, 4.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(-8.0F, -13.0F, 0.0F, 0.4363F, 0.0F, 0.4712F)); PartDefinition fin_r4 = shaft.addOrReplaceChild("fin_r4", CubeListBuilder.create().texOffs(34, 31).addBox(-1.0F, -21.0F, -2.0F, 2.0F, 22.0F, 4.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(8.0F, -13.0F, 0.0F, -0.4363F, 0.0F, -0.4712F)); return LayerDefinition.create(meshdefinition, 128, 128); } @Override public void setupAnim(T entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { } @Override public void renderToBuffer(PoseStack poseStack, VertexConsumer vertexConsumer, int packedLight, int packedOverlay, float red, float green, float blue, float alpha) { root.render(poseStack, vertexConsumer, packedLight, packedOverlay, red, green, blue, alpha); } } ================================================ FILE: src/main/java/weather2/client/entity/model/WindVaneModel.java ================================================ package weather2.client.entity.model;// Made with Blockbench 4.8.3 // Exported for Minecraft version 1.17 or later with Mojang mappings // Paste this class into your mod and generate all required imports import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import net.minecraft.client.model.HierarchicalModel; import net.minecraft.client.model.geom.ModelLayerLocation; import net.minecraft.client.model.geom.ModelPart; import net.minecraft.client.model.geom.PartPose; import net.minecraft.client.model.geom.builders.*; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.Entity; import weather2.Weather; import weather2.blockentity.AnemometerBlockEntity; import weather2.blockentity.WindVaneBlockEntity; public class WindVaneModel extends HierarchicalModel { // This layer location should be baked with EntityRendererProvider.Context in the entity renderer and passed into this model's constructor public static final ModelLayerLocation LAYER_LOCATION = new ModelLayerLocation(new ResourceLocation(Weather.MODID, "wind_vane"), "main"); private final ModelPart root; public WindVaneModel(ModelPart root) { this.root = root; } @Override public ModelPart root() { return this.root; } public static LayerDefinition createBodyLayer() { MeshDefinition meshdefinition = new MeshDefinition(); PartDefinition partdefinition = meshdefinition.getRoot(); PartDefinition root = partdefinition.addOrReplaceChild("root", CubeListBuilder.create(), PartPose.offset(0.0F, 24.0F, 0.0F)); PartDefinition base = root.addOrReplaceChild("base", CubeListBuilder.create().texOffs(13, 15).addBox(-0.25F, -7.0F, -0.25F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) .texOffs(0, 0).addBox(-0.75F, -0.5F, -0.75F, 1.5F, 0.5F, 1.5F, new CubeDeformation(0.0F)), PartPose.offset(0.0F, 0.0F, 0.0F)); PartDefinition middle = base.addOrReplaceChild("middle", CubeListBuilder.create().texOffs(17, 14).addBox(-0.25F, 0.5F, -0.25F, 0.5F, 3.0F, 0.5F, new CubeDeformation(0.0F)) .texOffs(0, 0).addBox(-0.75F, 3.25F, -0.75F, 1.5F, 1.5F, 1.5F, new CubeDeformation(0.0F)), PartPose.offset(0.0F, -11.5F, 0.0F)); PartDefinition cube_r1 = middle.addOrReplaceChild("cube_r1", CubeListBuilder.create().texOffs(0, 0).addBox(-0.75F, -0.75F, -0.75F, 1.5F, 1.5F, 1.5F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 4.0F, 0.0F, 0.0F, 0.0F, 0.7854F)); PartDefinition cube_r2 = middle.addOrReplaceChild("cube_r2", CubeListBuilder.create().texOffs(0, 0).addBox(-0.75F, -0.75F, -0.75F, 1.5F, 1.5F, 1.5F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 4.0F, 0.0F, 0.0F, -0.7854F, 0.0F)); PartDefinition cube_r3 = middle.addOrReplaceChild("cube_r3", CubeListBuilder.create().texOffs(0, 0).addBox(-0.75F, -0.75F, -0.75F, 1.5F, 1.5F, 1.5F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 4.0F, 0.0F, -0.7854F, 0.0F, 0.0F)); PartDefinition arm1 = middle.addOrReplaceChild("arm1", CubeListBuilder.create().texOffs(4, 13).addBox(-0.25F, -0.25F, -4.0F, 0.5F, 0.5F, 4.0F, new CubeDeformation(0.0F)) .texOffs(0, 28).addBox(-0.05F, -1.0F, -6.0F, 0.05F, 2.0F, 2.0F, new CubeDeformation(0.0F)), PartPose.offset(0.0F, 4.0F, 0.0F)); PartDefinition top = middle.addOrReplaceChild("top", CubeListBuilder.create().texOffs(0, 0).addBox(-0.75F, -0.75F, -0.75F, 1.5F, 1.5F, 1.5F, new CubeDeformation(0.0F)) .texOffs(23, 0).addBox(-0.05F, -5.0F, -2.5F, 0.05F, 4.0F, 4.0F, new CubeDeformation(0.0F)), PartPose.offset(0.0F, 0.0F, 0.0F)); PartDefinition cube_r4 = top.addOrReplaceChild("cube_r4", CubeListBuilder.create().texOffs(0, 0).addBox(-0.75F, -0.75F, -0.75F, 1.5F, 1.5F, 1.5F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.7854F, 0.0F)); PartDefinition cube_r5 = top.addOrReplaceChild("cube_r5", CubeListBuilder.create().texOffs(0, 0).addBox(-0.75F, -0.75F, -0.75F, 1.5F, 1.5F, 1.5F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, -0.7854F, 0.0F, 0.0F)); PartDefinition cube_r6 = top.addOrReplaceChild("cube_r6", CubeListBuilder.create().texOffs(0, 0).addBox(-0.75F, -0.75F, -0.75F, 1.5F, 1.5F, 1.5F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.7854F)); PartDefinition toparm1 = top.addOrReplaceChild("toparm1", CubeListBuilder.create().texOffs(12, 0).addBox(-0.25F, -0.25F, -4.0F, 0.5F, 0.5F, 4.0F, new CubeDeformation(0.0F)) .texOffs(20, 28).addBox(-0.05F, -1.0F, -6.0F, 0.05F, 2.0F, 2.0F, new CubeDeformation(0.0F)), PartPose.offset(0.0F, 0.0F, 0.0F)); PartDefinition toparm2 = top.addOrReplaceChild("toparm2", CubeListBuilder.create().texOffs(4, 9).addBox(-0.25F, -0.25F, -4.0F, 0.5F, 0.5F, 4.0F, new CubeDeformation(0.0F)) .texOffs(25, 28).addBox(-0.05F, -1.0F, -6.0F, 0.05F, 2.0F, 2.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 3.1416F, 0.0F)); PartDefinition arm2 = middle.addOrReplaceChild("arm2", CubeListBuilder.create().texOffs(-1, 11).addBox(-0.25F, -0.25F, -4.0F, 0.5F, 0.5F, 4.0F, new CubeDeformation(0.0F)) .texOffs(15, 28).addBox(-0.05F, -1.0F, -6.0F, 0.05F, 2.0F, 2.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 4.0F, 0.0F, 0.0F, -1.5708F, 0.0F)); PartDefinition arm3 = middle.addOrReplaceChild("arm3", CubeListBuilder.create().texOffs(9, 10).addBox(-0.25F, -0.25F, -4.0F, 0.5F, 0.5F, 4.0F, new CubeDeformation(0.0F)) .texOffs(10, 28).addBox(-0.05F, -1.0F, -6.0F, 0.05F, 2.0F, 2.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 4.0F, 0.0F, 0.0F, 3.1416F, 0.0F)); PartDefinition arm4 = middle.addOrReplaceChild("arm4", CubeListBuilder.create().texOffs(10, 6).addBox(-0.25F, -0.25F, -4.0F, 0.5F, 0.5F, 4.0F, new CubeDeformation(0.0F)) .texOffs(5, 28).addBox(-0.05F, -1.0F, -6.0F, 0.05F, 2.0F, 2.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 4.0F, 0.0F, 0.0F, 1.5708F, 0.0F)); return LayerDefinition.create(meshdefinition, 32, 32); } @Override public void setupAnim(T entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { } @Override public void renderToBuffer(PoseStack poseStack, VertexConsumer vertexConsumer, int packedLight, int packedOverlay, float red, float green, float blue, float alpha) { root.render(poseStack, vertexConsumer, packedLight, packedOverlay, red, green, blue, alpha); } } ================================================ FILE: src/main/java/weather2/client/entity/particle/ParticleHail.java ================================================ package weather2.client.entity.particle; import com.corosus.coroutil.util.CoroUtilBlock; import com.corosus.coroutil.util.CoroUtilMisc; import extendedrenderer.particle.entity.ParticleCrossSection; import extendedrenderer.particle.entity.ParticleTexExtraRender; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.sounds.SoundSource; import net.minecraft.sounds.SoundEvents; import net.minecraft.core.BlockPos; public class ParticleHail extends ParticleCrossSection { public ParticleHail(ClientLevel worldIn, double posXIn, double posYIn, double posZIn, double mX, double mY, double mZ, TextureAtlasSprite par8Item) { super(worldIn, posXIn, posYIn, posZIn, mX, mY, mZ, par8Item); } @Override public void tick() { super.tick(); } @Override public void onHit() { super.onHit(); if (CoroUtilMisc.random.nextInt(30) == 0) { level.playLocalSound(CoroUtilBlock.blockPos(x, y, z), SoundEvents.WOOD_BREAK, SoundSource.WEATHER, 0.2F, 2F, false); } } } ================================================ FILE: src/main/java/weather2/client/entity/particle/ParticleSandstorm.java ================================================ package weather2.client.entity.particle; import net.minecraft.client.Camera; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.world.level.Level; import com.mojang.blaze3d.vertex.VertexConsumer; import extendedrenderer.particle.entity.ParticleTexFX; public class ParticleSandstorm extends ParticleTexFX { public double angleToStorm = 0; public int heightLayer = 0; public double distAdj = 0; public boolean lockPosition = false; public ParticleSandstorm(Level worldIn, double posXIn, double posYIn, double posZIn, double mX, double mY, double mZ, TextureAtlasSprite par8Item) { super((ClientLevel) worldIn, posXIn, posYIn, posZIn, mX, mY, mZ, par8Item); } @Override public void render(VertexConsumer buffer, Camera renderInfo, float partialTicks) { super.render(buffer, renderInfo, partialTicks); } } ================================================ FILE: src/main/java/weather2/client/entity/render/LightningBoltWeatherNewRenderer.java ================================================ package weather2.client.entity.render; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import java.util.Random; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.entity.EntityRenderer; import net.minecraft.client.renderer.entity.EntityRendererProvider; import net.minecraft.client.renderer.texture.TextureAtlas; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import org.joml.Matrix4f; import weather2.weathersystem.storm.LightningBoltWeatherNew; @OnlyIn(Dist.CLIENT) public class LightningBoltWeatherNewRenderer extends EntityRenderer { public LightningBoltWeatherNewRenderer(EntityRendererProvider.Context p_174286_) { super(p_174286_); } public void render(LightningBoltWeatherNew p_115266_, float p_115267_, float p_115268_, PoseStack p_115269_, MultiBufferSource p_115270_, int p_115271_) { float[] afloat = new float[8]; float[] afloat1 = new float[8]; float f = 0.0F; float f1 = 0.0F; Random random = new Random(p_115266_.seed); for(int i = 7; i >= 0; --i) { afloat[i] = f; afloat1[i] = f1; f += (float)(random.nextInt(11) - 5); f1 += (float)(random.nextInt(11) - 5); } VertexConsumer vertexconsumer = p_115270_.getBuffer(RenderType.lightning()); Matrix4f matrix4f = p_115269_.last().pose(); for(int j = 0; j < 4; ++j) { Random random1 = new Random(p_115266_.seed); for(int k = 0; k < 3; ++k) { int l = 7; int i1 = 0; if (k > 0) { l = 7 - k; } if (k > 0) { i1 = l - 2; } float f2 = afloat[l] - f; float f3 = afloat1[l] - f1; for(int j1 = l; j1 >= i1; --j1) { float f4 = f2; float f5 = f3; if (k == 0) { f2 += (float)(random1.nextInt(11) - 5); f3 += (float)(random1.nextInt(11) - 5); } else { f2 += (float)(random1.nextInt(31) - 15); f3 += (float)(random1.nextInt(31) - 15); } float f6 = 0.5F; float f7 = 0.45F; float f8 = 0.45F; float f9 = 0.5F; float f10 = 0.1F + (float)j * 0.2F; if (k == 0) { f10 *= (float)j1 * 0.1F + 1.0F; } float f11 = 0.1F + (float)j * 0.2F; if (k == 0) { f11 *= ((float)j1 - 1.0F) * 0.1F + 1.0F; } quad(matrix4f, vertexconsumer, f2, f3, j1, f4, f5, 0.45F, 0.45F, 0.5F, f10, f11, false, false, true, false); quad(matrix4f, vertexconsumer, f2, f3, j1, f4, f5, 0.45F, 0.45F, 0.5F, f10, f11, true, false, true, true); quad(matrix4f, vertexconsumer, f2, f3, j1, f4, f5, 0.45F, 0.45F, 0.5F, f10, f11, true, true, false, true); quad(matrix4f, vertexconsumer, f2, f3, j1, f4, f5, 0.45F, 0.45F, 0.5F, f10, f11, false, true, false, false); } } } } private static void quad(Matrix4f p_115273_, VertexConsumer p_115274_, float p_115275_, float p_115276_, int p_115277_, float p_115278_, float p_115279_, float p_115280_, float p_115281_, float p_115282_, float p_115283_, float p_115284_, boolean p_115285_, boolean p_115286_, boolean p_115287_, boolean p_115288_) { p_115274_.vertex(p_115273_, p_115275_ + (p_115285_ ? p_115284_ : -p_115284_), (float)(p_115277_ * 16), p_115276_ + (p_115286_ ? p_115284_ : -p_115284_)).color(p_115280_, p_115281_, p_115282_, 0.3F).endVertex(); p_115274_.vertex(p_115273_, p_115278_ + (p_115285_ ? p_115283_ : -p_115283_), (float)((p_115277_ + 1) * 16), p_115279_ + (p_115286_ ? p_115283_ : -p_115283_)).color(p_115280_, p_115281_, p_115282_, 0.3F).endVertex(); p_115274_.vertex(p_115273_, p_115278_ + (p_115287_ ? p_115283_ : -p_115283_), (float)((p_115277_ + 1) * 16), p_115279_ + (p_115288_ ? p_115283_ : -p_115283_)).color(p_115280_, p_115281_, p_115282_, 0.3F).endVertex(); p_115274_.vertex(p_115273_, p_115275_ + (p_115287_ ? p_115284_ : -p_115284_), (float)(p_115277_ * 16), p_115276_ + (p_115288_ ? p_115284_ : -p_115284_)).color(p_115280_, p_115281_, p_115282_, 0.3F).endVertex(); } public ResourceLocation getTextureLocation(LightningBoltWeatherNew p_115264_) { return TextureAtlas.LOCATION_BLOCKS; } } ================================================ FILE: src/main/java/weather2/client/tile/AnemometerEntityRenderer.java ================================================ package weather2.client.tile; import com.google.common.collect.Maps; import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.Minecraft; import net.minecraft.client.model.Model; import net.minecraft.client.model.geom.ModelPart; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; import net.minecraft.client.renderer.texture.TextureAtlas; import net.minecraft.client.resources.model.Material; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Mth; import net.minecraft.world.level.LightLayer; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.phys.Vec3; import org.joml.Vector3f; import weather2.ClientTickHandler; import weather2.Weather; import weather2.WeatherBlocks; import weather2.blockentity.AnemometerBlockEntity; import weather2.client.entity.model.AnemometerModel; import weather2.weathersystem.WeatherManagerClient; import weather2.weathersystem.wind.WindManager; import java.util.Map; import java.util.Random; public class AnemometerEntityRenderer implements BlockEntityRenderer { private static Map resLocMap = Maps.newHashMap(); private static Map materialMap = Maps.newHashMap(); public static Material getTEMaterial(final String path) { return materialMap.computeIfAbsent(path, m -> createTEMaterial(path)); } private static Material createTEMaterial(final String path) { return new Material(TextureAtlas.LOCATION_BLOCKS, getTextureTE(path)); } public static ResourceLocation getTextureTE(String path) { return getTexture(String.format("textures/blocks/te/%s.png", path)); } public static ResourceLocation getTexture(String path) { return resLocMap.computeIfAbsent(path, k -> getResLoc(path)); } private static ResourceLocation getResLoc(String path) { return new ResourceLocation(Weather.MODID, path); } public static void renderModel(final Material material, final Model model, PoseStack stack, MultiBufferSource buffer, int combinedLightIn, int combinedOverlayIn) { model.renderToBuffer(stack, buffer.getBuffer(model.renderType(material.texture())), combinedLightIn, combinedOverlayIn, 1, 1, 1, 1); } private final Block block; protected final AnemometerModel model; public AnemometerEntityRenderer(final BlockEntityRendererProvider.Context context) { super(); this.block = WeatherBlocks.BLOCK_ANEMOMETER.get(); this.model = new AnemometerModel<>(Minecraft.getInstance().getEntityModels().bakeLayer(AnemometerModel.LAYER_LOCATION)); } @Override public void render(T te, float partialTicks, PoseStack stack, MultiBufferSource buffer, int combinedLightIn, int combinedOverlayIn) { this.model.root().getAllParts().forEach(ModelPart::resetPose); //fixes for block ModelPart root = this.model.root(); root.x += 8; root.y += 8; root.z += 8; root.xRot += Math.toRadians(180); root.yRot += Math.toRadians(180); root.y -= 32; ModelPart top = this.model.root().getChild("base").getChild("top"); if (top != null) { WeatherManagerClient weatherMan = ClientTickHandler.weatherManager; if (weatherMan == null) return; WindManager windMan = weatherMan.getWindManager(); if (windMan == null) return; float lerpAngle = (float) Mth.lerp((double)partialTicks, ((AnemometerBlockEntity) te).smoothAnglePrev, ((AnemometerBlockEntity) te).smoothAngle); float renderAngle = lerpAngle; top.yRot = (float) Math.toRadians(renderAngle); boolean shaking = ((AnemometerBlockEntity) te).smoothAngleRotationalVel > 45; if (shaking) { Random rand = new Random(te.getLevel().getGameTime()); top.xRot = (float) ((rand.nextFloat() - rand.nextFloat()) * Math.toRadians(7)); top.zRot = (float) ((rand.nextFloat() - rand.nextFloat()) * Math.toRadians(7)); } } renderModel(getTEMaterial("anemometer"), model, stack, buffer, combinedLightIn, combinedOverlayIn); } } ================================================ FILE: src/main/java/weather2/client/tile/WindTurbineEntityRenderer.java ================================================ package weather2.client.tile; import com.google.common.collect.Maps; import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.Minecraft; import net.minecraft.client.model.Model; import net.minecraft.client.model.geom.ModelPart; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; import net.minecraft.client.renderer.texture.TextureAtlas; import net.minecraft.client.resources.model.Material; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Mth; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import org.joml.Vector3f; import weather2.ClientTickHandler; import weather2.Weather; import weather2.WeatherBlocks; import weather2.blockentity.WindTurbineBlockEntity; import weather2.blockentity.WindVaneBlockEntity; import weather2.client.entity.model.WindTurbineModel; import weather2.client.entity.model.WindVaneModel; import weather2.weathersystem.WeatherManagerClient; import weather2.weathersystem.wind.WindManager; import java.util.Map; import java.util.Random; public class WindTurbineEntityRenderer implements BlockEntityRenderer { private static Map resLocMap = Maps.newHashMap(); private static Map materialMap = Maps.newHashMap(); public static Material getTEMaterial(final String path) { return materialMap.computeIfAbsent(path, m -> createTEMaterial(path)); } private static Material createTEMaterial(final String path) { return new Material(TextureAtlas.LOCATION_BLOCKS, getTextureTE(path)); } public static ResourceLocation getTextureTE(String path) { return getTexture(String.format("textures/blocks/te/%s.png", path)); } public static ResourceLocation getTexture(String path) { return resLocMap.computeIfAbsent(path, k -> getResLoc(path)); } private static ResourceLocation getResLoc(String path) { return new ResourceLocation(Weather.MODID, path); } public static void renderModel(final Material material, final Model model, PoseStack stack, MultiBufferSource buffer, int combinedLightIn, int combinedOverlayIn) { model.renderToBuffer(stack, buffer.getBuffer(model.renderType(material.texture())), combinedLightIn, combinedOverlayIn, 1, 1, 1, 1); } private final Block block; protected final WindTurbineModel model; public WindTurbineEntityRenderer(final BlockEntityRendererProvider.Context context) { super(); this.block = WeatherBlocks.BLOCK_WIND_TURBINE.get(); this.model = new WindTurbineModel<>(Minecraft.getInstance().getEntityModels().bakeLayer(WindTurbineModel.LAYER_LOCATION)); } @Override public void render(T te, float partialTicks, PoseStack stack, MultiBufferSource buffer, int combinedLightIn, int combinedOverlayIn) { this.model.root().getAllParts().forEach(ModelPart::resetPose); //fixes for block ModelPart root = this.model.root(); root.x += 8; root.y += 8; root.z += 8; root.xRot += Math.toRadians(180); root.yRot += Math.toRadians(180); //te.getLevel().getBrightness(LightLayer.BLOCK, te.getBlockPos().above()) root.y += 16; ModelPart top = this.model.root().getChild("root").getChild("shaft"); if (top != null) { float lerpAngle = (float) Mth.lerp((double)partialTicks, ((WindTurbineBlockEntity) te).smoothAnglePrev, ((WindTurbineBlockEntity) te).smoothAngle); float renderAngle = lerpAngle; top.yRot = (float) Math.toRadians(renderAngle); } renderModel(getTEMaterial("wind_turbine"), model, stack, buffer, combinedLightIn, combinedOverlayIn); } } ================================================ FILE: src/main/java/weather2/client/tile/WindVaneEntityRenderer.java ================================================ package weather2.client.tile; import com.google.common.collect.Maps; import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.Minecraft; import net.minecraft.client.model.Model; import net.minecraft.client.model.geom.ModelPart; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; import net.minecraft.client.renderer.texture.TextureAtlas; import net.minecraft.client.resources.model.Material; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Mth; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import org.joml.Vector3f; import weather2.ClientTickHandler; import weather2.Weather; import weather2.WeatherBlocks; import weather2.blockentity.AnemometerBlockEntity; import weather2.blockentity.WindVaneBlockEntity; import weather2.client.entity.model.WindVaneModel; import weather2.weathersystem.WeatherManagerClient; import weather2.weathersystem.wind.WindManager; import java.util.Map; import java.util.Random; public class WindVaneEntityRenderer implements BlockEntityRenderer { private static Map resLocMap = Maps.newHashMap(); private static Map materialMap = Maps.newHashMap(); public static Material getTEMaterial(final String path) { return materialMap.computeIfAbsent(path, m -> createTEMaterial(path)); } private static Material createTEMaterial(final String path) { return new Material(TextureAtlas.LOCATION_BLOCKS, getTextureTE(path)); } public static ResourceLocation getTextureTE(String path) { return getTexture(String.format("textures/blocks/te/%s.png", path)); } public static ResourceLocation getTexture(String path) { return resLocMap.computeIfAbsent(path, k -> getResLoc(path)); } private static ResourceLocation getResLoc(String path) { return new ResourceLocation(Weather.MODID, path); } public static void renderModel(final Material material, final Model model, PoseStack stack, MultiBufferSource buffer, int combinedLightIn, int combinedOverlayIn) { model.renderToBuffer(stack, buffer.getBuffer(model.renderType(material.texture())), combinedLightIn, combinedOverlayIn, 1, 1, 1, 1); } private final Block block; protected final WindVaneModel model; public WindVaneEntityRenderer(final BlockEntityRendererProvider.Context context) { super(); this.block = WeatherBlocks.BLOCK_WIND_VANE.get(); this.model = new WindVaneModel<>(Minecraft.getInstance().getEntityModels().bakeLayer(WindVaneModel.LAYER_LOCATION)); } @Override public void render(T te, float partialTicks, PoseStack stack, MultiBufferSource buffer, int combinedLightIn, int combinedOverlayIn) { this.model.root().getAllParts().forEach(ModelPart::resetPose); //fixes for block ModelPart root = this.model.root(); root.x += 8; root.y += 8; root.z += 8; root.xRot += Math.toRadians(180); root.yRot += Math.toRadians(180); root.y += 28; float scale = 0.5F; root.offsetScale(new Vector3f(scale, scale, scale)); ModelPart top = this.model.root().getChild("root").getChild("base").getChild("middle").getChild("top"); if (top != null) { WeatherManagerClient weatherMan = ClientTickHandler.weatherManager; if (weatherMan == null) return; WindManager windMan = weatherMan.getWindManager(); if (windMan == null) return; float lerpAngle = (float) Mth.lerp((double)partialTicks, ((WindVaneBlockEntity) te).smoothAnglePrev, ((WindVaneBlockEntity) te).smoothAngle); float renderAngle = lerpAngle; top.yRot = (float) Math.toRadians(renderAngle); boolean shaking = windMan.getWindSpeed(te.getBlockPos()) >= 1.5; if (shaking) { Random rand = new Random(te.getLevel().getGameTime()); top.yRot += (float) ((rand.nextFloat() - rand.nextFloat()) * Math.toRadians(2)); top.zRot = (float) ((rand.nextFloat() - rand.nextFloat()) * Math.toRadians(1)); } } renderModel(getTEMaterial("wind_vane"), model, stack, buffer, combinedLightIn, combinedOverlayIn); } } ================================================ FILE: src/main/java/weather2/command/CommandWeather2Client.java ================================================ package weather2.command; import com.corosus.coroutil.util.CULog; import com.corosus.modconfig.ConfigMod; import com.mojang.brigadier.Command; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.arguments.BoolArgumentType; import com.mojang.brigadier.arguments.FloatArgumentType; import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.brigadier.context.CommandContext; import net.minecraft.client.Minecraft; import net.minecraft.client.particle.Particle; import net.minecraft.client.particle.ParticleEngine; import net.minecraft.client.particle.ParticleRenderType; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; import net.minecraft.network.chat.Component; import net.minecraftforge.fml.util.ObfuscationReflectionHelper; import weather2.ClientTickHandler; import weather2.config.ConfigDebug; import weather2.config.ConfigParticle; import java.lang.reflect.Field; import java.util.Map; import java.util.Queue; import static net.minecraft.commands.Commands.argument; import static net.minecraft.commands.Commands.literal; public class CommandWeather2Client { public static void register(final CommandDispatcher dispatcher) { dispatcher.register( Commands.literal(getCommandName()) .then(literal("client") .then(literal("particle_rate") .then(argument("value", FloatArgumentType.floatArg(0, 1F)).executes(c -> { float value = FloatArgumentType.getFloat(c, "value"); ConfigParticle.Particle_effect_rate = value; c.getSource().sendSuccess(() -> Component.literal("Set weather2 particle effect rate to " + value), true); ConfigMod.forceSaveAllFilesFromRuntimeSettings(); return Command.SINGLE_SUCCESS; })) ) /*.then(literal("particle_reset_frequency") .then(argument("seconds", IntegerArgumentType.integer(0, 20*60*24)).executes(c -> { int value = IntegerArgumentType.getInteger(c, "seconds"); ConfigDebug.Particle_Reset_Frequency = value * 20; c.getSource().sendSuccess(() -> Component.literal("Set weather2 particle reset frequency " + value), true); ConfigMod.forceSaveAllFilesFromRuntimeSettings(); ConfigMod.forceSaveAllFilesFromRuntimeSettings(); return Command.SINGLE_SUCCESS; })) )*/ .then(literal("particle_vanilla_precipitation") .then(argument("value", BoolArgumentType.bool()).executes(c -> { boolean value = BoolArgumentType.getBool(c, "value"); ConfigParticle.Particle_vanilla_precipitation = value; c.getSource().sendSuccess(() -> Component.literal("Set weather2 to use vanilla particles?: " + value), true); ConfigMod.forceSaveAllFilesFromRuntimeSettings(); return Command.SINGLE_SUCCESS; })) ) .then(literal("particle_engine") .then(literal("weather2").executes(c -> { ConfigParticle.Particle_engine_weather2 = true; c.getSource().sendSuccess(() -> Component.literal("Set particle engine to weather2"), true); ConfigMod.forceSaveAllFilesFromRuntimeSettings(); return Command.SINGLE_SUCCESS; })) .then(literal("vanilla").executes(c -> { ConfigParticle.Particle_engine_weather2 = false; ClientTickHandler.particleManagerExtended().clearParticles(); c.getSource().sendSuccess(() -> Component.literal("Set particle engine to vanilla"), true); ConfigMod.forceSaveAllFilesFromRuntimeSettings(); return Command.SINGLE_SUCCESS; })) ) .then(literal("debug") .then(literal("particles_weather2").executes(c -> { msg(c, "total particle count: " + ClientTickHandler.particleManagerExtended().countParticles()); Map> particles = ClientTickHandler.particleManagerExtended().getParticles(); if (particles != null) { msg(c, "particle type count: " + particles.size()); msg(c, "detailed particle info output to log file"); //Map> particles = particleEngine.particles; int maxCount = 200; int count = 0; CULog.log("outputting particle data:"); for (Map.Entry> type : particles.entrySet()) { CULog.log("type: " + type.getKey() + " -> " + type.getValue().size() + " - classpath: " + type.getKey().getClass().getName()); if (count > maxCount) { CULog.log("aborted due to large particle type list"); break; } count++; } } else { msg(c, "failed to get particles list"); } return Command.SINGLE_SUCCESS; })) .then(literal("particles_vanilla").executes(c -> { ParticleEngine particleEngine = Minecraft.getInstance().particleEngine; //Map> particles = ObfuscationReflectionHelper.getPrivateValue(ParticleEngine.class, particleEngine, "particles"); msg(c, "total particle count: " + particleEngine.countParticles()); msg(c, "emitter count: " + particleEngine.trackingEmitters.size()); Map> particles = getParticles(); if (particles != null) { msg(c, "particle type count: " + particles.size()); msg(c, "detailed particle info output to log file"); //Map> particles = particleEngine.particles; int maxCount = 200; int count = 0; CULog.log("outputting particle data:"); for (Map.Entry> type : particles.entrySet()) { CULog.log("type: " + type.getKey() + " -> " + type.getValue().size() + " - classpath: " + type.getKey().getClass().getName()); if (count > maxCount) { CULog.log("aborted due to large particle type list"); break; } count++; } } else { msg(c, "failed to get particles list"); } return Command.SINGLE_SUCCESS; })) .then(literal("particle_engine_render") .then(argument("value", BoolArgumentType.bool()).executes(c -> { boolean value = BoolArgumentType.getBool(c, "value"); ConfigDebug.Particle_engine_render = value; c.getSource().sendSuccess(() -> Component.literal("ConfigParticle.Particle_engine_render: " + value), true); ConfigMod.forceSaveAllFilesFromRuntimeSettings(); return Command.SINGLE_SUCCESS; })) ) .then(literal("particle_engine_tick") .then(argument("value", BoolArgumentType.bool()).executes(c -> { boolean value = BoolArgumentType.getBool(c, "value"); ConfigDebug.Particle_engine_tick = value; c.getSource().sendSuccess(() -> Component.literal("ConfigParticle.Particle_engine_tick: " + value), true); ConfigMod.forceSaveAllFilesFromRuntimeSettings(); return Command.SINGLE_SUCCESS; })) ) .then(literal("reset_vanilla_particles") .executes(c -> { Minecraft.getInstance().particleEngine.clearParticles(); c.getSource().sendSuccess(() -> Component.literal("cleared particles"), true); return Command.SINGLE_SUCCESS; }) ) ) ) ); } public static Map> getParticles() { try { Field[] fields = ParticleEngine.class.getDeclaredFields(); fields[6].setAccessible(true); return (Map>) fields[6].get(Minecraft.getInstance().particleEngine); } catch (IllegalAccessException ex) { ex.printStackTrace(); } return null; } public static void msg(CommandContext c, String msg) { c.getSource().sendSuccess(() -> Component.literal(msg), true); } public static String getCommandName() { return "weather2"; } } ================================================ FILE: src/main/java/weather2/command/WeatherCommand.java ================================================ package weather2.command; import com.corosus.coroutil.util.CoroUtilBlock; import com.mojang.brigadier.Command; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.arguments.FloatArgumentType; import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.brigadier.context.CommandContext; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.arguments.RangeArgument; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.world.phys.Vec3; import net.minecraftforge.fml.InterModComms; import weather2.ServerTickHandler; import weather2.config.ConfigMisc; import weather2.config.ConfigWind; import weather2.config.WeatherUtilConfig; import weather2.util.WeatherUtil; import weather2.weathersystem.WeatherManagerServer; import weather2.weathersystem.storm.StormObject; import weather2.weathersystem.storm.WeatherObjectParticleStorm; import static net.minecraft.commands.Commands.argument; import static net.minecraft.commands.Commands.literal; public class WeatherCommand { public static void register(final CommandDispatcher dispatcher) { dispatcher.register( literal("weather2") .then(literal("kill_all_storms").requires(s -> s.hasPermission(2)).executes(c -> { WeatherManagerServer wm = ServerTickHandler.getWeatherManagerFor(c.getSource().getLevel().dimension()); wm.clearAllStorms(); c.getSource().sendSuccess(() -> Component.literal("Killed all storms"), true); return Command.SINGLE_SUCCESS; })) .then(literal("debug").requires(s -> s.hasPermission(2)) .then(literal("print_grab_list").executes(c -> { WeatherUtil.testAllBlocks(); c.getSource().sendSuccess(() -> Component.literal("Tornado grab list printed to debug.log"), true); return Command.SINGLE_SUCCESS; })) .then(literal("storm_chance").executes(c -> { WeatherManagerServer wm = ServerTickHandler.getWeatherManagerFor(c.getSource().getLevel().dimension()); float chance = wm.getBiomeBasedStormSpawnChanceInArea(CoroUtilBlock.blockPos(c.getSource().getPosition().x, c.getSource().getPosition().y, c.getSource().getPosition().z)); c.getSource().sendSuccess(() -> Component.literal("Likelyhood of storms to spawn here within 1024 blocks: " + (chance * 100)), true); return Command.SINGLE_SUCCESS; })) ) .then(literal("wind_event").requires(s -> s.hasPermission(2)) .then(literal("clear").executes(c -> { WeatherManagerServer wm = ServerTickHandler.getWeatherManagerFor(c.getSource().getLevel().dimension()); wm.getWindManager().stopLowWindEvent(); wm.getWindManager().stopHighWindEvent(); c.getSource().sendSuccess(() -> Component.literal("Stopped any active high or low wind events"), true); return Command.SINGLE_SUCCESS; })) .then(literal("high").executes(c -> { WeatherManagerServer wm = ServerTickHandler.getWeatherManagerFor(c.getSource().getLevel().dimension()); wm.getWindManager().stopLowWindEvent(); wm.getWindManager().startHighWindEvent(); c.getSource().sendSuccess(() -> Component.literal("Started high wind event"), true); return Command.SINGLE_SUCCESS; })) .then(literal("low").executes(c -> { WeatherManagerServer wm = ServerTickHandler.getWeatherManagerFor(c.getSource().getLevel().dimension()); wm.getWindManager().stopHighWindEvent(); wm.getWindManager().startLowWindEvent(); wm.getWindManager().windSpeedGlobal = (float) (ConfigWind.windSpeedMin + 0.2F); c.getSource().sendSuccess(() -> Component.literal("Started low wind event"), true); return Command.SINGLE_SUCCESS; })) ) .then(literal("wind_angle").requires(s -> s.hasPermission(2)) .then(argument("angle", IntegerArgumentType.integer(0, 359)).executes(c -> { int angle = IntegerArgumentType.getInteger(c, "angle"); WeatherManagerServer wm = ServerTickHandler.getWeatherManagerFor(c.getSource().getLevel().dimension()); wm.getWindManager().windAngleGlobal = angle; c.getSource().sendSuccess(() -> Component.literal("Set wind angle for clouds to " + angle), true); return Command.SINGLE_SUCCESS; })) ) .then(literal("wind_speed").requires(s -> s.hasPermission(2)) .then(argument("speed", FloatArgumentType.floatArg(0, 1.5F)).executes(c -> { float speed = FloatArgumentType.getFloat(c, "speed"); WeatherManagerServer wm = ServerTickHandler.getWeatherManagerFor(c.getSource().getLevel().dimension()); wm.getWindManager().windSpeedGlobal = speed; c.getSource().sendSuccess(() -> Component.literal("Set wind speed for clouds to " + speed), true); return Command.SINGLE_SUCCESS; })) ) .then(literal("server_precipitation").requires(s -> s.hasPermission(2)) .then(argument("amount", FloatArgumentType.floatArg(0, 1.0F)).executes(c -> { float amount = FloatArgumentType.getFloat(c, "amount"); WeatherManagerServer wm = ServerTickHandler.getWeatherManagerFor(c.getSource().getLevel().dimension()); wm.vanillaRainAmountOnServer = amount; if (ConfigMisc.overcastMode) { c.getSource().sendSuccess(() -> Component.literal("Server precipitation amount set to " + amount), true); } else { c.getSource().sendSuccess(() -> Component.literal("overcastMode not on, this will change nothing"), true); } return Command.SINGLE_SUCCESS; })) ) .then(literal("summon").requires(s -> s.hasPermission(2)).requires(s -> WeatherUtilConfig.listDimensionsWeather.contains(s.getLevel().dimension().location().toString())) .then(literal("storm_rain").executes(c -> { StormObject stormObject = summonStorm(c, StormObject.STATE_NORMAL); c.getSource().sendSuccess(() -> Component.literal("Summoned rain storm"), true); return Command.SINGLE_SUCCESS; })) .then(literal("storm_lightning").executes(c -> { StormObject stormObject = summonStorm(c, StormObject.STATE_THUNDER); stormObject.initRealStorm(null, null); stormObject.levelCurIntensityStage = StormObject.STATE_THUNDER; stormObject.levelStormIntensityMax = StormObject.STATE_THUNDER; c.getSource().sendSuccess(() -> Component.literal("Summoned lightning storm"), true); return Command.SINGLE_SUCCESS; })) .then(literal("storm_highwind").executes(c -> { StormObject stormObject = summonStorm(c, StormObject.STATE_HIGHWIND); stormObject.initRealStorm(null, null); stormObject.levelCurIntensityStage = StormObject.STATE_HIGHWIND; stormObject.levelStormIntensityMax = StormObject.STATE_HIGHWIND; c.getSource().sendSuccess(() -> Component.literal("Summoned highwind storm"), true); return Command.SINGLE_SUCCESS; })) .then(literal("storm_hail").executes(c -> { StormObject stormObject = summonStorm(c, StormObject.STATE_HAIL); stormObject.initRealStorm(null, null); stormObject.levelCurIntensityStage = StormObject.STATE_HAIL; stormObject.levelStormIntensityMax = StormObject.STATE_HAIL; c.getSource().sendSuccess(() -> Component.literal("Summoned hail storm"), true); return Command.SINGLE_SUCCESS; })) .then(literal("tornado_f0").executes(c -> { StormObject stormObject = summonStorm(c, StormObject.STATE_FORMING); stormObject.levelStormIntensityMax = StormObject.STATE_STAGE1; c.getSource().sendSuccess(() -> Component.literal("Summoned forming tornado"), true); return Command.SINGLE_SUCCESS; })) .then(literal("tornado_f1").executes(c -> { StormObject stormObject = summonStorm(c, StormObject.STATE_STAGE1); c.getSource().sendSuccess(() -> Component.literal("Summoned f1 tornado"), true); return Command.SINGLE_SUCCESS; })) .then(literal("tornado_f2").executes(c -> { StormObject stormObject = summonStorm(c, StormObject.STATE_STAGE2); c.getSource().sendSuccess(() -> Component.literal("Summoned f2 tornado"), true); return Command.SINGLE_SUCCESS; })) .then(literal("tornado_f3").executes(c -> { StormObject stormObject = summonStorm(c, StormObject.STATE_STAGE3); c.getSource().sendSuccess(() -> Component.literal("Summoned f3 tornado"), true); return Command.SINGLE_SUCCESS; })) .then(literal("tornado_f4").executes(c -> { StormObject stormObject = summonStorm(c, StormObject.STATE_STAGE4); c.getSource().sendSuccess(() -> Component.literal("Summoned f4 tornado"), true); return Command.SINGLE_SUCCESS; })) .then(literal("sharknado").executes(c -> { StormObject stormObject = summonStorm(c, StormObject.STATE_STAGE1); stormObject.levelStormIntensityMax = StormObject.STATE_STAGE4; stormObject.setSharknado(true); c.getSource().sendSuccess(() -> Component.literal("Summoned sharknado"), true); return Command.SINGLE_SUCCESS; })) .then(literal("firenado_f0").executes(c -> { StormObject stormObject = summonStorm(c, StormObject.STATE_FORMING); stormObject.levelStormIntensityMax = StormObject.STATE_STAGE4; stormObject.isFirenado = true; c.getSource().sendSuccess(() -> Component.literal("Summoned firenado"), true); return Command.SINGLE_SUCCESS; })) .then(literal("firenado_f1").executes(c -> { StormObject stormObject = summonStorm(c, StormObject.STATE_STAGE1); stormObject.levelStormIntensityMax = StormObject.STATE_STAGE4; stormObject.isFirenado = true; c.getSource().sendSuccess(() -> Component.literal("Summoned firenado"), true); return Command.SINGLE_SUCCESS; })) /*.then(literal("tornado_player_baby").executes(c -> { StormObject stormObject = summonStorm(c, StormObject.STATE_STAGE1); stormObject.setupPlayerControlledTornado(c.getSource().getEntity()); stormObject.setPlayerControlledTimeLeft(800); stormObject.setBaby(true); c.getSource().sendSuccess(() -> Component.literal("Summoned baby player tornado"), true); return Command.SINGLE_SUCCESS; })) .then(literal("tornado_baby").executes(c -> { StormObject stormObject = summonStorm(c, StormObject.STATE_STAGE1); stormObject.setBaby(true); c.getSource().sendSuccess(() -> Component.literal("Summoned baby tornado"), true); return Command.SINGLE_SUCCESS; }))*/ .then(literal("tornado_player").executes(c -> { StormObject stormObject = summonStorm(c, StormObject.STATE_STAGE1); stormObject.setupPlayerControlledTornado(c.getSource().getEntity()); stormObject.setPlayerControlledTimeLeft(600); c.getSource().sendSuccess(() -> Component.literal("Summoned player tornado"), true); return Command.SINGLE_SUCCESS; }))/* .then(literal("tornado_pet").executes(c -> { StormObject stormObject = summonStorm(c, StormObject.STATE_STAGE1); stormObject.setupPlayerControlledTornado(c.getSource().getEntity()); stormObject.setPlayerControlledTimeLeft(-1); stormObject.setPet(true); stormObject.setPetGrabsItems(true); c.getSource().sendSuccess(() -> Component.literal("Summoned pet tornado"), true); return Command.SINGLE_SUCCESS; })) .then(literal("tornado_pet_no_item_grab").executes(c -> { StormObject stormObject = summonStorm(c, StormObject.STATE_STAGE1); stormObject.setupPlayerControlledTornado(c.getSource().getEntity()); stormObject.setPlayerControlledTimeLeft(-1); stormObject.setPet(true); stormObject.setPetGrabsItems(false); c.getSource().sendSuccess(() -> Component.literal("Summoned pet tornado with no item grabbing"), true); return Command.SINGLE_SUCCESS; })) .then(literal("tornadotestimc").executes(c -> { InterModComms.sendTo("weather2", "sharknado", () -> { CompoundTag tag = new CompoundTag(); tag.putString("uuid", c.getSource().getEntity().getUUID().toString()); tag.putInt("time_ticks", 1200); tag.putBoolean("baby", false); tag.putBoolean("sharknado", true); tag.putString("dimension", c.getSource().getEntity().getLevel().dimension().location().toString()); return tag; }); c.getSource().sendSuccess(() -> Component.literal("Summoned tornado test"), true); return Command.SINGLE_SUCCESS; }))*/ .then(literal("tornado_f0_max").executes(c -> { StormObject stormObject = summonStorm(c, StormObject.STATE_FORMING); stormObject.levelStormIntensityMax = StormObject.STATE_FORMING; stormObject.alwaysProgresses = false; c.getSource().sendSuccess(() -> Component.literal("Summoned tornado"), true); return Command.SINGLE_SUCCESS; })) .then(literal("sandstorm_try").executes(c -> { WeatherManagerServer wm = ServerTickHandler.getWeatherManagerFor(c.getSource().getLevel().dimension()); boolean sandstormMade = wm.trySpawnParticleStormNearPos(c.getSource().getLevel(), c.getSource().getPosition(), WeatherObjectParticleStorm.StormType.SANDSTORM); if (sandstormMade) { c.getSource().sendSuccess(() -> Component.literal("Summoned sandstorm"), true); wm.getWindManager().stopLowWindEvent(); wm.getWindManager().startHighWindEvent(); } else { c.getSource().sendSuccess(() -> Component.literal("Couldn't spawn, try being in a large desert"), true); } return Command.SINGLE_SUCCESS; })) .then(literal("snowstorm_try").executes(c -> { WeatherManagerServer wm = ServerTickHandler.getWeatherManagerFor(c.getSource().getLevel().dimension()); boolean sandstormMade = wm.trySpawnParticleStormNearPos(c.getSource().getLevel(), c.getSource().getPosition(), WeatherObjectParticleStorm.StormType.SNOWSTORM); if (sandstormMade) { c.getSource().sendSuccess(() -> Component.literal("Summoned snowstorm"), true); wm.getWindManager().stopLowWindEvent(); wm.getWindManager().startHighWindEvent(); } else { c.getSource().sendSuccess(() -> Component.literal("Couldn't spawn, try being in a large snowy area"), true); } return Command.SINGLE_SUCCESS; })) .then(literal("sandstorm_force").executes(c -> { WeatherManagerServer wm = ServerTickHandler.getWeatherManagerFor(c.getSource().getLevel().dimension()); wm.spawnParticleStorm(CoroUtilBlock.blockPos(c.getSource().getPosition()), WeatherObjectParticleStorm.StormType.SANDSTORM); wm.getWindManager().stopLowWindEvent(); wm.getWindManager().startHighWindEvent(); c.getSource().sendSuccess(() -> Component.literal("Summoned sandstorm"), true); return Command.SINGLE_SUCCESS; })) .then(literal("snowstorm_force").executes(c -> { WeatherManagerServer wm = ServerTickHandler.getWeatherManagerFor(c.getSource().getLevel().dimension()); wm.spawnParticleStorm(CoroUtilBlock.blockPos(c.getSource().getPosition()), WeatherObjectParticleStorm.StormType.SNOWSTORM); wm.getWindManager().stopLowWindEvent(); wm.getWindManager().startHighWindEvent(); c.getSource().sendSuccess(() -> Component.literal("Summoned snowstorm"), true); return Command.SINGLE_SUCCESS; })) ) ); } private static StormObject summonStorm(CommandContext c, int intensity) { WeatherManagerServer wm = ServerTickHandler.getWeatherManagerFor(c.getSource().getLevel().dimension()); StormObject stormObject = new StormObject(wm); stormObject.setupStorm(c.getSource().getEntity()); stormObject.levelCurIntensityStage = intensity; stormObject.levelStormIntensityMax = intensity; stormObject.initPositions(new Vec3(c.getSource().getPosition().x, StormObject.layers.get(stormObject.layer), c.getSource().getPosition().z)); wm.addStormObject(stormObject); wm.syncStormNew(stormObject); return stormObject; } } ================================================ FILE: src/main/java/weather2/config/ClientConfigData.java ================================================ package weather2.config; import net.minecraft.nbt.CompoundTag; /** * Used for anything that needs to be used on both client and server side, to avoid config mismatch between dedicated server and clients */ public class ClientConfigData { public boolean overcastMode = false; public boolean Storm_Tornado_grabPlayer = true; public boolean Storm_Tornado_grabPlayersOnly = false; public boolean Storm_Tornado_grabMobs = true; public boolean Storm_Tornado_grabAnimals = true; public boolean Storm_Tornado_grabItems = false; public boolean Storm_Tornado_grabVillagers = true; public boolean Aesthetic_Only_Mode = false; /** * For client side * * @param nbt */ public void readNBT(CompoundTag nbt) { overcastMode = nbt.getBoolean("overcastMode"); Storm_Tornado_grabPlayer = nbt.getBoolean("Storm_Tornado_grabPlayer"); Storm_Tornado_grabPlayersOnly = nbt.getBoolean("Storm_Tornado_grabPlayersOnly"); Storm_Tornado_grabMobs = nbt.getBoolean("Storm_Tornado_grabMobs"); Storm_Tornado_grabAnimals = nbt.getBoolean("Storm_Tornado_grabAnimals"); Storm_Tornado_grabVillagers = nbt.getBoolean("Storm_Tornado_grabVillagers"); Storm_Tornado_grabItems = nbt.getBoolean("Storm_Tornado_grabItems"); Aesthetic_Only_Mode = nbt.getBoolean("Aesthetic_Only_Mode"); } /** * For server side * * @param data */ public static void writeNBT(CompoundTag data) { data.putBoolean("overcastMode", ConfigMisc.overcastMode); data.putBoolean("Storm_Tornado_grabPlayer", ConfigTornado.Storm_Tornado_grabPlayer); data.putBoolean("Storm_Tornado_grabPlayersOnly", ConfigTornado.Storm_Tornado_grabPlayersOnly); data.putBoolean("Storm_Tornado_grabMobs", ConfigTornado.Storm_Tornado_grabMobs); data.putBoolean("Storm_Tornado_grabAnimals", ConfigTornado.Storm_Tornado_grabAnimals); data.putBoolean("Storm_Tornado_grabVillagers", ConfigTornado.Storm_Tornado_grabVillagers); data.putBoolean("Storm_Tornado_grabItems", ConfigTornado.Storm_Tornado_grabItems); data.putBoolean("Aesthetic_Only_Mode", ConfigMisc.Aesthetic_Only_Mode); } } ================================================ FILE: src/main/java/weather2/config/ConfigDebug.java ================================================ package weather2.config; import com.corosus.modconfig.IConfigCategory; import weather2.Weather; import java.io.File; public class ConfigDebug implements IConfigCategory { //public static int Particle_Reset_Frequency = 20*60*20; public static int Particle_Reset_Frequency = 0; public static boolean Particle_engine_render = true; public static boolean Particle_engine_tick = true; @Override public String getName() { return "Debug"; } @Override public String getRegistryName() { return Weather.MODID + getName(); } @Override public String getConfigFileName() { return "Weather2" + File.separator + getName(); } @Override public String getCategory() { return "Weather2: " + getName(); } @Override public void hookUpdatedValues() { } } ================================================ FILE: src/main/java/weather2/config/ConfigFoliage.java ================================================ package weather2.config; import com.corosus.modconfig.IConfigCategory; import weather2.Weather; import java.io.File; public class ConfigFoliage implements IConfigCategory { /*public static int foliageShaderRange = 40; public static int Thread_Foliage_Process_Delay = 1000; public static boolean extraGrass = false;*/ @Override public String getName() { return "Foliage"; } @Override public String getRegistryName() { return Weather.MODID + getName(); } @Override public String getConfigFileName() { return "Weather2" + File.separator + getName(); } @Override public String getCategory() { return "Weather2: " + getName(); } @Override public void hookUpdatedValues() { } } ================================================ FILE: src/main/java/weather2/config/ConfigMisc.java ================================================ package weather2.config; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import com.corosus.modconfig.ConfigComment; import com.corosus.modconfig.IConfigCategory; import weather2.Weather; import weather2.weathersystem.storm.StormObject; public class ConfigMisc implements IConfigCategory { //misc //public static boolean Misc_proxyRenderOverrideEnabled = true; //public static boolean Misc_takeControlOfGlobalRain = true; //cutoff a bit extra, noticed lots of storms being insta killed on creation public static int Misc_simBoxRadiusCutoff = 1024+100; public static int Misc_simBoxRadiusSpawn = 1024; /*public static boolean Misc_ForceVanillaCloudsOff = false; public static int Misc_AutoDataSaveIntervalInTicks = 20*60*30; public static boolean consoleDebug = false; public static boolean radarCloudDebug = false;*/ //Weather @ConfigComment("If true, lets server side do vanilla weather rules, weather2 will only make storms when server side says 'rain' is on") public static boolean overcastMode = false; @ConfigComment("Used if overcastMode is off, 1 = lock weather on, 0 = lock weather off, -1 = dont lock anything, let server do whatever") public static int lockServerWeatherMode = 0; //is only used if overcastMode is off //cloudOption @ConfigComment("How many ticks between cloud particle spawning") public static int Cloud_ParticleSpawnDelay = 2; @ConfigComment("Distance between cloud formations, not particles, this includes invisible cloudless formations used during partial cloud coverage") public static int Cloud_Formation_MinDistBetweenSpawned = 300; @ConfigComment("For a second layer of passive non storm progressing cloudOption") public static boolean Cloud_Layer1_Enable = false; public static int Cloud_Layer0_Height = 200 + 64; public static int Cloud_Layer1_Height = 350 + 64; @ConfigComment("Not used at the moment") public static int Cloud_Layer2_Height = 500 + 64; @ConfigComment("How much to randomly change cloud coverage % amount, performed every 10 seconds") public static double Cloud_Coverage_Random_Change_Amount = 0.05D; @ConfigComment("Minimum percent of cloud coverage, supports negative for extended cloudless sky coverage") public static double Cloud_Coverage_Min_Percent = 0D; @ConfigComment("Maximum percent of cloud coverage, supports over 100% for extended full cloud sky coverage") public static double Cloud_Coverage_Max_Percent = 100D; /*public static int Thread_Particle_Process_Delay = 400; //sound public static double volWindScale = 0.05D; public static double volWaterfallScale = 0.5D; public static double volWindTreesScale = 0.5D; public static double volWindLightningScale = 1D;*/ //blocks public static double sirenActivateDistance = 256D; /*public static double sensorActivateDistance = 256D; public static boolean Block_WeatherMachineNoTornadosOrCyclones = false; public static boolean Block_WeatherMachineNoRecipe = false; public static boolean Block_SensorNoRecipe = false; public static boolean Block_SirenNoRecipe = false; public static boolean Block_SirenManualNoRecipe = false; public static boolean Block_WindVaneNoRecipe = false; public static boolean Block_AnemometerNoRecipe = false; public static boolean Block_WeatherForecastNoRecipe = false; public static boolean Block_WeatherDeflectorNoRecipe = false; public static boolean Block_SandLayerNoRecipe = false; public static boolean Block_SandNoRecipe = false; public static boolean Item_PocketSandNoRecipe = false; @ConfigComment("Disabling this recipe will keep them from using other recipes since it depends on this item") public static boolean Item_WeatherItemNoRecipe = false;*/ //dimension settings public static String Dimension_List_Weather = "minecraft:overworld, tropicraft:tropicraft"; public static String Dimension_List_Clouds = "minecraft:overworld, tropicraft:tropicraft"; public static String Dimension_List_Storms = "minecraft:overworld, tropicraft:tropicraft"; public static String Dimension_List_WindEffects = "minecraft:overworld, tropicraft:tropicraft"; /*public static boolean Villager_MoveInsideForStorms = true; public static int Villager_MoveInsideForStorms_Dist = 256; public static double shaderParticleRateAmplifier = 3D;*/ public static boolean blockBreakingInvokesCancellableEvent = false; /*@ConfigComment("If true, will cancel vanilla behavior of setting clear weather when the player sleeps, for global overcast mode") public static boolean Global_Overcast_Prevent_Rain_Reset_On_Sleep = false;*/ /*@ConfigComment("Use if you are on a server with weather but want it ALL off client side for performance reasons, overrides basically every client based setting") public static boolean Client_PotatoPC_Mode = false;*/ @ConfigComment("Server and client side, Locks down the mod to only do wind, leaves, foliage shader if on, etc. No weather systems, turns overcast mode on") public static boolean Aesthetic_Only_Mode = false; @ConfigComment("Runs regardless of Aesthetic_Only_Mode, makes snowstorms possible everywhere") public static boolean Winter_Wonderland = false; public ConfigMisc() { } @Override public String getName() { return "Misc"; } @Override public String getRegistryName() { return Weather.MODID + getName(); } @Override public String getConfigFileName() { return "Weather2" + File.separator + getName(); } @Override public String getCategory() { return "Weather2: " + getName(); } @Override public void hookUpdatedValues() { //Weather.dbg("block list processing disabled"); //TODO: 1.14 uncomment //WeatherUtil.doBlockList(); WeatherUtilConfig.processLists(); StormObject.static_YPos_layer0 = Cloud_Layer0_Height; StormObject.static_YPos_layer1 = Cloud_Layer1_Height; StormObject.static_YPos_layer2 = Cloud_Layer2_Height; StormObject.layers = new ArrayList<>(Arrays.asList(StormObject.static_YPos_layer0, StormObject.static_YPos_layer1, StormObject.static_YPos_layer2)); } } ================================================ FILE: src/main/java/weather2/config/ConfigParticle.java ================================================ package weather2.config; import com.corosus.modconfig.ConfigComment; import com.corosus.modconfig.IConfigCategory; import weather2.Weather; import java.io.File; public class ConfigParticle implements IConfigCategory { //particles /*public static boolean Wind_Particle_leafs = true; @ConfigComment("Currently used for rates of leaf, waterfall, and fire particles") public static double Wind_Particle_effect_rate = 0.7D; public static boolean Wind_Particle_waterfall = true; //public static boolean Wind_Particle_snow = false; public static boolean Wind_Particle_fire = false; @ConfigComment("Enables or disables all precipitation particle types") public static boolean Particle_RainSnow = true; public static boolean Particle_Rain = false; public static boolean Particle_Rain_GroundSplash = true; public static boolean Particle_Rain_DownfallSheet = false; public static boolean Particle_VanillaAndWeatherOnly = false;*/ @ConfigComment("Adjust amount of precipitation based particles, works as a multiplier") public static double Precipitation_Particle_effect_rate = 0.7D; //public static double Sandstorm_Particle_Debris_effect_rate = 0.6D; //public static double Sandstorm_Particle_Dust_effect_rate = 0.6D; @ConfigComment("Adjust amount of all weather2 based particles, works as a multiplier") public static double Particle_effect_rate = 1D; @ConfigComment("If true, uses vanilla rain/snow non particle precipitation") public static boolean Particle_vanilla_precipitation = false; @ConfigComment("If set to false, particles are spawned in using the vanilla particle renderer, may cause issues, performance seems worse") public static boolean Particle_engine_weather2 = true; @ConfigComment("Extra flying block particles to spawn when the tornado rips up a block") public static int Particle_Tornado_extraParticleCubes = 2; @Override public String getName() { return "Particle"; } @Override public String getRegistryName() { return Weather.MODID + getName(); } @Override public String getConfigFileName() { return "Weather2" + File.separator + getName(); } @Override public String getCategory() { return "Weather2: " + getName(); } @Override public void hookUpdatedValues() { } } ================================================ FILE: src/main/java/weather2/config/ConfigSand.java ================================================ package weather2.config; import com.corosus.modconfig.ConfigComment; import com.corosus.modconfig.IConfigCategory; import weather2.Weather; import java.io.File; public class ConfigSand implements IConfigCategory { @ConfigComment("Takes the sand out of sandwiches") public static boolean Storm_NoSandstorms = false; //sandstorm settings public static boolean Sandstorm_UseGlobalServerRate = false; public static int Sandstorm_OddsTo1 = 30; @ConfigComment("Time between sandstorms for either each player or entire server depending on if global rate is on, default: 3 client days") public static int Sandstorm_TimeBetweenInTicks = 20*60*20*3; @ConfigComment("Amount of game ticks between sand buildup iterations, keep it high to prevent client side chunk tick spam that destroys FPS") public static int Sandstorm_Sand_Buildup_TickRate = 40; @ConfigComment("Base amount of loops done per iteration, scaled by the sandstorms intensity (value given here is the max possible)") public static int Sandstorm_Sand_Buildup_LoopAmountBase = 800; @ConfigComment("Max height of sand allowed to buildup against something, higher = things get more buried over time") public static int Sandstorm_Sand_Block_Max_Height = 3; @ConfigComment("Allow layered sand blocks to buildup outside deserty biomes where sandstorm is") public static boolean Sandstorm_Sand_Buildup_AllowOutsideDesert = true; public static double Sandstorm_Particle_Dust_effect_rate = 0.6D; //public static double Precipitation_Particle_effect_rate = 0.7D; public static double Sandstorm_Particle_Debris_effect_rate = 0.6D; public static boolean Sandstorm_Siren_PleaseNoDarude = false; @Override public String getName() { return "Sand"; } @Override public String getRegistryName() { return Weather.MODID + getName(); } @Override public String getConfigFileName() { return "Weather2" + File.separator + getName(); } @Override public String getCategory() { return "Weather2: " + getName(); } @Override public void hookUpdatedValues() { } } ================================================ FILE: src/main/java/weather2/config/ConfigSnow.java ================================================ package weather2.config; import com.corosus.modconfig.ConfigComment; import com.corosus.modconfig.IConfigCategory; import weather2.Weather; import java.io.File; public class ConfigSnow implements IConfigCategory { public static boolean Storm_NoSnowstorms = false; public static boolean Snowstorm_UseGlobalServerRate = false; public static int Snowstorm_OddsTo1 = 30; @ConfigComment("Time between snowstorms for either each player or entire server depending on if global rate is on, default: 3 client days") public static int Snowstorm_TimeBetweenInTicks = 20*60*20*3; @ConfigComment("Amount of game ticks between snow buildup iterations, keep it high to prevent client side chunk tick spam that destroys FPS") public static int Snowstorm_Snow_Buildup_TickRate = 40; @ConfigComment("Base amount of loops done per iteration, scaled by the snowstorms intensity (value given here is the max possible), eg: at max storm intensity, every 40th tick, itll try to build up snow in 800 places around the storm") public static int Snowstorm_Snow_Buildup_LoopAmountBase = 800; @ConfigComment("Max height of snow allowed to buildup against something, higher = things get more buried over time") public static int Snowstorm_Snow_Block_Max_Height = 5; @ConfigComment("Allow layered snow blocks to buildup outside cold biomes where snowstorm is") public static boolean Snowstorm_Snow_Buildup_AllowOutsideColdBiomes = true; @Override public String getName() { return "Snow"; } @Override public String getRegistryName() { return Weather.MODID + getName(); } @Override public String getConfigFileName() { return "Weather2" + File.separator + getName(); } @Override public String getCategory() { return "Weather2: " + getName(); } @Override public void hookUpdatedValues() { } } ================================================ FILE: src/main/java/weather2/config/ConfigSound.java ================================================ package weather2.config; import com.corosus.modconfig.ConfigParams; import com.corosus.modconfig.IConfigCategory; import weather2.Weather; import java.io.File; public class ConfigSound implements IConfigCategory { @ConfigParams(min = 0, max = 5) public static double leavesVolume = 0F; @ConfigParams(min = 0, max = 5) public static double tornadoWindVolume = 1F; @ConfigParams(min = 0, max = 5) public static double tornadoDamageVolume = 1F; @ConfigParams(min = 0, max = 5) public static double windyStormVolume = 1F; @ConfigParams(min = 0, max = 5) public static double sirenVolume = 1F; @Override public String getName() { return "Sound"; } @Override public String getRegistryName() { return Weather.MODID + getName(); } @Override public String getConfigFileName() { return "Weather2" + File.separator + getName(); } @Override public String getCategory() { return "Weather2: " + getName(); } @Override public void hookUpdatedValues() { } } ================================================ FILE: src/main/java/weather2/config/ConfigStorm.java ================================================ package weather2.config; import com.corosus.modconfig.ConfigComment; import com.corosus.modconfig.IConfigCategory; import weather2.Weather; import java.io.File; public class ConfigStorm implements IConfigCategory { public static int Storm_OddsTo1OfHighWindWaterSpout = 150; public static boolean Storm_FlyingBlocksHurt = true; public static int Storm_MaxPerPlayerPerLayer = 20; public static int Storm_Deadly_CollideDistance = 128; public static int Storm_LightningStrikeBaseValueOddsTo1 = 200; public static boolean Storm_NoRainVisual = false; public static int Storm_MaxRadius = 300; public static int Storm_AllTypes_TickRateDelay = 60; public static int Storm_Rain_WaterBuildUpRate = 10; public static int Storm_Rain_WaterSpendRate = 3; public static int Storm_Rain_WaterBuildUpOddsTo1FromSource = 15; public static int Storm_Rain_WaterBuildUpOddsTo1FromNothing = 100; public static int Storm_Rain_WaterBuildUpOddsTo1FromOvercastRaining = 30; //public static int Storm_Rain_WaterBuildUp = 150; public static double Storm_TemperatureAdjustRate = 0.1D; //public static double Storm_Deadly_MinIntensity = 5.3D; public static int Storm_HailPerTick = 10; public static int Storm_OddsTo1OfOceanBasedStorm = 300; //public static int Storm_OddsTo1OfLandBasedStorm = -1; //public static int Storm_OddsTo1OfProgressionBase = 15; //public static int Storm_OddsTo1OfProgressionStageMultiplier = 3; public static int Storm_PercentChanceOf_HighWind = 90; public static int Storm_PercentChanceOf_Hail = 80; public static int Storm_PercentChanceOf_F0_Tornado = 70; public static int Storm_PercentChanceOf_C0_Cyclone = 70; public static int Storm_PercentChanceOf_F1_Tornado = 50; public static int Storm_PercentChanceOf_C1_Cyclone = 50; public static int Storm_PercentChanceOf_F2_Tornado = 40; public static int Storm_PercentChanceOf_C2_Cyclone = 40; public static int Storm_PercentChanceOf_F3_Tornado = 30; public static int Storm_PercentChanceOf_C3_Cyclone = 30; public static int Storm_PercentChanceOf_F4_Tornado = 20; public static int Storm_PercentChanceOf_C4_Cyclone = 20; public static int Storm_PercentChanceOf_F5_Tornado = 10; @ConfigComment("Also known as full blown hurricane") public static int Storm_PercentChanceOf_C5_Cyclone = 10; public static int Storm_ParticleSpawnDelay = 3; //per player storm settings public static int Player_Storm_Deadly_OddsTo1 = 30; public static int Player_Storm_Deadly_TimeBetweenInTicks = 20*60*20*3; //3 mc days //per server storm settings public static boolean Server_Storm_Deadly_UseGlobalRate = true; @ConfigComment("Used if Server_Storm_Deadly_UseGlobalRate is on, replaces use of Player_Storm_Deadly_OddsTo1") public static int Server_Storm_Deadly_OddsTo1 = 30; @ConfigComment("Used if Server_Storm_Deadly_UseGlobalRate is on, replaces use of Player_Storm_Deadly_TimeBetweenInTicks") public static int Server_Storm_Deadly_TimeBetweenInTicks = 20*60*20*3; @ConfigComment("For areas without the right mix of hot and cold biomes") public static int Player_Storm_Deadly_OddsTo1_Land_Based = 1200; @ConfigComment("For areas without the right mix of hot and cold biomes") public static int Player_Storm_Deadly_TimeBetweenInTicks_Land_Based = 20*60*20*10; //10 mc days @ConfigComment("Used if Server_Storm_Deadly_UseGlobalRate is on, for areas without the right mix of hot and cold biomes") public static int Server_Storm_Deadly_OddsTo1_Land_Based = 1200; @ConfigComment("Used if Server_Storm_Deadly_UseGlobalRate is on, for areas without the right mix of hot and cold biomes") public static int Server_Storm_Deadly_TimeBetweenInTicks_Land_Based = 20*60*20*10; //10 mc days public static boolean preventServerThunderstorms = true; //lightning public static int Lightning_OddsTo1OfFire = 20; public static int Lightning_lifetimeOfFire = 3; public static int Lightning_DistanceToPlayerForEffects = 256; public static boolean Lightning_StartsFires = false; public static int Storm_Deflector_RadiusOfStormRemoval = 150; @ConfigComment("The minimum stage a storm has to be at to be removed, stages are: 0 = anything, 1 = thunder, 2 = high wind, 3 = hail, 4 = F0/C0, 5 = F1/C1, 6 = F2/C2, 7 = F3/C3, 8 = F4/C4, 9 = F5/C5") public static int Storm_Deflector_MinStageRemove = 1; public static boolean Storm_Deflector_RemoveRainstorms = false; public static boolean Storm_Deflector_RemoveSandstorms = true; /*@ConfigComment("Minimum amount of visual rain shown when its raining globally during overcast mode") public static double Storm_Rain_Overcast_Amount = 0.01D;*/ public static int Storm_Rain_Overcast_OddsTo1 = 50; public static int Storm_Rain_OddsTo1 = 150; @ConfigComment("How often in ticks, a rainstorm updates its list of entities under the rainstorm to extinguish. Extinguishes entities under rainclouds when globalOvercast is off. Set to 0 or less to disable") public static int Storm_Rain_TrackAndExtinguishEntitiesRate = 200; @Override public String getName() { return "Storm"; } @Override public String getRegistryName() { return Weather.MODID + getName(); } @Override public String getConfigFileName() { return "Weather2" + File.separator + getName(); } @Override public String getCategory() { return "Weather2: " + getName(); } @Override public void hookUpdatedValues() { } } ================================================ FILE: src/main/java/weather2/config/ConfigTornado.java ================================================ package weather2.config; import com.corosus.coroutil.config.ConfigCoroUtil; import com.corosus.modconfig.ConfigComment; import com.corosus.modconfig.IConfigCategory; import weather2.Weather; import weather2.util.WeatherUtil; import java.io.File; public class ConfigTornado implements IConfigCategory { @ConfigComment("Prevents tearing up of dirt, grass, sand and logs, overrides strength based grabbing") public static boolean Storm_Tornado_RefinedGrabRules = true; @ConfigComment("Makes weather boring! or peacefull?") public static boolean Storm_NoTornadosOrCyclones = false; //tornado @ConfigComment("Grab player or not") public static boolean Storm_Tornado_grabPlayer = true; @ConfigComment("Prevent grabbing of non players") public static boolean Storm_Tornado_grabPlayersOnly = false; @ConfigComment("Grab hostile mobs, overridden by Storm_Tornado_grabPlayersOnly") public static boolean Storm_Tornado_grabMobs = true; @ConfigComment("Grab animals, overridden by Storm_Tornado_grabPlayersOnly") public static boolean Storm_Tornado_grabAnimals = true; @ConfigComment("Grab villagers, overridden by Storm_Tornado_grabPlayersOnly") public static boolean Storm_Tornado_grabVillagers = true; @ConfigComment("Tear up blocks from the ground based on conditions defined") public static boolean Storm_Tornado_grabBlocks = true; @ConfigComment("Grab entity items, overridden by Storm_Tornado_grabPlayersOnly") public static boolean Storm_Tornado_grabItems = false; @ConfigComment("Grab blocks based on how well a diamond axe can mine the block, so mostly wooden blocks") public static boolean Storm_Tornado_GrabCond_StrengthGrabbing = true; @ConfigComment("Use a list of blocks or block tags instead of grabbing based on calculated strength of block, if true this overrides StrengthGrabbing and RefinedGrabRules") public static boolean Storm_Tornado_GrabCond_List = false; //removed, because tags should suffice, i hope //public static boolean Storm_Tornado_GrabCond_List_PartialMatches = false; //public static boolean Storm_Tornado_GrabCond_List_TrimSpaces = true; @ConfigComment("Treat block grab list as a blacklist instead of whitelist") public static boolean Storm_Tornado_GrabListBlacklistMode = false; @ConfigComment("Enable Storm_Tornado_GrabCond_List to use, add registered block names or block tags to list, for tags, indicate with #, use commas to separate values, if namespace missing, 'minecraft:' is automatically used") public static String Storm_Tornado_GrabList = "#fences, #minecraft:fence_gates, #wooden_doors, #wooden_stairs, #wooden_slabs, #flowers, #planks, #wool, #wooden_trapdoors, #wooden_pressure_plates, #cave_vines, #saplings, #banners, #leaves, #small_flowers, #beds, #tall_flowers, #flowers, #candles, #wall_signs, #signs, #fire, #campfires, #replaceable_plants, #wall_post_override"; @ConfigComment("Max amount of flying entity blocks allowed active, if it goes over this, it stops turning destroyed blocks into entities") public static int Storm_Tornado_maxFlyingEntityBlocks = 200; public static int Storm_Tornado_maxBlocksGrabbedPerTick = 5; //@ConfigComment("How rarely a block will be removed while spinning around a tornado") //public static int Storm_Tornado_rarityOfDisintegrate = 15; //public static int Storm_Tornado_rarityOfBreakOnFall = 5; //@ConfigComment(":D") //public static int Storm_Tornado_rarityOfFirenado = -1; @ConfigComment("Make tornados initial heading aimed towards closest player") public static boolean Storm_Tornado_aimAtPlayerOnSpawn = true; @ConfigComment("Accuracy of tornado aimed at player") public static int Storm_Tornado_aimAtPlayerAngleVariance = 5; @ConfigComment("Extra bit of grab angle for the tornado, tweak for different grab shapes, higher = tigher grab, lower = wider grab, might toss them away") public static int Storm_Tornado_extraGrabAngle = 20; public static boolean Storm_Tornado_fallDamage = true; //@ConfigComment("Experimental idea, places the WIP repairing block where a tornado does damage instead of removing the block, causes tornado damage to self repair, recommend setting Storm_Tornado_rarityOfBreakOnFall to 0 to avoid duplicated blocks") //public static boolean Storm_Tornado_grabbedBlocksRepairOverTime = false; //@ConfigComment("Used if Storm_Tornado_grabbedBlocksRepairOverTime is true, minimum of 600 ticks (30 seconds) required") //public static int Storm_Tornado_TicksToRepairBlock = 20*60*5; @Override public String getName() { return "Tornado"; } @Override public String getRegistryName() { return Weather.MODID + getName(); } @Override public String getConfigFileName() { return "Weather2" + File.separator + getName(); } @Override public String getCategory() { return "Weather2: " + getName(); } @Override public void hookUpdatedValues() { WeatherUtil.updateGrabBlockList(Storm_Tornado_GrabList); //if (ConfigCoroUtil.useLoggingDebug) { //WeatherUtil.testAllBlocks(); //} } } ================================================ FILE: src/main/java/weather2/config/ConfigWind.java ================================================ package weather2.config; import com.corosus.modconfig.ConfigComment; import com.corosus.modconfig.IConfigCategory; import weather2.Weather; import java.io.File; public class ConfigWind implements IConfigCategory { public static boolean Misc_windOn = true; public static boolean Wind_LowWindEvents = true; public static boolean Wind_HighWindEvents = true; public static boolean Wind_UsePerlinNoise = false; public static int lowWindTimerEnableAmountBase = 20*60*2; public static int lowWindTimerEnableAmountRnd = 20*60*10; public static int lowWindOddsTo1 = 20*200; public static int highWindTimerEnableAmountBase = 20*60*2; public static int highWindTimerEnableAmountRnd = 20*60*10; public static int highWindOddsTo1 = 20*400; public static double globalWindAngleChangeAmountRate = 1F; public static double windSpeedMin = 0.00001D; public static double windSpeedMax = 1D; @ConfigComment("Min wind speed to maintain if its raining with global overcast mode on, overrides low wind events and windSpeedMin") public static double windSpeedMinGlobalOvercastRaining = 0.01D; public static int Wind_Turbine_FE_Generated_Per_Tick = 10; @Override public String getName() { return "Wind"; } @Override public String getRegistryName() { return Weather.MODID + getName(); } @Override public String getConfigFileName() { return "Weather2" + File.separator + getName(); } @Override public String getCategory() { return "Weather2: " + getName(); } @Override public void hookUpdatedValues() { } } ================================================ FILE: src/main/java/weather2/config/WeatherUtilConfig.java ================================================ package weather2.config; import net.minecraft.resources.ResourceKey; import net.minecraft.world.level.Level; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class WeatherUtilConfig { public static List listDimensionsWeather = new ArrayList<>(); public static List listDimensionsClouds = new ArrayList<>(); //used for deadly storms and sandstorms public static List listDimensionsStorms = new ArrayList<>(); public static List listDimensionsWindEffects = new ArrayList<>(); public static boolean shouldTickClouds(String levelResourceKey) { return listDimensionsClouds.contains(levelResourceKey); } public static void processLists() { listDimensionsWeather = parseList(ConfigMisc.Dimension_List_Weather); listDimensionsClouds = parseList(ConfigMisc.Dimension_List_Clouds); listDimensionsStorms = parseList(ConfigMisc.Dimension_List_Storms); listDimensionsWindEffects = parseList(ConfigMisc.Dimension_List_WindEffects); } public static List parseList(String parData) { String listStr = parData; listStr = listStr.replace(",", " "); String[] arrStr = listStr.split(" "); for (int i = 0; i < arrStr.length; i++) { try { arrStr[i] = arrStr[i]; } catch (Exception ex) { arrStr[i] = "minecraft:none"; //set to -999999, hope no dimension id of this exists } } return new ArrayList(Arrays.asList(arrStr)); } } ================================================ FILE: src/main/java/weather2/data/BlockAndItemProvider.java ================================================ package weather2.data; import net.minecraft.client.renderer.texture.atlas.sources.SingleFile; import net.minecraft.data.PackOutput; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.common.data.ExistingFileHelper; import net.minecraftforge.common.data.SpriteSourceProvider; import weather2.Weather; import java.util.Optional; public class BlockAndItemProvider extends SpriteSourceProvider { public BlockAndItemProvider(PackOutput output, ExistingFileHelper fileHelper) { super(output, fileHelper, Weather.MODID); } @Override protected void addSources() { addSpriteBlock("tornado_siren"); addSpriteBlock("tornado_siren_manual"); addSpriteBlock("tornado_siren_manual_on"); addSpriteBlock("tornado_sensor"); addSpriteBlock("weather_deflector"); addSpriteBlock("weather_forecast"); addSpriteBlock("weather_machine"); addSpriteBlock("anemometer"); addSpriteBlock("wind_vane"); addSpriteBlock("wind_turbine"); addSpriteItem("weather_item"); addSpriteItem("sand_layer"); addSpriteItem("sand_layer_placeable"); } public void addSpriteBlock(String textureName) { atlas(SpriteSourceProvider.BLOCKS_ATLAS).addSource(new SingleFile(new ResourceLocation(Weather.MODID + ":blocks/" + textureName), Optional.empty())); } public void addSpriteItem(String textureName) { atlas(SpriteSourceProvider.BLOCKS_ATLAS).addSource(new SingleFile(new ResourceLocation(Weather.MODID + ":items/" + textureName), Optional.empty())); } } ================================================ FILE: src/main/java/weather2/data/BlockLootTables.java ================================================ package weather2.data; import net.minecraft.data.loot.BlockLootSubProvider; import net.minecraft.world.flag.FeatureFlags; import net.minecraft.world.level.block.Block; import net.minecraftforge.registries.ForgeRegistries; import weather2.Weather; import weather2.WeatherBlocks; import java.util.Set; import java.util.stream.Collectors; public class BlockLootTables extends BlockLootSubProvider { public BlockLootTables() { super(Set.of(), FeatureFlags.REGISTRY.allFlags()); } @Override protected void generate() { dropSelf(WeatherBlocks.BLOCK_WIND_TURBINE.get()); dropSelf(WeatherBlocks.BLOCK_WIND_VANE.get()); dropSelf(WeatherBlocks.BLOCK_ANEMOMETER.get()); dropSelf(WeatherBlocks.BLOCK_DEFLECTOR.get()); dropSelf(WeatherBlocks.BLOCK_FORECAST.get()); dropSelf(WeatherBlocks.BLOCK_TORNADO_SENSOR.get()); dropSelf(WeatherBlocks.BLOCK_SAND_LAYER.get()); dropSelf(WeatherBlocks.BLOCK_TORNADO_SIREN.get()); } @Override protected Iterable getKnownBlocks() { return ForgeRegistries.BLOCKS.getValues().stream().filter(block -> ForgeRegistries.BLOCKS.getKey(block).getNamespace().equals(Weather.MODID)).collect(Collectors.toList()); } } ================================================ FILE: src/main/java/weather2/data/WeatherRecipeProvider.java ================================================ package weather2.data; import net.minecraft.data.DataGenerator; import net.minecraft.data.PackOutput; import net.minecraft.data.recipes.FinishedRecipe; import net.minecraft.data.recipes.RecipeCategory; import net.minecraft.data.recipes.RecipeProvider; import net.minecraft.data.recipes.ShapedRecipeBuilder; import net.minecraft.world.item.Items; import weather2.Weather; import weather2.WeatherBlocks; import weather2.WeatherItems; import java.util.function.Consumer; public class WeatherRecipeProvider extends RecipeProvider { public WeatherRecipeProvider(PackOutput p_125973_) { super(p_125973_); } @Override protected void buildRecipes(Consumer consumer) { ShapedRecipeBuilder.shaped(RecipeCategory.MISC, WeatherItems.WEATHER_ITEM.get(), 1) .pattern("X X").pattern("DID").pattern("X X") .define('D', Items.REDSTONE) .define('I', Items.GOLD_INGOT) .define('X', Items.IRON_INGOT) .unlockedBy("has_redstone", has(Items.REDSTONE)) .save(consumer); ShapedRecipeBuilder.shaped(RecipeCategory.MISC, WeatherItems.BLOCK_DEFLECTOR_ITEM.get(), 1) .pattern("XDX").pattern("DID").pattern("XDX") .define('D', Items.REDSTONE) .define('I', WeatherItems.WEATHER_ITEM.get()) .define('X', Items.IRON_INGOT) .unlockedBy("has_weather_item", has(WeatherItems.WEATHER_ITEM.get())) .save(consumer); ShapedRecipeBuilder.shaped(RecipeCategory.MISC, WeatherItems.BLOCK_FORECAST_ITEM.get(), 1) .pattern("XDX").pattern("DID").pattern("XDX") .define('D', Items.REDSTONE) .define('I', Items.COMPASS) .define('X', WeatherItems.WEATHER_ITEM.get()) .unlockedBy("has_weather_item", has(WeatherItems.WEATHER_ITEM.get())) .save(consumer); ShapedRecipeBuilder.shaped(RecipeCategory.MISC, WeatherItems.BLOCK_TORNADO_SENSOR_ITEM.get(), 1) .pattern("X X").pattern("DID").pattern("X X") .define('D', Items.REDSTONE) .define('I', WeatherItems.WEATHER_ITEM.get()) .define('X', Items.IRON_INGOT) .unlockedBy("has_weather_item", has(WeatherItems.WEATHER_ITEM.get())) .save(consumer); ShapedRecipeBuilder.shaped(RecipeCategory.MISC, WeatherItems.BLOCK_TORNADO_SIREN_ITEM.get(), 1) .pattern("XDX").pattern("DID").pattern("XDX") .define('D', Items.REDSTONE) .define('I', WeatherItems.BLOCK_TORNADO_SENSOR_ITEM.get()) .define('X', Items.IRON_INGOT) .unlockedBy("has_sensor_item", has(WeatherItems.BLOCK_TORNADO_SENSOR_ITEM.get())) .save(consumer); ShapedRecipeBuilder.shaped(RecipeCategory.MISC, WeatherItems.BLOCK_WIND_VANE_ITEM.get(), 1) .pattern("X X").pattern("DXD").pattern("X X") .define('D', Items.REDSTONE) .define('X', WeatherItems.WEATHER_ITEM.get()) .unlockedBy("has_weather_item", has(WeatherItems.WEATHER_ITEM.get())) .save(consumer); ShapedRecipeBuilder.shaped(RecipeCategory.MISC, WeatherItems.BLOCK_WIND_TURBINE_ITEM.get(), 1) .pattern("ODO").pattern("IVI").pattern("RGR") .define('I', Items.IRON_BLOCK) .define('O', Items.IRON_INGOT) .define('D', Items.DIAMOND) .define('V', WeatherItems.BLOCK_WIND_VANE_ITEM.get()) .define('R', Items.REDSTONE_BLOCK) .define('G', Items.GOLD_INGOT) .unlockedBy("has_wind_vane", has(WeatherItems.BLOCK_WIND_TURBINE_ITEM.get())) .save(consumer); ShapedRecipeBuilder.shaped(RecipeCategory.MISC, WeatherItems.BLOCK_ANEMOMETER_ITEM.get(), 1) .pattern("X X").pattern("XDX").pattern("X X") .define('D', Items.REDSTONE) .define('X', WeatherItems.WEATHER_ITEM.get()) .unlockedBy("has_weather_item", has(WeatherItems.WEATHER_ITEM.get())) .save(consumer); ShapedRecipeBuilder.shaped(RecipeCategory.MISC, WeatherItems.BLOCK_SAND_LAYER_ITEM.get(), 8) .pattern("DDD").pattern("D D").pattern("DDD") .define('D', Items.SAND) .unlockedBy("has_sand", has(Items.SAND)) .save(consumer); } } ================================================ FILE: src/main/java/weather2/datatypes/PrecipitationType.java ================================================ package weather2.datatypes; public enum PrecipitationType { NORMAL, ACID, HAIL, SNOW; public static final PrecipitationType[] VALUES = values(); } ================================================ FILE: src/main/java/weather2/datatypes/StormState.java ================================================ package weather2.datatypes; import net.minecraft.network.FriendlyByteBuf; public final class StormState { private final int buildupTickRate; private final int maxStackable; public StormState(int buildupTickRate, int maxStackable) { this.buildupTickRate = buildupTickRate; this.maxStackable = maxStackable; } public int getBuildupTickRate() { return buildupTickRate; } public int getMaxStackable() { return maxStackable; } public void encode(FriendlyByteBuf buffer) { buffer.writeVarInt(this.buildupTickRate); buffer.writeVarInt(this.maxStackable); } public static StormState decode(FriendlyByteBuf buffer) { int buildupTickRate = buffer.readVarInt(); int maxStackable = buffer.readVarInt(); return new StormState(buildupTickRate, maxStackable); } } ================================================ FILE: src/main/java/weather2/datatypes/WeatherEventType.java ================================================ package weather2.datatypes; public enum WeatherEventType { HEAVY_RAIN("heavy_rain"), ACID_RAIN("acid_rain"), HAIL("hail"), HEATWAVE("heatwave"), SANDSTORM("sandstorm"), SNOWSTORM("snowstorm"); private final String key; public static final WeatherEventType[] VALUES = values(); WeatherEventType(String key) { this.key = key; } public String getKey() { return key; } } ================================================ FILE: src/main/java/weather2/energy/EnergyManager.java ================================================ package weather2.energy; import net.minecraft.nbt.CompoundTag; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.ForgeCapabilities; import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.energy.EnergyStorage; import net.minecraftforge.energy.IEnergyStorage; public class EnergyManager extends EnergyStorage { private boolean canExtract = true; public EnergyManager(int maxTransfer, int capacity) { super(capacity, maxTransfer, maxTransfer); } public int getMaxExtract() { return maxExtract; } public void setReceiveOnly() { canExtract = false; }/* @Override public void read(CompoundTag nbt) { setEnergyStored(nbt.getInt("Energy")); } @Override public CompoundTag write(CompoundTag nbt) { nbt.putInt("Energy", energy); return nbt; }*/ public int getMaxEnergyReceived() { return this.maxReceive; } /** * Drains an amount of energy, due to decay from lack of work or other factors */ public void drainEnergy(int amount) { setEnergyStored(energy - amount); } public void addEnergy(int amount) { setEnergyStored(energy + amount); } public int getEnergy() { return energy; } public void setEnergyStored(int energyStored) { this.energy = energyStored; if (this.energy > capacity) { this.energy = capacity; } else if (this.energy < 0) { this.energy = 0; } } public LazyOptional getCapability(Capability capability) { if (capability == ForgeCapabilities.ENERGY) { //IEnergyStorage energyStorage = new EnergyStorageWrapper(this, canExtract); return LazyOptional.of(() -> this).cast(); } return LazyOptional.empty(); } } ================================================ FILE: src/main/java/weather2/item/WeatherItem.java ================================================ package weather2.item; import net.minecraft.core.NonNullList; import net.minecraft.world.item.CreativeModeTab; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; public class WeatherItem extends Item { public WeatherItem(Item.Properties properties) { super(properties); } } ================================================ FILE: src/main/java/weather2/ltcompat/ClientWeatherIntegration.java ================================================ package weather2.ltcompat; import weather2.datatypes.PrecipitationType; public final class ClientWeatherIntegration { private static ClientWeatherIntegration instance = new ClientWeatherIntegration(); private ClientWeatherIntegration() { } public static ClientWeatherIntegration get() { return instance; } public static void reset() { instance = new ClientWeatherIntegration(); } public float getRainAmount() { return 0; } public float getVanillaRainAmount() { return 0; } public PrecipitationType getPrecipitationType() { return PrecipitationType.VALUES[0]; } public float getWindSpeed() { return 0; } public boolean isHeatwave() { return false; } public boolean isSandstorm() { return false; } public boolean isSnowstorm() { return false; } public boolean hasWeather() { return false; } /** * TODO: for LT, turn back on when LT is needed, activates dependency on LTWeather */ /*public float getRainAmount() { return ClientWeather.get().getRainAmount(); } public float getVanillaRainAmount() { return ClientWeather.get().getVanillaRainAmount(); } public PrecipitationType getPrecipitationType() { return PrecipitationType.VALUES[TypeBridge.getPrecipitationTypeOrdinal(ClientWeather.get())]; } public float getWindSpeed() { return ClientWeather.get().getWindSpeed(); } public boolean isHeatwave() { return ClientWeather.get().isHeatwave(); } public boolean isSandstorm() { return ClientWeather.get().isSandstorm(); } public boolean isSnowstorm() { return ClientWeather.get().isSnowstorm(); } public boolean hasWeather() { return ClientWeather.get().hasWeather(); }*/ } ================================================ FILE: src/main/java/weather2/ltcompat/ServerWeatherIntegration.java ================================================ package weather2.ltcompat; import net.minecraft.server.level.ServerLevel; import net.minecraft.util.Tuple; import weather2.datatypes.StormState; public class ServerWeatherIntegration { public static float getWindSpeed(ServerLevel level) { return 0; } public static StormState getSandstormForEverywhere(ServerLevel level) { return null; } public static StormState getSnowstormForEverywhere(ServerLevel level) { return null; } /** * TODO: for LT, turn back on when LT is needed, activates dependency on LTWeather */ /*public static float getWindSpeed(ServerLevel level) { return TypeBridge.getWindSpeed(level); } public static StormState getSandstormForEverywhere(ServerLevel level) { Tuple data = TypeBridge.getSandstormData(level); return data != null ? new StormState(data.getA(), data.getB()) : null; } public static StormState getSnowstormForEverywhere(ServerLevel level) { Tuple data = TypeBridge.getSnowstormData(level); return data != null ? new StormState(data.getA(), data.getB()) : null; }*/ } ================================================ FILE: src/main/java/weather2/mixin/client/GameRendererOverride.java ================================================ package weather2.mixin.client; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.GameRenderer; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; @Mixin(GameRenderer.class) public abstract class GameRendererOverride { /** * @author Corosus * @reason render particle clouds further * * UNUSED ATM */ @Overwrite public float getDepthFar() { //CULog.dbg("getDepthFar override"); return Minecraft.getInstance().gameRenderer.getRenderDistance() * 4F; } } ================================================ FILE: src/main/java/weather2/mixin/client/RenderParticlesOverride.java ================================================ package weather2.mixin.client; import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.Camera; import net.minecraft.client.Minecraft; import net.minecraft.client.particle.ParticleEngine; import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.MultiBufferSource; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; import weather2.ClientTickHandler; import weather2.config.ConfigParticle; import javax.annotation.Nullable; @Mixin(LevelRenderer.class) public abstract class RenderParticlesOverride { //replaced by RenderLevelStageEvent.Stage.AFTER_PARTICLES /*@Redirect(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/ParticleEngine;render(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;Lnet/minecraft/client/renderer/LightTexture;Lnet/minecraft/client/Camera;FLnet/minecraft/client/renderer/culling/Frustum;)V")) public void render(ParticleEngine particleManager, PoseStack matrixStackIn, MultiBufferSource.BufferSource bufferIn, LightTexture lightTextureIn, Camera activeRenderInfoIn, float partialTicks, @Nullable net.minecraft.client.renderer.culling.Frustum clippingHelper) { ClientTickHandler.particleManagerExtended().render(matrixStackIn, bufferIn, lightTextureIn, activeRenderInfoIn, partialTicks, clippingHelper); particleManager.render(matrixStackIn, bufferIn, lightTextureIn, activeRenderInfoIn, partialTicks, clippingHelper); }*/ @Redirect(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/LevelRenderer;renderSnowAndRain(Lnet/minecraft/client/renderer/LightTexture;FDDD)V")) public void renderSnowAndRain(LevelRenderer worldRenderer, LightTexture lightmapIn, float partialTicks, double xIn, double yIn, double zIn) { //CULog.dbg("renderSnowAndRain hook"); //stopping vanilla from running renderRainSnow if (ConfigParticle.Particle_vanilla_precipitation) { worldRenderer.renderSnowAndRain(lightmapIn, partialTicks, xIn, yIn, zIn); } } /*@Redirect(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/LevelRenderer;renderClouds(Lcom/mojang/blaze3d/vertex/PoseStack;Lcom/mojang/math/Matrix4f;FDDD)V")) public void renderClouds(LevelRenderer instance, PoseStack poseStack, Matrix4f l, float i1, double f1, double f2, double d0) { //CULog.dbg("renderClouds hook"); //workaround for missing projection matrix info ICloudRenderHandler cloudRenderHandler = Minecraft.getInstance().level().effects().getCloudRenderHandler(); if (cloudRenderHandler instanceof CloudRenderHandler) { ((CloudRenderHandler)cloudRenderHandler).render(poseStack, l, i1, f1, f2, d0); } else { instance.renderClouds(poseStack, l, i1, f1, f2, d0); } }*/ } ================================================ FILE: src/main/java/weather2/player/PlayerData.java ================================================ package weather2.player; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import net.minecraft.nbt.CompoundTag; import weather2.Weather; public class PlayerData { public static HashMap playerNBT = new HashMap<>(); public static CompoundTag getPlayerNBT(String username) { if (!playerNBT.containsKey(username)) { //TODO: 1.18 //tryLoadPlayerNBT(username); //TODO: 1.18 remove this playerNBT.put(username, new CompoundTag()); } return playerNBT.get(username); } /*public static void tryLoadPlayerNBT(String username) { //try read from hw/playerdata/player.dat //init with data, if fail, init default blank CompoundTag playerData = new CompoundTag(); try { String fileURL = CoroUtilFile.getWorldSaveFolderPath() + CoroUtilFile.getWorldFolderName() + File.separator + "weather2" + File.separator + "PlayerData" + File.separator + username + ".dat"; if ((new File(fileURL)).exists()) { playerData = CompressedStreamTools.readCompressed(new FileInputStream(fileURL)); } } catch (Exception ex) { //Weather.dbg("no saved data found for " + username); } playerNBT.put(username, playerData); } public static void writeAllPlayerNBT(boolean resetData) { //Weather.dbg("writing out all player nbt"); String fileURL = CoroUtilFile.getWorldSaveFolderPath() + CoroUtilFile.getWorldFolderName() + File.separator + "weather2" + File.separator + "PlayerData"; if (!new File(fileURL).exists()) new File(fileURL).mkdir(); Iterator it = playerNBT.entrySet().iterator(); while (it.hasNext()) { Map.Entry pairs = (Map.Entry)it.next(); //Weather.dbg(pairs.getKey() + " = " + pairs.getValue()); writePlayerNBT((String)pairs.getKey(), (CompoundTag)pairs.getValue()); } if (resetData) { playerNBT.clear(); } } public static void writePlayerNBT(String username, CompoundTag parData) { //Weather.dbg("writing " + username); String fileURL = CoroUtilFile.getWorldSaveFolderPath() + CoroUtilFile.getWorldFolderName() + File.separator + "weather2" + File.separator + "PlayerData" + File.separator + username + ".dat"; try { FileOutputStream fos = new FileOutputStream(fileURL); CompressedStreamTools.writeCompressed(parData, fos); fos.close(); } catch (Exception ex) { ex.printStackTrace(); Weather.dbg("Error writing Weather2 player data for " + username); } }*/ } ================================================ FILE: src/main/java/weather2/util/CachedNBTTagCompound.java ================================================ package weather2.util; import net.minecraft.nbt.CompoundTag; /** * Caches nbt data to remove redundant data sending over network * * @author cosmicdan * * revisions made to further integrate it into the newer design of WeatherObjects */ public class CachedNBTTagCompound { private CompoundTag newData; private CompoundTag cachedData; private boolean forced = false; public CachedNBTTagCompound() { this.newData = new CompoundTag(); this.cachedData = new CompoundTag(); } public void setCachedNBT(CompoundTag cachedData) { if (cachedData == null) cachedData = new CompoundTag(); this.cachedData = cachedData; } public CompoundTag getCachedNBT() { return cachedData; } public CompoundTag getNewNBT() { return newData; } public void setNewNBT(CompoundTag newData) { this.newData = newData; } public void setUpdateForced(boolean forced) { this.forced = forced; } public long getLong(String key) { if (!newData.contains(key)) newData.putLong(key, cachedData.getLong(key)); return newData.getLong(key); } public void putLong(String key, long newVal) { if (!cachedData.contains(key) || cachedData.getLong(key) != newVal || forced) { newData.putLong(key, newVal); } cachedData.putLong(key, newVal); } public int getInt(String key) { if (!newData.contains(key)) newData.putInt(key, cachedData.getInt(key)); return newData.getInt(key); } public void putInt(String key, int newVal) { if (!cachedData.contains(key) || cachedData.getInt(key) != newVal || forced) { newData.putInt(key, newVal); } cachedData.putInt(key, newVal); } public short getShort(String key) { if (!newData.contains(key)) newData.putShort(key, cachedData.getShort(key)); return newData.getShort(key); } public void putShort(String key, short newVal) { if (!cachedData.contains(key) || cachedData.getShort(key) != newVal || forced) { newData.putShort(key, newVal); } cachedData.putShort(key, newVal); } public String getString(String key) { if (!newData.contains(key)) newData.putString(key, cachedData.getString(key)); return newData.getString(key); } public void putString(String key, String newVal) { if (!cachedData.contains(key) || !cachedData.getString(key).equals(newVal) || forced) { newData.putString(key, newVal); } cachedData.putString(key, newVal); } public boolean getBoolean(String key) { if (!newData.contains(key)) newData.putBoolean(key, cachedData.getBoolean(key)); return newData.getBoolean(key); } public void putBoolean(String key, boolean newVal) { if (!cachedData.contains(key) || cachedData.getBoolean(key) != newVal || forced) { newData.putBoolean(key, newVal); } cachedData.putBoolean(key, newVal); } public float getFloat(String key) { if (!newData.contains(key)) newData.putFloat(key, cachedData.getFloat(key)); return newData.getFloat(key); } public void putFloat(String key, float newVal) { if (!cachedData.contains(key) || cachedData.getFloat(key) != newVal || forced) { newData.putFloat(key, newVal); } cachedData.putFloat(key, newVal); } public double getDouble(String key) { if (!newData.contains(key)) newData.putDouble(key, cachedData.getDouble(key)); return newData.getDouble(key); } public void putDouble(String key, double newVal) { if (!cachedData.contains(key) || cachedData.getDouble(key) != newVal || forced) { newData.putDouble(key, newVal); } cachedData.putDouble(key, newVal); } public CompoundTag get(String key) { return newData.getCompound(key); } /** warning, not cached **/ public void put(String key, CompoundTag tag) { newData.put(key, tag); cachedData.put(key, tag); } public boolean contains(String key) { return newData.contains(key); } public void updateCacheFromNew() { this.cachedData = this.newData; } } ================================================ FILE: src/main/java/weather2/util/WeatherUtil.java ================================================ package weather2.util; import com.corosus.coroutil.util.CULog; import net.minecraft.client.Minecraft; import net.minecraft.core.BlockPos; import net.minecraft.core.Registry; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.tags.BlockTags; import net.minecraft.tags.TagKey; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.*; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.MapColor; import net.minecraftforge.registries.ForgeRegistries; import net.minecraftforge.server.ServerLifecycleHooks; import org.joml.Vector3d; import org.joml.Vector3f; import weather2.config.ConfigTornado; import java.util.*; public class WeatherUtil { public static HashMap listGrabBlockCache = new HashMap<>(); public static List listGrabBlocks = new ArrayList<>(); public static List listGrabBlockTags = new ArrayList<>(); public static String lastConfigChecked = ""; public static void updateGrabBlockList(String grabListStr) { CULog.dbg("Updating weather2 tornado grab list"); listGrabBlocks.clear(); listGrabBlockTags.clear(); listGrabBlockCache.clear(); try { String[] splEnts = grabListStr.split(","); for (String str : splEnts) { str = str.trim(); if (str.contains("#")) { listGrabBlockTags.add(addNamespaceIfMissing(str.substring(1))); } else { listGrabBlocks.add(addNamespaceIfMissing(str)); } } } catch (ArrayIndexOutOfBoundsException ex) { ex.printStackTrace(); } } public static void testAllBlocks() { //Blocks.GLASS //if (!ConfigTornado.Storm_Tornado_GrabList.equals(lastConfigChecked)) { lastConfigChecked = ConfigTornado.Storm_Tornado_GrabList; CULog.log("PRINTING OUT ALL WEATHER2 TORNADO GRABBABLE BLOCKS WITH CURRENT CONFIG: "); ForgeRegistries.BLOCKS.forEach(block -> { List list = block.getStateDefinition().getPossibleStates(); for (BlockState state : list) { boolean result = canGrabViaLists(state); if (result) { CULog.log(state + " -> " + result); } } }); //boolean wat = canGrabViaLists(Blocks.TORCH.defaultBlockState()); //System.out.println("wat: " + wat); //} } public static String addNamespaceIfMissing(String str) { if (!str.contains(":")) { str = "minecraft:" + str; } return str; } public static boolean isStateInListOfTags(BlockState state) { for (String str : listGrabBlockTags) { TagKey key = getTagKeyFor(str); if (key != null) { if (state.is(key)) { return true; } } } return false; } public static TagKey getTagKeyFor(String str) { return TagKey.create(ForgeRegistries.BLOCKS.getRegistryKey(), new ResourceLocation(str)); } public static boolean canGrabViaLists(BlockState state) { boolean returnVal = !ConfigTornado.Storm_Tornado_GrabListBlacklistMode; ResourceLocation registeredName = ForgeRegistries.BLOCKS.getKey(state.getBlock()); if (listGrabBlockCache.containsKey(registeredName)) { return listGrabBlockCache.get(registeredName); } if (listGrabBlocks.contains(registeredName.toString())) { listGrabBlockCache.put(registeredName, returnVal); return returnVal; } if (isStateInListOfTags(state)) { listGrabBlockCache.put(registeredName, returnVal); return returnVal; } listGrabBlockCache.put(registeredName, !returnVal); return !returnVal; } public static boolean isPaused() { if (Minecraft.getInstance().isPaused()) return true; return false; } public static boolean isPausedSideSafe(Level world) { //return false if server side because it cant be paused legit if (!world.isClientSide) return false; return isPausedForClient(); } public static boolean isPausedForClient() { if (Minecraft.getInstance().isPaused()) return true; return false; } public static boolean isAprilFoolsDay() { Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(System.currentTimeMillis()); //test //return calendar.get(Calendar.MONTH) == Calendar.MARCH && calendar.get(Calendar.DAY_OF_MONTH) == 25; return calendar.get(Calendar.MONTH) == Calendar.APRIL && calendar.get(Calendar.DAY_OF_MONTH) == 1; } public static boolean shouldRemoveBlock(BlockState blockID) { //water no if (blockID.getBlock().defaultMapColor() == MapColor.WATER) { return false; } return true; } public static boolean isOceanBlock(Block blockID) { return false; } public static boolean isSolidBlock(Block id) { return (id == Blocks.STONE || id == Blocks.COBBLESTONE || id == Blocks.SANDSTONE); } public static boolean shouldGrabBlock(Level parWorld, BlockState state) { try { ItemStack itemStr = new ItemStack(Items.DIAMOND_AXE); Block block = state.getBlock(); boolean result = true; if (ConfigTornado.Storm_Tornado_GrabCond_List) { try { result = canGrabViaLists(state); } catch (Exception e) { //sometimes NPEs (pre 1.18), just assume false if so e.printStackTrace(); result = false; } } else { if (ConfigTornado.Storm_Tornado_GrabCond_StrengthGrabbing) { float strMin = 0.0F; float strMax = 0.74F; if (block == null) { result = false; return result; //force return false to prevent unchecked future code outside scope } else { //float strVsBlock = block.getBlockHardness(block.defaultBlockState(), parWorld, new BlockPos(0, 0, 0)) - (((itemStr.getStrVsBlock(block.defaultBlockState()) - 1) / 4F)); float strVsBlock = state.getDestroySpeed(parWorld, new BlockPos(0, 0, 0)) - (((itemStr.getDestroySpeed(block.defaultBlockState()) - 1) / 4F)); //System.out.println(strVsBlock); if (/*block.getHardness() <= 10000.6*/ (strVsBlock <= strMax && strVsBlock >= strMin) || (state.getBlock().defaultMapColor() == MapColor.WOOD) || state.getBlock().defaultMapColor() == MapColor.WOOL || state.getBlock().defaultMapColor() == MapColor.PLANT ||/* state.getMaterial() == Material.VINE ||*/ block instanceof TallGrassBlock) { if (!safetyCheck(state)) { result = false; } } else { result = false; } } } if (ConfigTornado.Storm_Tornado_RefinedGrabRules) { if (block == Blocks.DIRT || block == Blocks.COARSE_DIRT || block == Blocks.ROOTED_DIRT || block == Blocks.GRASS_BLOCK || block == Blocks.DIRT_PATH || block == Blocks.SAND || block == Blocks.RED_SAND || (block instanceof RotatedPillarBlock && state.getBlock().defaultMapColor() == MapColor.WOOD)) { result = false; } if (!canTornadoGrabBlockRefinedRules(state)) { result = false; } } } //TODO: 1.18 /*if (block == CommonProxy.blockWeatherMachine) { result = false; }*/ return result; } catch (Exception ex) { ex.printStackTrace(); return false; } } public static boolean safetyCheck(BlockState state) { Block id = state.getBlock(); if (id != Blocks.BEDROCK && id != Blocks.ACACIA_LOG && id != Blocks.CHEST && id != Blocks.JUKEBOX/* && id != Block.waterMoving.blockID && id != Block.waterStill.blockID */) { return true; } else { return false; } } public static ServerLevel getWorld(ResourceKey levelResourceKey) { return ServerLifecycleHooks.getCurrentServer().getLevel(levelResourceKey); } public static boolean canTornadoGrabBlockRefinedRules(BlockState state) { ResourceLocation registeredName = ForgeRegistries.BLOCKS.getKey(state.getBlock()); if (registeredName.getNamespace().equals("dynamictrees")) { if (registeredName.getPath().contains("rooty") || registeredName.getPath().contains("branch")) { return false; } } return true; } public static float dist(Vector3f vec1, Vector3f vec2) { double d0 = vec2.x() - vec1.x(); double d1 = vec2.y() - vec1.y(); double d2 = vec2.z() - vec1.z(); return (float) Math.sqrt(d0 * d0 + d1 * d1 + d2 * d2); } public static double dist(Vector3d vec1, Vector3d vec2) { double d0 = vec2.x - vec1.x; double d1 = vec2.y - vec1.y; double d2 = vec2.z - vec1.z; return Math.sqrt(d0 * d0 + d1 * d1 + d2 * d2); } } ================================================ FILE: src/main/java/weather2/util/WeatherUtilBlock.java ================================================ package weather2.util; import com.corosus.coroutil.util.CoroUtilBlock; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.util.Mth; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.SlabBlock; import net.minecraft.world.level.block.SnowLayerBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.material.MapColor; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.BooleanOp; import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; import weather2.WeatherBlocks; import weather2.block.SandLayerBlock; import java.util.ArrayList; import java.util.List; /** * All stackable block code in this class considers "height" as a meta val height, not actual pixel height of AABB, basically 1 meta height = 2 pixel height, this is also used for all amount values * * @author Corosus * */ public class WeatherUtilBlock { //TODO: 1.14 restore removed methods from previous git commits public static int layerableHeightPropMax = 8; public static void fillAgainstWallSmoothly(Level world, Vec3 posSource, float directionYaw, float scanDistance, float fillRadius, Block blockLayerable, int maxBlockStackingAllowed) { fillAgainstWallSmoothly(world, posSource, directionYaw, scanDistance, fillRadius, blockLayerable, 4, maxBlockStackingAllowed); } public static void fillAgainstWallSmoothly(Level world, Vec3 posSource, float directionYaw, float scanDistance, float fillRadius, Block blockLayerable, int heightDiff, int maxBlockStackingAllowed) { //layerableHeightPropMax = 8; /** * for now, work in halves * if "wall" is 4 height (aka 8 pixels high) or less, we can "go over it" aka continue onto next block past it * * starting point needs to be air above solid * * scan forward till not air or not placeable * * get block height, if height < 4 * - place infront of wall * if height >= 4 * - progress onto it and continue past it * * * * - factor in height of current block we are on if its not air, aka half filled sand block vs next block */ //fix for starting on a layerable block BlockState stateTest = world.getBlockState(CoroUtilBlock.blockPos(posSource)); if (stateTest.getBlock() == blockLayerable) { int heightTest = getHeightForAnyBlock(stateTest); if (heightTest < 8) { //posSource = new Vector3d(posSource.addVector(0, -1, 0)); } } BlockPos posSourcei = CoroUtilBlock.blockPos(posSource); //int ySource = world.getHeight(posSourcei).getY(); int y = posSourcei.getY(); float tickStep = 0.75F; //float startScan = scanDistance; Vec3 posLastNonWall = new Vec3(posSource.x, posSource.y, posSource.z); Vec3 posWall = null; BlockPos lastScannedPosXZ = null;//new BlockPos(posSourcei); //System.out.println("Start block (should be air): " + world.getBlockState(posSourcei)); int previousBlockHeight = 0; /*System.out.println("directionYaw: " + directionYaw); System.out.println("x: " + -Math.sin(Math.toRadians(directionYaw))); System.out.println("z: " + Math.cos(Math.toRadians(directionYaw)));*/ //looking for a proper wall we cant fly over as sand for (float i = 0; i < scanDistance; i += tickStep) { double vecX = (-Math.sin(Math.toRadians(directionYaw)) * (i)); double vecZ = (Math.cos(Math.toRadians(directionYaw)) * (i)); int x = Mth.floor(posSource.x + vecX); int z = Mth.floor(posSource.z + vecZ); BlockPos pos = new BlockPos(x, y, z); BlockPos posXZ = new BlockPos(x, 0, z); BlockState state = world.getBlockState(pos); if (lastScannedPosXZ == null || !posXZ.equals(lastScannedPosXZ)) { lastScannedPosXZ = new BlockPos(posXZ); AABB aabbCompare = new AABB(pos); List listAABBCollision = new ArrayList<>(); VoxelShape voxelshape = Shapes.create(aabbCompare); //boolean collided = VoxelShapes.compare(state.getCollisionShapeUncached(world, pos).withOffset((double)l1, (double)k2, (double)i2), voxelshape, IBooleanFunction.AND); boolean collided = Shapes.joinIsNotEmpty(state.getCollisionShape(world, pos).move(pos.getX(), pos.getY(), pos.getZ()), voxelshape, BooleanOp.AND); //state.addCollisionBoxToList(world, pos, aabbCompare, listAABBCollision, null, false); //TODO: isReplaceable would require a fake player, see if we can avoid using isReplaceable, it let us place into things like grass? maybe? /*System.out.println("try: " + pos + " - collided: " + collided); if (pos.getX() == 80 && pos.getY() == 87 && pos.getZ() == 153) { System.out.println("!!!!: " + pos + " - collided: " + collided); }*/ //if solid ground we can place on if (!state.isAir() && state.getBlock().defaultMapColor() != MapColor.PLANT && /*(!state.getBlock().isReplaceable(world, pos) && */collided) { BlockPos posUp = new BlockPos(x, y + 1, z); BlockState stateUp = world.getBlockState(posUp); //if above it is air if (stateUp.isAir()) { int height = getHeightForAnyBlock(state); //if height of block minus block we are on/comparing against is short enough, we can continue onto it if (height - previousBlockHeight <= heightDiff) { //if block we are progressing to is a full block, reset height val if (height == 8) { previousBlockHeight = 0; y++; } else { previousBlockHeight = height; } posLastNonWall = new Vec3(posSource.x + vecX, y, posSource.z + vecZ); //System.out.println(posLastNonWall); continue; //too high, count as a wall } else { posWall = new Vec3(posSource.x + vecX, y, posSource.z + vecZ); break; } //hit a wall } else { posWall = new Vec3(posSource.x + vecX, y, posSource.z + vecZ); break; } //startScan = i; //posWall = new Vector3d(posSource.x + vecX, y, posSource.z + vecZ); //break; } else { posLastNonWall = new Vec3(posSource.x + vecX, y, posSource.z + vecZ); } } else { continue; } } if (posWall != null) { int amountWeHave = 1; int amountToAddPerXZ = 1; BlockState state = world.getBlockState(CoroUtilBlock.blockPos(posWall)); BlockState state1 = world.getBlockState(CoroUtilBlock.blockPos(posLastNonWall).offset(1, 0, 0)); BlockState state22 = world.getBlockState(CoroUtilBlock.blockPos(posLastNonWall).offset(-1, 0, 0)); BlockState state3 = world.getBlockState(CoroUtilBlock.blockPos(posLastNonWall).offset(0, 0, 1)); BlockState state4 = world.getBlockState(CoroUtilBlock.blockPos(posLastNonWall).offset(0, 0, -1)); //check all around place spot for cactus and cancel if true, to prevent cactus pop off when we place next to it if (state.getBlock() == Blocks.CACTUS || state1.getBlock() == Blocks.CACTUS || state22.getBlock() == Blocks.CACTUS || state3.getBlock() == Blocks.CACTUS || state4.getBlock() == Blocks.CACTUS) { return; } BlockPos pos2 = CoroUtilBlock.blockPos(posLastNonWall.x, posLastNonWall.y, posLastNonWall.z); BlockState state2 = world.getBlockState(pos2); if (state2.getBlock().defaultMapColor() == MapColor.WATER || state2.getBlock().defaultMapColor() == MapColor.FIRE) { return; } amountWeHave = trySpreadOnPos2(world, CoroUtilBlock.blockPos(posLastNonWall.x, posLastNonWall.y, posLastNonWall.z), amountWeHave, amountToAddPerXZ, 10, blockLayerable, maxBlockStackingAllowed); } else { //System.out.println("no wall found"); } } public static int trySpreadOnPos2(Level world, BlockPos posSpreadTo, int amount, int amountAllowedToAdd, int maxDropAllowed, Block blockLayerable, int maxBlockStackingAllowed) { if (amount <= 0) return amount; /** * - check pos for solid * - if air, tick down till not air or drop limit * - at first non air, find first block with up face solid or snow block * - set air to everything between not air and up face solid or snow block (2 high tall grass removal) * - * - run code that sets snow, deals with solid up face or existing snow, fully or partially layered * * */ //must have clear air above first spots //TODO: might need special case so we can fill up a partially layered snow block if (!world.getBlockState(posSpreadTo.offset(0, 1, 0)).isAir()) { return amount; } BlockState statePos = world.getBlockState(posSpreadTo); BlockPos posCheckNonAir = new BlockPos(posSpreadTo); BlockState stateCheckNonAir = world.getBlockState(posCheckNonAir); int depth = 0; //find first non air while (stateCheckNonAir.isAir()) { posCheckNonAir = posCheckNonAir.offset(0, -1, 0); stateCheckNonAir = world.getBlockState(posCheckNonAir); depth++; //bail if drop too far, aka sand/snow fully particleizes if (depth > maxDropAllowed) { return amount; } } BlockPos posCheckPlaceable = new BlockPos(posCheckNonAir); BlockState stateCheckPlaceable = world.getBlockState(posCheckPlaceable); //new check to limit how high snow/sand can stack if (maxBlockStackingAllowed > 0) { boolean sandMode = false; if (blockLayerable == Blocks.SNOW) { sandMode = false; } else if (blockLayerable == WeatherBlocks.BLOCK_SAND_LAYER.get()) { sandMode = true; } int foundBlocks = 0; BlockPos posCheckDownForStacks = new BlockPos(posCheckPlaceable); BlockState stateCheckDownForStacks = world.getBlockState(posCheckPlaceable); if ((!sandMode && stateCheckPlaceable.getBlock() == Blocks.SNOW_BLOCK) || (sandMode && stateCheckPlaceable.getBlock() == Blocks.SAND)) { while ((!sandMode && stateCheckDownForStacks.getBlock() == Blocks.SNOW_BLOCK) || (sandMode && stateCheckDownForStacks.getBlock() == Blocks.SAND)) { foundBlocks++; if (foundBlocks >= maxBlockStackingAllowed) { //System.out.println("max snow stack allowed, bail"); return amount; } posCheckDownForStacks = posCheckDownForStacks.offset(0, -1, 0); stateCheckDownForStacks = world.getBlockState(posCheckDownForStacks); } } } int distForPlaceableBlocks = 0; while (true && distForPlaceableBlocks < 10) { //if can be placed into, continue, as long as its not our block as it is replacable at layer height 1 AABB aabbCompare = new AABB(posCheckPlaceable); //List listAABBCollision = new ArrayList<>(); VoxelShape voxelshape = Shapes.create(aabbCompare); boolean collided = Shapes.joinIsNotEmpty(stateCheckPlaceable.getCollisionShape(world, posCheckPlaceable).move(posCheckPlaceable.getX(), posCheckPlaceable.getY(), posCheckPlaceable.getZ()), voxelshape, BooleanOp.AND); //stateCheckPlaceable.addCollisionBoxToList(world, posCheckPlaceable, aabbCompare, listAABBCollision, null, false); //TODO: isReplaceable would require a fake player, see if we can avoid using isReplaceable, it let us place into things like grass? maybe? if (stateCheckPlaceable.getBlock() != blockLayerable && /*stateCheckPlaceable.getBlock().isReplaceable(world, posCheckPlaceable) && */!collided && !stateCheckPlaceable.liquid()) { posCheckPlaceable = posCheckPlaceable.offset(0, -1, 0); stateCheckPlaceable = world.getBlockState(posCheckPlaceable); distForPlaceableBlocks++; continue; //if its the kind of solid we want, break loop } else if (stateCheckPlaceable.isFaceSturdy(world, posCheckPlaceable, Direction.UP) || stateCheckPlaceable.getBlock() == blockLayerable) { break; //its something we cant stack onto } else { //System.out.println("found unstackable block: " + stateCheckPlaceable); return amount; } } //for some reason theres 10+ blocks of half solid blocks, lets just abort if (distForPlaceableBlocks >= 10) { return amount; } //at this point the block we are about to work with is solid facing up, or snow if (!stateCheckPlaceable.isFaceSturdy(world, posCheckPlaceable, Direction.UP) && stateCheckPlaceable.getBlock() != blockLayerable) { System.out.println("shouldnt be, failed a check somewhere!"); return amount; } //lets clear out the blocks we found between air and solid or snow block for (int i = 0; i < distForPlaceableBlocks; i++) { //System.out.println("clear out pos: " + posCheckNonAir); //System.out.println("clear out pos: " + world.getBlockState(posCheckNonAir)); world.setBlockAndUpdate(posCheckNonAir.offset(0, -i, 0), Blocks.AIR.defaultBlockState()); } BlockPos posPlaceLayerable = new BlockPos(posCheckPlaceable); BlockState statePlaceLayerable = world.getBlockState(posPlaceLayerable); int amountToAdd = amountAllowedToAdd; //add in the amount of air blocks we found //distForPlaceableBlocks += depth; //just place while stuff to add and air above while (amountAllowedToAdd > 0 && world.getBlockState(posPlaceLayerable.offset(0, 1, 0)).isAir()) { //if no more layers to add if (amountAllowedToAdd <= 0) { break; } //if its snow we can add snow to if (statePlaceLayerable.getBlock() == blockLayerable && getHeightForLayeredBlock(statePlaceLayerable) < layerableHeightPropMax) { int height = getHeightForLayeredBlock(statePlaceLayerable); //System.out.println("old height: " + height); //if (height < snowMetaMax) { height += amountAllowedToAdd; if (height > layerableHeightPropMax) { amountAllowedToAdd = height - layerableHeightPropMax; height = layerableHeightPropMax; } else { amountAllowedToAdd = 0; } try { //System.out.println("new height: " + height); world.setBlockAndUpdate(posPlaceLayerable, setBlockWithLayerState(blockLayerable, height)); } catch (Exception e) { e.printStackTrace(); } //if we maxed it, up the val if (height == layerableHeightPropMax) { posPlaceLayerable = posPlaceLayerable.offset(0, 1, 0); statePlaceLayerable = world.getBlockState(posPlaceLayerable); } //} //solid block------------------- or air because we moved up 1 due to the previous being fully filled snow } else if (statePlaceLayerable.isFaceSturdy(world, posPlaceLayerable, Direction.UP)) { posPlaceLayerable = posPlaceLayerable.offset(0, 1, 0); statePlaceLayerable = world.getBlockState(posPlaceLayerable); //air } else if (statePlaceLayerable.isAir()) { //copypasta, refactor/reduce once things work int height = amountAllowedToAdd; if (height > layerableHeightPropMax) { amountAllowedToAdd = height - layerableHeightPropMax; height = layerableHeightPropMax; } else { amountAllowedToAdd = 0; } try { //System.out.println("air logic"); //TODO: if other idea fails, put the stacks of snow count check here world.setBlockAndUpdate(posPlaceLayerable, setBlockWithLayerState(blockLayerable, height)); } catch (Exception e) { e.printStackTrace(); } //if we maxed it, up the val if (height == layerableHeightPropMax) { posPlaceLayerable = posPlaceLayerable.offset(0, 1, 0); statePlaceLayerable = world.getBlockState(posPlaceLayerable); } } else { //System.out.println("wat! - " + statePlaceLayerable); } } if (amountAllowedToAdd < 0) { //System.out.println("wat"); } int amountAdded = amountToAdd - amountAllowedToAdd; amount -= amountAdded; return amount; } public static int getHeightForAnyBlock(BlockState state) { Block block = state.getBlock(); if (block == Blocks.SNOW) { return state.getValue(SnowLayerBlock.LAYERS).intValue(); } else if (block == WeatherBlocks.BLOCK_SAND_LAYER.get()) { return state.getValue(SandLayerBlock.LAYERS).intValue(); } else if (block == Blocks.SAND || block == Blocks.SNOW_BLOCK) { return 8; } else if (block instanceof SlabBlock) { return 4; } else if (block == Blocks.AIR) { return 0; } else { return 8; } } public static int getHeightForLayeredBlock(BlockState state) { if (state.getBlock() == Blocks.SNOW) { return (state.getValue(SnowLayerBlock.LAYERS)).intValue(); } else if (state.getBlock() == WeatherBlocks.BLOCK_SAND_LAYER.get()) { return state.getValue(SandLayerBlock.LAYERS).intValue(); } else if (state.getBlock() == Blocks.SAND || state.getBlock() == Blocks.SNOW_BLOCK) { return 8; } else { //missing implementation return 0; } } public static BlockState setBlockWithLayerState(Block block, int height) { boolean solidBlockUnderMode = true; if (block == Blocks.SNOW) { if (height == layerableHeightPropMax && solidBlockUnderMode) { return Blocks.SNOW_BLOCK.defaultBlockState(); } else { return block.defaultBlockState().setValue(SnowLayerBlock.LAYERS, height); } } else if (block == WeatherBlocks.BLOCK_SAND_LAYER.get()) { if (height == layerableHeightPropMax && solidBlockUnderMode) { return Blocks.SAND.defaultBlockState(); } else { return block.defaultBlockState().setValue(SandLayerBlock.LAYERS, height); } } else { //means missing implementation return null; } } public static BlockPos getPrecipitationHeightSafe(Level world, BlockPos pos) { return getPrecipitationHeightSafe(world, pos, Heightmap.Types.MOTION_BLOCKING); } /** * Safe version of World.getPrecipitationHeight that wont invoke chunkgen/chunkload if its requesting height in unloaded chunk * * @param world * @param pos * @return */ public static BlockPos getPrecipitationHeightSafe(Level world, BlockPos pos, Heightmap.Types heightmapType) { if (world.hasChunkAt(pos)) { return world.getHeightmapPos(heightmapType, pos); } else { return new BlockPos(pos.getX(), -255, pos.getZ()); } } } ================================================ FILE: src/main/java/weather2/util/WeatherUtilDim.java ================================================ package weather2.util; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.core.BlockPos; import net.minecraft.world.level.Level; public class WeatherUtilDim { public static boolean canBlockSeeSky(Level world, BlockPos pos) { if (pos.getY() >= getSeaLevel(world)) { return world.canSeeSkyFromBelowWater(pos); } else { BlockPos blockpos = new BlockPos(pos.getX(), getSeaLevel(world), pos.getZ()); if (!world.canSeeSkyFromBelowWater(blockpos)) { return false; } else { for(BlockPos blockpos1 = blockpos.below(); blockpos1.getY() > pos.getY(); blockpos1 = blockpos1.below()) { BlockState blockstate = world.getBlockState(blockpos1); if (blockstate.getLightBlock(world, blockpos1) > 0 && !blockstate.liquid()) { return false; } } return true; } } } public static int getSeaLevel(Level world) { //TODO: sync customizable sea level to client, also use World.getSeaLevel if logical server return 63; } } ================================================ FILE: src/main/java/weather2/util/WeatherUtilEntity.java ================================================ package weather2.util; import com.corosus.coroutil.util.CoroUtilEntOrParticle; import extendedrenderer.particle.entity.EntityRotFX; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.animal.Squid; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.projectile.FishingHook; import net.minecraft.world.entity.vehicle.AbstractMinecart; import net.minecraft.world.entity.vehicle.Boat; import net.minecraft.world.item.ArmorItem; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.ClipContext; import net.minecraft.world.level.Level; import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; import net.minecraftforge.fml.LogicalSide; import net.minecraftforge.fml.util.thread.EffectiveSide; import java.util.HashMap; public class WeatherUtilEntity { public static float getWeight(Object entity1, boolean forTornado) { Level world = CoroUtilEntOrParticle.getWorld(entity1); if (world == null) { return 1F; } if (isParticleRotServerSafe(world, entity1)) { float var = WeatherUtilParticle.getParticleWeight((EntityRotFX)entity1); if (var != -1) { return var; } } if (entity1 instanceof Squid) { return 400F; } if (entity1 instanceof LivingEntity) { LivingEntity livingEnt = (LivingEntity) entity1; int airTime = livingEnt.getPersistentData().getInt("timeInAir"); if (livingEnt.onGround() || livingEnt.isInWater()) { airTime = 0; } else { airTime++; } livingEnt.getPersistentData().putInt("timeInAir", airTime); if (entity1 instanceof Player) { if (((Player) entity1).abilities.instabuild) return 99999999F; return 5.0F + airTime / 400.0F; } else { return 500.0F + (livingEnt.onGround() ? 2.0F : 0.0F) + (airTime / 400.0F); } } if (entity1 instanceof Boat || entity1 instanceof ItemEntity || entity1 instanceof FishingHook) { return 4000F; } if (entity1 instanceof AbstractMinecart) { return 80F; } return 1F; } public static HashMap armorToWeight = new HashMap<>(); static { armorToWeight.put(Items.IRON_HELMET, 0.1F); armorToWeight.put(Items.IRON_CHESTPLATE, 0.2F); armorToWeight.put(Items.IRON_LEGGINGS, 0.15F); armorToWeight.put(Items.IRON_BOOTS, 0.1F); armorToWeight.put(Items.GOLDEN_HELMET, 0.1F); armorToWeight.put(Items.GOLDEN_CHESTPLATE, 0.2F); armorToWeight.put(Items.GOLDEN_LEGGINGS, 0.15F); armorToWeight.put(Items.GOLDEN_BOOTS, 0.1F); armorToWeight.put(Items.DIAMOND_HELMET, 0.1F); armorToWeight.put(Items.DIAMOND_CHESTPLATE, 0.2F); armorToWeight.put(Items.DIAMOND_LEGGINGS, 0.15F); armorToWeight.put(Items.DIAMOND_BOOTS, 0.1F); } public static float getWeightAdjFromEquipment(float weightIn, Player player) { float influence = 1.2F; for (ItemStack stack : player.getArmorSlots()) { if (armorToWeight.containsKey(stack.getItem())) { weightIn += armorToWeight.get(stack.getItem()) * influence; } } return weightIn; } public static float getWeight(Object entity1) { Level world = CoroUtilEntOrParticle.getWorld(entity1); if (world == null) { return 1F; } if (isParticleRotServerSafe(world, entity1)) { float var = WeatherUtilParticle.getParticleWeight((EntityRotFX)entity1); if (var != -1) { return var; } } if (entity1 instanceof Squid) { return 400F; } if (entity1 instanceof LivingEntity) { LivingEntity livingEnt = (LivingEntity) entity1; int airTime = livingEnt.getPersistentData().getInt("timeInAir"); if (livingEnt.onGround() || livingEnt.isInWater()) { airTime = 0; } else { airTime++; } livingEnt.getPersistentData().putInt("timeInAir", airTime); if (entity1 instanceof Player) { if (((Player) entity1).abilities.instabuild) return 99999999F; return 5.0F + airTime / 400.0F; } else { return 500.0F + (livingEnt.onGround() ? 2.0F : 0.0F) + (airTime / 400.0F); } } if (entity1 instanceof Boat || entity1 instanceof ItemEntity || entity1 instanceof FishingHook) { return 4000F; } if (entity1 instanceof AbstractMinecart) { return 80F; } return 1F; } public static boolean isParticleRotServerSafe(Level world, Object obj) { if (EffectiveSide.get().equals(LogicalSide.SERVER)) { return false; } if (!world.isClientSide) return false; return isParticleRotClientCheck(obj); } public static boolean isParticleRotClientCheck(Object obj) { return obj instanceof EntityRotFX; } public static double getDistanceSqEntToPos(Entity ent, BlockPos pos) { return ent.position().distanceToSqr(Vec3.atCenterOf(pos)); } public static boolean isEntityOutside(Entity parEnt) { return isEntityOutside(parEnt, false); } public static boolean isEntityOutside(Entity parEnt, boolean cheapCheck) { return isPosOutside(parEnt.level(), parEnt.position(), cheapCheck, false); } public static boolean isPosOutside(Level parWorld, Vec3 parPos) { return isPosOutside(parWorld, parPos, false, false); } public static boolean isPosOutside(Level parWorld, Vec3 parPos, boolean cheapCheck, boolean eachSideClearCheck) { int height = WeatherUtilBlock.getPrecipitationHeightSafe(parWorld, new BlockPos(Mth.floor(parPos.x), 0, Mth.floor(parPos.z))).getY(); if (height < parPos.y+1) { return true; } if (cheapCheck) return false; int rangeCheck = 5; int yOffset = 0; //start 1 block away from start position to avoid colliding with self when used for a block int xzInitialOffset = 1; boolean nsCheck = false; Vec3 vecTry = new Vec3(parPos.x + Direction.NORTH.getStepX()*rangeCheck, parPos.y+yOffset, parPos.z + Direction.NORTH.getStepZ()*rangeCheck); Vec3 parPosTry = new Vec3(parPos.x + Direction.NORTH.getStepX()*xzInitialOffset, parPos.y+yOffset, parPos.z + Direction.NORTH.getStepZ()*xzInitialOffset); if (checkVecOutside(parWorld, parPosTry, vecTry)) { if (eachSideClearCheck) { nsCheck = true; } else { return true; } } vecTry = new Vec3(parPos.x + Direction.SOUTH.getStepX()*rangeCheck, parPos.y+yOffset, parPos.z + Direction.SOUTH.getStepZ()*rangeCheck); parPosTry = new Vec3(parPos.x + Direction.SOUTH.getStepX()*xzInitialOffset, parPos.y+yOffset, parPos.z + Direction.SOUTH.getStepZ()*xzInitialOffset); if (checkVecOutside(parWorld, parPosTry, vecTry)) { if (eachSideClearCheck) { return nsCheck; } else { return true; } } nsCheck = false; vecTry = new Vec3(parPos.x + Direction.EAST.getStepX()*rangeCheck, parPos.y+yOffset, parPos.z + Direction.EAST.getStepZ()*rangeCheck); parPosTry = new Vec3(parPos.x + Direction.EAST.getStepX()*xzInitialOffset, parPos.y+yOffset, parPos.z + Direction.EAST.getStepZ()*xzInitialOffset); if (checkVecOutside(parWorld, parPosTry, vecTry)) { if (eachSideClearCheck) { nsCheck = true; } else { return true; } } vecTry = new Vec3(parPos.x + Direction.WEST.getStepX()*rangeCheck, parPos.y+yOffset, parPos.z + Direction.WEST.getStepZ()*rangeCheck); parPosTry = new Vec3(parPos.x + Direction.WEST.getStepX()*xzInitialOffset, parPos.y+yOffset, parPos.z + Direction.WEST.getStepZ()*xzInitialOffset); if (checkVecOutside(parWorld, parPosTry, vecTry)) { if (eachSideClearCheck) { return nsCheck; } else { return true; } } return false; } public static boolean checkVecOutside(Level parWorld, Vec3 parPos, Vec3 parCheckPos) { BlockHitResult blockhitresult = parWorld.clip(new ClipContext(parPos, parCheckPos, ClipContext.Block.VISUAL, ClipContext.Fluid.NONE, null)); if (blockhitresult.getType() == HitResult.Type.MISS) { int height = WeatherUtilBlock.getPrecipitationHeightSafe(parWorld, new BlockPos(Mth.floor(parCheckPos.x), 0, Mth.floor(parCheckPos.z))).getY(); //System.out.println("height: " + height + " vs " + parCheckPos.y); if (height < parCheckPos.y) { return true; } } return false; } public static boolean isPlayerSheltered(Entity player) { int x = Mth.floor(player.getX()); int y = Mth.floor(player.getY() + player.getEyeHeight()); int z = Mth.floor(player.getZ()); return player.level().getHeight(Heightmap.Types.MOTION_BLOCKING, x, z) > y; } public static boolean canPosSeePos(Level level, Vec3 pos1, Vec3 pos2) { Vec3 vec3 = new Vec3(pos1.x(), pos1.y(), pos1.z()); Vec3 vec31 = new Vec3(pos2.x(), pos2.y(), pos2.z()); if (vec31.distanceTo(vec3) > 128.0D) { return false; } else { return level.clip(new ClipContext(vec3, vec31, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, null)).getType() == HitResult.Type.MISS; } } } ================================================ FILE: src/main/java/weather2/util/WeatherUtilParticle.java ================================================ package weather2.util; import java.lang.reflect.Field; import java.util.ArrayDeque; import java.util.Map; import java.util.Queue; import java.util.Random; import com.corosus.coroutil.util.CoroUtilEntOrParticle; import com.google.common.collect.Maps; import net.minecraft.client.Minecraft; import net.minecraft.client.particle.ParticleRenderType; import net.minecraft.client.particle.Particle; import net.minecraft.client.particle.ParticleEngine; import net.minecraft.core.BlockPos; import net.minecraft.util.Mth; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import extendedrenderer.particle.entity.EntityRotFX; import extendedrenderer.particle.entity.ParticleTexFX; import weather2.IWindHandler; public class WeatherUtilParticle { //public static ArrayDeque[][] fxLayers; public static Map> fxLayers; public static int effLeafID = 0; public static int effRainID = 1; public static int effWindID = 2; public static int effSnowID = 3; /*public static int effSandID = 4; public static int effWind2ID = 2;*/ public static Random rand = new Random(); //public static int rainDrops = 20; //weather2: not sure what will happen to this in 1.7, copied over for convenience public static int getParticleAge(Particle ent) { return ent.age; //return (Integer) OldUtil.getPrivateValueBoth(Particle.class, ent, "age", "age"); } //weather2: not sure what will happen to this in 1.7, copied over for convenience public static void setParticleAge(Particle ent, int val) { ent.age = val; //OldUtil.setPrivateValueBoth(Particle.class, ent, "age", "age", val); } @OnlyIn(Dist.CLIENT) public static void getFXLayers() { //fxLayers Field field = null; try { field = (ParticleEngine.class).getDeclaredField("particles");//ObfuscationReflectionHelper.remapFieldNames("net.minecraft.client.particle.EffectRenderer", new String[] { "fxLayers" })[0]); field.setAccessible(true); fxLayers = (Map>)field.get(Minecraft.getInstance().particleEngine); } catch (Exception ex) { //System.out.println("temp message: obf reflection fail!"); //ex.printStackTrace(); try { field = (ParticleEngine.class).getDeclaredField("f_107289_"); field.setAccessible(true); fxLayers = (Map>)field.get(Minecraft.getInstance().particleEngine); } catch (Exception ex2) { ex2.printStackTrace(); } } } @OnlyIn(Dist.CLIENT) public static float getParticleWeight(EntityRotFX entity1) { //commented out for weather2 copy /*if (entity1 instanceof EntityFallingRainFX) { return 1.1F; }*/ if (entity1 instanceof IWindHandler) { return ((IWindHandler) entity1).getWindWeight(); } if (entity1 instanceof ParticleTexFX) { return 5.0F + ((float)entity1.getAge() / 200); } //commented out for weather2 copy /*if (entity1 instanceof EntityWindFX) { return 1.4F + ((float)entity1.getAge() / 200); }*/ if (entity1 instanceof Particle) { return 5.0F + ((float)entity1.getAge() / 200); } return -1; } public static BlockPos getPos(Particle particle) { return new BlockPos(Mth.floor(particle.x), Mth.floor(particle.y), Mth.floor(Mth.floor(particle.z))); } /*@SideOnly(Side.CLIENT) public static void shakeTrees(int range) { int size = range; int hsize = size / 2; int curX = (int)player.posX; int curY = (int)player.posY - 1; int curZ = (int)player.posZ; //if (true) return; float windStr = 1F; for (int xx = curX - hsize; xx < curX + hsize; xx++) { for (int yy = curY - hsize; yy < curY + hsize + 10; yy++) { for (int zz = curZ - hsize; zz < curZ + hsize; zz++) { //REMOVE VINES!!!!! int uh = (int)(40 / (windStr + 0.001)); //System.out.println(uh); if (uh < 1) { uh = 1; } if (worldRef.rand.nextInt(uh) == 0) { for (int i = 0; i < p_blocks_leaf.size(); i++) { int id = getBlockId(xx, yy, zz); if (id == ((Block)p_blocks_leaf.get(i)).blockID) { if (id == Block.leaves.blockID) { if (getBlockId(xx, yy - 1, zz) == 0) { EntityRotFX var31 = new EntityTexBiomeColorFX(worldRef, (double)xx, (double)yy - 0.5, (double)zz, 0D, 0D, 0D, 10D, 0, effLeafID, id, getBlockMetadata(xx, yy, zz), xx, yy, zz); WeatherUtil.setParticleGravity((EntityFX)var31, 0.3F); for (int ii = 0; ii < 10; ii++) { applyWindForce(var31); } var31.rotationYaw = rand.nextInt(360); //var31.spawnAsWeatherEffect(); spawnQueue.add(var31); } } else { //This is non leaves, as in wildgrass or wahtever is in the p_blocks_leaf list (no special rules) EntityRotFX var31 = new EntityTexBiomeColorFX(worldRef, (double)xx, (double)yy + 0.5, (double)zz, 0D, 0D, 0D, 10D, 0, effLeafID, id, getBlockMetadata(xx, yy, zz), xx, yy, zz); WeatherUtil.setParticleGravity((EntityFX)var31, 0.3F); //var31.spawnAsWeatherEffect(); spawnQueue.add(var31); } } else if (id == 0) { if (weatherMan.wind.strength > 0.02) { if (worldRef.rand.nextInt(400 - (int)(weatherMan.wind.strength * 100)) == 0) { //EntityFX var31 = new EntitySmokeFX(worldRef, (double)xx, (double)yy+0.5, (double)zz, 0D, 0D, 0D); EntityRotFX var31 = new EntityTexFX(worldRef, (double)xx, (double)yy + 0.5, (double)zz, 0D, 0D, 0D, 10D, 0, effWind2ID); //var31.particleGravity = 0.3F; //mod_ExtendedRenderer.rotEffRenderer.addEffect(var31); for (int ii = 0; ii < 20; ii++) { applyWindForce(var31); } WeatherUtil.setParticleGravity((EntityFX)var31, 0.0F); var31.noClip = true; WeatherUtil.setParticleScale((EntityFX)var31, 0.3F); var31.rotationYaw = rand.nextInt(360); //var31.spawnAsWeatherEffect(); spawnQueue.add(var31); } } } } int id = getBlockId(xx, yy, zz); if (id == ((Block)p_blocks_sand.get(0)).blockID) { if (id == Block.sand.blockID) { if (getBlockId(xx, yy + 1, zz) == 0) { EntityTexFX var31 = new EntityTexFX(worldRef, (double)xx, (double)yy + 0.5, (double)zz, 0D, 0D, 0D, 10D, 0, effSandID); //var31 = new EntityWindFX(worldRef, (double)xx, (double)yy+1.2, (double)zz, 0D, 0.0D, 0D, 9.5D, 1); var31.rotationYaw = rand.nextInt(360) - 180F; var31.type = 1; WeatherUtil.setParticleGravity((EntityFX)var31, 0.6F); WeatherUtil.setParticleScale((EntityFX)var31, 0.3F); //var31.spawnAsWeatherEffect(); spawnQueue.add(var31); } } } } } } } }*/ } ================================================ FILE: src/main/java/weather2/util/WeatherUtilPhysics.java ================================================ package weather2.util; import net.minecraft.world.phys.Vec3; import java.util.List; /** * Full of repurposed stack overflow examples * * @author Corosus * */ public class WeatherUtilPhysics { /** * Return true if the given point is contained inside the boundary. * See: http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html * @param test The point to check * @return true if the point is inside the boundary, false otherwise * */ public static boolean isInConvexShape(Vec3 test, List points) { int i; int j; boolean result = false; for (i = 0, j = points.size() - 1; i < points.size(); j = i++) { Vec3 vecI = points.get(i); Vec3 vecJ = points.get(j); if ((vecI.z > test.z) != (vecJ.z > test.z) && (test.x < (vecJ.x - vecI.x) * (test.z - vecI.z) / (vecJ.z-vecI.z) + vecI.x)) { result = !result; } } return result; } /** * Gets minimum distance to shape, 2D only, y not used * Doesnt check if you are inside shape, use isInConvexShape for that * * @param point * @param points * @return */ public static double getDistanceToShape(Vec3 point, List points) { float closestDist1 = 9999; float closestDist2 = 9999; Vec3 closestPoint1 = null; Vec3 closestPoint2 = null; //loop twice to account for edge case where points are ordered in increasing order of closeness, causing second closest clause to never trigger for (int i = 0; i < 2; i++) { for (Vec3 pointTest : points) { double dist = pointTest.distanceTo(point); if (dist < closestDist1) { closestDist1 = (float) dist; closestPoint1 = pointTest; } else if (dist < closestDist2 && pointTest != closestPoint1) { closestDist2 = (float) dist; closestPoint2 = pointTest; } } } if (closestPoint1 == null || closestPoint2 == null) { //should never happen unless too few points return -1; } return distBetweenPointAndLine(point.x, point.z, closestPoint1.x, closestPoint1.z, closestPoint2.x, closestPoint2.z); } /** * x and y are point, rest is line, 2D only * * @param x * @param y * @param x1 * @param y1 * @param x2 * @param y2 * @return */ public static double distBetweenPointAndLine(double x, double y, double x1, double y1, double x2, double y2) { // A - the standalone point (x, y) // B - start point of the line segment (x1, y1) // C - end point of the line segment (x2, y2) // D - the crossing point between line from A to BC double AB = distBetween(x, y, x1, y1); double BC = distBetween(x1, y1, x2, y2); double AC = distBetween(x, y, x2, y2); // Heron's formula double s = (AB + BC + AC) / 2; double area = Math.sqrt(s * (s - AB) * (s - BC) * (s - AC)); // but also area == (BC * AD) / 2 // BC * AD == 2 * area // AD == (2 * area) / BC // TODO: check if BC == 0 double AD = (2 * area) / BC; return AD; } public static double distBetween(double x, double y, double x1, double y1) { double xx = x1 - x; double yy = y1 - y; return Math.sqrt(xx * xx + yy * yy); } } ================================================ FILE: src/main/java/weather2/util/WeatherUtilSound.java ================================================ package weather2.util; import net.minecraft.client.Minecraft; import net.minecraft.sounds.SoundSource; import net.minecraft.sounds.SoundEvent; import net.minecraft.world.phys.Vec3; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import weather2.SoundRegistry; import weather2.client.MovingSoundStreamingSource; import weather2.weathersystem.storm.StormObject; import java.util.HashMap; import java.util.Random; /** * TODO: rewrite this to use a class that contains array of sounds, amount of them, length of them, and the last played time and next random index * would help cleanup the weird array use this class does */ public class WeatherUtilSound { public static String snd_tornado_dmg_close[] = new String[3]; public static String snd_wind_close[] = new String[3]; public static String snd_wind_far[] = new String[3]; public static String snd_sandstorm_low[] = new String[2]; public static String snd_sandstorm_med[] = new String[2]; public static String snd_sandstorm_high[] = new String[1]; public static HashMap soundToLength = new HashMap<>(); /** * These need to match the amount of array'd strings we use for sounds, was 3, now 6 for sandstorm addition */ public static int snd_rand[] = new int[6]; public static long soundTimer[] = new long[6]; public static void init() { Random rand = new Random(); snd_tornado_dmg_close[0] = "destruction_0_"; snd_tornado_dmg_close[1] = "destruction_1_"; snd_tornado_dmg_close[2] = "destruction_2_"; snd_wind_close[0] = "wind_close_0_"; snd_wind_close[1] = "wind_close_1_"; snd_wind_close[2] = "wind_close_2_"; snd_wind_far[0] = "wind_far_0_"; snd_wind_far[1] = "wind_far_1_"; snd_wind_far[2] = "wind_far_2_"; snd_sandstorm_low[0] = "sandstorm_low1"; snd_sandstorm_low[1] = "sandstorm_low2"; snd_sandstorm_med[0] = "sandstorm_med1"; snd_sandstorm_med[1] = "sandstorm_med2"; snd_sandstorm_high[0] = "sandstorm_high1"; snd_rand[0] = rand.nextInt(snd_tornado_dmg_close.length); snd_rand[1] = rand.nextInt(snd_wind_close.length); snd_rand[2] = rand.nextInt(snd_wind_far.length); snd_rand[3] = rand.nextInt(snd_sandstorm_high.length); snd_rand[4] = rand.nextInt(snd_sandstorm_med.length); snd_rand[5] = rand.nextInt(snd_sandstorm_low.length); /*soundID[0] = -1; soundID[1] = -1; soundID[2] = -1;*/ soundToLength.put(snd_tornado_dmg_close[0], 2515); soundToLength.put(snd_tornado_dmg_close[1], 2580); soundToLength.put(snd_tornado_dmg_close[2], 2741); soundToLength.put(snd_wind_close[0], 4698); soundToLength.put(snd_wind_close[1], 7324); soundToLength.put(snd_wind_close[2], 6426); soundToLength.put(snd_wind_far[0], 12892); soundToLength.put(snd_wind_far[1], 9653); soundToLength.put(snd_wind_far[2], 12003); soundToLength.put(snd_sandstorm_low[0], 8004); soundToLength.put(snd_sandstorm_low[1], 7119); soundToLength.put(snd_sandstorm_med[0], 16325); soundToLength.put(snd_sandstorm_med[1], 12776); soundToLength.put(snd_sandstorm_high[0], 23974); soundToLength.put("siren_sandstorm_1", 11923); soundToLength.put("siren_sandstorm_2", 20122); soundToLength.put("siren_sandstorm_3", 10366); soundToLength.put("siren_sandstorm_4", 44274); soundToLength.put("siren_sandstorm_5_extra", 1282); } @OnlyIn(Dist.CLIENT) public static void playNonMovingSound(Vec3 parPos, String var1, float parVolume, float parPitch, float parCutOffRange) { //String prefix = "streaming."; String affix = ".ogg"; //ResourceLocation res = new ResourceLocation(var1); SoundEvent event = SoundRegistry.get(var1); MovingSoundStreamingSource sound = new MovingSoundStreamingSource(parPos, event, SoundSource.WEATHER, parVolume, parPitch, parCutOffRange); Minecraft.getInstance().getSoundManager().play(sound); } @OnlyIn(Dist.CLIENT) public static void playMovingSound(StormObject parStorm, String var1, float parVolume, float parPitch, float parCutOffRange) { //String prefix = "streaming."; String affix = ".ogg"; //ResourceLocation res = new ResourceLocation(var1); SoundEvent event = SoundRegistry.get(var1); try { MovingSoundStreamingSource sound = new MovingSoundStreamingSource(parStorm, event, SoundSource.WEATHER, parVolume, parPitch, parCutOffRange); Minecraft.getInstance().getSoundManager().play(sound); } catch (Exception ex) { //catching annoying 'java.lang.NoClassDefFoundError: weather2/client/MovingSoundStreamingSource' crash when hot reloading in dev ex.printStackTrace(); } } @OnlyIn(Dist.CLIENT) public static void playPlayerLockedSound(Vec3 parPos, String var1, float var5, float var6) { SoundEvent event = SoundRegistry.get(var1); MovingSoundStreamingSource sound = new MovingSoundStreamingSource(parPos, event, SoundSource.WEATHER, var5, var6, true); Minecraft.getInstance().getSoundManager().play(sound); } } ================================================ FILE: src/main/java/weather2/util/WindReader.java ================================================ package weather2.util; import net.minecraft.core.BlockPos; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import weather2.ClientTickHandler; import weather2.ServerTickHandler; import weather2.weathersystem.WeatherManager; import javax.annotation.Nullable; public class WindReader { public static float getWindAngle(Level world) { return getWindAngle(world,null); } public static float getWindAngle(Level world, Vec3 pos) { WeatherManager weather = getWeatherManagerFor(world); return weather != null ? weather.getWindManager().getWindAngle(pos) : 0; } public static float getWindSpeed(Level world) { return getWindSpeed(world, null); } public static float getWindSpeed(Level world, @Nullable BlockPos pos) { return getWindSpeed(world, pos, 1); } public static float getWindSpeed(Level world, @Nullable BlockPos pos, float extraHeightAmpMax) { WeatherManager weather = getWeatherManagerFor(world); return weather != null ? weather.getWindManager().getWindSpeed(pos, extraHeightAmpMax) : 0; } public static float getWindSpeedCached(Level world, @Nullable BlockPos pos, float extraHeightAmpMax) { WeatherManager weather = getWeatherManagerFor(world); return weather != null ? weather.getWindManager().getCachedWindSpeedForHeight(pos, extraHeightAmpMax) : 0; } public static WeatherManager getWeatherManagerFor(Level world) { if (world.isClientSide) { return getWeatherManagerClient(); } else { return ServerTickHandler.getWeatherManagerFor((world.dimension())); } } @OnlyIn(Dist.CLIENT) private static WeatherManager getWeatherManagerClient() { return ClientTickHandler.weatherManager; } } ================================================ FILE: src/main/java/weather2/weathersystem/WeatherManager.java ================================================ package weather2.weathersystem; import com.corosus.coroutil.util.CULog; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceKey; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; import weather2.IWorldData; import weather2.Weather; import weather2.WorldNBTData; import weather2.config.ConfigStorm; import weather2.config.WeatherUtilConfig; import weather2.weathersystem.storm.*; import weather2.weathersystem.wind.WindManager; import java.util.*; public abstract class WeatherManager implements IWorldData { public final ResourceKey dimension; private final WindManager wind = new WindManager(this); private List listStormObjects = new ArrayList<>(); public HashMap lookupStormObjectsByID = new HashMap<>(); //non particle storm public long lastStormFormed = 0; public long lastSandstormFormed = 0; public long lastSnowstormFormed = 0; //0 = none, 1 = usual max overcast public float cloudIntensity = 1F; //for client only public boolean isVanillaRainActiveOnServer = false; public boolean isVanillaThunderActiveOnServer = false; public int vanillaRainTimeOnServer = 0; //going to vary the amount randomly over time like wind, for aesthetic only mode public float vanillaRainAmountOnServer = 0; private HashMap lookupWeatherBlockDamageDeflector = new HashMap<>(); public WeatherManager(ResourceKey dimension) { this.dimension = dimension; } public abstract Level getWorld(); public void tick() { Level world = getWorld(); if (world != null) { //tick storms List list = getStormObjects(); for (int i = 0; i < list.size(); i++) { WeatherObject so = list.get(i); if (this instanceof WeatherManagerServer && so.isDead) { removeStormObject(so.ID); ((WeatherManagerServer)this).syncStormRemove(so); } else { if (!so.isDead) { so.tick(); } else { if (getWorld().isClientSide) { Weather.dbg("WARNING!!! - detected isDead storm object still in client side list, had to remove storm object with ID " + so.ID + " from client side, wasnt properly isDead via main channels"); removeStormObject(so.ID); } } } } //tick wind if (WeatherUtilConfig.listDimensionsWindEffects.contains(getWorld().dimension().location().toString())) { wind.tick(); } } } public WeatherObjectParticleStorm getClosestParticleStormByIntensity(Vec3 parPos, WeatherObjectParticleStorm.StormType type) { return getClosestParticleStormByIntensity(parPos, type, false); } /** * Gets the most intense sandstorm, used for effects and sounds * * @param parPos * @return */ public WeatherObjectParticleStorm getClosestParticleStormByIntensity(Vec3 parPos, WeatherObjectParticleStorm.StormType type, boolean forced) { WeatherObjectParticleStorm bestStorm = null; double closestDist = 9999999; List listStorms = getStormObjects(); for (int i = 0; i < listStorms.size(); i++) { WeatherObject wo = listStorms.get(i); if (wo instanceof WeatherObjectParticleStorm) { WeatherObjectParticleStorm sandstorm = (WeatherObjectParticleStorm) wo; if (sandstorm == null || sandstorm.isDead || sandstorm.getType() != type) continue; double dist = parPos.distanceTo(sandstorm.pos); if (closestDist > 0/* && dist < maxDist*/) { if (dist < closestDist) { closestDist = dist; bestStorm = sandstorm; } } } } return bestStorm; } public void reset() { for (int i = 0; i < getStormObjects().size(); i++) { WeatherObject so = getStormObjects().get(i); so.reset(); } getStormObjects().clear(); lookupStormObjectsByID.clear(); wind.reset(); //do not reset this, its static (shared between client and server) and client side calls reset() //WeatherObject.lastUsedStormID = 0; } public void tickRender(float partialTick) { Level world = getWorld(); if (world != null) { //tick storms //There are scenarios where getStormObjects().get(i) returns a null storm, uncertain why, for now try to catch it and move on try { for (int i = 0; i < getStormObjects().size(); i++) { WeatherObject obj = getStormObjects().get(i); if (obj != null) { obj.tickRender(partialTick); } } } catch (Exception e) { e.printStackTrace(); } } } public List getStormObjects() { return listStormObjects; } public StormObject getStormObjectByID(long ID) { WeatherObject obj = lookupStormObjectsByID.get(ID); if (obj instanceof StormObject) { return (StormObject) obj; } else { return null; } } public void addStormObject(WeatherObject so) { if (!lookupStormObjectsByID.containsKey(so.ID)) { listStormObjects.add(so); lookupStormObjectsByID.put(so.ID, so); if (so instanceof StormObject) { StormObject so2 = (StormObject) so; } } else { Weather.dbg("Weather2 WARNING!!! Received new storm create for an ID that is already active! design bug or edgecase with PlayerEvent.Clone, ID: " + so.ID); //Weather.dbgStackTrace(); } } public void removeStormObject(long ID) { WeatherObject so = lookupStormObjectsByID.get(ID); if (so != null) { so.remove(); listStormObjects.remove(so); lookupStormObjectsByID.remove(ID); if (so instanceof StormObject) { StormObject so2 = (StormObject) so; } } else { Weather.dbg("error looking up storm ID on server for removal: " + ID + " - lookup count: " + lookupStormObjectsByID.size() + " - last used ID: " + WeatherObject.lastUsedStormID); } } public StormObject getClosestStormAny(Vec3 parPos, double maxDist) { return getClosestStorm(parPos, maxDist, -1, -1, true); } public StormObject getClosestStorm(Vec3 parPos, double maxDist, int severityFlagMin) { return getClosestStorm(parPos, maxDist, severityFlagMin, -1, false); } public StormObject getClosestStorm(Vec3 parPos, double maxDist, int severityFlagMin, int severityFlagMax, boolean orRain) { StormObject closestStorm = null; double closestDist = Double.MAX_VALUE; List listStorms = getStormObjects(); for (int i = 0; i < listStorms.size(); i++) { WeatherObject wo = listStorms.get(i); if (wo instanceof StormObject) { StormObject storm = (StormObject) wo; if (storm == null || storm.isDead) continue; double dist = storm.pos.distanceTo(parPos); if (dist < closestDist && dist <= maxDist && !storm.isFirenado) { if ((storm.attrib_precipitation && orRain) || ((severityFlagMin == -1 || storm.levelCurIntensityStage >= severityFlagMin) && (severityFlagMax == -1 || storm.levelCurIntensityStage <= severityFlagMax))) { closestStorm = storm; closestDist = dist; } } } } return closestStorm; //not sure i can avoid a double use of distance calculation adding to iteration cost, this method might not be stream worthy /*return getStormObjects().stream() .map(wo -> (StormObject)wo) .filter(so -> !so.isDead) .filter(so -> (so.attrib_precipitation && orRain) || (severityFlagMin == -1 || so.levelCurIntensityStage >= severityFlagMin)) .filter(so -> so.pos.distanceTo(parPos) < maxDist) .min(Comparator.comparing(so -> so.pos.distanceTo(parPos))).orElse(null);*/ } public boolean isPrecipitatingAt(BlockPos pos) { return isPrecipitatingAt(new Vec3(pos.getX(), pos.getY(), pos.getZ())); } /** * TODO: Heavy on the processing, consider caching the result by location for 20 ticks * * @param parPos * @return */ public boolean isPrecipitatingAt(Vec3 parPos) { /*List listStorms = getStormObjects(); for (int i = 0; i < listStorms.size(); i++) { WeatherObject wo = listStorms.get(i); if (wo instanceof StormObject) { StormObject storm = (StormObject) wo; if (storm == null || storm.isDead) continue; if (storm.attrib_precipitation) { double dist = storm.pos.distanceTo(parPos); if (dist < storm.size) { return true; } } } } return false;*/ return getStormObjects().stream() .map(wo -> (StormObject)wo) .anyMatch(so -> !so.isDead && so.attrib_precipitation && so.pos.distanceTo(parPos) < so.size); } /** * Simply compares stormfront distances, doesnt factor in tail * * @param parPos * @param maxDist * @return */ public WeatherObjectSandstormOld getClosestSandstorm(Vec3 parPos, double maxDist) { WeatherObjectSandstormOld closestStorm = null; double closestDist = 9999999; List listStorms = getStormObjects(); for (int i = 0; i < listStorms.size(); i++) { WeatherObject wo = listStorms.get(i); if (wo instanceof WeatherObjectSandstormOld) { WeatherObjectSandstormOld storm = (WeatherObjectSandstormOld) wo; if (storm == null || storm.isDead) continue; double dist = storm.pos.distanceTo(parPos); /*if (getWorld().isRemote) { System.out.println("close storm candidate: " + dist + " - " + storm.state + " - " + storm.attrib_rain); }*/ if (dist < closestDist && dist <= maxDist) { //if ((storm.attrib_precipitation && orRain) || (severityFlagMin == -1 || storm.levelCurIntensityStage >= severityFlagMin)) { closestStorm = storm; closestDist = dist; //} } } } return closestStorm; } public List getSandstormsAround(Vec3 parPos, double maxDist) { List storms = new ArrayList<>(); for (int i = 0; i < getStormObjects().size(); i++) { WeatherObject wo = getStormObjects().get(i); if (wo instanceof WeatherObjectSandstormOld) { WeatherObjectSandstormOld storm = (WeatherObjectSandstormOld) wo; if (storm.isDead) continue; if (storm.pos.distanceTo(parPos) < maxDist) { storms.add(storm); } } } return storms; } public List getStormsAroundForDeflector(Vec3 parPos, double maxDist) { List storms = new ArrayList<>(); for (int i = 0; i < getStormObjects().size(); i++) { WeatherObject wo = getStormObjects().get(i); if (wo.isDead) continue; if (wo instanceof StormObject) { StormObject storm = (StormObject) wo; if (storm.pos.distanceTo(parPos) < maxDist && ((storm.attrib_precipitation && ConfigStorm.Storm_Deflector_RemoveRainstorms) || storm.levelCurIntensityStage >= ConfigStorm.Storm_Deflector_MinStageRemove)) { storms.add(storm); } } else if (wo instanceof WeatherObjectSandstormOld && ConfigStorm.Storm_Deflector_RemoveSandstorms) { WeatherObjectSandstormOld sandstorm = (WeatherObjectSandstormOld)wo; double distToStorm = parPos.distanceTo(sandstorm.pos); if (distToStorm < maxDist) { storms.add(wo); } } } return storms; } public List getStormsAround(Vec3 parPos, double maxDist) { List storms = new ArrayList<>(); for (int i = 0; i < getStormObjects().size(); i++) { WeatherObject wo = getStormObjects().get(i); if (wo.isDead) continue; if (wo instanceof StormObject) { StormObject storm = (StormObject) wo; if (storm.pos.distanceTo(parPos) < maxDist && (storm.attrib_precipitation || storm.levelCurIntensityStage > StormObject.STATE_NORMAL)) { storms.add(storm); } } else if (wo instanceof WeatherObjectSandstormOld) { WeatherObjectSandstormOld sandstorm = (WeatherObjectSandstormOld)wo; double distToStorm = parPos.distanceTo(sandstorm.pos); if (distToStorm < maxDist) { storms.add(wo); } } } return storms; } @Override public CompoundTag save(CompoundTag data) { CULog.dbg("WeatherManager save"); CompoundTag listStormsNBT = new CompoundTag(); for (int i = 0; i < listStormObjects.size(); i++) { WeatherObject obj = listStormObjects.get(i); obj.getNbtCache().setUpdateForced(true); obj.write(); obj.getNbtCache().setUpdateForced(false); listStormsNBT.put("storm_" + obj.ID, obj.getNbtCache().getNewNBT()); } data.put("stormData", listStormsNBT); CompoundTag listDeflectorsNBT = new CompoundTag(); int i = 0; for (Map.Entry entry : lookupWeatherBlockDamageDeflector.entrySet()) { CULog.dbg("writing out deflector to disk: " + entry.getKey()); listDeflectorsNBT.putLong("deflector_" + i, entry.getKey()); i++; } data.put("deflectorData", listDeflectorsNBT); data.putLong("lastUsedIDStorm", WeatherObject.lastUsedStormID); data.putLong("lastStormFormed", lastStormFormed); data.putLong("lastSandstormFormed", lastSandstormFormed); data.putLong("lastSnowstormFormed", lastSnowstormFormed); data.putFloat("cloudIntensity", this.cloudIntensity); data.put("windMan", wind.write(new CompoundTag())); return data; } public void read() { WorldNBTData worldNBTData = ((ServerLevel)getWorld()).getDataStorage().computeIfAbsent(WorldNBTData::load, WorldNBTData::new, Weather.MODID + "-" + "weather_data"); worldNBTData.setDataHandler(this); CULog.dbg("weather data: " + worldNBTData.getData()); CompoundTag data = worldNBTData.getData(); lastStormFormed = data.getLong("lastStormFormed"); lastSandstormFormed = data.getLong("lastSandstormFormed"); lastSnowstormFormed = data.getLong("lastSnowstormFormed"); //prevent setting to 0 for worlds updating to new weather version if (data.contains("cloudIntensity")) { cloudIntensity = data.getFloat("cloudIntensity"); } WeatherObject.lastUsedStormID = data.getLong("lastUsedIDStorm"); wind.read(data.getCompound("windMan")); CompoundTag nbtStorms = data.getCompound("stormData"); Iterator it = nbtStorms.getAllKeys().iterator(); while (it.hasNext()) { String tagName = (String) it.next(); CompoundTag stormData = nbtStorms.getCompound(tagName); //if (ServerTickHandler.getWeatherManagerFor(dimension) != null) { WeatherObject wo = null; if (stormData.getInt("weatherObjectType") == EnumWeatherObjectType.CLOUD.ordinal()) { wo = new StormObject(this); } else if (stormData.getInt("weatherObjectType") == EnumWeatherObjectType.SAND.ordinal()) { wo = new WeatherObjectParticleStorm(this); ((WeatherObjectParticleStorm)wo).setType(WeatherObjectParticleStorm.StormType.SANDSTORM); //initStormNew??? } else if (stormData.getInt("weatherObjectType") == EnumWeatherObjectType.SNOW.ordinal()) { wo = new WeatherObjectParticleStorm(this); ((WeatherObjectParticleStorm)wo).setType(WeatherObjectParticleStorm.StormType.SNOWSTORM); //initStormNew??? } try { wo.getNbtCache().setNewNBT(stormData); wo.read(); wo.getNbtCache().updateCacheFromNew(); } catch (Exception ex) { ex.printStackTrace(); } addStormObject(wo); //TODO: possibly unneeded/redundant/bug inducing, packets will be sent upon request from client ((WeatherManagerServer)(this)).syncStormNew(wo); /*} else { System.out.println("WARNING: trying to load storm objects for missing dimension: " + dimension); }*/ } CompoundTag nbtDeflectors = data.getCompound("deflectorData"); Iterator it2 = nbtDeflectors.getAllKeys().iterator(); while (it2.hasNext()) { String tagName = (String) it2.next(); long hash = nbtDeflectors.getLong(tagName); if (!BlockPos.of(hash).equals(new BlockPos(0, 0, 0))) { CULog.dbg("adding deflector from disk: " + BlockPos.of(hash)); registerDeflector(BlockPos.of(hash)); } else { CULog.dbg("????????"); } } CULog.dbg("reloaded weather objects: " + listStormObjects.size()); } public WindManager getWindManager() { return this.wind; } public HashMap getLookupWeatherBlockDamageDeflector() { return lookupWeatherBlockDamageDeflector; } public void registerDeflector(BlockPos pos) { long hash = BlockPos.asLong(pos.getX(), pos.getY(), pos.getZ()); if (!this.lookupWeatherBlockDamageDeflector.containsKey(hash)) { CULog.dbg("adding weather deflector poi at " + pos); this.lookupWeatherBlockDamageDeflector.put(hash, pos); } } public void removeDeflector(BlockPos pos) { long hash = BlockPos.asLong(pos.getX(), pos.getY(), pos.getZ()); CULog.dbg("removing weather deflector poi at " + pos); this.lookupWeatherBlockDamageDeflector.remove(hash); } } ================================================ FILE: src/main/java/weather2/weathersystem/WeatherManagerClient.java ================================================ package weather2.weathersystem; import com.corosus.coroutil.util.CoroUtilMisc; import extendedrenderer.particle.entity.ParticleCube; import net.minecraft.client.Minecraft; import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtUtils; import net.minecraft.resources.ResourceKey; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import weather2.ClientTickHandler; import weather2.Weather; import weather2.client.SceneEnhancer; import weather2.config.ConfigParticle; import weather2.weathersystem.storm.EnumWeatherObjectType; import weather2.weathersystem.storm.StormObject; import weather2.weathersystem.storm.WeatherObject; import weather2.weathersystem.storm.WeatherObjectParticleStorm; import java.util.Random; @OnlyIn(Dist.CLIENT) public class WeatherManagerClient extends WeatherManager { //public CloudManager cloudManager = new CloudManager(); public WeatherManagerClient(ResourceKey dimension) { super(dimension); } @Override public void tick() { super.tick(); if (!Weather.isLoveTropicsInstalled()) { //TODO: disabled for 1.20, might need to go mixin from here /*ICloudRenderHandler cloudRenderHandler = ((ClientLevel) getWorld()).effects().getCloudRenderHandler(); if (cloudRenderHandler == null) { ((ClientLevel) getWorld()).effects().setCloudRenderHandler(new CloudRenderHandler()); } IWeatherParticleRenderHandler handler = ((ClientLevel) getWorld()).effects().getWeatherParticleRenderHandler(); if (handler == null) { ((ClientLevel) getWorld()).effects().setWeatherParticleRenderHandler(new WeatherParticleRenderHandler()); }*/ boolean cloudTest = false; if (cloudTest) { //cloudManager.tick(); } } //((ClientLevel)getWorld()).effects().setCloudRenderHandler(null); } @Override public Level getWorld() { return Minecraft.getInstance().level; } public void nbtSyncFromServer(CompoundTag parNBT) { //check command //commands: //new storm //tick storm //remove storm //new volcano //tick volcano //remove volcano??? String command = parNBT.getString("command"); if (command.equals("syncStormNew")) { //Weather.dbg("creating client side storm"); CompoundTag stormNBT = parNBT.getCompound("data"); long ID = stormNBT.getLong("ID"); Weather.dbg("syncStormNew, ID: " + ID); EnumWeatherObjectType weatherObjectType = EnumWeatherObjectType.get(stormNBT.getInt("weatherObjectType")); WeatherObject wo = null; if (weatherObjectType == EnumWeatherObjectType.CLOUD) { wo = new StormObject(ClientTickHandler.weatherManager); } else if (weatherObjectType == EnumWeatherObjectType.SAND) { wo = new WeatherObjectParticleStorm(ClientTickHandler.weatherManager); ((WeatherObjectParticleStorm)wo).setType(WeatherObjectParticleStorm.StormType.SANDSTORM); } else if (weatherObjectType == EnumWeatherObjectType.SNOW) { wo = new WeatherObjectParticleStorm(ClientTickHandler.weatherManager); ((WeatherObjectParticleStorm)wo).setType(WeatherObjectParticleStorm.StormType.SNOWSTORM); } //StormObject so wo.getNbtCache().setNewNBT(stormNBT); wo.nbtSyncFromServer(); wo.getNbtCache().updateCacheFromNew(); addStormObject(wo); } else if (command.equals("syncStormRemove")) { //Weather.dbg("removing client side storm"); CompoundTag stormNBT = parNBT.getCompound("data"); long ID = stormNBT.getLong("ID"); WeatherObject so = lookupStormObjectsByID.get(ID); if (so != null) { Weather.dbg("syncStormRemove, ID: " + ID); removeStormObject(ID); } else { Weather.dbg("error removing storm, cant find by ID: " + ID); } } else if (command.equals("syncStormUpdate")) { //Weather.dbg("updating client side storm"); CompoundTag stormNBT = parNBT.getCompound("data"); long ID = stormNBT.getLong("ID"); WeatherObject so = lookupStormObjectsByID.get(ID); if (so != null) { so.getNbtCache().setNewNBT(stormNBT); so.nbtSyncFromServer(); so.getNbtCache().updateCacheFromNew(); } else { Weather.dbg("error syncing storm, cant find by ID: " + ID + ", probably due to client resetting and waiting on full resync (this is ok)"); //Weather.dbgStackTrace(); } } else if (command.equals("syncWindUpdate")) { //Weather.dbg("updating client side wind"); CompoundTag nbt = parNBT.getCompound("data"); getWindManager().nbtSyncFromServer(nbt); } else if (command.equals("syncWeatherUpdate")) { //Weather.dbg("updating client side wind"); //NBTTagCompound nbt = parNBT.getCompound("data"); isVanillaRainActiveOnServer = parNBT.getBoolean("isVanillaRainActiveOnServer"); isVanillaThunderActiveOnServer = parNBT.getBoolean("isVanillaThunderActiveOnServer"); vanillaRainTimeOnServer = parNBT.getInt("vanillaRainTimeOnServer"); vanillaRainAmountOnServer = parNBT.getFloat("vanillaRainAmountOnServer"); //windMan.nbtSyncFromServer(nbt); } else if (command.equals("syncBlockParticleNew")) { //Weather.dbg("updating client side wind"); CompoundTag nbt = parNBT.getCompound("data"); int posX = nbt.getInt("posX"); int posY = nbt.getInt("posY") + 1; int posZ = nbt.getInt("posZ"); BlockState state = NbtUtils.readBlockState(getWorld().holderLookup(Registries.BLOCK), nbt.getCompound("blockstate")); long ownerID = nbt.getLong("ownerID"); //CULog.dbg("add cube at " + posX + " " + posY + " " + posZ); StormObject storm = getStormObjectByID(ownerID); if (storm != null) { if (ConfigParticle.Particle_effect_rate > 0) { int extraCubes = (int) (1 + ConfigParticle.Particle_Tornado_extraParticleCubes * ConfigParticle.Particle_effect_rate); Random rand = CoroUtilMisc.random(); float randRange = 3; for (int i = 0; i < extraCubes; i++) { ParticleCube hail = new ParticleCube(getWorld(), posX + (rand.nextFloat() - rand.nextFloat()) * randRange, posY + (rand.nextFloat() - rand.nextFloat()) * randRange, posZ + (rand.nextFloat() - rand.nextFloat()) * randRange, 0D, 0D, 0D, state); SceneEnhancer.checkParticleBehavior(); SceneEnhancer.particleBehavior.initParticleCube(hail); storm.listParticlesDebris.add(hail); hail.spawnAsWeatherEffect(); } } } } } } ================================================ FILE: src/main/java/weather2/weathersystem/WeatherManagerServer.java ================================================ package weather2.weathersystem; import com.corosus.coroutil.util.*; import com.google.common.collect.Lists; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtUtils; import net.minecraft.server.level.ChunkHolder; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntitySelector; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.Level; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.phys.Vec3; import net.minecraftforge.network.NetworkDirection; import net.minecraftforge.network.PacketDistributor; import weather2.*; import weather2.config.*; import weather2.datatypes.StormState; import weather2.util.CachedNBTTagCompound; import weather2.util.WeatherUtilBlock; import weather2.weathersystem.storm.StormObject; import weather2.weathersystem.storm.WeatherObject; import weather2.weathersystem.storm.WeatherObjectParticleStorm; import weather2.weathersystem.wind.WindManager; import javax.annotation.Nullable; import java.util.*; public class WeatherManagerServer extends WeatherManager { private final ServerLevel world; public WeatherManagerServer(ServerLevel world) { super(world.dimension()); this.world = world; } @Override public Level getWorld() { return world; } @Override public void tick() { super.tick(); StormState snowstorm = ServerWeatherProxy.getSnowstormForEverywhere(world); if (snowstorm != null) { tickStormBlockBuildup(snowstorm, Blocks.SNOW); } StormState sandstorm = ServerWeatherProxy.getSandstormForEverywhere(world); if (sandstorm != null) { tickStormBlockBuildup(sandstorm, WeatherBlocks.BLOCK_SAND_LAYER.get()); } //tickStormBlockBuildup(new StormState(1, 2), WeatherBlocks.blockSandLayer); tickWeatherCoverage(); if (world != null) { WindManager windMan = getWindManager(); getStormObjects().stream() .filter(wo -> world.getGameTime() % wo.getUpdateRateForNetwork() == 0) .forEach(this::syncStormUpdate); /*getStormObjects().stream() .filter(wo -> world.getGameTime() % 2 == 0) .forEach(this::syncStormUpdate);*/ //sync wind if (world.getGameTime() % 60 == 0) { syncWindUpdate(windMan); } /*for (Entity ent : world.getEntities().getAll()) { if (ent instanceof LivingEntity) { ((LivingEntity) ent).addEffect(new MobEffectInstance(MobEffects.SLOW_FALLING, 600, 0, false, false, true)); } }*/ //sim box work int rate = 20; if (world.getGameTime() % rate == 0) { //removal pass for (int i = 0; i < getStormObjects().size(); i++) { WeatherObject so = getStormObjects().get(i); Player closestPlayer = world.getNearestPlayer(so.posGround.x, so.posGround.y, so.posGround.z, ConfigMisc.Misc_simBoxRadiusCutoff, EntitySelector.ENTITY_STILL_ALIVE); if (so instanceof StormObject && ((StormObject) so).isPet()) continue; if (ConfigMisc.Winter_Wonderland && so instanceof WeatherObjectParticleStorm && ((WeatherObjectParticleStorm) so).getType() == WeatherObjectParticleStorm.StormType.SNOWSTORM) continue; //removed check is done in WeatherManagerBase if (closestPlayer == null || ConfigMisc.Aesthetic_Only_Mode) { so.ticksSinceNoNearPlayer += rate; //finally remove if nothing near for 30 seconds, gives multiplayer server a chance to get players in if (so.ticksSinceNoNearPlayer > 20 * 30 || ConfigMisc.Aesthetic_Only_Mode) { if (world.getPlayers(LivingEntity::isAlive).size() == 0) { Weather.dbg("removing distant storm: " + so.ID + ", running without players"); } else { Weather.dbg("removing distant storm: " + so.ID); } removeStormObject(so.ID); syncStormRemove(so); } } else { so.ticksSinceNoNearPlayer = 0; } } /*if (world.getGameTime() % rate == 0) { if (ConfigMisc.Aesthetic_Only_Mode) { getStormObjects().stream().forEach(this::removeWeatherObjectAndSync); } else { //streams are probably not actually ideal for this situation since we need to run code on both states of player presence //and streams work best for just filtering out instead of splitting, and running .stream() 3 times is costly probably //split weather objects into list of ones near player and ones not Map> playersNearWeatherObjects = getStormObjects() .stream() .collect(Collectors.partitioningBy(wo -> world.getNearestPlayer(wo.posGround.x, wo.posGround.y, wo.posGround.z, ConfigMisc.Misc_simBoxRadiusCutoff, EntitySelector.ENTITY_STILL_ALIVE) != null)); //ones near playersNearWeatherObjects.get(true).stream() .forEach(wo -> wo.ticksSinceNoNearPlayer = 0); //ones not near playersNearWeatherObjects.get(false).stream() .peek(wo -> wo.ticksSinceNoNearPlayer += rate) .filter(wo -> wo.ticksSinceNoNearPlayer > 20 * 30) .forEach(this::removeWeatherObjectAndSync); } }*/ Random rand = new Random(); //test with high wind to maximize movement/recycling //cloud data: //0.6: 19-20/20 //0.5: 17-18/20 //0.4: 16-17/20 //0.3: 16-18/20? //0.2: 12/20? /** * max size of cloud sets = 300 radius * sim box size = 1024 radius * * ~9 cloud sets in a player simbox */ //TEMP!!! /*windMan.startHighWindEvent(); cloudIntensity = 0.3F; int countDbg = 0; int countDbg2 = 0; for (StormObject so : getStormObjectsByLayer(0)) { if (!so.isCloudlessStorm()) { countDbg++; } else { countDbg2++; } } System.out.println("cloud/cloudless/max count: " + countDbg + "/" + countDbg2 + "/" + (ConfigStorm.Storm_MaxPerPlayerPerLayer * world.playerEntities.size()));*/ //cloud formation spawning - REFINE ME! boolean spawnClouds = true; if (spawnClouds && !Weather.isLoveTropicsInstalled() && !ConfigMisc.Aesthetic_Only_Mode && WeatherUtilConfig.shouldTickClouds(world.dimension().location().toString())) { for (int i = 0; i < world.players().size(); i++) { Player entP = world.players().get(i); //Weather.dbg("getStormObjects().size(): " + getStormObjects().size()); //layer 0 if (getStormObjects().size() < ConfigStorm.Storm_MaxPerPlayerPerLayer * world.players().size()) { if (rand.nextInt(5) == 0) { //if (rand.nextFloat() <= cloudIntensity) { trySpawnStormCloudNearPlayerForLayer(entP, 0); //} } } //layer 1 /*if (getStormObjectsByLayer(1).size() < ConfigStorm.Storm_MaxPerPlayerPerLayer * world.players().size()) { if (ConfigMisc.Cloud_Layer1_Enable) { if (rand.nextInt(5) == 0) { //if (rand.nextFloat() <= cloudIntensity) { trySpawnStormCloudNearPlayerForLayer(entP, 1); //} } } }*/ } } } //if dimension can have storms, tick sandstorm spawning every 10 seconds if (!Weather.isLoveTropicsInstalled() && WeatherUtilConfig.listDimensionsStorms.contains(world.dimension().location().toString()) && world.getGameTime() % 200 == 0 && windMan.isHighWindEventActive()) { if (!ConfigSand.Storm_NoSandstorms) { tryParticleStorm(world, WeatherObjectParticleStorm.StormType.SANDSTORM); } if (!ConfigSnow.Storm_NoSnowstorms && (!ConfigMisc.Aesthetic_Only_Mode || ConfigMisc.Winter_Wonderland)) { tryParticleStorm(world, WeatherObjectParticleStorm.StormType.SNOWSTORM); } } } } public Optional findWeatherDeflector(BlockPos pos, int range) { double closestDist = Float.MAX_VALUE; BlockPos closestPos = null; for (Map.Entry entrySet : getLookupWeatherBlockDamageDeflector().entrySet()) { double dist = pos.distSqr(entrySet.getValue()); if (dist < range * range) { if (dist < closestDist) { closestDist = dist; closestPos = entrySet.getValue(); } } } if (closestPos != null) { return Optional.of(closestPos); } else { return Optional.empty(); } } public void tickStormBlockBuildup(StormState stormState, Block block) { Level world = getWorld(); WindManager windMan = getWindManager(); Random rand = CoroUtilMisc.random(); float angle = windMan.getWindAngle(null); int rate = 1; int maxStack; if (stormState != null) { rate = stormState.getBuildupTickRate(); maxStack = stormState.getMaxStackable(); } else { maxStack = 4; } if (world.getGameTime() % rate == 0) { List list = Lists.newArrayList(((ServerLevel)world).getChunkSource().chunkMap.getChunks()); Collections.shuffle(list); list.forEach((p_241099_7_) -> { Optional optional = p_241099_7_.getTickingChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK).left(); if (optional.isPresent()) { for (int i = 0; i < 10; i++) { BlockPos blockPos = new BlockPos((optional.get().getPos().x * 16) + rand.nextInt(16), 0, (optional.get().getPos().z * 16) + rand.nextInt(16)); int y = WeatherUtilBlock.getPrecipitationHeightSafe(world, blockPos).getY(); Vec3 pos = new Vec3(blockPos.getX(), y, blockPos.getZ()); WeatherUtilBlock.fillAgainstWallSmoothly(world, pos, angle, 15, 2, block, maxStack); } } }); } } public void syncStormRemove(WeatherObject parStorm) { //packets CompoundTag data = new CompoundTag(); data.putString("packetCommand", "WeatherData"); data.putString("command", "syncStormRemove"); parStorm.nbtSyncForClient(); data.put("data", parStorm.getNbtCache().getNewNBT()); //data.put("data", parStorm.nbtSyncForClient(new NBTTagCompound())); //fix for client having broken states data.getCompound("data").putBoolean("removed", true); //Weather.eventChannel.sendToDimension(PacketHelper.getNBTPacket(data, Weather.eventChannelName), getWorld().getDimension().getType().getId()); WeatherNetworking.HANDLER.send(PacketDistributor.DIMENSION.with(() -> getWorld().dimension()), new PacketNBTFromServer(data)); } public void syncWindUpdate(WindManager parManager) { //packets CompoundTag data = new CompoundTag(); data.putString("packetCommand", "WeatherData"); data.putString("command", "syncWindUpdate"); data.put("data", parManager.nbtSyncForClient()); WeatherNetworking.HANDLER.send(PacketDistributor.DIMENSION.with(() -> getWorld().dimension()), new PacketNBTFromServer(data)); } public void tickWeatherCoverage() { ServerLevel world = (ServerLevel) this.getWorld(); if (world != null) { if (!ConfigMisc.overcastMode) { if (ConfigMisc.lockServerWeatherMode != -1) { world.serverLevelData.setRaining(ConfigMisc.lockServerWeatherMode == 1); world.serverLevelData.setThundering(ConfigMisc.lockServerWeatherMode == 1); } } if (ConfigStorm.preventServerThunderstorms && !ConfigMisc.Aesthetic_Only_Mode) { world.serverLevelData.setThundering(false); } //if (ConfigMisc.overcastMode) { if (world.getGameTime() % 40 == 0) { isVanillaRainActiveOnServer = world.isRaining(); isVanillaThunderActiveOnServer = world.isThundering(); vanillaRainTimeOnServer = world.serverLevelData.getRainTime(); float minRain = 0; float maxRain = 0; if (world.isThundering()) { minRain = 0.3F; maxRain = 1F; } else if (world.isRaining()) { minRain = 0.1F; maxRain = 0.7F; } vanillaRainAmountOnServer = Math.max(minRain, Math.min(maxRain, vanillaRainAmountOnServer + (CoroUtilMisc.random().nextFloat() - CoroUtilMisc.random().nextFloat()) * 0.02F)); //vanillaRainAmountOnServer = 1F; //System.out.println("server precip: " + vanillaRainAmountOnServer); syncWeatherVanilla(); } //} if (world.getGameTime() % 400 == 0) { //Weather.dbg("for dim: " + world.provider.dimensionId + " - is server dimension raining?: " + world.isRaining() + " time: " + world.serverLevelData.getRainTime()); } //tick partial cloud cover variation //windMan.startHighWindEvent(); //windMan.stopLowWindEvent(); //cloudIntensity = 0.3F; if (world.getGameTime() % 200 == 0) { Random rand = new Random(); cloudIntensity += (float)((rand.nextDouble() * ConfigMisc.Cloud_Coverage_Random_Change_Amount) - (rand.nextDouble() * ConfigMisc.Cloud_Coverage_Random_Change_Amount)); if (ConfigMisc.overcastMode && world.isRaining()) { cloudIntensity = 1; } else { if (cloudIntensity < ConfigMisc.Cloud_Coverage_Min_Percent / 100F) { cloudIntensity = (float) ConfigMisc.Cloud_Coverage_Min_Percent / 100F; } else if (cloudIntensity > ConfigMisc.Cloud_Coverage_Max_Percent / 100F) { cloudIntensity = (float) ConfigMisc.Cloud_Coverage_Max_Percent / 100F; } } if (world.getGameTime() % 2000 == 0) { //Weather.dbg("cloudIntensity FORCED MAX: " + cloudIntensity); } //force full cloudIntensity if server side raining //note: storms also revert to clouded storms for same condition } //temp lock to max for fps comparisons //cloudIntensity = 1F; } } public void tryParticleStorm(Level level, WeatherObjectParticleStorm.StormType type) { //boolean stormMade = false; int stormOdds = ConfigSand.Sandstorm_OddsTo1; int timeBetweenTicks = ConfigSand.Sandstorm_TimeBetweenInTicks; long lastStormTime = this.lastSandstormFormed; boolean useGlobalServerRate = ConfigSand.Sandstorm_UseGlobalServerRate; String stormString; if (type == WeatherObjectParticleStorm.StormType.SNOWSTORM) { stormOdds = ConfigSnow.Snowstorm_OddsTo1; stormString = "lastSnowstormTime"; useGlobalServerRate = ConfigSnow.Snowstorm_UseGlobalServerRate; lastStormTime = this.lastSnowstormFormed; } else { stormString = "lastSandstormTime"; } if (stormOdds <= 0 || CoroUtilMisc.random().nextInt(stormOdds) == 0) { if (useGlobalServerRate) { if (lastStormTime == 0 || lastStormTime + timeBetweenTicks < level.getGameTime()) { if (world.players().size() > 0) { Player entP = world.players().get(CoroUtilMisc.random().nextInt(world.players().size())); boolean stormMade = trySpawnParticleStormNearPos(level, entP.position(), type); if (stormMade) { if (type == WeatherObjectParticleStorm.StormType.SANDSTORM) { lastSandstormFormed = world.getGameTime(); } else if (type == WeatherObjectParticleStorm.StormType.SNOWSTORM) { lastSnowstormFormed = world.getGameTime(); } } } } } else { world.players().stream().forEach(player -> { CompoundTag playerNBT = player.getPersistentData(); long lastStormTimePlayer = playerNBT.getLong(stormString); if (lastStormTimePlayer == 0 || lastStormTimePlayer + timeBetweenTicks < level.getGameTime()) { boolean stormMade = trySpawnParticleStormNearPos(player.level(), player.position(), type); if (stormMade) { playerNBT.putLong(stormString, world.getGameTime()); } } }); } } } public boolean trySpawnParticleStormNearPos(Level world, Vec3 posIn, WeatherObjectParticleStorm.StormType type) { return trySpawnParticleStormNearPos(world, posIn, type, false); } public boolean trySpawnParticleStormNearPos(Level world, Vec3 posIn, WeatherObjectParticleStorm.StormType type, boolean force) { /** * 1. Start upwind * 2. Find random spot near there loaded and in desert * 3. scan upwind and downwind, require a good stretch of sand/snow for a storm */ int searchRadius = 64; double angle = getWindManager().getWindAngleForClouds(); //-1 for upwind double dirX = -Math.sin(Math.toRadians(angle)); double dirZ = Math.cos(Math.toRadians(angle)); double vecX = dirX * searchRadius/2 * -1; double vecZ = dirZ * searchRadius/2 * -1; Random rand = new Random(); BlockPos foundPos = null; int findTriesMax = 30; for (int i = 0; i < findTriesMax; i++) { int x = Mth.floor(posIn.x + vecX + rand.nextInt(searchRadius * 2) - searchRadius); int z = Mth.floor(posIn.z + vecZ + rand.nextInt(searchRadius * 2) - searchRadius); BlockPos pos = WeatherUtilBlock.getPrecipitationHeightSafe(world, new BlockPos(x, 0, z)); if (!world.isLoaded(pos)) continue; //Biome biomeIn = world.m_204166_ForCoordsBody(pos); Biome biomeIn = world.getBiome(pos).get(); if (force || WeatherObjectParticleStorm.canSpawnHere(world, pos, type, true)) { //found foundPos = pos; //break; //check left and right about 20 blocks, if its not still desert, force retry double dirXLeft = -Math.sin(Math.toRadians(angle-90)); double dirZLeft = Math.cos(Math.toRadians(angle-90)); double dirXRight = -Math.sin(Math.toRadians(angle+90)); double dirZRight = Math.cos(Math.toRadians(angle+90)); double distLeftRight = 20; BlockPos posLeft = WeatherUtilBlock.getPrecipitationHeightSafe(world, CoroUtilBlock.blockPos(foundPos.getX() + (dirXLeft * distLeftRight), 0, foundPos.getZ() + (dirZLeft * distLeftRight))); if (!world.isLoaded(posLeft)) continue; //if (!WeatherObjectSandstorm.isDesert(world.m_204166_ForCoordsBody(posLeft))) continue; if (!WeatherObjectParticleStorm.canSpawnHere(world, posLeft, type, false)) continue; //if (!WeatherObjectSandstorm.isDesert(world.m_204166_(posLeft).m_203334_())) continue; BlockPos posRight = WeatherUtilBlock.getPrecipitationHeightSafe(world, CoroUtilBlock.blockPos(foundPos.getX() + (dirXRight * distLeftRight), 0, foundPos.getZ() + (dirZRight * distLeftRight))); if (!world.isLoaded(posRight)) continue; //if (!WeatherObjectSandstorm.isDesert(world.m_204166_ForCoordsBody(posRight))) continue; if (!WeatherObjectParticleStorm.canSpawnHere(world, posRight, type, false)) continue; //if (!WeatherObjectSandstorm.isDesert(world.m_204166_(posRight).m_203334_())) continue; //go as far upwind as possible until no desert / unloaded area BlockPos posFind = new BlockPos(foundPos); BlockPos posFindLastGoodUpwind = new BlockPos(foundPos); BlockPos posFindLastGoodDownwind = new BlockPos(foundPos); double tickDist = 10; //while (world.isLoaded(posFind) && WeatherObjectSandstorm.isDesert(world.m_204166_ForCoordsBody(posFind))) { while (world.isLoaded(posFind) && WeatherObjectParticleStorm.canSpawnHere(world, posFind, type, true)) { //tick last good posFindLastGoodUpwind = new BlockPos(posFind); //scan against wind (upwind) int xx = Mth.floor(posFind.getX() + (dirX * -1D * tickDist)); int zz = Mth.floor(posFind.getZ() + (dirZ * -1D * tickDist)); posFind = WeatherUtilBlock.getPrecipitationHeightSafe(world, new BlockPos(xx, 0, zz)); } //reset for downwind scan posFind = new BlockPos(foundPos); //while (world.isLoaded(posFind) && WeatherObjectSandstorm.isDesert(world.m_204166_ForCoordsBody(posFind))) { while (world.isLoaded(posFind) && WeatherObjectParticleStorm.canSpawnHere(world, posFind, type, true)) { //tick last good posFindLastGoodDownwind = new BlockPos(posFind); //scan with wind (downwind) int xx = Mth.floor(posFind.getX() + (dirX * 1D * tickDist)); int zz = Mth.floor(posFind.getZ() + (dirZ * 1D * tickDist)); posFind = WeatherUtilBlock.getPrecipitationHeightSafe(world, new BlockPos(xx, 0, zz)); } int minDistanceOfDesertStretchNeeded = 20; double dist = posFindLastGoodUpwind.distSqr(posFindLastGoodDownwind); if (force || dist >= minDistanceOfDesertStretchNeeded * minDistanceOfDesertStretchNeeded) { if (ConfigMisc.Winter_Wonderland && type == WeatherObjectParticleStorm.StormType.SNOWSTORM) { //spawn it right on the player so they are guaranteed to see it, might want to do this anyways in future since sandstorm changes spawnParticleStorm(CoroUtilBlock.blockPos(posIn), type); } else { spawnParticleStorm(posFindLastGoodUpwind, type); } Weather.dbg("found decent spot and stretch for particle storm, stretch: " + dist + ", type: " + type); return true; } } } Weather.dbg("couldnt spawn particle storm"); return false; } public void spawnParticleStorm(BlockPos pos, WeatherObjectParticleStorm.StormType type) { WeatherObjectParticleStorm storm = new WeatherObjectParticleStorm(this); storm.setType(type); storm.initFirstTime(); BlockPos posSpawn = new BlockPos(WeatherUtilBlock.getPrecipitationHeightSafe(world, pos)).above(); storm.initStormSpawn(new Vec3(posSpawn.getX(), posSpawn.getY(), posSpawn.getZ())); addStormObject(storm); syncStormNew(storm); } public void trySpawnStormCloudNearPlayerForLayer(Player entP, int layer) { //if (true) return; Random rand = new Random(); int tryCountMax = 10; int tryCountCur = 0; int spawnX = -1; int spawnZ = -1; Vec3 tryPos = null; StormObject soClose = null; Player playerClose = null; int closestToPlayer = 128; //use 256 or the cutoff val if its configured small float windOffsetDist = Math.min(256, ConfigMisc.Misc_simBoxRadiusCutoff / 4 * 3); double angle = getWindManager().getWindAngleForClouds(); double vecX = -Math.sin(Math.toRadians(angle)) * windOffsetDist; double vecZ = Math.cos(Math.toRadians(angle)) * windOffsetDist; while (tryCountCur++ == 0 || (tryCountCur < tryCountMax && (soClose != null || playerClose != null))) { spawnX = (int) (entP.getX() - vecX + rand.nextInt(ConfigMisc.Misc_simBoxRadiusSpawn) - rand.nextInt(ConfigMisc.Misc_simBoxRadiusSpawn)); spawnZ = (int) (entP.getZ() - vecZ + rand.nextInt(ConfigMisc.Misc_simBoxRadiusSpawn) - rand.nextInt(ConfigMisc.Misc_simBoxRadiusSpawn)); tryPos = new Vec3(spawnX, StormObject.layers.get(layer), spawnZ); soClose = getClosestStormAny(tryPos, ConfigMisc.Cloud_Formation_MinDistBetweenSpawned); playerClose = entP.level().getNearestPlayer(spawnX, 50, spawnZ, closestToPlayer, false); } if (soClose == null) { //Weather.dbg("spawning storm at: " + spawnX + " - " + spawnZ); StormObject so = new StormObject(this); so.pos = tryPos; so.layer = layer; so.initFirstTime(); //make only layer 0 produce deadly storms if (layer != 0) { so.canBeDeadly = false; } so.spawnerUUID = entP.getStringUUID(); if (rand.nextFloat() >= cloudIntensity) { so.setCloudlessStorm(true); } addStormObject(so); syncStormNew(so); } else { //Weather.dbg("couldnt find space to spawn cloud formation"); } } public void playerJoinedWorldSyncFull(ServerPlayer entP) { Weather.dbg("Weather2: playerJoinedWorldSyncFull for dim: " + dimension); Level world = getWorld(); if (world != null) { Weather.dbg("Weather2: playerJoinedWorldSyncFull, sending " + getStormObjects().size() + " weather objects to: " + entP.getName() + ", dim: " + dimension); //sync storms for (int i = 0; i < getStormObjects().size(); i++) { syncStormNew(getStormObjects().get(i), entP); } } } //populate data with rain storms and deadly storms /*public void nbtStormsForIMC() { CompoundTag data = new CompoundTag(); for (int i = 0; i < getStormObjects().size(); i++) { WeatherObject wo = getStormObjects().get(i); if (wo instanceof StormObject) { StormObject so = (StormObject) wo; if (so.levelCurIntensityStage > 0 || so.attrib_precipitation) { CompoundTag nbtStorm = so.nbtForIMC(); data.put("storm_" + so.ID, nbtStorm); } } } if (!data.hasNoTags()) { FMLInterModComms.sendRuntimeMessage(Weather.instance, Weather.MODID, "weather.storms", data); } }*/ public void syncLightningNew(Entity parEnt, boolean custom) { CompoundTag data = new CompoundTag(); data.putString("packetCommand", "WeatherData"); data.putString("command", "syncLightningNew"); CompoundTag nbt = new CompoundTag(); nbt.putInt("posX", Mth.floor(parEnt.getX())); nbt.putInt("posY", Mth.floor(parEnt.getY())); nbt.putInt("posZ", Mth.floor(parEnt.getZ())); nbt.putInt("entityID", parEnt.getId()); nbt.putBoolean("custom", custom); data.put("data", nbt); WeatherNetworking.HANDLER.send(PacketDistributor.DIMENSION.with(() -> getWorld().dimension()), new PacketNBTFromServer(data)); } public void syncBlockParticleNew(BlockPos pos, BlockState state, WeatherObject owner) { CompoundTag data = new CompoundTag(); data.putString("packetCommand", "WeatherData"); data.putString("command", "syncBlockParticleNew"); CompoundTag nbt = new CompoundTag(); nbt.putInt("posX", pos.getX()); nbt.putInt("posY", pos.getY()); nbt.putInt("posZ", pos.getZ()); nbt.put("blockstate", NbtUtils.writeBlockState(state)); nbt.putLong("ownerID", owner.ID); data.put("data", nbt); WeatherNetworking.HANDLER.send(PacketDistributor.DIMENSION.with(() -> getWorld().dimension()), new PacketNBTFromServer(data)); } public void syncStormNew(WeatherObject parStorm) { syncStormNew(parStorm, null); } public void syncStormNew(WeatherObject parStorm, @Nullable ServerPlayer entP) { CompoundTag data = new CompoundTag(); data.putString("packetCommand", "WeatherData"); data.putString("command", "syncStormNew"); CachedNBTTagCompound cache = parStorm.getNbtCache(); cache.setUpdateForced(true); parStorm.nbtSyncForClient(); cache.setUpdateForced(false); data.put("data", cache.getNewNBT()); if (entP == null) { WeatherNetworking.HANDLER.send(PacketDistributor.DIMENSION.with(() -> getWorld().dimension()), new PacketNBTFromServer(data)); } else { WeatherNetworking.HANDLER.sendTo(new PacketNBTFromServer(data), entP.connection.connection, NetworkDirection.PLAY_TO_CLIENT); } } public void syncStormUpdate(WeatherObject parStorm) { //packets CompoundTag data = new CompoundTag(); data.putString("packetCommand", "WeatherData"); data.putString("command", "syncStormUpdate"); parStorm.getNbtCache().setNewNBT(new CompoundTag()); parStorm.nbtSyncForClient(); data.put("data", parStorm.getNbtCache().getNewNBT()); boolean testNetworkData = false; if (testNetworkData) { System.out.println("sending to client: " + parStorm.getNbtCache().getNewNBT().getAllKeys().size()); if (parStorm instanceof StormObject) { System.out.println("Real: " + ((StormObject) parStorm).levelCurIntensityStage); if (parStorm.getNbtCache().getNewNBT().contains("levelCurIntensityStage")) { System.out.println(" vs " + parStorm.getNbtCache().getNewNBT().getInt("levelCurIntensityStage")); } else { System.out.println("no key!"); } } Iterator iterator = parStorm.getNbtCache().getNewNBT().getAllKeys().iterator(); String keys = ""; while (iterator.hasNext()) { keys = keys.concat((String) iterator.next() + "; "); } System.out.println("sending " + keys); } WeatherNetworking.HANDLER.send(PacketDistributor.DIMENSION.with(() -> getWorld().dimension()), new PacketNBTFromServer(data)); } public void syncWeatherVanilla() { CompoundTag data = new CompoundTag(); data.putString("packetCommand", "WeatherData"); data.putString("command", "syncWeatherUpdate"); data.putBoolean("isVanillaRainActiveOnServer", isVanillaRainActiveOnServer); data.putBoolean("isVanillaThunderActiveOnServer", isVanillaThunderActiveOnServer); data.putInt("vanillaRainTimeOnServer", vanillaRainTimeOnServer); data.putFloat("vanillaRainAmountOnServer", vanillaRainAmountOnServer); WeatherNetworking.HANDLER.send(PacketDistributor.DIMENSION.with(() -> getWorld().dimension()), new PacketNBTFromServer(data)); } public void removeWeatherObjectAndSync(WeatherObject parStorm) { //because stream()s if (parStorm == null) { return; } if (getWorld().players().size() == 0) { Weather.dbg("removing distant storm: " + parStorm.ID + ", running without players"); } else { Weather.dbg("removing distant storm: " + parStorm.ID); } removeStormObject(parStorm.ID); syncStormRemove(parStorm); } public void clearAllStorms() { Iterator it = getStormObjects().iterator(); while (it.hasNext()) { WeatherObject so = it.next(); //removeStormObject(so.ID); so.remove(); syncStormRemove(so); } getStormObjects().clear(); lookupStormObjectsByID.clear(); } /** * @param posCenter * @return value between 0 and 1, 0 = no chance, 1 = high chance */ public float getBiomeBasedStormSpawnChanceInArea(BlockPos posCenter) { int scanResolution = 64; float samples = 0; float allTemperaturesAdded = 0; /** * The closer to 0 allTemperaturesAdded is the more likely storms can spawn, the closer it is to samples or -samples the less likely storms can spawn * 0 = found equal amount of warm and cold biomes, great env for spawning * negative or positive sample count = found either only warm or only cold */ for (int x = -ConfigMisc.Misc_simBoxRadiusSpawn; x <= ConfigMisc.Misc_simBoxRadiusSpawn; x += scanResolution) { for (int z = -ConfigMisc.Misc_simBoxRadiusSpawn; z <= ConfigMisc.Misc_simBoxRadiusSpawn; z += scanResolution) { BlockPos pos = new BlockPos(posCenter.getX() + x, posCenter.getY(), posCenter.getZ() + z); if (getWorld().isLoaded(pos)) { pos = WeatherUtilBlock.getPrecipitationHeightSafe(getWorld(), pos); Biome bgb = getWorld().getBiome(pos).get(); allTemperaturesAdded += StormObject.getTemperatureMCToWeatherSys(CoroUtilCompatibility.getAdjustedTemperature(getWorld(), bgb, pos)); samples++; } } } CULog.dbg("samples: " + samples); CULog.dbg("allTemperaturesAdded: " + allTemperaturesAdded); float chance = 1 - (Math.abs(allTemperaturesAdded) / samples); return chance; } } ================================================ FILE: src/main/java/weather2/weathersystem/fog/FogAdjuster.java ================================================ package weather2.weathersystem.fog; import com.corosus.coroutil.util.CULog; import net.minecraft.util.Mth; import net.minecraftforge.client.event.ViewportEvent; import org.joml.Vector3f; import weather2.datatypes.WeatherEventType; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.FogRenderer; import net.minecraft.world.entity.player.Player; import weather2.ClientTickHandler; import weather2.ClientWeatherProxy; import weather2.client.SceneEnhancer; import weather2.util.WeatherUtilEntity; import java.util.Random; public class FogAdjuster { private FogProfile fogHeatwave; private FogProfile fogSandstorm; private FogProfile fogSnowstorm; //initial values arent really used for this one, just used to store dynamically updated values for smooth transitions private FogProfile fogVanilla; private FogProfile targetProfile; private FogProfile activeProfile; private FogProfile activeProfileLerps; private int lerpTicksCur = 20 * 15; private int lerpTicksMax = 20 * 15; //reinit fog values when changes private boolean useFarFog = false; public static WeatherEventType lastWeatherType = null; public int randDelay = 0; private boolean firstUseInit = true; /** * * new fog adjust way: * when theres a new request to change the state * - set old state to prev state * - for each thing we have to fade (each color, density) * -- calculate a lerp rate so they all take the same amount of time? * - actually, how do we want to decide on how long itll take? we might want that dynamic * - maybe average it based on the distance between each color, so like, white to black and far dist fog change = long * - but white to grey with not much fog dist change = short * * - we could let color and dist change at diff rates * - important the rgbs transition the same, for obvious reasons * * important note: activeProfile will now be updated with current actual vals * - so when we get interrupted, we actually have last state we were at * * - never change active profile, just set a new target and rates * * - monitor vanilla color/fog changes and i guess push a new target? should be fine, will be slow jank with my static 100 tick update for now */ public FogAdjuster() { initProfiles(false); activeProfile = new FogProfile(fogVanilla); targetProfile = fogVanilla; activeProfileLerps = new FogProfile(new Vector3f(0F, 0F, 0F), 0, 0); } public void initProfiles(boolean spectator) { float distAmp = 1F; if (spectator) { distAmp = 4F; } fogHeatwave = new FogProfile(new Vector3f(0.5F, 0.2F, 0.1F), 0, 75); fogSandstorm = new FogProfile(new Vector3f(0.7F, 0.5F, 0.2F), 0, 18 * distAmp); fogSnowstorm = new FogProfile(new Vector3f(0.7F, 0.7F, 0.7F), 0, 20 * distAmp); fogVanilla = new FogProfile(new Vector3f(-1F, -1F, -1F), -1, -1); } public void tickGame(ClientWeatherProxy weather) { updateWeatherState(); boolean fogDisco = false; if (fogDisco) { //if (lastWeatherType != null) { if (randDelay <= 0) { Random rand = new Random(); randDelay = 20 + rand.nextInt(5); startRandom(); } //} randDelay--; } if ((SceneEnhancer.getWeatherState() == WeatherEventType.SANDSTORM || SceneEnhancer.getWeatherState() == WeatherEventType.SNOWSTORM)) { Player player = Minecraft.getInstance().player; //use non cached version of isPlayerOutside to fix data mismatch that is timing crucial here boolean isPlayerOutside = WeatherUtilEntity.isEntityOutside(player); boolean playerOutside = isPlayerOutside || player.isInWater(); boolean setFogFar = !playerOutside || player.isSpectator(); /*CULog.dbg("set to far mode?: " + setFogFar); CULog.dbg("playerOutside: " + SceneEnhancer.isPlayerOutside); CULog.dbg("isInWater: " + player.isInWater()); CULog.dbg("setFogFar: " + setFogFar);*/ if (player != null) { if ((setFogFar && !useFarFog) || !setFogFar && useFarFog) { initProfiles(setFogFar); if (SceneEnhancer.getWeatherState() == WeatherEventType.SANDSTORM) { startSandstorm(); } else if (SceneEnhancer.getWeatherState() == WeatherEventType.SNOWSTORM) { startSnowstorm(); } } useFarFog = setFogFar; } } if (lerpTicksCur < lerpTicksMax) { float newLerpX = activeProfile.getRgb().x() + activeProfileLerps.getRgb().x(); float newLerpY = activeProfile.getRgb().y() + activeProfileLerps.getRgb().y(); float newLerpZ = activeProfile.getRgb().z() + activeProfileLerps.getRgb().z(); activeProfile.getRgb().set(newLerpX, newLerpY, newLerpZ); activeProfile.setFogStart(activeProfile.getFogStart() + activeProfileLerps.getFogStart()); activeProfile.setFogEnd(activeProfile.getFogEnd() + activeProfileLerps.getFogEnd()); activeProfile.setFogStartSky(activeProfile.getFogStartSky() + activeProfileLerps.getFogStartSky()); activeProfile.setFogEndSky(activeProfile.getFogEndSky() + activeProfileLerps.getFogEndSky()); lerpTicksCur++; //System.out.println(lerpTicksCur + " - " + activeProfile.getFogStart() + " - " + activeProfile.getFogEnd()); } } public void onFogColors(ViewportEvent.ComputeFogColor event) { updateWeatherState(); //get vanilla settings fogVanilla.getRgb().set(event.getRed(), event.getGreen(), event.getBlue()); if (SceneEnhancer.isFogOverridding()) { float brightness = Mth.clamp(Mth.cos(Minecraft.getInstance().level.getTimeOfDay(1F) * ((float)Math.PI * 2F)) * 2.0F + 0.5F, 0.0F, 1.0F); event.setRed(activeProfile.getRgb().x() * brightness); event.setGreen(activeProfile.getRgb().y() * brightness); event.setBlue(activeProfile.getRgb().z() * brightness); } } public void onFogRender(ViewportEvent.RenderFog event) { updateWeatherState(); //get vanilla settings if (event.getMode() == FogRenderer.FogMode.FOG_SKY) { fogVanilla.setFogStartSky(event.getNearPlaneDistance()); fogVanilla.setFogEndSky(event.getFarPlaneDistance()); } else { fogVanilla.setFogStart(event.getNearPlaneDistance()); fogVanilla.setFogEnd(event.getFarPlaneDistance()); } if (SceneEnhancer.isFogOverridding()) { if (event.getMode() == FogRenderer.FogMode.FOG_SKY) { event.setNearPlaneDistance(activeProfile.getFogStartSky()); event.setFarPlaneDistance(activeProfile.getFogEndSky()); event.setCanceled(true); } else { event.setNearPlaneDistance(activeProfile.getFogStart()); event.setFarPlaneDistance(activeProfile.getFogEnd()); event.setCanceled(true); } } } public void startRandom() { Random rand = new Random(); int randFog = 0; if (activeProfile.getFogEnd() < 50) { randFog = 50 + rand.nextInt(50); } else { randFog = rand.nextInt(50); } randFog = rand.nextInt(100); targetProfile = new FogProfile(new Vector3f(rand.nextFloat(), rand.nextFloat(), rand.nextFloat()), 0, randFog); lerpTicksMax = 20 + rand.nextInt(50); setupNewLerpRates(); CULog.dbg("startRandom: " + randFog); } public void startHeatwave() { CULog.dbg("startHeatwave"); //activeProfile = targetProfile; targetProfile = new FogProfile(fogHeatwave); setupNewLerpRates(); } public void startSandstorm() { CULog.dbg("startSandstorm"); //activeProfile = targetProfile; targetProfile = new FogProfile(fogSandstorm); setupNewLerpRates(); } public void startSnowstorm() { CULog.dbg("startSnowstorm"); //activeProfile = targetProfile; targetProfile = new FogProfile(fogSnowstorm); setupNewLerpRates(); } public void restoreVanilla() { CULog.dbg("restoreVanilla"); //activeProfile = targetProfile; targetProfile = new FogProfile(fogVanilla); setupNewLerpRates(); } public void setupNewLerpRates() { if (firstUseInit) { //if we've correctly set the starting vanilla fog values for both event states if (fogVanilla.getFogEnd() != -1 && fogVanilla.getFogEndSky() != -1) { activeProfile = new FogProfile(fogVanilla); firstUseInit = false; } } lerpTicksCur = 0; float partialLerpX = getLerpRate(activeProfile.getRgb().x(), targetProfile.getRgb().x(), lerpTicksMax); float partialLerpY = getLerpRate(activeProfile.getRgb().y(), targetProfile.getRgb().y(), lerpTicksMax); float partialLerpZ = getLerpRate(activeProfile.getRgb().z(), targetProfile.getRgb().z(), lerpTicksMax); activeProfileLerps.getRgb().set(partialLerpX, partialLerpY, partialLerpZ); activeProfileLerps.setFogStart(getLerpRate(activeProfile.getFogStart(), targetProfile.getFogStart(), lerpTicksMax)); activeProfileLerps.setFogEnd(getLerpRate(activeProfile.getFogEnd(), targetProfile.getFogEnd(), lerpTicksMax)); activeProfileLerps.setFogStartSky(getLerpRate(activeProfile.getFogStartSky(), targetProfile.getFogStartSky(), lerpTicksMax)); activeProfileLerps.setFogEndSky(getLerpRate(activeProfile.getFogEndSky(), targetProfile.getFogEndSky(), lerpTicksMax)); } public float getLerpRate(float curVal, float endVal, float fullLerpTicks) { return (endVal - curVal) / fullLerpTicks; } public boolean isFogOverriding() { ClientTickHandler.getClientWeather(); ClientWeatherProxy weather = ClientWeatherProxy.get(); return (weather.isHeatwave() || weather.isSandstorm() || weather.isSnowstorm()) || lerpTicksCur < lerpTicksMax; } /** * In its own method so quick render update calls can force an update check to prevent old data use which causes flickers */ public void updateWeatherState() { WeatherEventType curWeather = SceneEnhancer.getWeatherState(); //System.out.println("curWeather: " + curWeather); //System.out.println("lastWeatherType: " + lastWeatherType); /*if (curWeather != WeatherEventType.SANDSTORM && curWeather != WeatherEventType.SNOWSTORM && curWeather != WeatherEventType.HEATWAVE && curWeather != null) { return; }*/ //count ones we dont want fog for as null, to keep the transitions clean and less glitchy if (curWeather == WeatherEventType.ACID_RAIN || curWeather == WeatherEventType.HEAVY_RAIN || curWeather == WeatherEventType.HAIL) { curWeather = null; } boolean match = false; if (curWeather != lastWeatherType) { if (curWeather == WeatherEventType.SANDSTORM) { startSandstorm(); match = true; } else if (curWeather == WeatherEventType.SNOWSTORM) { startSnowstorm(); match = true; } else if (curWeather == WeatherEventType.HEATWAVE) { startHeatwave(); match = true; } else if (curWeather == null) { restoreVanilla(); match = true; } } if (match) { lastWeatherType = curWeather; } } /** * 0 = off * 1 = max on * @return */ public float getLerpFraction() { if (lerpTicksMax == 0) return 0; return lerpTicksCur / lerpTicksMax; } } ================================================ FILE: src/main/java/weather2/weathersystem/fog/FogProfile.java ================================================ package weather2.weathersystem.fog; import org.joml.Vector3f; public class FogProfile { private Vector3f rgb; private float fogStart; private float fogEnd; private float fogStartSky; private float fogEndSky; public FogProfile(FogProfile profile) { this.rgb = new Vector3f(profile.rgb.x(), profile.rgb.y(), profile.rgb.z()); this.fogStart = profile.fogStart; this.fogStartSky = profile.fogStartSky; this.fogEnd = profile.fogEnd; this.fogEndSky = profile.fogEndSky; } public FogProfile(Vector3f rgb, float fogStart, float fogEnd) { this.rgb = new Vector3f(rgb.x(), rgb.y(), rgb.z());; this.fogStart = fogStart; this.fogStartSky = fogStart; this.fogEnd = fogEnd; this.fogEndSky = fogEnd; } public Vector3f getRgb() { return rgb; } public void setRgb(Vector3f rgb) { this.rgb = rgb; } public float getFogStart() { return fogStart; } public void setFogStart(float fogStart) { this.fogStart = fogStart; } public float getFogEnd() { return fogEnd; } public void setFogEnd(float fogEnd) { this.fogEnd = fogEnd; } public float getFogStartSky() { return fogStartSky; } public void setFogStartSky(float fogStartSky) { this.fogStartSky = fogStartSky; } public float getFogEndSky() { return fogEndSky; } public void setFogEndSky(float fogEndSky) { this.fogEndSky = fogEndSky; } } ================================================ FILE: src/main/java/weather2/weathersystem/storm/EnumWeatherObjectType.java ================================================ package weather2.weathersystem.storm; import java.util.EnumSet; import java.util.HashMap; import java.util.Map; public enum EnumWeatherObjectType { CLOUD, SAND, SNOW; private static final Map lookup = new HashMap(); static { for(EnumWeatherObjectType e : EnumSet.allOf(EnumWeatherObjectType.class)) { lookup.put(e.ordinal(), e); } } public static EnumWeatherObjectType get(int intValue) { return lookup.get(intValue); } } ================================================ FILE: src/main/java/weather2/weathersystem/storm/LightningBoltWeather.java ================================================ package weather2.weathersystem.storm; import com.corosus.coroutil.util.CoroUtilBlock; import com.google.common.collect.Sets; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Stream; import javax.annotation.Nullable; import net.minecraft.advancements.CriteriaTriggers; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ClientboundAddEntityPacket; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.world.Difficulty; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import net.minecraft.world.level.GameRules; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.BaseFireBlock; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.LightningRodBlock; import net.minecraft.world.level.block.WeatheringCopper; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; public class LightningBoltWeather extends Entity { private static final int START_LIFE = 2; private static final double DAMAGE_RADIUS = 3.0D; private static final double DETECTION_RADIUS = 15.0D; private int life; public long seed; private int flashes; private boolean visualOnly; @Nullable private ServerPlayer cause; private final Set hitEntities = Sets.newHashSet(); private int blocksSetOnFire; private float damage = 5.0F; public LightningBoltWeather(EntityType p_20865_, Level p_20866_) { super(p_20865_, p_20866_); this.noCulling = true; this.life = 2; this.seed = this.random.nextLong(); this.flashes = this.random.nextInt(3) + 1; } public LightningBoltWeather(EntityType p_20865_, Level p_20866_, double x, double y, double z) { this(p_20865_, p_20866_); this.setPos(x, y, z); } public void setVisualOnly(boolean p_20875_) { this.visualOnly = p_20875_; } public SoundSource getSoundSource() { return SoundSource.WEATHER; } @Nullable public ServerPlayer getCause() { return this.cause; } public void setCause(@Nullable ServerPlayer p_20880_) { this.cause = p_20880_; } private void powerLightningRod() { BlockPos blockpos = this.getStrikePosition(); BlockState blockstate = this.level().getBlockState(blockpos); if (blockstate.is(Blocks.LIGHTNING_ROD)) { ((LightningRodBlock)blockstate.getBlock()).onLightningStrike(blockstate, this.level(), blockpos); } } public void setDamage(float damage) { this.damage = damage; } public float getDamage() { return this.damage; } public void tick() { super.tick(); if (this.life == 2) { if (this.level().isClientSide()) { this.level().playLocalSound(this.getX(), this.getY(), this.getZ(), SoundEvents.LIGHTNING_BOLT_THUNDER, SoundSource.WEATHER, 10000.0F, 0.8F + this.random.nextFloat() * 0.2F, false); this.level().playLocalSound(this.getX(), this.getY(), this.getZ(), SoundEvents.LIGHTNING_BOLT_IMPACT, SoundSource.WEATHER, 2.0F, 0.5F + this.random.nextFloat() * 0.2F, false); } else { Difficulty difficulty = this.level().getDifficulty(); if (difficulty == Difficulty.NORMAL || difficulty == Difficulty.HARD) { this.spawnFire(4); } this.powerLightningRod(); clearCopperOnLightningStrike(this.level(), this.getStrikePosition()); this.gameEvent(GameEvent.LIGHTNING_STRIKE); } } --this.life; if (this.life < 0) { if (this.flashes == 0) { this.discard(); } else if (this.life < -this.random.nextInt(10)) { --this.flashes; this.life = 1; this.seed = this.random.nextLong(); this.spawnFire(0); } } if (this.life >= 0) { if (!(this.level() instanceof ServerLevel)) { this.level().setSkyFlashTime(2); } else if (!this.visualOnly) { List list1 = this.level().getEntities(this, new AABB(this.getX() - 3.0D, this.getY() - 3.0D, this.getZ() - 3.0D, this.getX() + 3.0D, this.getY() + 6.0D + 3.0D, this.getZ() + 3.0D), Entity::isAlive); /*for(Entity entity : list1) { if (!net.minecraftforge.event.ForgeEventFactory.onEntityStruckByLightning(entity, this)) entity.thunderHit((ServerLevel)this.level, this); }*/ this.hitEntities.addAll(list1); if (this.cause != null) { CriteriaTriggers.CHANNELED_LIGHTNING.trigger(this.cause, list1); } } } } private BlockPos getStrikePosition() { Vec3 vec3 = this.position(); return CoroUtilBlock.blockPos(vec3.x, vec3.y - 1.0E-6D, vec3.z); } private void spawnFire(int p_20871_) { if (!this.visualOnly && !this.level().isClientSide && this.level().getGameRules().getBoolean(GameRules.RULE_DOFIRETICK)) { BlockPos blockpos = this.blockPosition(); BlockState blockstate = BaseFireBlock.getState(this.level(), blockpos); if (this.level().getBlockState(blockpos).isAir() && blockstate.canSurvive(this.level(), blockpos)) { this.level().setBlockAndUpdate(blockpos, blockstate); ++this.blocksSetOnFire; } for(int i = 0; i < p_20871_; ++i) { BlockPos blockpos1 = blockpos.offset(this.random.nextInt(3) - 1, this.random.nextInt(3) - 1, this.random.nextInt(3) - 1); blockstate = BaseFireBlock.getState(this.level(), blockpos1); if (this.level().getBlockState(blockpos1).isAir() && blockstate.canSurvive(this.level(), blockpos1)) { this.level().setBlockAndUpdate(blockpos1, blockstate); ++this.blocksSetOnFire; } } } } private static void clearCopperOnLightningStrike(Level p_147151_, BlockPos p_147152_) { BlockState blockstate = p_147151_.getBlockState(p_147152_); BlockPos blockpos; BlockState blockstate1; if (blockstate.is(Blocks.LIGHTNING_ROD)) { blockpos = p_147152_.relative(blockstate.getValue(LightningRodBlock.FACING).getOpposite()); blockstate1 = p_147151_.getBlockState(blockpos); } else { blockpos = p_147152_; blockstate1 = blockstate; } if (blockstate1.getBlock() instanceof WeatheringCopper) { p_147151_.setBlockAndUpdate(blockpos, WeatheringCopper.getFirst(p_147151_.getBlockState(blockpos))); BlockPos.MutableBlockPos blockpos$mutableblockpos = p_147152_.mutable(); int i = p_147151_.random.nextInt(3) + 3; for(int j = 0; j < i; ++j) { int k = p_147151_.random.nextInt(8) + 1; randomWalkCleaningCopper(p_147151_, blockpos, blockpos$mutableblockpos, k); } } } private static void randomWalkCleaningCopper(Level p_147146_, BlockPos p_147147_, BlockPos.MutableBlockPos p_147148_, int p_147149_) { p_147148_.set(p_147147_); for(int i = 0; i < p_147149_; ++i) { Optional optional = randomStepCleaningCopper(p_147146_, p_147148_); if (!optional.isPresent()) { break; } p_147148_.set(optional.get()); } } private static Optional randomStepCleaningCopper(Level p_147154_, BlockPos p_147155_) { for(BlockPos blockpos : BlockPos.randomInCube(p_147154_.random, 10, p_147155_, 1)) { BlockState blockstate = p_147154_.getBlockState(blockpos); if (blockstate.getBlock() instanceof WeatheringCopper) { WeatheringCopper.getPrevious(blockstate).ifPresent((p_147144_) -> { p_147154_.setBlockAndUpdate(blockpos, p_147144_); }); p_147154_.levelEvent(3002, blockpos, -1); return Optional.of(blockpos); } } return Optional.empty(); } public boolean shouldRenderAtSqrDistance(double p_20869_) { double d0 = 64.0D * getViewScale(); return p_20869_ < d0 * d0; } protected void defineSynchedData() { } protected void readAdditionalSaveData(CompoundTag p_20873_) { } protected void addAdditionalSaveData(CompoundTag p_20877_) { } public int getBlocksSetOnFire() { return this.blocksSetOnFire; } public Stream getHitEntities() { return this.hitEntities.stream().filter(Entity::isAlive); } } ================================================ FILE: src/main/java/weather2/weathersystem/storm/LightningBoltWeatherNew.java ================================================ package weather2.weathersystem.storm; import com.corosus.coroutil.util.CoroUtilBlock; import com.google.common.collect.Sets; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Stream; import javax.annotation.Nullable; import net.minecraft.advancements.CriteriaTriggers; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ClientboundAddEntityPacket; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.world.Difficulty; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import net.minecraft.world.level.GameRules; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.BaseFireBlock; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.LightningRodBlock; import net.minecraft.world.level.block.WeatheringCopper; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; public class LightningBoltWeatherNew extends Entity { private static final int START_LIFE = 2; private static final double DAMAGE_RADIUS = 3.0D; private static final double DETECTION_RADIUS = 15.0D; private int life; public long seed; private int flashes; private boolean visualOnly; @Nullable private ServerPlayer cause; private final Set hitEntities = Sets.newHashSet(); private int blocksSetOnFire; private float damage = 5.0F; public LightningBoltWeatherNew(EntityType p_20865_, Level p_20866_) { super(p_20865_, p_20866_); this.noCulling = true; this.life = 2; this.seed = this.random.nextLong(); this.flashes = this.random.nextInt(3) + 1; } public void setVisualOnly(boolean p_20875_) { this.visualOnly = p_20875_; } public SoundSource getSoundSource() { return SoundSource.WEATHER; } @Nullable public ServerPlayer getCause() { return this.cause; } public void setCause(@Nullable ServerPlayer p_20880_) { this.cause = p_20880_; } private void powerLightningRod() { BlockPos blockpos = this.getStrikePosition(); BlockState blockstate = this.level().getBlockState(blockpos); if (blockstate.is(Blocks.LIGHTNING_ROD)) { ((LightningRodBlock)blockstate.getBlock()).onLightningStrike(blockstate, this.level(), blockpos); } } public void setDamage(float damage) { this.damage = damage; } public float getDamage() { return this.damage; } public void tick() { super.tick(); if (this.life == 2) { if (this.level().isClientSide()) { this.level().playLocalSound(this.getX(), this.getY(), this.getZ(), SoundEvents.LIGHTNING_BOLT_THUNDER, SoundSource.WEATHER, 10000.0F, 0.8F + this.random.nextFloat() * 0.2F, false); this.level().playLocalSound(this.getX(), this.getY(), this.getZ(), SoundEvents.LIGHTNING_BOLT_IMPACT, SoundSource.WEATHER, 2.0F, 0.5F + this.random.nextFloat() * 0.2F, false); } else { /*Difficulty difficulty = this.level().getDifficulty(); if (difficulty == Difficulty.NORMAL || difficulty == Difficulty.HARD) { this.spawnFire(4); }*/ this.powerLightningRod(); clearCopperOnLightningStrike(this.level(), this.getStrikePosition()); this.gameEvent(GameEvent.LIGHTNING_STRIKE); } } --this.life; if (this.life < 0) { if (this.flashes == 0) { if (this.level() instanceof ServerLevel) { /*List list = this.level().getEntities(this, new AABB(this.getX() - 15.0D, this.getY() - 15.0D, this.getZ() - 15.0D, this.getX() + 15.0D, this.getY() + 6.0D + 15.0D, this.getZ() + 15.0D), (p_147140_) -> { return p_147140_.isAlive() && !this.hitEntities.contains(p_147140_); }); for(ServerPlayer serverplayer : ((ServerLevel)this.level()).getPlayers((p_147157_) -> { return p_147157_.distanceTo(this) < 256.0F; })) { CriteriaTriggers.LIGHTNING_STRIKE.trigger(serverplayer, this, list); }*/ } this.discard(); } else if (this.life < -this.random.nextInt(10)) { --this.flashes; this.life = 1; this.seed = this.random.nextLong(); /*this.spawnFire(0);*/ } } if (this.life >= 0) { if (!(this.level() instanceof ServerLevel)) { this.level().setSkyFlashTime(2); } else if (!this.visualOnly) { List list1 = this.level().getEntities(this, new AABB(this.getX() - 3.0D, this.getY() - 3.0D, this.getZ() - 3.0D, this.getX() + 3.0D, this.getY() + 6.0D + 3.0D, this.getZ() + 3.0D), Entity::isAlive); /*for(Entity entity : list1) { if (!net.minecraftforge.event.ForgeEventFactory.onEntityStruckByLightning(entity, this)) entity.thunderHit((ServerLevel)this.level(), this); }*/ this.hitEntities.addAll(list1); if (this.cause != null) { CriteriaTriggers.CHANNELED_LIGHTNING.trigger(this.cause, list1); } } } } private BlockPos getStrikePosition() { Vec3 vec3 = this.position(); return CoroUtilBlock.blockPos(vec3.x, vec3.y - 1.0E-6D, vec3.z); } private void spawnFire(int p_20871_) { if (!this.visualOnly && !this.level().isClientSide && this.level().getGameRules().getBoolean(GameRules.RULE_DOFIRETICK)) { BlockPos blockpos = this.blockPosition(); BlockState blockstate = BaseFireBlock.getState(this.level(), blockpos); if (this.level().getBlockState(blockpos).isAir() && blockstate.canSurvive(this.level(), blockpos)) { this.level().setBlockAndUpdate(blockpos, blockstate); ++this.blocksSetOnFire; } for(int i = 0; i < p_20871_; ++i) { BlockPos blockpos1 = blockpos.offset(this.random.nextInt(3) - 1, this.random.nextInt(3) - 1, this.random.nextInt(3) - 1); blockstate = BaseFireBlock.getState(this.level(), blockpos1); if (this.level().getBlockState(blockpos1).isAir() && blockstate.canSurvive(this.level(), blockpos1)) { this.level().setBlockAndUpdate(blockpos1, blockstate); ++this.blocksSetOnFire; } } } } private static void clearCopperOnLightningStrike(Level p_147151_, BlockPos p_147152_) { BlockState blockstate = p_147151_.getBlockState(p_147152_); BlockPos blockpos; BlockState blockstate1; if (blockstate.is(Blocks.LIGHTNING_ROD)) { blockpos = p_147152_.relative(blockstate.getValue(LightningRodBlock.FACING).getOpposite()); blockstate1 = p_147151_.getBlockState(blockpos); } else { blockpos = p_147152_; blockstate1 = blockstate; } if (blockstate1.getBlock() instanceof WeatheringCopper) { p_147151_.setBlockAndUpdate(blockpos, WeatheringCopper.getFirst(p_147151_.getBlockState(blockpos))); BlockPos.MutableBlockPos blockpos$mutableblockpos = p_147152_.mutable(); int i = p_147151_.random.nextInt(3) + 3; for(int j = 0; j < i; ++j) { int k = p_147151_.random.nextInt(8) + 1; randomWalkCleaningCopper(p_147151_, blockpos, blockpos$mutableblockpos, k); } } } private static void randomWalkCleaningCopper(Level p_147146_, BlockPos p_147147_, BlockPos.MutableBlockPos p_147148_, int p_147149_) { p_147148_.set(p_147147_); for(int i = 0; i < p_147149_; ++i) { Optional optional = randomStepCleaningCopper(p_147146_, p_147148_); if (!optional.isPresent()) { break; } p_147148_.set(optional.get()); } } private static Optional randomStepCleaningCopper(Level p_147154_, BlockPos p_147155_) { for(BlockPos blockpos : BlockPos.randomInCube(p_147154_.random, 10, p_147155_, 1)) { BlockState blockstate = p_147154_.getBlockState(blockpos); if (blockstate.getBlock() instanceof WeatheringCopper) { WeatheringCopper.getPrevious(blockstate).ifPresent((p_147144_) -> { p_147154_.setBlockAndUpdate(blockpos, p_147144_); }); p_147154_.levelEvent(3002, blockpos, -1); return Optional.of(blockpos); } } return Optional.empty(); } public boolean shouldRenderAtSqrDistance(double p_20869_) { double d0 = 64.0D * getViewScale(); return p_20869_ < d0 * d0; } protected void defineSynchedData() { } protected void readAdditionalSaveData(CompoundTag p_20873_) { } protected void addAdditionalSaveData(CompoundTag p_20877_) { } public int getBlocksSetOnFire() { return this.blocksSetOnFire; } public Stream getHitEntities() { return this.hitEntities.stream().filter(Entity::isAlive); } } ================================================ FILE: src/main/java/weather2/weathersystem/storm/StormObject.java ================================================ package weather2.weathersystem.storm; import com.corosus.coroutil.config.ConfigCoroUtil; import com.corosus.coroutil.util.*; import extendedrenderer.particle.ParticleRegistry; import extendedrenderer.particle.behavior.ParticleBehaviorFog; import extendedrenderer.particle.entity.EntityRotFX; import extendedrenderer.particle.entity.ParticleCrossSection; import extendedrenderer.particle.entity.ParticleCube; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.particles.DustParticleOptions; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.nbt.CompoundTag; import net.minecraft.server.level.ServerLevel; import net.minecraft.tags.BlockTags; import net.minecraft.util.Mth; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.animal.Dolphin; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.Level; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.LiquidBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.Shapes; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.fml.LogicalSide; import net.minecraftforge.fml.util.thread.EffectiveSide; import net.minecraftforge.registries.ForgeRegistries; import weather2.EntityRegistry; import weather2.ServerTickHandler; import weather2.Weather; import weather2.client.SceneEnhancer; import weather2.config.*; import weather2.player.PlayerData; import weather2.util.*; import weather2.weathersystem.WeatherManager; import weather2.weathersystem.WeatherManagerServer; import weather2.weathersystem.tornado.ActiveTornadoConfig; import weather2.weathersystem.tornado.simple.Layer; import weather2.weathersystem.tornado.simple.TornadoFunnelSimple; import java.util.*; public class StormObject extends WeatherObject { //used on both server and client side, mark things SideOnly where needed //size, state //should they extend entity? //management stuff public String spawnerUUID = ""; //newer cloud managing list for more strict render optimized positioning @OnlyIn(Dist.CLIENT) public HashMap lookupParticlesCloud; @OnlyIn(Dist.CLIENT) public HashMap lookupParticlesCloudLower; @OnlyIn(Dist.CLIENT) public HashMap lookupParticlesFunnel; @OnlyIn(Dist.CLIENT) public List listParticlesCloud; @OnlyIn(Dist.CLIENT) public List listParticlesGround; @OnlyIn(Dist.CLIENT) public List listParticlesFunnel; @OnlyIn(Dist.CLIENT) public List listParticlesDebris; @OnlyIn(Dist.CLIENT) public ParticleBehaviorFog particleBehaviorFog; public int sizeMaxFunnelParticles = 600; //public WeatherEntityConfig conf = WeatherTypes.weatherEntTypes.get(1); //this was pulled over from weather1 i believe //public int curWeatherType = 1; //NEEDS SYNCING //basic info public static int static_YPos_layer0 = ConfigMisc.Cloud_Layer0_Height; public static int static_YPos_layer1 = ConfigMisc.Cloud_Layer1_Height; public static int static_YPos_layer2 = ConfigMisc.Cloud_Layer2_Height; public static List layers = new ArrayList(Arrays.asList(static_YPos_layer0, static_YPos_layer1, static_YPos_layer2)); public int layer = 0; public boolean angleIsOverridden = false; public float angleMovementTornadoOverride = 0; public float tempAngleFormingTornado = 0; //growth / progression info public boolean isGrowing = true; //cloud formation data, helps storms public int levelWater = 0; //builds over water and humid biomes, causes rainfall (not technically a storm) public float levelWindMomentum = 0; //high elevation builds this, plains areas lowers it, 0 = no additional speed ontop of global speed public float levelTemperature = 0; //negative for cold, positive for warm, we subtract 0.7 from vanilla values to make forest = 0, plains 0.1, ocean -0.5, etc //public float levelWindDirectionAdjust = 0; //for persistant direction change i- wait just calculate on the fly based on temperature public int levelWaterStartRaining = 100; //storm data, used when its determined a storm will happen from cloud front collisions public int levelStormIntensityMax = 0; //calculated from colliding warm and cold fronts, used to determine how crazy a storm _will_ get //revision, ints for each stage of intensity, and a float for the intensity of THAT current stage public int levelCurIntensityStage = 0; //since we want storms to build up to a climax still, this will start from 0 and peak to levelStormIntensityMax public float levelCurStagesIntensity = 0; //public boolean isRealStorm = false; public boolean hasStormPeaked = false; public int maxIntensityStage = STATE_STAGE5; //used to mark difference between land and water based storms public int stormType = TYPE_LAND; public static int TYPE_LAND = 0; //for tornados public static int TYPE_WATER = 1; //for tropical cyclones / hurricanes //used to mark intensity stages public static int STATE_NORMAL = 0; public static int STATE_THUNDER = 1; public static int STATE_HIGHWIND = 2; public static int STATE_HAIL = 3; public static int STATE_FORMING = 4; //forming tornado for land, for water... stage 0 or something? public static int STATE_STAGE1 = 5; //these are for both tornados (land) and tropical cyclones (water) public static int STATE_STAGE2 = 6; public static int STATE_STAGE3 = 7; public static int STATE_STAGE4 = 8; public static int STATE_STAGE5 = 9; //counts as hurricane for water //helper val, adjust with flags method public static float levelStormIntensityFormingStartVal = STATE_FORMING; //spin speed for potential tornado formations, should go up with intensity increase; public double spinSpeed = 0.02D; //PENDING REVISION \\ - use based on levelStormIntensityCur ??? //states that combine all lesser states //public int state = STATE_NORMAL; //used for sure, rain is dependant on water level values public boolean attrib_precipitation = false; public boolean attrib_waterSpout = false; //copied from EntTornado //buildup var - unused in new system currently, but might be needed for touchdown effect //unused tornado scale, always 1F public float scale = 1F; public float strength = 100; public int maxHeight = 60; public int currentTopYBlock = -1; public TornadoHelper tornadoHelper = new TornadoHelper(this); private TornadoFunnelSimple tornadoFunnelSimple; //public Set doneChunks = new HashSet(); public int updateLCG = (new Random()).nextInt(); public float formingStrength = 0; //for transition from 0 (in clouds) to 1 (touch down) public Vec3 posBaseFormationPos = new Vec3(pos.x, pos.y, pos.z); //for formation / touchdown progress, where all the ripping methods scan from public boolean naturallySpawned = true; //to prevent things like it progressing to next stage before weather machine undoes it public boolean weatherMachineControlled = false; public boolean canSnowFromCloudTemperature = false; public boolean alwaysProgresses = false; //to let client know server is raining (since we override client side raining state for render changes) //public boolean overCastModeAndRaining = false; /*@SideOnly(Side.CLIENT) public RenderCubeCloud renderBlock;*/ //there is an issue with rainstorms sometimes never going away, this is a patch to mend the underlying issue i cant find yet public long ticksSinceLastPacketReceived = 0; //public static long lastStormFormed = 0; public boolean canBeDeadly = true; /** * Populate sky with stormless/cloudless storm objects in order to allow clear skies with current design */ public boolean cloudlessStorm = false; //used to cache a scan for blocks ahead of storm, to move around public float cachedAngleAvoidance = 0; public boolean isFirenado = false; public List listEntitiesUnderClouds = new ArrayList<>(); private boolean playerControlled = false; private int playerControlledTimeLeft = 20; private boolean baby = false; private boolean pet = false; private boolean petGrabsItems = false; private boolean sharknado = false; private boolean configNeedsSync = true; private int age; private int ageSinceTornadoTouchdown; private boolean isBeingDeflectedCached = true; private boolean debugCloudTemperature = false; public StormObject(WeatherManager parManager) { super(parManager); pos = new Vec3(0, static_YPos_layer0, 0); maxSize = ConfigStorm.Storm_MaxRadius; if (parManager.getWorld().isClientSide()) { listParticlesCloud = new ArrayList<>(); listParticlesFunnel = new ArrayList<>(); listParticlesDebris = new ArrayList<>(); listParticlesGround = new ArrayList<>(); lookupParticlesCloud = new HashMap<>(); lookupParticlesCloudLower = new HashMap<>(); lookupParticlesFunnel = new HashMap<>(); //renderBlock = new RenderCubeCloud(); } } public void initFirstTime() { super.initFirstTime(); Biome bgb = manager.getWorld().getBiome(WeatherUtilBlock.getPrecipitationHeightSafe(manager.getWorld(), new BlockPos(Mth.floor(pos.x), 0, Mth.floor(pos.z)))).get(); float temp = 1; if (bgb != null) { //temp = bgb.getFloatTemperature(new BlockPos(Mth.floor(pos.x), Mth.floor(pos.y), Mth.floor(pos.z))); temp = CoroUtilCompatibility.getAdjustedTemperature(manager.getWorld(), bgb, new BlockPos(Mth.floor(pos.x), Mth.floor(pos.y), Mth.floor(pos.z))); } //initial setting, more apparent than gradual adjustments if (naturallySpawned) { levelTemperature = getTemperatureMCToWeatherSys(temp); CULog.dbg("init levelTemperature: " + levelTemperature); } //levelWater = 0; levelWindMomentum = 0; //Weather.dbg("initialize temp to: " + levelTemperature + " - biome: " + bgb.biomeName); } public boolean isCloudlessStorm() { return cloudlessStorm; } public void setCloudlessStorm(boolean cloudlessStorm) { this.cloudlessStorm = cloudlessStorm; } public boolean isPrecipitating() { return attrib_precipitation; } public void setPrecipitating(boolean parVal) { attrib_precipitation = parVal; } public boolean isRealStorm() { return levelCurIntensityStage > STATE_NORMAL; } public boolean isTornadoFormingOrGreater() { return (stormType == TYPE_LAND && levelCurIntensityStage >= STATE_FORMING) || isPet(); } public boolean isCycloneFormingOrGreater() { return stormType == TYPE_WATER && levelCurIntensityStage >= STATE_FORMING; } public boolean isSpinning() { return levelCurIntensityStage >= STATE_HIGHWIND; } public boolean isTropicalCyclone() { return levelCurIntensityStage >= STATE_STAGE1; } public boolean isHurricane() { return levelCurIntensityStage >= STATE_STAGE5; } @Override public void read() { super.read(); nbtSyncFromServer(); CachedNBTTagCompound var1 = this.getNbtCache(); angleIsOverridden = var1.getBoolean("angleIsOverridden"); angleMovementTornadoOverride = var1.getFloat("angleMovementTornadoOverride"); spawnerUUID = var1.getString("spawnerUUID"); } @Override public void write() { super.write(); nbtSyncForClient(); CachedNBTTagCompound nbt = this.getNbtCache(); nbt.putBoolean("angleIsOverridden", angleIsOverridden); nbt.putFloat("angleMovementTornadoOverride", angleMovementTornadoOverride); nbt.putString("spawnerUUID", spawnerUUID); } //receiver method @Override public void nbtSyncFromServer() { CachedNBTTagCompound parNBT = this.getNbtCache(); boolean testNetworkData = false; if (testNetworkData) { System.out.println("Received payload from server; length=" + parNBT.getNewNBT().getAllKeys().size()); Iterator iterator = parNBT.getNewNBT().getAllKeys().iterator(); String keys = ""; while (iterator.hasNext()) { keys = keys.concat((String) iterator.next() + "; "); } System.out.println("Received " + keys); } super.nbtSyncFromServer(); //state = parNBT.getInt("state"); //attrib_tornado_severity = parNBT.getInt("attrib_tornado_severity"); //attrib_highwind = parNBT.getBoolean("attrib_highwind"); //attrib_tornado = parNBT.getBoolean("attrib_tornado"); //attrib_hurricane = parNBT.getBoolean("attrib_hurricane"); attrib_precipitation = parNBT.getBoolean("attrib_rain"); attrib_waterSpout = parNBT.getBoolean("attrib_waterSpout"); currentTopYBlock = parNBT.getInt("currentTopYBlock"); levelTemperature = parNBT.getFloat("levelTemperature"); levelWater = parNBT.getInt("levelWater"); layer = parNBT.getInt("layer"); //curWeatherType = parNBT.getInt("curWeatherType"); //formingStrength = parNBT.getFloat("formingStrength"); levelCurIntensityStage = parNBT.getInt("levelCurIntensityStage"); levelStormIntensityMax = parNBT.getInt("levelStormIntensityMax"); levelCurStagesIntensity = parNBT.getFloat("levelCurStagesIntensity"); stormType = parNBT.getInt("stormType"); hasStormPeaked = parNBT.getBoolean("hasStormPeaked"); //overCastModeAndRaining = parNBT.getBoolean("overCastModeAndRaining"); isDead = parNBT.getBoolean("isDead"); cloudlessStorm = parNBT.getBoolean("cloudlessStorm"); isFirenado = parNBT.getBoolean("isFirenado"); ticksSinceLastPacketReceived = 0;//manager.getWorld().getGameTime(); weatherMachineControlled = parNBT.getBoolean("weatherMachineControlled"); //TODO: sync tornado config for size etc String prefix = "tornadoFunnelData_layer_"; if (parNBT.contains(prefix + "count") && tornadoFunnelSimple != null) { int count = parNBT.getInt(prefix + "count"); for (int i = 0; i < tornadoFunnelSimple.listLayers.size(); i++) { Layer layer = tornadoFunnelSimple.listLayers.get(i); Vec3 newPos = new Vec3(parNBT.getDouble(prefix + i + "_posX"), parNBT.getDouble(prefix + i + "_posY"), parNBT.getDouble(prefix + i + "_posZ")); layer.setPos(newPos); } } playerControlled = parNBT.getBoolean("playerControlled"); spawnerUUID = parNBT.getString("spawnerUUID"); if (tornadoFunnelSimple != null && parNBT.contains("config")) { tornadoFunnelSimple.setConfig(ActiveTornadoConfig.deserialize(parNBT.get("config"))); } baby = parNBT.getBoolean("baby"); pet = parNBT.getBoolean("pet"); petGrabsItems = parNBT.getBoolean("petGrabsItems"); sharknado = parNBT.getBoolean("sharknado"); if (posBaseFormationPos == Vec3.ZERO) { posBaseFormationPos = new Vec3(parNBT.getDouble("posBaseFormationPosX"), parNBT.getDouble("posBaseFormationPosX"), parNBT.getDouble("posBaseFormationPosX")); } isBeingDeflectedCached = parNBT.getBoolean("isBeingDeflectedCached"); } //compose nbt data for packet (and serialization in future) @Override public void nbtSyncForClient() { super.nbtSyncForClient(); CachedNBTTagCompound data = this.getNbtCache(); //data.putInt("state", state); //data.putInt("attrib_tornado_severity", attrib_tornado_severity); //data.putBoolean("attrib_highwind", attrib_highwind); //data.putBoolean("attrib_tornado", attrib_tornado); //data.putBoolean("attrib_hurricane", attrib_hurricane); if (attrib_precipitation) { //CULog.dbg("syncing rain state true: " + pos); } data.putBoolean("attrib_rain", attrib_precipitation); data.putBoolean("attrib_waterSpout", attrib_waterSpout); data.putInt("currentTopYBlock", currentTopYBlock); data.putFloat("levelTemperature", levelTemperature); data.putInt("levelWater", levelWater); data.putInt("layer", layer); //data.putInt("curWeatherType", curWeatherType); //data.putFloat("formingStrength", formingStrength); data.putInt("levelCurIntensityStage", levelCurIntensityStage); data.putFloat("levelCurStagesIntensity", levelCurStagesIntensity); data.putFloat("levelStormIntensityMax", levelStormIntensityMax); data.putInt("stormType", stormType); data.putBoolean("hasStormPeaked", hasStormPeaked); //data.putBoolean("overCastModeAndRaining", overCastModeAndRaining); data.putBoolean("isDead", isDead); data.putBoolean("cloudlessStorm", cloudlessStorm); data.putBoolean("isFirenado", isFirenado); data.putBoolean("weatherMachineControlled", weatherMachineControlled); //sync the data heavy tornado funnel every 5 seconds if (manager != null && tornadoFunnelSimple != null && (manager.getWorld().getGameTime() % 100 == 0 || configNeedsSync)) { String prefix = "tornadoFunnelData_layer_"; data.putInt(prefix + "count", tornadoFunnelSimple.listLayers.size()); for (int i = 0; i < tornadoFunnelSimple.listLayers.size(); i++) { Vec3 layerPos = tornadoFunnelSimple.listLayers.get(i).getPos(); data.putDouble(prefix + i + "_posX", layerPos.x); data.putDouble(prefix + i + "_posY", layerPos.y); data.putDouble(prefix + i + "_posZ", layerPos.z); } } data.putBoolean("playerControlled", playerControlled); data.putString("spawnerUUID", spawnerUUID); if (configNeedsSync && tornadoFunnelSimple != null) { data.put("config", tornadoFunnelSimple.getConfig().serialize()); configNeedsSync = false; } data.putBoolean("baby", baby); data.putBoolean("pet", pet); data.putBoolean("petGrabsItems", petGrabsItems); data.putBoolean("sharknado", sharknado); //do a force sync every 30 seconds, solves issues like first data sometimes not coming in right: eg top y block if height never changes in flat world if (manager != null && (manager.getWorld().getGameTime()) % (20*30) == 0) { data.setUpdateForced(true); } else { data.setUpdateForced(false); } data.putDouble("posBaseFormationPosX", posBaseFormationPos.x); data.putDouble("posBaseFormationPosY", posBaseFormationPos.y); data.putDouble("posBaseFormationPosZ", posBaseFormationPos.z); data.putBoolean("isBeingDeflectedCached", isBeingDeflectedCached); } public CompoundTag nbtForIMC() { //we basically need all the same data minus a few soooo whatever nbtSyncForClient(); return getNbtCache().getNewNBT(); } @OnlyIn(Dist.CLIENT) public void tickRender(float partialTick) { super.tickRender(partialTick); //renderBlock.doRenderClouds(this, 0, 0, 0, 0, partialTick); /*if (layer == 1) { renderBlock.doRenderClouds(this, pos.x, pos.y, pos.z, 0, partialTick); }*/ //TODO: consider only putting funnel in this method since its the fast part, the rest might be slow enough to only need to do per gametick if (!WeatherUtil.isPaused()) { int count = 8+1; //ParticleBehaviorFog.newCloudWay = true; Iterator> it = lookupParticlesCloud.entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = it.next(); EntityRotFX ent = entry.getValue(); if (!ent.isAlive()) { it.remove(); } else { int i = entry.getKey(); Vec3 tryPos = null; double spawnRad = 120;//(ticksExisted % 100) + 10; double speed = 2D / (spawnRad); if (isSpinning()) { speed = 50D / (spawnRad); } ent.rotationSpeedAroundCenter = (float)speed; if (i == 0) { tryPos = new Vec3(pos.x, layers.get(layer), pos.z); ent.rotationYaw = ent.rotationAroundCenter; } else { double rad = Math.toRadians(ent.rotationAroundCenter - ent.rotationSpeedAroundCenter + (ent.rotationSpeedAroundCenter * partialTick)); double x = -Math.sin(rad) * spawnRad; double z = Math.cos(rad) * spawnRad; tryPos = new Vec3(pos.x + x, layers.get(layer), pos.z + z); double var16 = this.pos.x - ent.getPosX(); double var18 = this.pos.z - ent.getPosZ(); ent.rotationYaw = (float)(Math.atan2(var18, var16) * 180.0D / Math.PI) - 90.0F; //ent.rotationPitch = -20F;// - (ent.getEntityId() % 10); //ent.setAge(100); } ent.setPosition(tryPos.x, tryPos.y, tryPos.z); } } count = 16*2; it = lookupParticlesCloudLower.entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = it.next(); EntityRotFX ent = entry.getValue(); if (!ent.isAlive()) { it.remove(); } else { int i = entry.getKey(); Vec3 tryPos = null; ent.setScale(800 * 0.15F); double countPerLayer = 16; double rotPos = i % 16; int layerRot = i / 16; double spawnRad = 80; if (layerRot == 1) { spawnRad = 60; ent.setScale(600 * 0.15F); } double speed = 50D / (spawnRad * 2D); ent.rotationSpeedAroundCenter = (float)speed; double rad = Math.toRadians(ent.rotationAroundCenter - ent.rotationSpeedAroundCenter + (ent.rotationSpeedAroundCenter * partialTick)); double x = -Math.sin(rad) * spawnRad; double z = Math.cos(rad) * spawnRad; tryPos = new Vec3(pos.x + x, layers.get(layer) - 20, pos.z + z); ent.setPosition(tryPos.x, tryPos.y, tryPos.z); double var16 = this.pos.x - ent.getPosX(); double var18 = this.pos.z - ent.getPosZ(); ent.rotationYaw = (float)(Math.atan2(var18, var16) * 180.0D / Math.PI) - 90.0F; ent.rotationPitch = -20F;// - (ent.getEntityId() % 10); } } } } public void setupTornado() { ActiveTornadoConfig activeTornadoConfig; if (isPet()) { activeTornadoConfig = new ActiveTornadoConfig() .setHeight(1.7F) //this is overwritten in TornadoFunnelSimple .setRadiusOfBase(0.5F) .setSpinSpeed(360F / 20F) .setRadiusIncreasePerLayer(0.02F) /*.setRadiusIncreasePerLayer(0.08F)*/ .setEntityPullDistXZ(2) .setEntityPullDistXZForY(2); } else if (isBaby()) { activeTornadoConfig = new ActiveTornadoConfig() .setHeight(20) //this is overwritten in TornadoFunnelSimple .setRadiusOfBase(5F + 0F) .setSpinSpeed(360F / 20F) .setRadiusIncreasePerLayer(0.2F) .setEntityPullDistXZ(20) .setEntityPullDistXZForY(5); } else { activeTornadoConfig = new ActiveTornadoConfig() .setHeight(150) //this is overwritten in TornadoFunnelSimple .setRadiusOfBase(5F + 5F) .setSpinSpeed(360F / 20F) .setRadiusIncreasePerLayer(0.2F) .setEntityPullDistXZ(120) .setEntityPullDistXZForY(90); } tornadoFunnelSimple = new TornadoFunnelSimple(activeTornadoConfig, this); } public void tick() { super.tick(); age++; if (levelCurIntensityStage >= STATE_STAGE1) ageSinceTornadoTouchdown++; //Weather.dbg("ticking storm " + ID + " - manager: " + manager); //CULog.dbg("gametime: " + manager.getWorld().getGameTime()); //System.out.println("1gametime: " + manager.getWorld().getGameTime()); //adjust posGround to be pos with the ground Y pos for convinient usage if (!playerControlled) { posGround = new Vec3(pos.x, currentTopYBlock, pos.z); } else { posGround = new Vec3(pos.x, pos.y, pos.z); } LogicalSide side = EffectiveSide.get(); if (side == LogicalSide.CLIENT) { if (!WeatherUtil.isPaused()) { //CULog.dbg("levelTemperature: " + levelTemperature); if (isTornadoFormingOrGreater() || isCycloneFormingOrGreater()) { setAndUpdateTornado(); tornadoFunnelSimple.tick(); } else { if (tornadoFunnelSimple != null && tornadoFunnelSimple.listLayers.size() > 0) { tornadoFunnelSimple.fadeOut(); } } ticksSinceLastPacketReceived++; //if (layer == 0) { tickClient(); //} if (isTornadoFormingOrGreater() || isCycloneFormingOrGreater()) { setAndUpdateTornado(); if (!isBeingDeflectedCached()) { tornadoHelper.tick(manager.getWorld()); } tornadoFunnelSimple.tickClient(); } if (levelCurIntensityStage >= STATE_HIGHWIND) { if (manager.getWorld().isClientSide()) { tornadoHelper.soundUpdates(true, isTornadoFormingOrGreater() || isCycloneFormingOrGreater()); } } tickMovementClient(); if (layer == 0) { //sync X Y Z, Y gets changed below posBaseFormationPos = calculateBaseFormationPos(); } } } else { /*if (ConfigCoroUtil.useLoggingDebug) { ((ServerLevel) this.manager.getWorld()).sendParticles(ParticleTypes.HEART, this.pos.x, 200, this.pos.z, 10, 0.3D, 0D, 0.3D, 1D); }*/ if (isTornadoFormingOrGreater() || isCycloneFormingOrGreater()) { setAndUpdateTornado(); tornadoFunnelSimple.tick(); } if (isCloudlessStorm()) { if (ConfigMisc.overcastMode && manager.getWorld().isRaining()) { this.setCloudlessStorm(false); } } if (isTornadoFormingOrGreater() || isCycloneFormingOrGreater()) { if (!isBeingDeflectedCached()) { tornadoHelper.tick(manager.getWorld()); } } if (levelCurIntensityStage >= STATE_HIGHWIND) { if (manager.getWorld().isClientSide()) { tornadoHelper.soundUpdates(true, isTornadoFormingOrGreater() || isCycloneFormingOrGreater()); } } //debug \\ //maxSize = 200; //isGrowing = true; /*maxSize = 200; //size = maxSize; isGrowing = true; //state = STATE_HAIL; state = STATE_NORMAL; attrib_hurricane = false; attrib_tornado = true; attrib_tornado = false; attrib_highwind = false; attrib_tornado_severity = 0;*/ //attrib_tornado_severity = ATTRIB_F1; //debug // tickMovement(); //System.out.println("cloud motion: " + motion + " wind angle: " + angle); if (layer == 0) { if (!isCloudlessStorm()) { tickWeatherEvents(); tickProgression(); //tickSnowFall(); } } else { //make layer 1 max size for visuals size = maxSize; } if (layer == 0) { //sync X Y Z, Y gets changed below posBaseFormationPos = calculateBaseFormationPos(); } //overCastModeAndRaining = ConfigMisc.overcastMode && manager.getWorld().isRaining(); } } public Vec3 calculateBaseFormationPos() { Vec3 calculatedPos = new Vec3(pos.x, pos.y, pos.z); if (levelCurIntensityStage >= StormObject.levelStormIntensityFormingStartVal) { if (levelCurIntensityStage >= StormObject.levelStormIntensityFormingStartVal + 1) { formingStrength = 1; calculatedPos = new Vec3(calculatedPos.x, posGround.y, calculatedPos.z); } else { //make it so storms touchdown at 0.5F intensity instead of 1 then instantly start going back up, keeps them down for a full 1F worth of intensity val float intensityAdj = Math.min(1F, levelCurStagesIntensity * 2F); //shouldnt this just be intensityAdj? float val = (levelCurIntensityStage + intensityAdj) - StormObject.levelStormIntensityFormingStartVal; formingStrength = val; double yDiff = pos.y - posGround.y; calculatedPos = new Vec3(calculatedPos.x, pos.y - (yDiff * formingStrength), calculatedPos.z); } } else { if (levelCurIntensityStage == STATE_HIGHWIND) { formingStrength = 1; calculatedPos = new Vec3(calculatedPos.x, posGround.y, calculatedPos.z); } else { formingStrength = 0; calculatedPos = new Vec3(calculatedPos.x, pos.y, calculatedPos.z); } } return calculatedPos; } public void tickMovement() { double distToTarget = 1F; if (playerControlled) { Player entP = getPlayer(); if (entP != null) { if (!isPet()) { //aimStormAtClosestOrProvidedPlayer(entP); this.pos = new Vec3(entP.position().x, entP.position().y, entP.position().z); this.posGround = new Vec3(entP.position().x, entP.position().y, entP.position().z); } else { distToTarget = entP.position().distanceTo(posGround); if (distToTarget > 5F) { aimStormAtClosestOrProvidedPlayer(entP); } } } } //storm movement via wind float angle = getAdjustedAngle(); Random rand = new Random(); if (angleIsOverridden) { angle = angleMovementTornadoOverride; //debug /*if (manager.getWorld().getGameTime() % 20 == 0) { EntityPlayer entP = manager.getWorld().getClosestPlayer(pos.x, pos.y, pos.z, -1); if (entP != null) { //even more debug, heat seak test //Random rand = new Random(); double var11 = entP.posX - pos.x; double var15 = entP.posZ - pos.z; float yaw = -((float)Math.atan2(var11, var15)) * 180.0F / (float)Math.PI; //weather override! //yaw = weatherMan.wind.direction; //int size = ConfigMisc.Storm_Tornado_aimAtPlayerAngleVariance; //yaw += rand.nextInt(size) - (size / 2); angleMovementTornadoOverride = yaw; Weather.dbg("angle override: " + angle + " - dist from player: " + entP.getDistance(pos.x, pos.y, pos.z)); } }*/ } if (levelCurIntensityStage == STATE_FORMING) { angle = tempAngleFormingTornado; //if its an F0 forming, make it all over the place, adding on top of cloud direction each tick angle += (rand.nextFloat() - rand.nextFloat()) * 30F; } //despite overridden angle, still avoid obstacles //slight randomness to angle angle += (rand.nextFloat() - rand.nextFloat()) * 0.15F; //avoid large obstacles double scanDist = 50; double scanX = this.pos.x + (-Math.sin(Math.toRadians(angle)) * scanDist); double scanZ = this.pos.z + (Math.cos(Math.toRadians(angle)) * scanDist); int height = WeatherUtilBlock.getPrecipitationHeightSafe(this.manager.getWorld(), CoroUtilBlock.blockPos(scanX, 0, scanZ)).getY(); if (this.pos.y < height) { float angleAdj = 45; if (this.ID % 2 == 0) { angleAdj = -45; } angle += angleAdj; } //update new angle back to temp forming tornado angle tempAngleFormingTornado = angle; //Weather.dbg("cur angle: " + angle); double vecX = -Math.sin(Math.toRadians(angle)); double vecZ = Math.cos(Math.toRadians(angle)); float cloudSpeedAmp = 0.2F; boolean love_tropics_tweaks = Weather.isLoveTropicsInstalled(); //tweaking for lt if (love_tropics_tweaks) cloudSpeedAmp = 3F; float finalSpeed = getAdjustedSpeed() * cloudSpeedAmp; if (levelCurIntensityStage == STATE_FORMING) { finalSpeed = 0.2F; } else if (levelCurIntensityStage >= STATE_THUNDER) { finalSpeed = 0.15F; } if (!love_tropics_tweaks && levelCurIntensityStage >= levelStormIntensityFormingStartVal) { finalSpeed /= ((float)(levelCurIntensityStage-levelStormIntensityFormingStartVal+1F)); } if (finalSpeed < 0.03F) { finalSpeed = 0.03F; } if (!love_tropics_tweaks && finalSpeed > 0.3F) { finalSpeed = 0.3F; } if (levelCurIntensityStage == STATE_FORMING) { finalSpeed = 0.3F; } if (love_tropics_tweaks) { finalSpeed = 0.1F; //finalSpeed = 0F; } if (playerControlled) { finalSpeed = 0.5F; Player player = getPlayer(); if (player != null) { if (posGround.distanceTo(player.position()) > 30) { pos = new Vec3(player.position().x, player.position().y, player.position().z); } } } if (isPet()) { finalSpeed = 0.05F; if (distToTarget > 5.5F) { finalSpeed = 0.5F; } } if (isSharknado()) { finalSpeed = 0.1F; } if (manager.getWorld().getGameTime() % 100 == 0 && levelCurIntensityStage >= STATE_FORMING) { //finalSpeed = 0.5F; //Weather.dbg("storm ID: " + this.ID + ", stage: " + levelCurIntensityStage + ", storm speed: " + finalSpeed); } //push tornado away faster if being deflected if (isBeingDeflectedCached()) { finalSpeed *= 5; } boolean testing = false; if (testing) { finalSpeed = 0.5F; } if (!weatherMachineControlled) { motion = new Vec3(vecX * finalSpeed, 0, vecZ * finalSpeed); double max = 0.2D; //max speed /*if (motion.x < -max) motion.x = -max; if (motion.x > max) motion.x = max; if (motion.z < -max) motion.z = -max; if (motion.z > max) motion.z = max;*/ //actually move storm pos = pos.add(motion); } if (levelCurIntensityStage >= STATE_FORMING) { Optional optional = ((WeatherManagerServer) manager).findWeatherDeflector(CoroUtilBlock.blockPos(posGround), 128); if (optional.isPresent()) { isBeingDeflectedCached = true; //CULog.dbg("optional.get(): " + optional.get()); aimAwayFromCoords(new Vec3(optional.get().getX(), optional.get().getY(), optional.get().getZ())); ((ServerLevel) this.manager.getWorld()).sendParticles(DustParticleOptions.REDSTONE, optional.get().getX() + 0.5D, optional.get().getY() + 0.5D, optional.get().getZ() + 0.5D, 1, 0.3D, 0D, 0.3D, 1D); } else { isBeingDeflectedCached = false; } } else { isBeingDeflectedCached = false; } } public void tickMovementClient() { if (!weatherMachineControlled) { pos = pos.add(motion); } } public void tickWeatherEvents() { Random rand = new Random(); Level world = manager.getWorld(); //if (world.getGameTime() % 20 == 0){ currentTopYBlock = calculateTopYBlock(); //} //Weather.dbg("currentTopYBlock: " + currentTopYBlock); if (levelCurIntensityStage >= STATE_THUNDER && !isBaby() && !isPet()) { if (rand.nextInt((int)Math.max(1, ConfigStorm.Storm_LightningStrikeBaseValueOddsTo1 - (levelCurIntensityStage * 10))) == 0) { int x = (int) Math.floor(pos.x + rand.nextInt(size) - rand.nextInt(size)); int z = (int) Math.floor(pos.z + rand.nextInt(size) - rand.nextInt(size)); if (world.isLoaded(new BlockPos(x, 0, z))) { int y = world.getHeight(Heightmap.Types.MOTION_BLOCKING, x, z); if (world instanceof ServerLevel) { Optional optional = ((ServerLevel)world).findLightningRod(new BlockPos(x, y, z)); if (optional.isPresent()) { x = optional.get().getX(); y = optional.get().getY(); z = optional.get().getZ(); } } LightningBoltWeatherNew ent = new LightningBoltWeatherNew(EntityRegistry.LIGHTNING_BOLT.get(), world); ent.setPos(x + 0.5, y, z + 0.5); addWeatherEffectLightning(ent, false); } } } //dont forget, this doesnt account for storm size, so small storms have high concentration of hail, as it grows, it appears to lessen in rate if (isPrecipitating() && levelCurIntensityStage == STATE_HAIL && stormType == TYPE_LAND) { //if (rand.nextInt(1) == 0) { for (int i = 0; i < Math.max(1, ConfigStorm.Storm_HailPerTick * (size/maxSize)); i++) { int x = (int) Math.floor(pos.x + rand.nextInt(size) - rand.nextInt(size)); int z = (int) Math.floor(pos.z + rand.nextInt(size) - rand.nextInt(size)); if (world.isLoaded(new BlockPos(x, static_YPos_layer0, z)) && (world.getNearestPlayer(x, 50, z, 80, false) != null)) { //int y = world.getPrecipitationHeight(x, z); //if (world.canLightningStrikeAt(x, y, z)) { //TODO: 1.14 uncomment /*EntityIceBall hail = new EntityIceBall(world); hail.setPosition(x, layers.get(layer), z); world.addEntity(hail);*/ //world.addWeatherEffect(new LightningBoltWeather(world, (double)x, (double)y, (double)z)); //} //System.out.println("spawned hail: " ); } else { //System.out.println("nope"); } } } trackAndExtinguishEntities(); } public void trackAndExtinguishEntities() { if (ConfigStorm.Storm_Rain_TrackAndExtinguishEntitiesRate <= 0) return; if (isPrecipitating()) { //efficient caching if ((manager.getWorld().getGameTime() + (ID * 20)) % ConfigStorm.Storm_Rain_TrackAndExtinguishEntitiesRate == 0) { listEntitiesUnderClouds.clear(); BlockPos posBP = CoroUtilBlock.blockPos(posGround.x, posGround.y, posGround.z); List listEnts = manager.getWorld().getEntitiesOfClass(LivingEntity.class, new AABB(posBP).inflate(size)); for (LivingEntity ent : listEnts) { if (ent.level().canSeeSky(ent.blockPosition())) { listEntitiesUnderClouds.add(ent); } } } for (LivingEntity ent : listEntitiesUnderClouds) { if (!isFirenado) { ent.clearFire(); } } } } public void tickProgression() { Level world = manager.getWorld(); //storm progression, heavy WIP if (world.getGameTime() % 3 == 0) { if (isGrowing) { if (size < maxSize) { size++; } else { //isGrowing = false; } } else { /*if (size > 0) { size--; } else if (size <= 0) { //kill //manager.removeStormObject(ID); }*/ } //System.out.println("cur size: " + size); } float tempAdjustRate = (float)ConfigStorm.Storm_TemperatureAdjustRate;//0.1F; int levelWaterBuildRate = ConfigStorm.Storm_Rain_WaterBuildUpRate; int levelWaterSpendRate = ConfigStorm.Storm_Rain_WaterSpendRate; int randomChanceOfWaterBuildFromWater = ConfigStorm.Storm_Rain_WaterBuildUpOddsTo1FromSource; int randomChanceOfWaterBuildFromNothing = ConfigStorm.Storm_Rain_WaterBuildUpOddsTo1FromNothing; int randomChanceOfWaterBuildFromOvercastRaining = ConfigStorm.Storm_Rain_WaterBuildUpOddsTo1FromOvercastRaining; randomChanceOfWaterBuildFromOvercastRaining = 10; //int randomChanceOfRain = ConfigMisc.Player_Storm_Rain_OddsTo1; boolean isInOcean = false; boolean isOverWater = false; boolean tryFormStorm = false; float levelStormIntensityRate = 0.02F; float minIntensityToProgress = 0.6F; if (world.getGameTime() % ConfigStorm.Storm_AllTypes_TickRateDelay == 0) { //boolean debug = false; //can be null if spawned via server console Player player = getPlayer(); CompoundTag playerNBT = null; if (player != null) { playerNBT = player.getPersistentData(); } else { playerNBT = new CompoundTag(); } //CompoundTag playerNBT = PlayerData.getPlayerNBT(spawnerUUID); long lastStormDeadlyTime = playerNBT.getLong("lastStormDeadlyTime"); //long lastStormRainTime = playerNBT.getLong("lastStormRainTime"); //set it up so that theres an initial delay before first deadly storm on first world load if (lastStormDeadlyTime == 0) { lastStormDeadlyTime = world.getGameTime(); } WeatherManagerServer wm = ServerTickHandler.getWeatherManagerFor(world.dimension()); if (wm.lastStormFormed == 0) { wm.lastStormFormed = world.getGameTime(); } //Biome bgb = null; Holder bgb = world.getBiome(WeatherUtilBlock.getPrecipitationHeightSafe(world, new BlockPos(Mth.floor(pos.x), 0, Mth.floor(pos.z)))); //temperature scan if (bgb.get() != null) { isInOcean = bgb.unwrap().left().toString().toLowerCase().contains("ocean"); //float biomeTempAdj = getTemperatureMCToWeatherSys(bgb.getFloatTemperature(new BlockPos(Mth.floor(pos.x), Mth.floor(pos.y), Mth.floor(pos.z)))); float biomeTempAdj = getTemperatureMCToWeatherSys(CoroUtilCompatibility.getAdjustedTemperature(manager.getWorld(), bgb.get(), new BlockPos(Mth.floor(pos.x), Mth.floor(pos.y), Mth.floor(pos.z)))); if (levelTemperature > biomeTempAdj) { levelTemperature -= tempAdjustRate; } else { levelTemperature += tempAdjustRate; } } boolean performBuildup = false; Random rand = new Random(); if (!isPrecipitating() && rand.nextInt(randomChanceOfWaterBuildFromNothing) == 0) { performBuildup = true; } if (!isPrecipitating() && ConfigMisc.overcastMode && manager.getWorld().isRaining() && rand.nextInt(randomChanceOfWaterBuildFromOvercastRaining) == 0) { performBuildup = true; } BlockPos tryPos = new BlockPos(Mth.floor(pos.x), currentTopYBlock - 1, Mth.floor(pos.z)); if (world.isLoaded(tryPos)) { BlockState state = world.getBlockState(tryPos); if (!CoroUtilBlock.isAir(state.getBlock())) { //Block block = Block.blocksList[blockID]; if (state.liquid()) { isOverWater = true; } } } //water scan - dont build up if raining already if (!performBuildup && !isPrecipitating() && rand.nextInt(randomChanceOfWaterBuildFromWater) == 0) { if (isOverWater) { performBuildup = true; } if (bgb.get() != null) { String biomecat = bgb.unwrap().left().toString().toLowerCase(); if (!performBuildup && (isInOcean || biomecat.contains("swamp") || biomecat.contains("jungle") || biomecat.contains("river"))) { performBuildup = true; } } } if (performBuildup) { //System.out.println("RAIN BUILD TEMP OFF"); levelWater += levelWaterBuildRate; Weather.dbg(ID + ": building rain: " + levelWater); } //water values adjust when raining if (isPrecipitating()) { levelWater -= levelWaterSpendRate; //TEMP!!! /*System.out.println("TEMP!!!"); levelWater = 0;*/ if (levelWater < 0) levelWater = 0; if (levelWater <= 0) { setPrecipitating(false); Weather.dbg("ending raining for: " + ID); } } else { if (levelWater >= levelWaterStartRaining) { if (ConfigMisc.overcastMode) { if (manager.getWorld().isRaining()) { if (ConfigStorm.Storm_Rain_Overcast_OddsTo1 != -1 && rand.nextInt(ConfigStorm.Storm_Rain_Overcast_OddsTo1) == 0) { setPrecipitating(true); Weather.dbg("starting raining for: " + ID); } } } else { if (ConfigStorm.Storm_Rain_OddsTo1 != -1 && rand.nextInt(ConfigStorm.Storm_Rain_OddsTo1) == 0) { setPrecipitating(true); Weather.dbg("starting raining for: " + ID); } } } } //actual storm formation chance boolean tempAlwaysFormStorm = false; if (this.canBeDeadly && this.levelCurIntensityStage == STATE_NORMAL) { if (ConfigStorm.Server_Storm_Deadly_UseGlobalRate) { if (ConfigStorm.Server_Storm_Deadly_TimeBetweenInTicks != -1) { if (wm.lastStormFormed == 0 || wm.lastStormFormed + ConfigStorm.Server_Storm_Deadly_TimeBetweenInTicks < world.getGameTime()) { tryFormStorm = true; } else if (ConfigStorm.Server_Storm_Deadly_TimeBetweenInTicks_Land_Based != -1 && wm.lastStormFormed + ConfigStorm.Server_Storm_Deadly_TimeBetweenInTicks_Land_Based < world.getGameTime()) { tryFormStorm = true; } } } else { if (tempAlwaysFormStorm || ConfigStorm.Player_Storm_Deadly_TimeBetweenInTicks != -1) { if (tempAlwaysFormStorm || lastStormDeadlyTime == 0 || lastStormDeadlyTime + ConfigStorm.Player_Storm_Deadly_TimeBetweenInTicks < world.getGameTime()) { tryFormStorm = true; } else if (ConfigStorm.Player_Storm_Deadly_TimeBetweenInTicks_Land_Based != -1 && lastStormDeadlyTime + ConfigStorm.Player_Storm_Deadly_TimeBetweenInTicks_Land_Based < world.getGameTime()) { tryFormStorm = true; } } } } if (weatherMachineControlled) { return; } if (((ConfigMisc.overcastMode && manager.getWorld().isRaining()) || !ConfigMisc.overcastMode) && WeatherUtilConfig.listDimensionsStorms.contains(manager.getWorld().dimension().location().toString()) && tryFormStorm) { int stormFrontCollideDist = ConfigStorm.Storm_Deadly_CollideDistance; int randomChanceOfCollide = ConfigStorm.Player_Storm_Deadly_OddsTo1; int randomChanceOfCollideLand = ConfigStorm.Player_Storm_Deadly_OddsTo1_Land_Based; if (ConfigStorm.Server_Storm_Deadly_UseGlobalRate) { randomChanceOfCollide = ConfigStorm.Server_Storm_Deadly_OddsTo1; randomChanceOfCollideLand = ConfigStorm.Server_Storm_Deadly_OddsTo1_Land_Based; } if (isInOcean && (ConfigStorm.Storm_OddsTo1OfOceanBasedStorm > 0 && rand.nextInt(ConfigStorm.Storm_OddsTo1OfOceanBasedStorm) == 0)) { Player entP = getPlayer(); if (entP != null) { initRealStorm(entP, null); } else { initRealStorm(null, null); } if (ConfigStorm.Server_Storm_Deadly_UseGlobalRate) { wm.lastStormFormed = world.getGameTime(); } else { playerNBT.putLong("lastStormDeadlyTime", world.getGameTime()); } } else if ((!isInOcean && randomChanceOfCollideLand > 0 && rand.nextInt(randomChanceOfCollideLand) == 0)) { Player entP = getPlayer(); if (entP != null) { initRealStorm(entP, null); } else { initRealStorm(null, null); } if (ConfigStorm.Server_Storm_Deadly_UseGlobalRate) { wm.lastStormFormed = world.getGameTime(); } else { playerNBT.putLong("lastStormDeadlyTime", world.getGameTime()); } } else if (rand.nextInt(randomChanceOfCollide) == 0) { for (int i = 0; i < manager.getStormObjects().size(); i++) { WeatherObject wo = manager.getStormObjects().get(i); if (wo instanceof StormObject) { StormObject so = (StormObject) wo; boolean startStorm = false; if (so.ID != this.ID && so.levelCurIntensityStage <= 0 && !so.isCloudlessStorm() && !so.weatherMachineControlled) { if (so.pos.distanceTo(pos) < stormFrontCollideDist) { if (this.levelTemperature < 0) { if (so.levelTemperature > 0) { startStorm = true; } } else if (this.levelTemperature > 0) { if (so.levelTemperature < 0) { startStorm = true; } } } } if (startStorm) { //Weather.dbg("start storm!"); playerNBT.putLong("lastStormDeadlyTime", world.getGameTime()); //EntityPlayer entP = manager.getWorld().getClosestPlayer(pos.x, pos.y, pos.z, -1); Player entP = getPlayer(); if (entP != null) { initRealStorm(entP, so); } else { initRealStorm(null, so); //can happen, chunkloaded emtpy overworld, let the storm do what it must without a player //Weather.dbg("Weather2 WARNING!!!! Failed to get a player object for new tornado, this shouldnt happen"); } break; } } } } } if (isRealStorm()) { //force storms to die if its no longer raining while overcast mode is active if (ConfigMisc.overcastMode) { if (!manager.getWorld().isRaining()) { hasStormPeaked = true; } } //force rain on while real storm and not dying if (!hasStormPeaked) { levelWater = levelWaterStartRaining; setPrecipitating(true); } //temp //levelWater = 0; //setPrecipitating(false); if ((levelCurIntensityStage == STATE_HIGHWIND || levelCurIntensityStage == STATE_HAIL) && isOverWater) { if (ConfigStorm.Storm_OddsTo1OfHighWindWaterSpout != 0 && rand.nextInt(ConfigStorm.Storm_OddsTo1OfHighWindWaterSpout) == 0) { attrib_waterSpout = true; } } else { attrib_waterSpout = false; } //change since storms have a predetermined max now, nevermind, storms take too long, limited simbox area //minIntensityToProgress = 0.8F; //int oddsTo1OfIntensityProgressionBase = ConfigStorm.Storm_OddsTo1OfProgressionBase; //int oddsTo1OfIntensityProgression = oddsTo1OfIntensityProgressionBase + (levelCurIntensityStage * ConfigStorm.Storm_OddsTo1OfProgressionStageMultiplier); if (!hasStormPeaked) { if (levelCurIntensityStage < maxIntensityStage && (!ConfigTornado.Storm_NoTornadosOrCyclones || levelCurIntensityStage < STATE_FORMING-1)) { if (levelCurStagesIntensity >= minIntensityToProgress) { //Weather.dbg("storm ID: " + this.ID + " trying to hit next stage"); if (alwaysProgresses || levelCurIntensityStage < levelStormIntensityMax/*rand.nextInt(oddsTo1OfIntensityProgression) == 0*/) { stageNext(); Weather.dbg("storm ID: " + this.ID + " - growing, stage: " + levelCurIntensityStage + " pos: " + pos); //mark is tropical cyclone if needed! and never unmark it! //TODO: re-enable this to allow hurricanes again if (isInOcean && false) { //make it ONLY allow to change during forming stage, so it locks in if (levelCurIntensityStage == STATE_FORMING) { Weather.dbg("storm ID: " + this.ID + " marked as tropical cyclone!"); stormType = TYPE_WATER; //reroll dice on ocean storm since we only just define it here levelStormIntensityMax = rollDiceOnMaxIntensity(); Weather.dbg("rerolled odds for ocean storm, max stage will be: " + levelStormIntensityMax); } } } } //new F0 forming touches down at 0.5, needs to switch to F1 if it can ASAP if (levelCurIntensityStage == STATE_FORMING) { if (levelCurStagesIntensity >= 0.5 && levelCurStagesIntensity < 0.9) { levelCurStagesIntensity = 0.90F; } } } Weather.dbg("storm ID: " + this.ID + " - growing, stage " + levelCurIntensityStage + " of max " + levelStormIntensityMax + ", at intensity: " + levelCurStagesIntensity + " pos: " + pos); if (levelCurStagesIntensity >= 1F) { Weather.dbg("storm peaked at: " + levelCurIntensityStage); hasStormPeaked = true; } } else { if (ConfigMisc.overcastMode && manager.getWorld().isRaining()) { levelCurStagesIntensity -= levelStormIntensityRate * 0.9F; } else { levelCurStagesIntensity -= levelStormIntensityRate * 0.3F; if (levelCurIntensityStage >= STATE_FORMING) { Weather.dbg("storm ID: " + this.ID + " - active info, stage: " + levelCurIntensityStage + " levelCurStagesIntensity: " + levelCurStagesIntensity + " pos: " + pos); } } //insta set it to 0.5 so it visually starts taking off if its dying if (levelCurIntensityStage == STATE_FORMING) { if (levelCurStagesIntensity > 0.5) { levelCurStagesIntensity = 0.5F; } //extra decay to above code levelCurStagesIntensity -= levelStormIntensityRate * 0.9F; } if (levelCurStagesIntensity <= 0) { stagePrev(); Weather.dbg("storm ID: " + this.ID + " - dying, stage: " + levelCurIntensityStage + " pos: " + pos); if (levelCurIntensityStage <= 0) { setNoStorm(); } } } //levelStormIntensityCur value ranges and what they influence //revised to remove rain and factor in tropical storm / hurricane //1 = thunderstorm (and more rain???) //2 = high wind //3 = hail //4 = tornado forming OR tropical cyclone (forming?) - logic splits off here where its marked as hurricane if its over water //5 = F1 OR TC 2 //6 = F2 OR TC 3 //7 = F3 OR TC 4 //8 = F4 OR TC 5 //9 = F5 OR hurricane ??? (perhaps hurricanes spawn differently, like over ocean only, and sustain when hitting land for a bit) //what about tropical storm? that is a mini hurricane, perhaps also ocean based //levelWindMomentum = rate of increase of storm??? (in addition to the pre storm system speeds) //POST DEV NOTES READ!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!: //it might be a good idea to make something else determine increase from high winds to tornado and higher //using temperatures is a little unstable at such a large range of variation.... //updateStormFlags(); //curWeatherType = Math.min(WeatherTypes.weatherEntTypes.size()-1, Math.max(1, levelCurIntensityStage - 1)); } else { if (ConfigMisc.overcastMode) { if (!manager.getWorld().isRaining()) { if (attrib_precipitation) { setPrecipitating(false); } } } } } //tick this more often for smoother forming on the client side (packet sync rate is 2 so 2 is good enough here) if (world.getGameTime() % 2 == 0) { if (((ConfigMisc.overcastMode && manager.getWorld().isRaining()) || !ConfigMisc.overcastMode) && WeatherUtilConfig.listDimensionsStorms.contains(manager.getWorld().dimension().location().toString())/* && tryFormStorm*/) { if (isRealStorm() && !hasStormPeaked) { //speed up forming and greater progression when past forming state if (levelCurIntensityStage >= levelStormIntensityFormingStartVal) { levelStormIntensityRate *= 3; //oddsTo1OfIntensityProgressionBase /= 3; } //30F because this was ticking once every 60 ticks, now its every 2 ticks levelCurStagesIntensity += levelStormIntensityRate / 30F; } } } if (playerControlled) { if (playerControlledTimeLeft > 0) { playerControlledTimeLeft--; if (playerControlledTimeLeft <= 0) { featherFallAllNearbyPlayers(); remove(); } } } } public void featherFallAllNearbyPlayers() { /*double dist = 100 * 2; AABB aabb = new AABB(pos.x, currentTopYBlock, pos.z, pos.x, currentTopYBlock, pos.z); aabb = aabb.inflate(dist, maxHeight * 3, dist); List list = manager.getWorld().getEntitiesOfClass(Entity.class, aabb);*/ tornadoHelper.forceRotate(manager.getWorld(), true); } public WeatherEntityConfig getWeatherEntityConfigForStorm() { //default spout WeatherEntityConfig weatherConfig = WeatherTypes.weatherEntTypes.get(0); if (levelCurIntensityStage >= STATE_STAGE5) { weatherConfig = WeatherTypes.weatherEntTypes.get(5); } else if (levelCurIntensityStage >= STATE_STAGE4) { weatherConfig = WeatherTypes.weatherEntTypes.get(4); } else if (levelCurIntensityStage >= STATE_STAGE3) { weatherConfig = WeatherTypes.weatherEntTypes.get(3); } else if (levelCurIntensityStage >= STATE_STAGE2) { weatherConfig = WeatherTypes.weatherEntTypes.get(2); } else if (levelCurIntensityStage >= STATE_STAGE1) { weatherConfig = WeatherTypes.weatherEntTypes.get(1); } else if (levelCurIntensityStage >= STATE_FORMING) { weatherConfig = WeatherTypes.weatherEntTypes.get(0); } return weatherConfig; } public void stageNext() { levelCurIntensityStage++; levelCurStagesIntensity = 0F; if (ConfigTornado.Storm_Tornado_aimAtPlayerOnSpawn) { if (!hasStormPeaked && levelCurIntensityStage == STATE_FORMING) { if (!Weather.isLoveTropicsInstalled()) { aimStormAtClosestOrProvidedPlayer(null); } } } } public void stagePrev() { levelCurIntensityStage--; levelCurStagesIntensity = 1F; } public void initRealStorm(Player entP, StormObject stormToAbsorb) { //new way of storm progression levelCurIntensityStage = STATE_THUNDER; //isRealStorm = true; float diff = 4; if (stormToAbsorb != null) { diff = this.levelTemperature - stormToAbsorb.levelTemperature; } if (naturallySpawned) { this.levelWater = this.levelWaterStartRaining * 2; /*this.levelStormIntensityMax = (float) (diff * ConfigMisc.Storm_IntensityAmplifier); if (levelStormIntensityMax < ConfigMisc.Storm_Deadly_MinIntensity) { levelStormIntensityMax = (float)ConfigMisc.Storm_Deadly_MinIntensity; }*/ } this.levelStormIntensityMax = rollDiceOnMaxIntensity(); Weather.dbg("rolled odds for storm, unless it becomes ocean storm, max stage will be: " + levelStormIntensityMax); this.attrib_precipitation = true; if (stormToAbsorb != null) { Weather.dbg("stormfront collision happened between ID " + this.ID + " and " + stormToAbsorb.ID); manager.removeStormObject(stormToAbsorb.ID); ((WeatherManagerServer)manager).syncStormRemove(stormToAbsorb); } else { Weather.dbg("ocean storm happened, ID " + this.ID); } if (ConfigTornado.Storm_Tornado_aimAtPlayerOnSpawn) { if (!Weather.isLoveTropicsInstalled()) { aimStormAtClosestOrProvidedPlayer(entP); } } } public int rollDiceOnMaxIntensity() { Random rand = new Random(); int randVal = rand.nextInt(100); if (stormType == TYPE_LAND) { if (randVal <= ConfigStorm.Storm_PercentChanceOf_F5_Tornado) { return STATE_STAGE5; } else if (randVal <= ConfigStorm.Storm_PercentChanceOf_F4_Tornado) { return STATE_STAGE4; } else if (randVal <= ConfigStorm.Storm_PercentChanceOf_F3_Tornado) { return STATE_STAGE3; } else if (randVal <= ConfigStorm.Storm_PercentChanceOf_F2_Tornado) { return STATE_STAGE2; } else if (randVal <= ConfigStorm.Storm_PercentChanceOf_F1_Tornado) { return STATE_STAGE1; } else if (randVal <= ConfigStorm.Storm_PercentChanceOf_F0_Tornado) { return STATE_FORMING; } else if (randVal <= ConfigStorm.Storm_PercentChanceOf_Hail) { return STATE_HAIL; } else if (randVal <= ConfigStorm.Storm_PercentChanceOf_HighWind) { return STATE_HIGHWIND; } } else if (stormType == TYPE_WATER) { if (randVal <= ConfigStorm.Storm_PercentChanceOf_C5_Cyclone) { return STATE_STAGE5; } else if (randVal <= ConfigStorm.Storm_PercentChanceOf_C4_Cyclone) { return STATE_STAGE4; } else if (randVal <= ConfigStorm.Storm_PercentChanceOf_C3_Cyclone) { return STATE_STAGE3; } else if (randVal <= ConfigStorm.Storm_PercentChanceOf_C2_Cyclone) { return STATE_STAGE2; } else if (randVal <= ConfigStorm.Storm_PercentChanceOf_C1_Cyclone) { return STATE_STAGE1; } else if (randVal <= ConfigStorm.Storm_PercentChanceOf_C0_Cyclone) { return STATE_FORMING; } else if (randVal <= ConfigStorm.Storm_PercentChanceOf_Hail) { return STATE_HAIL; } else if (randVal <= ConfigStorm.Storm_PercentChanceOf_HighWind) { return STATE_HIGHWIND; } } return STATE_THUNDER; } public void aimStormAtClosestOrProvidedPlayer(Player entP) { if (entP == null) { entP = manager.getWorld().getNearestPlayer(pos.x, pos.y, pos.z, -1, false); } if (entP != null) { aimAtCoords(entP.position()); } } public void aimAtCoords(Vec3 vec) { Random rand = new Random(); double var11 = vec.x() - pos.x; double var15 = vec.z() - pos.z; float yaw = -(float)(Math.atan2(var11, var15) * 180.0D / Math.PI); //weather override! //yaw = weatherMan.wind.direction; int size = ConfigTornado.Storm_Tornado_aimAtPlayerAngleVariance; if (size > 0) { if (!Weather.isLoveTropicsInstalled()) { yaw += rand.nextInt(size) - (size / 2); } } angleIsOverridden = true; angleMovementTornadoOverride = yaw; //Weather.dbg("stormfront aimed at player " + CoroUtilEntity.getName(entP)); } public void aimAwayFromCoords(Vec3 vec) { Random rand = new Random(); double var11 = vec.x() - pos.x; double var15 = vec.z() - pos.z; float yaw = -(float)(Math.atan2(var11, var15) * 180.0D / Math.PI); //invert yaw += 180; angleIsOverridden = true; angleMovementTornadoOverride = yaw; //Weather.dbg("stormfront aimed at player " + CoroUtilEntity.getName(entP)); } //FYI rain doesnt count as storm public void setNoStorm() { Weather.dbg("storm ID: " + this.ID + " - ended storm event"); levelCurIntensityStage = STATE_NORMAL; levelCurStagesIntensity = 0; } @OnlyIn(Dist.CLIENT) public void tickClient() { if (false && ConfigCoroUtil.useLoggingDebug && SceneEnhancer.particleBehavior != null) { TextureAtlasSprite sprite = ParticleRegistry.tumbleweed; ParticleCrossSection part = new ParticleCrossSection(manager.getWorld(), pos.x, pos.y - 20, pos.z, 0, 0, 0, sprite); SceneEnhancer.particleBehavior.initParticle(part); SceneEnhancer.particleBehavior.initParticleSandstormTumbleweed(part); part.windWeight = 9999; part.setGravity(0.5F); if (cloudlessStorm) { part.setGravity(0); } SceneEnhancer.particleBehavior.particles.add(part); part.spawnAsWeatherEffect(); } if (isCloudlessStorm()) return; if (particleBehaviorFog == null) { particleBehaviorFog = new ParticleBehaviorFog(new Vec3(pos.x, pos.y, pos.z)); //particleBehaviorFog.sourceEntity = this; } else { if (!Minecraft.getInstance().isPaused()) { particleBehaviorFog.tickUpdateList(); } } Player entP = Minecraft.getInstance().player; spinSpeed = 0.02D; double spinSpeedMax = 0.4D; /*if (isHurricane()) { spinSpeed = spinSpeedMax * 1.2D; Weather.dbg("spin speed: " + spinSpeed); } else */if (isCycloneFormingOrGreater()) { spinSpeed = spinSpeedMax * 0.00D + ((levelCurIntensityStage-levelStormIntensityFormingStartVal+1) * spinSpeedMax * 0.2D); //Weather.dbg("spin speed: " + spinSpeed); } else if (isTornadoFormingOrGreater()) { spinSpeed = spinSpeedMax * 0.2D; } else if (levelCurIntensityStage >= STATE_HIGHWIND) { spinSpeed = spinSpeedMax * 0.05D; } else { spinSpeed = spinSpeedMax * 0.02D; } //bonus! if (isHurricane()) { spinSpeed += 0.1D; } if (size == 0) size = 1; int delay = Math.max(1, (int)(100F / size * 1F)); int loopSize = 1;//(int)(1 * size * 0.1F); int extraSpawning = 0; if (isSpinning()) { loopSize += 4; extraSpawning = 300; } //adjust particle creation rate for upper tropical cyclone work if (stormType == TYPE_WATER) { if (levelCurIntensityStage >= STATE_STAGE5) { loopSize = 10; extraSpawning = 800; } else if (levelCurIntensityStage >= STATE_STAGE4) { loopSize = 8; extraSpawning = 700; } else if (levelCurIntensityStage >= STATE_STAGE3) { loopSize = 6; extraSpawning = 500; } else if (levelCurIntensityStage >= STATE_STAGE2) { loopSize = 4; extraSpawning = 400; } else { extraSpawning = 300; } } //Weather.dbg("size: " + size + " - delay: " + delay); Random rand = new Random(); Vec3 playerAdjPos = new Vec3(entP.getX(), pos.y, entP.getZ()); double maxSpawnDistFromPlayer = 512; //maintain clouds new system //spawn clouds if (!pet && !baby && this.manager.getWorld().getGameTime() % (delay + (isSpinning() ? ConfigStorm.Storm_ParticleSpawnDelay : ConfigMisc.Cloud_ParticleSpawnDelay)) == 0) { for (int i = 0; i < loopSize; i++) { /*if (listParticlesCloud.size() == 0) { double spawnRad = 1; Vec3 tryPos = new Vec3(pos.x + (rand.nextDouble()*spawnRad) - (rand.nextDouble()*spawnRad), layers.get(layer), pos.z + (rand.nextDouble()*spawnRad) - (rand.nextDouble()*spawnRad)); EntityRotFX particle; if (WeatherUtil.isAprilFoolsDay()) { particle = spawnFogParticle(tryPos.x, tryPos.y, tryPos.z, 0, ParticleRegistry.chicken); } else { particle = spawnFogParticle(tryPos.x, tryPos.y, tryPos.z, 0, ParticleRegistry.cloud256_test); } listParticlesCloud.add(particle); }*/ if (listParticlesCloud.size() < (size + extraSpawning) / 1F) { double spawnRad = size; /*if (layer != 0) { spawnRad = size * 5; }*/ //Weather.dbg("listParticlesCloud.size(): " + listParticlesCloud.size()); //Vec3 tryPos = new Vec3(pos.x + (rand.nextDouble()*spawnRad) - (rand.nextDouble()*spawnRad), layers.get(layer), pos.z + (rand.nextDouble()*spawnRad) - (rand.nextDouble()*spawnRad)); Vec3 tryPos = new Vec3(pos.x + (rand.nextDouble()*spawnRad) - (rand.nextDouble()*spawnRad), getPosTop().y + 30, pos.z + (rand.nextDouble()*spawnRad) - (rand.nextDouble()*spawnRad)); if (tryPos.distanceTo(playerAdjPos) < maxSpawnDistFromPlayer) { if (getAvoidAngleIfTerrainAtOrAheadOfPosition(getAdjustedAngle(), tryPos) == 0) { EntityRotFX particle; if (WeatherUtil.isAprilFoolsDay() && !Weather.isLoveTropicsInstalled()) { particle = spawnFogParticle(tryPos.x, tryPos.y, tryPos.z, 0, ParticleRegistry.chicken); } else { particle = spawnFogParticle(tryPos.x, tryPos.y, tryPos.z, 0); if (isFirenado && isSpinning()) { //if (particle.getEntityId() % 20 < 5) { particle.setSprite(ParticleRegistry.cloud256_fire); particle.setColor(1F, 1F, 1F); //} } } /*if (layer == 0) { particle.particleScale = 500; } else { particle.particleScale = 2000; }*/ listParticlesCloud.add(particle); } } } } } //ground effects if (levelCurIntensityStage >= STATE_HIGHWIND && !baby && !pet) { for (int i = 0; i < (stormType == TYPE_WATER ? 50 : 3)/*loopSize/2*/; i++) { if (listParticlesGround.size() < (stormType == TYPE_WATER ? 600 : 150)/*size + extraSpawning*/) { double spawnRad = size/4*3; if (stormType == TYPE_WATER) { spawnRad = size*3; } //Weather.dbg("listParticlesCloud.size(): " + listParticlesCloud.size()); Vec3 tryPos = new Vec3(pos.x + (rand.nextDouble()*spawnRad) - (rand.nextDouble()*spawnRad), posGround.y, pos.z + (rand.nextDouble()*spawnRad) - (rand.nextDouble()*spawnRad)); if (tryPos.distanceTo(playerAdjPos) < maxSpawnDistFromPlayer) { int groundY = WeatherUtilBlock.getPrecipitationHeightSafe(manager.getWorld(), new BlockPos((int)tryPos.x, 0, (int)tryPos.z)).getY(); EntityRotFX particle; if (WeatherUtil.isAprilFoolsDay() && !Weather.isLoveTropicsInstalled()) { particle = spawnFogParticle(tryPos.x, groundY + 3, tryPos.z, 0, ParticleRegistry.potato); } else { particle = spawnFogParticle(tryPos.x, groundY + 3, tryPos.z, 0); } particle.setScale(200 * 0.15F); particle.rotationYaw = rand.nextInt(360); particle.rotationPitch = rand.nextInt(360); listParticlesGround.add(particle); //Weather.dbg("ground fog!"); /*if (layer == 0) { particle.particleScale = 500; } else { particle.particleScale = 2000; }*/ //listParticlesCloud.add(particle); } } } } delay = 1; loopSize = 2; double spawnRad = size/48; if (levelCurIntensityStage >= STATE_STAGE5) { spawnRad = 200; loopSize = 10; sizeMaxFunnelParticles = 1200; } else if (levelCurIntensityStage >= STATE_STAGE4) { spawnRad = 150; loopSize = 8; sizeMaxFunnelParticles = 1000; } else if (levelCurIntensityStage >= STATE_STAGE3) { spawnRad = 100; loopSize = 6; sizeMaxFunnelParticles = 800; } else if (levelCurIntensityStage >= STATE_STAGE2) { spawnRad = 50; loopSize = 4; sizeMaxFunnelParticles = 600; } else { sizeMaxFunnelParticles = 600; } boolean tornadoV2 = true; //spawn funnel if (!tornadoV2) { if (isTornadoFormingOrGreater() || (attrib_waterSpout)) { if (this.manager.getWorld().getGameTime() % (delay + ConfigStorm.Storm_ParticleSpawnDelay) == 0) { for (int i = 0; i < loopSize; i++) { //temp comment out //if (attrib_tornado_severity > 0) { //Weather.dbg("spawn"); //trim! if (listParticlesFunnel.size() >= sizeMaxFunnelParticles) { listParticlesFunnel.get(0).remove(); listParticlesFunnel.remove(0); } if (listParticlesFunnel.size() < sizeMaxFunnelParticles) { Vec3 tryPos = new Vec3(pos.x + (rand.nextDouble() * spawnRad) - (rand.nextDouble() * spawnRad), pos.y, pos.z + (rand.nextDouble() * spawnRad) - (rand.nextDouble() * spawnRad)); //int y = entP.world.getPrecipitationHeight((int)tryPos.x, (int)tryPos.z); if (tryPos.distanceTo(playerAdjPos) < maxSpawnDistFromPlayer) { EntityRotFX particle; if (!isFirenado/* && false*/) { if (WeatherUtil.isAprilFoolsDay() && !Weather.isLoveTropicsInstalled()) { particle = spawnFogParticle(tryPos.x, posBaseFormationPos.y, tryPos.z, 1, ParticleRegistry.potato); } else { particle = spawnFogParticle(tryPos.x, posBaseFormationPos.y, tryPos.z, 1); } } else { particle = spawnFogParticle(tryPos.x, posBaseFormationPos.y, tryPos.z, 1, ParticleRegistry.cloud256_fire); } //move these to a damn profile damnit! particle.setMaxAge(150 + ((levelCurIntensityStage - 1) * 100) + rand.nextInt(100)); float baseBright = 0.3F; float randFloat = (rand.nextFloat() * 0.6F); particle.rotationYaw = rand.nextInt(360); float finalBright = Math.min(1F, baseBright + randFloat); //highwind aka spout in this current code location if (levelCurIntensityStage == STATE_HIGHWIND) { particle.setScale(150 * 0.15F); particle.setColor(finalBright - 0.2F, finalBright - 0.2F, finalBright); } else { particle.setScale(250 * 0.15F); particle.setColor(finalBright, finalBright, finalBright); } if (isFirenado) { particle.setColor(1F, 1F, 1F); particle.setScale(particle.getScale() * 0.7F); } listParticlesFunnel.add(particle); //System.out.println(listParticlesFunnel.size()); } } else { //Weather.dbg("particles maxed"); } } } } for (int i = 0; i < listParticlesFunnel.size(); i++) { EntityRotFX ent = listParticlesFunnel.get(i); //System.out.println(ent.getPosY()); if (!ent.isAlive()) { listParticlesFunnel.remove(ent); } else if (ent.getPosY() > pos.y) { ent.remove(); listParticlesFunnel.remove(ent); //System.out.println("asd"); } else { double var16 = this.pos.x - ent.getPosX(); double var18 = this.pos.z - ent.getPosZ(); ent.rotationYaw = -(float) (Math.atan2(var18, var16) * 180.0D / Math.PI) - 90.0F; ent.rotationYaw -= ent.getEntityId() % 90; ent.rotationPitch = 30F; //fade spout blue to grey if (levelCurIntensityStage == STATE_HIGHWIND) { int fadingDistStart = 30; if (ent.getPosY() > posGround.y + fadingDistStart) { float maxVal = ent.bCol; float fadeRate = 0.002F; ent.setColor(Math.min(maxVal, ent.rCol + fadeRate), Math.min(maxVal, ent.gCol + fadeRate), maxVal); } } spinEntity(ent); } } } if (isTornadoFormingOrGreater() || (attrib_waterSpout)) { if (manager.getWorld().getGameTime() % 5 == 0) { //CULog.dbg("listParticlesDebris.size(): " + listParticlesDebris.size()); } for (int i = 0; i < listParticlesDebris.size(); i++) { EntityRotFX ent = listParticlesDebris.get(i); if (!ent.isAlive()/* && false*/) { //CULog.dbg("removed: " + ent); listParticlesDebris.remove(ent); } else { /*this.xxa *= 0.98F; this.zza *= 0.98F;*/ double speedXZ = Math.sqrt(ent.getMotionX() * ent.getMotionX() + ent.getMotionY() * ent.getMotionY() + ent.getMotionZ() * ent.getMotionZ()); if (speedXZ < 30) { Vec3 motion = spinObject(ent.getPos(), new Vec3(ent.getMotionX(), ent.getMotionY(), ent.getMotionZ()), false, 0.91F, 1F, ent instanceof ParticleCube, 0); //Vec3 motion = spinObject(ent.getPos(), new Vec3(ent.getMotionX(), ent.getMotionY(), ent.getMotionZ()), false, 0.85F); float damp = 1F; damp = ent.getWindWeight() / 5F; motion = motion.multiply(damp, 1F, damp); //System.out.println("motion: " + motion); ent.setMotionX(motion.x); ent.setMotionY(motion.y); ent.setMotionZ(motion.z); //trying to match entity drag and gravity /*ent.setMotionX(ent.getMotionX() * 0.91); ent.setMotionY(ent.getMotionY() - 0.08); ent.setMotionZ(ent.getMotionZ() * 0.91);*/ //ent.setMotionX(ent.getMotionX() * 0.91); //ent.setMotionY(ent.getMotionY() - 0.08); //ent.setMotionZ(ent.getMotionZ() * 0.91); } } } } for (int i = 0; i < listParticlesCloud.size(); i++) { EntityRotFX ent = listParticlesCloud.get(i); if (!ent.isAlive()) { listParticlesCloud.remove(ent); } else { //ent.posX = pos.x + i*10; /*float radius = 50 + (i/1F); float posX = (float) Math.sin(ent.getEntityId()); float posZ = (float) Math.cos(ent.getEntityId()); ent.setPosition(pos.x + posX*radius, ent.posY, pos.z + posZ*radius);*/ double curSpeed = Math.sqrt(ent.getMotionX() * ent.getMotionX() + ent.getMotionY() * ent.getMotionY() + ent.getMotionZ() * ent.getMotionZ()); double curDist = ent.getDistance(getPosTop().x, ent.getPosY(), getPosTop().z); float dropDownRange = 15F; float extraDropCalc = 0; if (curDist < 200 && ent.getEntityId() % 20 < 5) { //cyclone and hurricane dropdown modifications here extraDropCalc = ((ent.getEntityId() % 20) * dropDownRange); if (isCycloneFormingOrGreater()) { extraDropCalc = ((ent.getEntityId() % 20) * dropDownRange * 5F); //Weather.dbg("extraDropCalc: " + extraDropCalc); } } if (isSpinning()) { double speed = spinSpeed + (rand.nextDouble() * 0.01D); double distt = size;//300D; double vecX = ent.getPosX() - getPosTop().x; double vecZ = ent.getPosZ() - getPosTop().z; float angle = (float)(Math.atan2(vecZ, vecX) * 180.0D / Math.PI); //System.out.println("angle: " + angle); //fix speed causing inner part of formation to have a gap angle += speed * 50D; //angle += 20; angle -= (ent.getEntityId() % 10) * 3D; //random addition angle += rand.nextInt(10) - rand.nextInt(10); if (curDist > distt) { //System.out.println("curving"); angle += 40; //speed = 1D; } //keep some near always - this is the lower formation part if (ent.getEntityId() % 20 < 5) { if (levelCurIntensityStage >= STATE_FORMING) { if (stormType == TYPE_WATER) { angle += 40 + ((ent.getEntityId() % 5) * 4); if (curDist > 150 + ((levelCurIntensityStage-levelStormIntensityFormingStartVal+1) * 30)) { angle += 10; } } else { angle += 30 + ((ent.getEntityId() % 5) * 4); } } else { //make a wider spinning lower area of cloud, for high wind if (curDist > 150) { angle += 50 + ((ent.getEntityId() % 5) * 4); } } double diffX = this.getPosTop().x - ent.getPosX(); double diffZ = this.getPosTop().z - ent.getPosZ(); ent.rotationYaw = (float)(Math.atan2(diffZ, diffX) * 180.0D / Math.PI) - 90.0F; ent.rotationYaw = -(float)(Math.atan2(diffZ, diffX) * 180.0D / Math.PI) - 90.0F; //ent.rotationYaw = (float) Math.toDegrees(Math.atan2(diffZ, diffX)) + 90F; ent.rotationPitch = 20F - (ent.getEntityId() % 10); } /*if (curDist < 30) { ent.motionY -= 0.2D; if (ent.rotationPitch > 0) { ent.rotationPitch--; } else if (ent.rotationPitch < 0) { ent.rotationPitch++; } else { ent.rotationPitch = 0; } angle = -45; } else { if (ent.rotationPitch > 90) { ent.rotationPitch--; } else if (ent.rotationPitch < 90) { ent.rotationPitch++; } else { ent.rotationPitch = 90; } //angle = 90; }*/ if (curSpeed < speed * 20D) { ent.setMotionX(ent.getMotionX() + -Math.sin(Math.toRadians(angle)) * speed); ent.setMotionZ(ent.getMotionZ() + Math.cos(Math.toRadians(angle)) * speed); } } else { float cloudMoveAmp = 0.2F * (1 + layer); float speed = getAdjustedSpeed() * cloudMoveAmp; float angle = getAdjustedAngle(); //TODO: prevent new particles spawning inside or near solid blocks if ((manager.getWorld().getGameTime()+this.ID) % 40 == 0) { ent.avoidTerrainAngle = getAvoidAngleIfTerrainAtOrAheadOfPosition(angle, ent.getPos()); } angle += ent.avoidTerrainAngle; if (ent.avoidTerrainAngle != 0) { /*float angleAdj = 90; if (this.ID % 2 == 0) { angleAdj = -90; } angle += angleAdj;*/ speed *= 0.5D; } dropDownRange = 5; if (/*curDist < 200 && */ent.getEntityId() % 20 < 5) { extraDropCalc = ((ent.getEntityId() % 20) * dropDownRange); } if (curSpeed < speed * 1D) { ent.setMotionX(ent.getMotionX() + -Math.sin(Math.toRadians(angle)) * speed); ent.setMotionZ(ent.getMotionZ() + Math.cos(Math.toRadians(angle)) * speed); } } if (Math.abs(ent.getPosY() - (getPosTop().y - extraDropCalc)) > 2F) { if (ent.getPosY() < getPosTop().y - extraDropCalc) { ent.setMotionY(ent.getMotionY() + 0.1D); } else { ent.setMotionY(ent.getMotionY() - 0.1D); } } float dropDownSpeedMax = 0.15F; if (isCycloneFormingOrGreater()) { dropDownSpeedMax = 0.9F; } if (ent.getMotionY() < -dropDownSpeedMax) { ent.setMotionY(-dropDownSpeedMax); } if (ent.getMotionY() > dropDownSpeedMax) { ent.setMotionY(dropDownSpeedMax); } //double distToGround = ent.world.getHeightValue((int)pos.x, (int)pos.z); //ent.setPosition(ent.posX, pos.y, ent.posZ); } /*if (ent.getAge() > 300) { ent.remove(); listParticles.remove(ent); }*/ } for (int i = 0; i < listParticlesGround.size(); i++) { EntityRotFX ent = listParticlesGround.get(i); double curDist = ent.getDistance(pos.x, ent.getPosY(), pos.z); if (!ent.isAlive()) { listParticlesGround.remove(ent); } else { double curSpeed = Math.sqrt(ent.getMotionX() * ent.getMotionX() + ent.getMotionY() * ent.getMotionY() + ent.getMotionZ() * ent.getMotionZ()); double speed = Math.max(0.2F, 5F * spinSpeed) + (rand.nextDouble() * 0.01D); double distt = size;//300D; double vecX = ent.getPosX() - pos.x; double vecZ = ent.getPosZ() - pos.z; float angle = (float)(Math.atan2(vecZ, vecX) * 180.0D / Math.PI); angle += 85; int maxParticleSize = 60; if (stormType == TYPE_WATER) { maxParticleSize = 150; speed /= 5D; } ent.setScale((float) Math.min(maxParticleSize * 0.15F, curDist * 2F * 0.15F)); if (curDist < 20) { ent.remove(); } double var16 = this.pos.x - ent.getPosX(); double var18 = this.pos.z - ent.getPosZ(); //ent.rotationYaw += 5;//(float)(Math.atan2(var18, var16) * 180.0D / Math.PI) - 90.0F; //ent.rotationPitch = 0;//-20F - (ent.getEntityId() % 10); if (curSpeed < speed * 20D) { ent.setMotionX(ent.getMotionX() + -Math.sin(Math.toRadians(angle)) * speed); ent.setMotionZ(ent.getMotionZ() + Math.cos(Math.toRadians(angle)) * speed); } } } //System.out.println("size: " + listParticlesCloud.size()); } public float getAdjustedSpeed() { return manager.getWindManager().getWindSpeedForClouds(); } public float getAdjustedAngle() { float angle = manager.getWindManager().getWindAngleForClouds(); //float angleAdjust = Math.max(10, Math.min(45, 45F * levelTemperature * 0.6F)); float angleAdjust = 45; float targetYaw = 0; //coldfronts go south to 0, warmfronts go north to 180 if (levelTemperature > 0) { //Weather.dbg("warmer!"); targetYaw = 180; } else { //Weather.dbg("colder!"); targetYaw = 0; } float bestMove = Mth.wrapDegrees(targetYaw - angle); if (Math.abs(bestMove) < 180/* - (angleAdjust * 2)*/) { if (bestMove > 0) angle -= angleAdjust; if (bestMove < 0) angle += angleAdjust; } if (manager.getWorld().getGameTime() % 40 == 0) { //Weather.dbg("ID: " + ID + " - " + manager.getWindManager().getWindAngleForClouds() + " - final angle: " + angle + " levelTemperature: " + levelTemperature); } return angle; } public float getAvoidAngleIfTerrainAtOrAheadOfPosition(float angle, Vec3 pos) { double scanDistMax = 120; for (int scanAngle = -20; scanAngle < 20; scanAngle += 10) { for (double scanDistRange = 20; scanDistRange < scanDistMax; scanDistRange += 10) { double scanX = pos.x + (-Math.sin(Math.toRadians(angle + scanAngle)) * scanDistRange); double scanZ = pos.z + (Math.cos(Math.toRadians(angle + scanAngle)) * scanDistRange); int height = WeatherUtilBlock.getPrecipitationHeightSafe(this.manager.getWorld(), CoroUtilBlock.blockPos(scanX, 0, scanZ)).getY(); if (pos.y < height) { if (scanAngle <= 0) { return 90; } else { return -90; } } } } return 0; } public Player getRandomNearPlayer(Entity entityLineOfSightSource) { //Player player = manager.getWorld().getNearbyPlayers(TargetingConditions.forCombat().range(64.0D), ) List players = manager.getWorld().players(); List playersNear = new ArrayList<>(); for (Player player : players) { if (!player.isSpectator() && player.position().distanceTo(posGround) < 196 && player.hasLineOfSight(entityLineOfSightSource)) { playersNear.add(player); } } if (!playersNear.isEmpty()) { Collections.shuffle(playersNear); return playersNear.get(0); } return null; } public void spinEntityv2(Entity entity) { if (entity.getPersistentData().getBoolean("tornado_shoot")) { if (!entity.getPersistentData().contains("tornado_shoot_target")) { Player player = getRandomNearPlayer(entity); if (player != null) { entity.getPersistentData().putString("tornado_shoot_target", player.getUUID().toString()); } } else { UUID uuid = UUID.fromString(entity.getPersistentData().getString("tornado_shoot_target")); Player player = manager.getWorld().getPlayerByUUID(uuid); if (player != null) { double vecx = player.position().x - entity.position().x; double vecy = player.position().y - entity.position().y; double vecz = player.position().z - entity.position().z; Vec3 vec = new Vec3(vecx, vecy, vecz); double speed = 2F; vec = vec.normalize().multiply(speed, speed, speed); entity.setDeltaMovement(vec.x, vec.y, vec.z); float rotationYaw = (float)(Mth.atan2(vecz, vecx) * 180.0D / Math.PI) - 90.0F; entity.setYRot(rotationYaw); if (player.position().distanceTo(entity.position()) < 3 && entity.isAlive()) { player.hurt(manager.getWorld().damageSources().cactus(), 1.5F); entity.kill(); } if (entity.isInWater()) { entity.getPersistentData().putBoolean("tornado_shoot", false); } } } } else { float dampenY = 1F; float dampenXZ = 1F; float grabAdj = 0F; double entHeightFromBase = Math.max(0.1F, entity.getY() - posBaseFormationPos.y); if (entity instanceof Player) { if (((Player) entity).getMainHandItem().getItem().toString().contains("acid_repellent_umbrella") || ((Player) entity).getOffhandItem().getItem().toString().contains("acid_repellent_umbrella")) { if (entHeightFromBase > 80) { dampenY = 0.7F; } dampenXZ = 1.4F; grabAdj -= 20; } float weightAdj = WeatherUtilEntity.getWeightAdjFromEquipment(1F, (Player) entity); dampenY /= weightAdj; dampenXZ /= weightAdj; } entity.setDeltaMovement(spinObject(entity.position(), entity.getDeltaMovement(), entity instanceof Player, dampenXZ, dampenY, false, grabAdj)); if (!ConfigTornado.Storm_Tornado_fallDamage || CoroUtilEntOrParticle.getMotionY(entity) > -0.8) { entity.fallDistance = 0F; } if (isFirenado) { Vec3 posEnt = entity.position(); Vec3 posFunnel = getFunnelCenter(posEnt); double distXZ = entity.position().distanceTo(new Vec3(posFunnel.x, posEnt.y, posFunnel.z)); if (entHeightFromBase > 5 && entHeightFromBase < 70 && distXZ < (entHeightFromBase * 1.3)) { entity.setSecondsOnFire(6); } } if (entHeightFromBase > 90) { if (Weather.isLoveTropicsInstalled()) { //TODO: for LT, reenable or make it a soft dependency somehow /*if (isSharknado() && entity instanceof SharkEntity) { entity.getPersistentData().putBoolean("tornado_shoot", true); }*/ if (isSharknado() && entity instanceof Dolphin) { entity.getPersistentData().putBoolean("tornado_shoot", true); } } else { if (isSharknado() && entity instanceof Dolphin) { entity.getPersistentData().putBoolean("tornado_shoot", true); } } } } } public Vec3 getFunnelCenter(Vec3 position) { Vec3 posCenter = getPosTop(); for (Layer layer : tornadoFunnelSimple.listLayers) { if (position.y - 1.5F < layer.getPos().y) { posCenter = layer.getPos(); break; } } return posCenter; } public Vec3 spinObject(Vec3 position, Vec3 motion, boolean forPlayer, float dampenXZ, float dampenY, boolean forCube, float grabAdj) { Vec3 posCenter = getFunnelCenter(position); double vecx = posCenter.x - position.x; double vecz = posCenter.z - position.z; Vec3 vecXZ = new Vec3(posCenter.x, position.y, posCenter.z); double distXZMax = tornadoFunnelSimple.getConfig().getEntityPullDistXZ(); double distXZMaxForYGrab = tornadoFunnelSimple.getConfig().getEntityPullDistXZForY(); double distXZ = Math.min(position.distanceTo(vecXZ), distXZMax); double distXZForYGrab = Math.min(position.distanceTo(vecXZ), distXZMaxForYGrab); double grabAmp = (float)(levelCurIntensityStage - STATE_FORMING) / (float)(STATE_STAGE5 - STATE_FORMING); float angle = (float)(Mth.atan2(vecz, vecx) * 180.0D / Math.PI + 180F); //more is tighter angle += ConfigTornado.Storm_Tornado_extraGrabAngle; if (pet) angle += 50; if (forCube) { angle += 20; } angle += grabAdj; double entHeightFromBase = Math.max(0.1F, position.y - posBaseFormationPos.y); double heightMathMax = 50 * 2.5; heightMathMax = 50 * 3.5; //amp from new size here? if (baby) heightMathMax = 15; if (pet) heightMathMax = 4; if (playerControlled) heightMathMax = 40; //double heightMathMax = tornadoFunnelSimple.getConfig().getHeight(); double heightAmp = (heightMathMax - entHeightFromBase) / heightMathMax; //CULog.dbg("heightAmp: " + heightAmp); if (!pet) { angle += (40 * heightAmp); } //wider grab as tornado gets bigger angle -= 40 * grabAmp; angle = (float) Math.toRadians(angle); double pullStrength = 0.2; double pullStrengthY = 0.2; pullStrengthY += 0.2 * grabAmp; double pullYAmp = 1D; //as tornado shrinks to and from forming, adjust its pull power if (levelCurIntensityStage == STATE_FORMING) { pullYAmp = levelCurStagesIntensity; } pullStrengthY *= pullYAmp; pullStrength *= pullYAmp; if (pet) pullStrength = 0.05; if (pet) pullStrengthY = 0.05; if (sharknado && forPlayer) pullStrength = 0.05; if (sharknado && forPlayer) pullStrengthY = 0.1; double pullY = pullStrengthY * (distXZMaxForYGrab - distXZForYGrab) / distXZMaxForYGrab; double pullXZ = pullStrength * (distXZMax - distXZ) / distXZMax; double xx = -Math.sin(angle) * pullXZ; double zz = Math.cos(angle) * pullXZ; double yy = pullY; if (!baby && motion.y > 0.5F) { yy = 0; } if (pet && motion.y > 0.15F) { yy = 0; } return new Vec3(motion.x + (xx * dampenXZ), motion.y + (yy * dampenY), motion.z + (zz * dampenXZ)); } public void spinEntity(Object entity1) { StormObject entT = this; StormObject entity = this; WeatherEntityConfig conf = getWeatherEntityConfigForStorm();//WeatherTypes.weatherEntTypes.get(curWeatherType); Random rand = new Random(); /*if (entity instanceof EntTornado) { entT = (EntTornado) entity; }*/ boolean forTornado = true;//entT != null; Level world = CoroUtilEntOrParticle.getWorld(entity1); long worldTime = world.getGameTime(); Entity ent = null; if (entity1 instanceof Entity) { ent = (Entity) entity1; } //ConfigTornado.Storm_Tornado_height; double radius = 10D; double scale = conf.tornadoWidthScale; double d1 = entity.pos.x - CoroUtilEntOrParticle.getPosX(entity1); double d2 = entity.pos.z - CoroUtilEntOrParticle.getPosZ(entity1); if (conf.type == conf.TYPE_SPOUT) { float range = 30F * (float) Math.sin((Math.toRadians(((worldTime * 0.5F) + (ID * 50)) % 360))); float heightPercent = (float) (1F - ((CoroUtilEntOrParticle.getPosY(entity1) - posGround.y) / (pos.y - posGround.y))); float posOffsetX = (float) Math.sin((Math.toRadians(heightPercent * 360F))); float posOffsetZ = (float) -Math.cos((Math.toRadians(heightPercent * 360F))); //Weather.dbg("posOffset: " + posOffset); //d1 += 50F*heightPercent*posOffset; d1 += range*posOffsetX; d2 += range*posOffsetZ; } float f = (float)((Math.atan2(d2, d1) * 180D) / Math.PI) - 90F; float f1; for (f1 = f; f1 < -180F; f1 += 360F) { } for (; f1 >= 180F; f1 -= 360F) { } double distY = entity.pos.y - CoroUtilEntOrParticle.getPosY(entity1); double distXZ = Math.sqrt(Math.abs(d1)) + Math.sqrt(Math.abs(d2)); if (CoroUtilEntOrParticle.getPosY(entity1) - entity.pos.y < 0.0D) { distY = 1.0D; } else { distY = CoroUtilEntOrParticle.getPosY(entity1) - entity.pos.y; } if (distY > maxHeight) { distY = maxHeight; } //float weight = WeatherUtilEntity.getWeight(entity1, forTornado); float weight = WeatherUtilEntity.getWeight(entity1); double grab = (10D / weight)/* / ((distY / maxHeight) * 1D)*/ * ((Math.abs((maxHeight - distY)) / maxHeight)); float pullY = 0.0F; //some random y pull if (rand.nextInt(5) != 0) { //pullY = 0.035F; } if (distXZ > 5D) { grab = grab * (radius / distXZ); } //Weather.dbg("TEMP!!!!"); //WeatherTypes.initWeatherTypes(); pullY += (float)(conf.tornadoLiftRate / (weight / 2F)/* * (Math.abs(radius - distXZ) / radius)*/); if (entity1 instanceof Player) { double adjPull = 0.2D / ((weight * ((distXZ + 1D) / radius))); /*if (!entity1.onGround) { adjPull /= (((float)(((double)playerInAirTime+1D) / 200D)) * 15D); }*/ pullY += adjPull; //0.2D / ((getWeight(entity1) * ((distXZ+1D) / radius)) * (((distY) / maxHeight)) * 3D); //grab = grab + (10D * ((distY / maxHeight) * 1D)); //double adjGrab = (10D * (((float)(((double)WeatherUtilEntity.playerInAirTime + 1D) / 400D)))); double adjGrab = (10D * (((float)(((double)0 + 1D) / 400D)))); if (adjGrab > 50) { adjGrab = 50D; } if (adjGrab < -50) { adjGrab = -50D; } grab = grab - adjGrab; if (CoroUtilEntOrParticle.getMotionY(entity1) > -0.8) { //System.out.println(entity1.motionY); ent.fallDistance = 0F; } } else if (entity1 instanceof LivingEntity) { double adjPull = 0.005D / ((weight * ((distXZ + 1D) / radius))); /*if (!entity1.onGround) { adjPull /= (((float)(((double)playerInAirTime+1D) / 200D)) * 15D); }*/ pullY += adjPull; //0.2D / ((getWeight(entity1) * ((distXZ+1D) / radius)) * (((distY) / maxHeight)) * 3D); //grab = grab + (10D * ((distY / maxHeight) * 1D)); int airTime = ent.getPersistentData().getInt("timeInAir"); double adjGrab = (10D * (((float)(((double)(airTime) + 1D) / 400D)))); if (adjGrab > 50) { adjGrab = 50D; } if (adjGrab < -50) { adjGrab = -50D; } grab = grab - adjGrab; if (ent.getDeltaMovement().y > -1.5) { ent.fallDistance = 0F; } if (ent.getDeltaMovement().y > 0.3F) ent.setDeltaMovement(ent.getDeltaMovement().x, 0.3F, ent.getDeltaMovement().z); if (forTornado) ent.setOnGround(false); //its always raining during these, might as well extinguish them if (!isFirenado) { ent.clearFire(); } //System.out.println(adjPull); } grab += conf.relTornadoSize; double profileAngle = Math.max(1, (75D + grab - (10D * scale))); f1 = (float)((double)f1 + profileAngle); //debug - dont do this here, breaks server /*if (entity1 instanceof EntityIconFX) { if (entity1.getEntityId() % 20 < 5) { if (((EntityIconFX) entity1).renderOrder != -1) { if (entity1.world.getGameTime() % 40 == 0) { //Weather.dbg("final grab angle: " + profileAngle); } } } }*/ if (entT != null) { if (entT.scale != 1F) f1 += 20 - (20 * entT.scale); } float f3 = (float)Math.cos(-f1 * 0.01745329F - (float)Math.PI); float f4 = (float)Math.sin(-f1 * 0.01745329F - (float)Math.PI); float f5 = conf.tornadoPullRate * 1; if (entT != null) { if (entT.scale != 1F) f5 *= entT.scale * 1.2F; } if (entity1 instanceof LivingEntity) { //f5 /= (WeatherUtilEntity.getWeight(entity1, forTornado) * ((distXZ + 1D) / radius)); f5 /= (WeatherUtilEntity.getWeight(entity1) * ((distXZ + 1D) / radius)); } //if player and not spout if (entity1 instanceof Player && conf.type != 0) { //System.out.println("grab: " + f5); if (ent.onGround()) { f5 *= 10.5F; } else { f5 *= 5F; } //if (entity1.world.rand.nextInt(2) == 0) entity1.onGround = false; } else if (entity1 instanceof LivingEntity && conf.type != 0) { f5 *= 1.5F; } if (conf.type == conf.TYPE_SPOUT && entity1 instanceof LivingEntity) { f5 *= 0.3F; } float moveX = f3 * f5; float moveZ = f4 * f5; //tornado strength changes float str = 1F; /*if (entity instanceof EntTornado) { str = ((EntTornado)entity).strength; }*/ str = strength; if (conf.type == conf.TYPE_SPOUT && entity1 instanceof LivingEntity) { str *= 0.3F; } pullY *= str / 100F; if (entT != null) { if (entT.scale != 1F) { pullY *= entT.scale * 1.0F; pullY += 0.002F; } } //prevent double+ pull on entities if (entity1 instanceof Entity) { long lastPullTime = ent.getPersistentData().getLong("lastPullTime"); if (lastPullTime == worldTime) { //System.out.println("preventing double pull"); pullY = 0; } ent.getPersistentData().putLong("lastPullTime", worldTime); } setVel(entity1, -moveX, pullY, moveZ); } public void setVel(Object entity, float f, float f1, float f2) { CoroUtilEntOrParticle.setMotionX(entity, CoroUtilEntOrParticle.getMotionX(entity) + f); CoroUtilEntOrParticle.setMotionY(entity, CoroUtilEntOrParticle.getMotionY(entity) + f1); CoroUtilEntOrParticle.setMotionZ(entity, CoroUtilEntOrParticle.getMotionZ(entity) + f2); } @OnlyIn(Dist.CLIENT) public EntityRotFX spawnFogParticle(double x, double y, double z, int parRenderOrder) { return spawnFogParticle(x, y, z, parRenderOrder, ParticleRegistry.cloud256); } @OnlyIn(Dist.CLIENT) public EntityRotFX spawnFogParticle(double x, double y, double z, int parRenderOrder, TextureAtlasSprite tex) { double speed = 0D; Random rand = new Random(); EntityRotFX entityfx = particleBehaviorFog.spawnNewParticleIconFX(Minecraft.getInstance().level, tex, x, y, z, (rand.nextDouble() - rand.nextDouble()) * speed, 0.0D/*(rand.nextDouble() - rand.nextDouble()) * speed*/, (rand.nextDouble() - rand.nextDouble()) * speed, parRenderOrder); particleBehaviorFog.initParticle(entityfx); //potato //entityfx.setColor(1f, 1f, 1f); //lock y //entityfx.spawnY = (float) entityfx.posY; //entityfx.spawnY = ((int)200 - 5) + rand.nextFloat() * 5; entityfx.setCanCollide(false); entityfx.callUpdatePB = false; debugCloudTemperature = false; boolean debug = debugCloudTemperature; if (debug) { //entityfx.setMaxAge(50 + rand.nextInt(10)); } else { } if (levelCurIntensityStage == STATE_NORMAL) { entityfx.setMaxAge(300 + rand.nextInt(100)); } else { entityfx.setMaxAge((size/2) + rand.nextInt(100)); } //pieces that move down with funnel need render order shift, also only for relevant storm formations if (entityfx.getEntityId() % 20 < 5 && isSpinning()) { entityfx.renderOrder = 1; entityfx.setMaxAge((size) + rand.nextInt(100)); } float randFloat = (rand.nextFloat() * 0.6F); float baseBright = 0.7F; if (levelCurIntensityStage > STATE_NORMAL) { baseBright = 0.2F; } else if (attrib_precipitation) { baseBright = 0.2F; } else if (manager.isVanillaRainActiveOnServer) { baseBright = 0.2F; } else { float adj = Math.min(1F, levelWater / levelWaterStartRaining) * 0.6F; baseBright -= adj; } /*if (layer == 1) { baseBright = 0.1F; }*/ float finalBright = Math.min(1F, baseBright+randFloat); /*if (isFirenado) { finalBright = 1F; }*/ entityfx.setColor(finalBright, finalBright, finalBright); //entityfx.setColor(1, 1, 1); //DEBUG if (debug) { if (levelTemperature < 0) { entityfx.setColor(0, 0, finalBright); } else if (levelTemperature > 0) { entityfx.setColor(finalBright, 0, 0); } } entityfx.setTicksFadeInMax(20); entityfx.setTicksFadeOutMax(20); entityfx.setTicksFadeOutMaxOnDeath(20); entityfx.setUseDynamicWindSpeed(false); if (ConfigParticle.Particle_effect_rate != 0) { entityfx.spawnAsWeatherEffect(); //Minecraft.getInstance().particleEngine.add(entityfx); particleBehaviorFog.particles.add(entityfx); } return entityfx; } @Override public void cleanup() { if (tornadoHelper != null) { //we must save the animals if (this.levelCurIntensityStage >= STATE_FORMING) { featherFallAllNearbyPlayers(); } tornadoHelper.cleanup(); } super.cleanup(); tornadoHelper = null; if (tornadoFunnelSimple != null) { tornadoFunnelSimple.cleanup(); } } @OnlyIn(Dist.CLIENT) @Override public void cleanupClient() { super.cleanupClient(); listParticlesCloud.clear(); listParticlesFunnel.clear(); if (particleBehaviorFog != null && particleBehaviorFog.particles != null) particleBehaviorFog.particles.clear(); particleBehaviorFog = null; tornadoHelper = null; if (tornadoFunnelSimple != null) { tornadoFunnelSimple.cleanupClient(); } } public static float getTemperatureMCToWeatherSys(float parOrigVal) { if (parOrigVal > 0) { return 1; } else { return -1; } } public void addWeatherEffectLightning(LightningBoltWeatherNew parEnt, boolean custom) { manager.getWorld().addFreshEntity(parEnt); ((WeatherManagerServer)manager).syncLightningNew(parEnt, custom); } @Override public int getUpdateRateForNetwork() { if (levelCurIntensityStage >= StormObject.STATE_HIGHWIND) { return 2; } else { return super.getUpdateRateForNetwork(); } } public Vec3 getPosTop() { if (isTornadoFormingOrGreater()) { return tornadoFunnelSimple.getPosTop(); } return pos; } public void setupStorm(Entity entity) { if (entity != null) { this.spawnerUUID = entity.getUUID().toString(); initPositions(entity.position()); } this.layer = 0; this.naturallySpawned = false; this.levelTemperature = 0.1F; this.levelWater = this.levelWaterStartRaining * 2; this.attrib_precipitation = true; this.levelCurIntensityStage = this.STATE_STAGE1; this.alwaysProgresses = false; this.initFirstTime(); //lock it to current stage or less this.levelStormIntensityMax = this.levelCurIntensityStage; } public void initPositions(Vec3 pos) { this.pos = pos; currentTopYBlock = calculateTopYBlock(); posGround = new Vec3(pos.x, currentTopYBlock, pos.z); posBaseFormationPos = calculateBaseFormationPos(); } public int calculateTopYBlock() { Level world = manager.getWorld(); int calculatedYPos = WeatherUtilBlock.getPrecipitationHeightSafe(world, new BlockPos(Mth.floor(pos.x), 0, Mth.floor(pos.z)), Heightmap.Types.WORLD_SURFACE_WG).getY(); //TODO: only recalc if pos x z changes boolean filterOutLogs = true; if (filterOutLogs && world.hasChunkAt(CoroUtilBlock.blockPos(pos))) { int y = calculatedYPos-1; BlockState state = world.getBlockState(CoroUtilBlock.blockPos(pos.x, y, pos.z)); //System.out.println("state: " + state); int iter = 0; while ((state.is(BlockTags.LOGS) || state.is(BlockTags.LEAVES) || state.is(BlockTags.CAVE_VINES) || state.is(BlockTags.REPLACEABLE_BY_TREES) || state.is(Blocks.COCOA) || state.is(Blocks.BAMBOO) || state.isAir()) && y > world.getMinBuildHeight()) { y--; state = world.getBlockState(CoroUtilBlock.blockPos(pos.x, y, pos.z)); //CULog.dbg("filter logs, found: " + state); iter++; } if (y > world.getMinBuildHeight()) calculatedYPos = y; //CULog.dbg("filterOutLogs iter: " + iter); } //for survive the tide rising mechanic that doesnt update heightmap if (Weather.isLoveTropicsInstalled()) { while (world.getBlockState(CoroUtilBlock.blockPos(pos.x, calculatedYPos, pos.z)).getBlock() instanceof LiquidBlock && calculatedYPos < world.getMaxBuildHeight()) { calculatedYPos++; } } if (isBaby()) { Player player = getPlayer(); if (player != null) { calculatedYPos = (int) player.position().y; } } //buggy, needs rework if (isPet()) { Player entP = getPlayer(); if (entP != null) { if (entP.position().distanceTo(posGround) < 10) { if (calculatedYPos + 4 > entP.position().y) { int y = (int) (entP.position().y + 2); while (y > entP.position().y - 3) { BlockPos blockPos = CoroUtilBlock.blockPos(pos.x, y, pos.z); BlockPos blockPosUp = CoroUtilBlock.blockPos(pos.x, y+1, pos.z); BlockState state = world.getBlockState(blockPos); BlockState stateUp = world.getBlockState(blockPosUp); if ((!state.isAir() && state.getCollisionShape(world, blockPosUp) != Shapes.empty()) && (stateUp.isAir() || stateUp.getCollisionShape(world, blockPosUp) == Shapes.empty())) { calculatedYPos = y+1; break; } y--; } } } } } return calculatedYPos; } public void setupTornadoAwayFromPlayersAimAtPlayers() { List players = manager.getWorld().players(); List playersNear = new ArrayList<>(); double xAdd = 0; double yAdd = 0; double zAdd = 0; Vec3 vecAdd = new Vec3(0, 0, 0); for (Player player : players) { vecAdd = vecAdd.add(player.position()); } vecAdd = new Vec3(vecAdd.x / players.size(), vecAdd.y / players.size(), vecAdd.z / players.size()); initPositions(vecAdd.add(150, 0, 150)); aimAtCoords(vecAdd); } public void setupPlayerControlledTornado(Entity entity) { this.playerControlled = true; } public Player getPlayer() { if (spawnerUUID.equals("")) return null; return manager.getWorld().getPlayerByUUID(UUID.fromString(spawnerUUID)); } public boolean isPlayerControlled() { return playerControlled; } public void setPlayerControlled(boolean playerControlled) { this.playerControlled = playerControlled; } public int getPlayerControlledTimeLeft() { return playerControlledTimeLeft; } public void setPlayerControlledTimeLeft(int playerControlledTimeLeft) { this.playerControlledTimeLeft = playerControlledTimeLeft; } public boolean isBaby() { return baby; } public void setBaby(boolean baby) { this.baby = baby; } public boolean isPet() { return pet; } public void setPet(boolean pet) { this.pet = pet; } public boolean isPetGrabsItems() { return petGrabsItems; } public void setPetGrabsItems(boolean petGrabsItems) { this.petGrabsItems = petGrabsItems; } public boolean isSharknado() { return sharknado; } public void setSharknado(boolean sharknado) { this.sharknado = sharknado; } public void setAndUpdateTornado() { if (tornadoFunnelSimple == null) { setupTornado(); } //tornadoFunnelSimple.pos = new Vec3(posGround.x, posGround.y, posGround.z); tornadoFunnelSimple.pos = new Vec3(posGround.x, posBaseFormationPos.y, posGround.z); } public TornadoFunnelSimple getTornadoFunnelSimple() { return tornadoFunnelSimple; } public void setTornadoFunnelSimple(TornadoFunnelSimple tornadoFunnelSimple) { this.tornadoFunnelSimple = tornadoFunnelSimple; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public int getAgeSinceTornadoTouchdown() { return ageSinceTornadoTouchdown; } public void setAgeSinceTornadoTouchdown(int ageSinceTornadoTouchdown) { this.ageSinceTornadoTouchdown = ageSinceTornadoTouchdown; } public boolean isBeingDeflectedCached() { return isBeingDeflectedCached; } public void setBeingDeflectedCached(boolean beingDeflectedCached) { isBeingDeflectedCached = beingDeflectedCached; } } ================================================ FILE: src/main/java/weather2/weathersystem/storm/TornadoHelper.java ================================================ package weather2.weathersystem.storm; import com.corosus.coroutil.util.CoroUtilBlock; import com.mojang.authlib.GameProfile; import net.minecraft.client.Minecraft; import net.minecraft.core.BlockPos; import net.minecraft.resources.ResourceKey; import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.util.Mth; import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.effect.MobEffects; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.animal.Animal; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.monster.Enemy; import net.minecraft.world.entity.npc.Npc; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.Level; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.material.MapColor; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.util.FakePlayerFactory; import net.minecraftforge.event.level.BlockEvent; import weather2.ClientTickHandler; import weather2.Weather; import weather2.config.*; import weather2.util.WeatherUtil; import weather2.util.WeatherUtilBlock; import weather2.util.WeatherUtilEntity; import weather2.util.WeatherUtilSound; import weather2.weathersystem.WeatherManagerServer; import weather2.weathersystem.tornado.simple.Layer; import weather2.weathersystem.tornado.simple.TornadoFunnelSimple; import java.util.*; public class TornadoHelper { public StormObject storm; //public int blockCount = 0; public int ripCount = 0; public long lastGrabTime = 0; public int tickGrabCount = 0; public int removeCount = 0; public int tryRipCount = 0; public int tornadoBaseSize = 5; public int grabDist = 100; //potentially an issue var public boolean lastTickPlayerClose; /** * this tick queue isnt perfect, created to reduce chunk updates on client, but not removing block right away messes with block rip logic: * - wont dig for blocks under this block until current is removed * - initially, entries were spam added as the block still existed, changed list to hashmap to allow for blockpos hash lookup before adding another entry * - entity creation relocated to queue processing to initially prevent entity spam, but with entry lookup, not needed, other issues like collision are now the reason why we still relocated entity creation to queue process */ private HashMap listBlockUpdateQueue = new HashMap(); private int queueProcessRate = 40; //for client player, for use of playing sounds public static boolean isOutsideCached = false; //for caching query on if a block damage preventer block is nearby, also assume blocked at first for safety public boolean isBlockGrabbingBlockedCached = true; public long isBlockGrabbingBlockedCached_LastCheck = 0; //static because its a shared list for the whole dimension //public static HashMap flyingBlock_LastQueryTime = new HashMap<>(); //public static HashMap flyingBlock_LastCount = new HashMap<>(); public static GameProfile fakePlayerProfile = null; public static class BlockUpdateSnapshot { //private int dimID; private ResourceKey dimension; private BlockState state; private BlockState statePrev; private BlockPos pos; private boolean createEntityForBlockRemoval; public BlockUpdateSnapshot(ResourceKey dimension, BlockState state, BlockState statePrev, BlockPos pos, boolean createEntityForBlockRemoval) { this.dimension = dimension; this.state = state; this.statePrev = statePrev; this.pos = pos; this.createEntityForBlockRemoval = createEntityForBlockRemoval; } public ResourceKey getDimension() { return dimension; } public void setDimension(ResourceKey dimension) { this.dimension = dimension; } public BlockState getState() { return state; } public void setState(BlockState state) { this.state = state; } public BlockPos getPos() { return pos; } public void setPos(BlockPos pos) { this.pos = pos; } public boolean isCreateEntityForBlockRemoval() { return createEntityForBlockRemoval; } public void setCreateEntityForBlockRemoval(boolean createEntityForBlockRemoval) { this.createEntityForBlockRemoval = createEntityForBlockRemoval; } public BlockState getStatePrev() { return statePrev; } public void setStatePrev(BlockState statePrev) { this.statePrev = statePrev; } } public TornadoHelper(StormObject parStorm) { storm = parStorm; } public int getTornadoBaseSize() { int sizeChange = 10; //special love tropics overrides if (storm.isPlayerControlled()) { if (storm.isBaby()) { return 8; } } if (storm.levelCurIntensityStage >= StormObject.STATE_STAGE5) { return sizeChange * 9; } else if (storm.levelCurIntensityStage >= StormObject.STATE_STAGE4) { return sizeChange * 7; } else if (storm.levelCurIntensityStage >= StormObject.STATE_STAGE3) { return sizeChange * 5; } else if (storm.levelCurIntensityStage >= StormObject.STATE_STAGE2) { return sizeChange * 4; } else if (storm.levelCurIntensityStage >= StormObject.STATE_STAGE1) { return sizeChange * 3; } else if (storm.levelCurIntensityStage >= StormObject.STATE_FORMING) { return sizeChange * 1; } else { return 5; } } public void tick(Level parWorld) { if (!parWorld.isClientSide()) { if (parWorld.getGameTime() % queueProcessRate == 0) { Iterator it = listBlockUpdateQueue.values().iterator(); while (it.hasNext()) { BlockUpdateSnapshot snapshot = it.next(); Level world = WeatherUtil.getWorld(snapshot.getDimension()); if (world != null) { world.setBlock(snapshot.getPos(), snapshot.getState(), 3); if (snapshot.isCreateEntityForBlockRemoval()) { //moved to where we add to the queue for less clunky visuals //((WeatherManagerServer)this.storm.manager).syncBlockParticleNew(snapshot.getPos(), snapshot.getStatePrev(), storm); } } } listBlockUpdateQueue.clear(); } } if (storm == null) return; boolean seesLight = false; tickGrabCount = 0; removeCount = 0; tryRipCount = 0; int tryRipMax = 300; if (storm.levelCurIntensityStage >= StormObject.STATE_STAGE5) { tryRipMax *= 2.5; } else if (storm.levelCurIntensityStage >= StormObject.STATE_STAGE4) { tryRipMax *= 1.8; } else if (storm.levelCurIntensityStage >= StormObject.STATE_STAGE3) { tryRipMax *= 1.5; } else if (storm.levelCurIntensityStage >= StormObject.STATE_STAGE2) { tryRipMax *= 1.2; } //tryRipMax = 1000; int firesPerTickMax = 1; //tornado profile changing from storm data tornadoBaseSize = getTornadoBaseSize(); if (storm.stormType == storm.TYPE_WATER) { tornadoBaseSize *= 3; } forceRotate(parWorld); Random rand = new Random(); if (!parWorld.isClientSide() && !Weather.isLoveTropicsInstalled() && (ConfigTornado.Storm_Tornado_grabBlocks || storm.isFirenado)) { //int yStart = (int) (storm.posGround.y - 10); int yStart = 0; int yEnd = (int)storm.pos.y/* + 72*/; int yInc = 1; Biome bgb = parWorld.getBiome(new BlockPos(WeatherUtilBlock.getPrecipitationHeightSafe(parWorld, new BlockPos(Mth.floor(storm.pos.x), 0, Mth.floor(storm.pos.z))))).get(); //prevent grabbing in high areas (hills) //TODO: 1.10 make sure minHeight/maxHeight converted to baseHeight/scale is correct, guessing we can just not factor in variation //TODO: 1.18: getDepth formally base height, seems entirely gone now double depth = 0; //depth = bgb.getDepth(); if (bgb != null && (depth/* + bgb.getScale()*/ <= 0.7 || storm.isFirenado)) { boolean newTest = false; int tryCount = 0; if (newTest) { TornadoFunnelSimple tornadoFunnelSimple = storm.getTornadoFunnelSimple(); int layers = tornadoFunnelSimple.listLayers.size(); for (int i = 0; i < layers; i++) { float radius = tornadoFunnelSimple.getConfig().getRadiusOfBase() + (tornadoFunnelSimple.getConfig().getRadiusIncreasePerLayer() * (i)); Layer layer = tornadoFunnelSimple.listLayers.get(i); float circumference = radius * 2 * Mth.PI; float particleSpaceOccupy = 1F; float scanResolution = (float) Math.floor(circumference / particleSpaceOccupy); float particleSpacingDegrees = 360 / scanResolution; //scanResolution = 1F; for (float deg = 0; deg < 360; deg += particleSpacingDegrees) { float radiusScanSize = 5F; //for (float radiusToUse = radius - radiusScanSize; radiusToUse <= radius; radiusToUse+=0.5F) { for (float radiusToUse = radius; radiusToUse <= radius; radiusToUse += 1F) { float x = (float) (layer.getPos().x + (-Math.sin(Math.toRadians(deg)) * radiusScanSize)); float z = (float) (layer.getPos().z + (Math.cos(Math.toRadians(deg)) * radiusScanSize)); float y = (float) layer.getPos().y; BlockPos pos = new BlockPos((int) Math.floor(x), (int) Math.floor(y) - 1, (int) Math.floor(z)); boolean performed = false; BlockState state = parWorld.getBlockState(pos); if (parWorld.getGameTime() % 10 == 0 && radiusToUse == radius - radiusScanSize) { //((ServerLevel) parWorld).sendParticles(ParticleTypes.HEART, pos.getX(), pos.getY(), pos.getZ(), 1, 0.3D, 0D, 0.3D, 1D); } tryCount++; if (canGrab(parWorld, state, pos)) { tryRipCount++; seesLight = tryRip(parWorld, pos.getX(), pos.getY(), pos.getZ()); performed = seesLight; } if (!performed && ConfigTornado.Storm_Tornado_RefinedGrabRules) { if (state.getBlock() == Blocks.GRASS_BLOCK) { if (!listBlockUpdateQueue.containsKey(pos)) { listBlockUpdateQueue.put(pos, new BlockUpdateSnapshot(parWorld.dimension(), Blocks.DIRT.defaultBlockState(), state, pos, false)); } } } } } } //CULog.dbg("tryCount: " + tryCount); } else { int ii = 2; int stageIntensity = (int) ((storm.levelCurIntensityStage+1 - storm.levelStormIntensityFormingStartVal)); int loopAmount = stageIntensity * 500; if (storm.stormType == StormObject.TYPE_WATER) { loopAmount = 1 + ii/2; } for (int k = 0; k < loopAmount; k++) { if (tryRipCount > tryRipMax) { break; } int bottomY = (int) Math.max(parWorld.getMinBuildHeight(), storm.posBaseFormationPos.y - 10); int topY = (int) Math.max(parWorld.getMaxBuildHeight(), storm.getPosTop().y); if (bottomY >= topY) bottomY = topY - 1; int tryY = rand.nextInt(bottomY, topY); if (tryY > parWorld.getMaxBuildHeight()) { tryY = parWorld.getMaxBuildHeight(); } int tryX = (int)storm.pos.x + rand.nextInt(tornadoBaseSize + (ii)) - ((tornadoBaseSize / 2) + (ii / 2)); int tryZ = (int)storm.pos.z + rand.nextInt(tornadoBaseSize + (ii)) - ((tornadoBaseSize / 2) + (ii / 2)); double d0 = storm.pos.x - tryX; double d2 = storm.pos.z - tryZ; double dist = Mth.sqrt((float) (d0 * d0 + d2 * d2)); BlockPos pos = new BlockPos(tryX, tryY, tryZ); if (dist < tornadoBaseSize/2 + ii/2 && tryRipCount < tryRipMax) { BlockState state = parWorld.getBlockState(pos); Block blockID = state.getBlock(); boolean performed = false; tryCount++; if (canGrab(parWorld, state, pos)) { tryRipCount++; seesLight = tryRip(parWorld, tryX, tryY, tryZ); performed = seesLight; } if (!performed && ConfigTornado.Storm_Tornado_RefinedGrabRules) { if (blockID == Blocks.GRASS_BLOCK) { if (!listBlockUpdateQueue.containsKey(pos)) { listBlockUpdateQueue.put(pos, new BlockUpdateSnapshot(parWorld.dimension(), Blocks.DIRT.defaultBlockState(), state, pos, false)); } } } } } int spawnYOffset = (int) storm.posBaseFormationPos.y - 10; for (int k = 0; k < 10; k++) { int randSize = 40; randSize = 10; int tryX = (int)storm.pos.x + rand.nextInt(randSize) - randSize/2; int tryY = (int)spawnYOffset - 2 + rand.nextInt(8); int tryZ = (int)storm.pos.z + rand.nextInt(randSize) - randSize/2; double d0 = storm.pos.x - tryX; double d2 = storm.pos.z - tryZ; double dist = Mth.sqrt((float) (d0 * d0 + d2 * d2)); if (dist < tornadoBaseSize/2 + randSize/2 && tryRipCount < tryRipMax) { BlockPos pos = new BlockPos(tryX, tryY, tryZ); BlockState state = parWorld.getBlockState(pos); tryCount++; if (canGrab(parWorld, state, pos)) { tryRipCount++; tryRip(parWorld, tryX, tryY, tryZ); } } } //CULog.dbg("tryCount: " + tryCount); } } } else { seesLight = true; } /*if (Math.abs((spawnYOffset - storm.pos.y)) > 5) { seesLight = true; }*/ if (!parWorld.isClientSide() && storm.isFirenado) { if (storm.levelCurIntensityStage >= storm.STATE_STAGE1) for (int i = 0; i < firesPerTickMax; i++) { BlockPos posUp = CoroUtilBlock.blockPos(storm.posGround.x, storm.posGround.y + rand.nextInt(30), storm.posGround.z); BlockState state = parWorld.getBlockState(posUp); if (CoroUtilBlock.isAir(state.getBlock())) { //parWorld.setBlockState(posUp, Blocks.FIRE.getDefaultState()); //TODO: 1.14 uncomment /*EntityMovingBlock mBlock = new EntityMovingBlock(parWorld, posUp.getX(), posUp.getY(), posUp.getZ(), Blocks.FIRE.getDefaultState(), storm); mBlock.metadata = 15; double speed = 2D; mBlock.motionX += (rand.nextDouble() - rand.nextDouble()) * speed; mBlock.motionZ += (rand.nextDouble() - rand.nextDouble()) * speed; mBlock.motionY = 1D; mBlock.mode = 0; parWorld.addEntity(mBlock);*/ } } int randSize = 10; int tryX = (int)storm.pos.x + rand.nextInt(randSize) - randSize/2; int tryZ = (int)storm.pos.z + rand.nextInt(randSize) - randSize/2; int tryY = parWorld.getHeight(Heightmap.Types.MOTION_BLOCKING, tryX, tryZ) - 1; int funnelBottomY = (int) storm.pos.y; if (storm.getTornadoFunnelSimple().listLayers.size() > 0) { Layer layer = storm.getTornadoFunnelSimple().listLayers.get(0); funnelBottomY = (int) layer.getPos().y(); } //if firenado funnel reached down enough to hit this y coord if (tryY > funnelBottomY - 3) { double d0 = storm.pos.x - tryX; double d2 = storm.pos.z - tryZ; double dist = Mth.sqrt((float) (d0 * d0 + d2 * d2)); if (dist < tornadoBaseSize / 2 + randSize / 2 && tryRipCount < tryRipMax) { BlockPos pos = new BlockPos(tryX, tryY, tryZ); Block block = parWorld.getBlockState(pos).getBlock(); BlockPos posUp = new BlockPos(tryX, tryY + 1, tryZ); Block blockUp = parWorld.getBlockState(posUp).getBlock(); if (!CoroUtilBlock.isAir(block) && CoroUtilBlock.isAir(blockUp)) { parWorld.setBlock(posUp, Blocks.FIRE.defaultBlockState(), 3); } } } } } public boolean isNoDigCoord(int x, int y, int z) { // MCPC start /*org.bukkit.entity.Entity bukkitentity = this.getBukkitEntity(); if ((bukkitentity instanceof Player)) { Player player = (Player)bukkitentity; BlockBreakEvent breakev = new BlockBreakEvent(player.getWorld().getBlockStateAt(x, y, z), player); Bukkit.getPluginManager().callEvent(breakev); if (breakev.isCancelled()) { return true; } }*/ // MCPC end return false; } public boolean tryRip(Level parWorld, int tryX, int tryY, int tryZ/*, boolean notify*/) { boolean tryRip = true; BlockPos pos = new BlockPos(tryX, tryY, tryZ); if (listBlockUpdateQueue.containsKey(pos)) { return true; } if (!tryRip) return true; if (!ConfigTornado.Storm_Tornado_grabBlocks) return true; if (isNoDigCoord(tryX, tryY, tryZ)) return true; boolean seesLight = false; BlockState state = parWorld.getBlockState(pos); Block blockID = state.getBlock(); if ((((WeatherUtilBlock.getPrecipitationHeightSafe(parWorld, new BlockPos(tryX, 0, tryZ)).getY() - 1 == tryY) || WeatherUtilBlock.getPrecipitationHeightSafe(parWorld, new BlockPos(tryX + 1, 0, tryZ)).getY() - 1 < tryY || WeatherUtilBlock.getPrecipitationHeightSafe(parWorld, new BlockPos(tryX, 0, tryZ + 1)).getY() - 1 < tryY || WeatherUtilBlock.getPrecipitationHeightSafe(parWorld, new BlockPos(tryX - 1, 0, tryZ)).getY() - 1 < tryY || WeatherUtilBlock.getPrecipitationHeightSafe(parWorld, new BlockPos(tryX, 0, tryZ - 1)).getY() - 1 < tryY))) { int blockCount = 0; //old per storm blockCount seems glitched... lets use a global we cache count of if (parWorld.isLoaded(CoroUtilBlock.blockPos(storm.pos.x, 128, storm.pos.z)) && lastGrabTime < System.currentTimeMillis() && tickGrabCount < ConfigTornado.Storm_Tornado_maxBlocksGrabbedPerTick) { lastGrabTime = System.currentTimeMillis() - 5; if (blockID != Blocks.PACKED_ICE && blockID != Blocks.ICE && blockID != Blocks.SNOW_BLOCK && blockID != Blocks.SNOW && blockID != Blocks.POWDER_SNOW) { boolean playerClose = parWorld.getNearestPlayer(storm.posBaseFormationPos.x, storm.posBaseFormationPos.y, storm.posBaseFormationPos.z, 140, false) != null; if (playerClose) { tickGrabCount++; ripCount++; seesLight = true; } if (WeatherUtil.shouldRemoveBlock(state)) { removeCount++; boolean shouldEntityify = blockCount <= ConfigTornado.Storm_Tornado_maxFlyingEntityBlocks; listBlockUpdateQueue.put(pos, new BlockUpdateSnapshot(parWorld.dimension(), Blocks.AIR.defaultBlockState(), state, pos, playerClose && shouldEntityify)); if (playerClose && shouldEntityify && (state.canOcclude() || state.getBlock().defaultMapColor() == MapColor.PLANT)) { ((WeatherManagerServer) this.storm.manager).syncBlockParticleNew(pos, state, storm); } } } if (blockID == Blocks.GLASS) { parWorld.playSound(null, new BlockPos(tryX, tryY, tryZ), SoundEvents.GLASS_BREAK, SoundSource.AMBIENT, 5.0F, 1.0F); } } } return seesLight; } public boolean canGrab(Level parWorld, BlockState state, BlockPos pos) { if (!CoroUtilBlock.isAir(state.getBlock()) && state.getBlock() != Blocks.FIRE && //TODO: 1.14 uncomment /*state.getBlock() != CommonProxy.blockRepairingBlock &&*/ WeatherUtil.shouldGrabBlock(parWorld, state) && !isBlockGrabbingBlocked(parWorld, state, pos)) { return canGrabEventCheck(parWorld, state, pos); } return false; } public boolean canGrabEventCheck(Level world, BlockState state, BlockPos pos) { if (!ConfigMisc.blockBreakingInvokesCancellableEvent) return true; if (world instanceof ServerLevel) { if (fakePlayerProfile == null) { fakePlayerProfile = new GameProfile(UUID.fromString("1396b887-2570-4948-86e9-0633d1d22946"), "weather2FakePlayer"); } BlockEvent.BreakEvent event = new BlockEvent.BreakEvent(world, pos, state, FakePlayerFactory.get((ServerLevel) world, fakePlayerProfile)); MinecraftForge.EVENT_BUS.post(event); return !event.isCanceled(); } else { return false; } } public boolean canGrabEntity(Entity ent) { if (ent.level().isClientSide()) { return canGrabEntityClient(ent); } else { if (ent instanceof Player) { if (!((Player) ent).isCreative()) { if (ConfigTornado.Storm_Tornado_grabPlayer) { if (storm.isPlayerControlled() && storm.spawnerUUID != null && storm.spawnerUUID.equals(ent.getUUID().toString())) { return false; } return true; } else { return false; } } else { return false; } } else { if (ConfigTornado.Storm_Tornado_grabPlayersOnly) { return false; } if (ent instanceof Npc) { return ConfigTornado.Storm_Tornado_grabVillagers; } if (ent instanceof ItemEntity) { return ConfigTornado.Storm_Tornado_grabItems || storm.isPet(); } if (ent instanceof Enemy) { return ConfigTornado.Storm_Tornado_grabMobs; } if (ent instanceof Animal) { return ConfigTornado.Storm_Tornado_grabAnimals; } } //for moving blocks, other non livings return true; } } @OnlyIn(Dist.CLIENT) public boolean canGrabEntityClient(Entity ent) { ClientConfigData clientConfig = ClientTickHandler.clientConfigData; if (ent instanceof Player) { if (!((Player) ent).isCreative()) { if (ConfigTornado.Storm_Tornado_grabPlayer) { if (storm.isPlayerControlled() && storm.spawnerUUID != null && storm.spawnerUUID.equals(ent.getUUID().toString())) { return false; } return true; } else { return false; } } else { return false; } } else { if (clientConfig.Storm_Tornado_grabPlayersOnly) { return false; } if (ent instanceof Npc) { return clientConfig.Storm_Tornado_grabVillagers; } if (ent instanceof ItemEntity) { return clientConfig.Storm_Tornado_grabItems || storm.isPet(); } if (ent instanceof Enemy) { return clientConfig.Storm_Tornado_grabMobs; } if (ent instanceof Animal) { return clientConfig.Storm_Tornado_grabAnimals; } } //for moving blocks, other non livings return true; } public boolean forceRotate(Level parWorld) { return forceRotate(parWorld, false); } public boolean forceRotate(Level parWorld, boolean featherFallInstead) { //changed for weather2: //canEntityBeSeen commented out till replaced with coord one, might cause issues double dist = grabDist * 2; if (storm.isPet()) { dist = 3F; } AABB aabb = new AABB(storm.pos.x, storm.currentTopYBlock, storm.pos.z, storm.pos.x, storm.currentTopYBlock, storm.pos.z); if (storm.isPet()) { aabb = aabb.inflate(dist, 3, dist); } else { aabb = aabb.inflate(dist, this.storm.maxHeight * 3.8, dist); } List list = parWorld.getEntitiesOfClass(Entity.class, aabb); boolean foundEnt = false; int killCount = 0; if (list != null) { for (int i = 0; i < list.size(); i++) { Entity entity1 = (Entity)list.get(i); if (canGrabEntity(entity1)) { if (getDistanceXZ(storm.posBaseFormationPos, entity1.getX(), entity1.getY(), entity1.getZ()) < dist) { if (!storm.isPet()) { if (false/* && (entity1 instanceof EntityMovingBlock && !((EntityMovingBlock)entity1).collideFalling)*/) { storm.spinEntity(entity1); //spin(entity, conf, entity1); foundEnt = true; } else { if (entity1 instanceof Player) { //dont waste cpu on server side doing LOS checks, since player movement is client side only, in all situations ive seen //actually we need to still change its motion var, otherwise weird things happen //if (entity1.world.isClientSide()) { if (WeatherUtilEntity.isEntityOutside(entity1) || (storm.isPlayerControlled() && WeatherUtilEntity.canPosSeePos(parWorld, entity1.position(), storm.posGround))) { //Weather.dbg("entity1.motionY: " + entity1.motionY); if (featherFallInstead) { ((Player) entity1).addEffect(new MobEffectInstance(MobEffects.SLOW_FALLING, 600, 0, false, true, true)); } else { storm.spinEntityv2(entity1); } foundEnt = true; } } else if (entity1 instanceof LivingEntity && (WeatherUtilEntity.isEntityOutside(entity1, false) || (storm.isPlayerControlled() && WeatherUtilEntity.canPosSeePos(parWorld, entity1.position(), storm.posGround)))) {//OldUtil.canVecSeeCoords(parWorld, storm.pos, entity1.posX, entity1.posY, entity1.posZ)/*OldUtil.canEntSeeCoords(entity1, entity.posX, entity.posY + 80, entity.posZ)*/) { //trying only server side to fix warp back issue (which might mean client and server are mismatching for some rules) //if (!entity1.world.isClientSide()) { if (featherFallInstead) { ((LivingEntity) entity1).addEffect(new MobEffectInstance(MobEffects.SLOW_FALLING, 600, 0, false, true, true)); } else { storm.spinEntityv2(entity1); } //spin(entity, conf, entity1); foundEnt = true; //} } } } else { if (entity1 instanceof ItemEntity && storm.isPetGrabsItems()) { storm.spinEntityv2(entity1); foundEnt = true; } } } } } } return foundEnt; } public double getDistanceXZ(Vec3 parVec, double var1, double var3, double var5) { double var7 = parVec.x - var1; //double var9 = ent.posY - var3; double var11 = parVec.z - var5; return Mth.sqrt((float) (var7 * var7/* + var9 * var9*/ + var11 * var11)); } public double getDistanceXZ(Entity ent, double var1, double var3, double var5) { double var7 = ent.getX() - var1; //double var9 = ent.posY - var3; double var11 = ent.getZ() - var5; return Mth.sqrt((float) (var7 * var7/* + var9 * var9*/ + var11 * var11)); } @OnlyIn(Dist.CLIENT) public void soundUpdates(boolean playFarSound, boolean playNearSound) { if (storm.isPet()) return; Minecraft mc = Minecraft.getInstance(); if (mc.player == null) { return; } //close sounds int far = 200; int close = 120; if (storm.stormType == storm.TYPE_WATER) { close = 200; } Vec3 plPos = new Vec3(mc.player.getX(), mc.player.getY(), mc.player.getZ()); float quietTornadoTweak = 1F; float quietAmbientTweak = 1F; if (Weather.isLoveTropicsInstalled()) { if (storm.isPlayerControlled()) { quietTornadoTweak = 0.25F; quietAmbientTweak = 0.1F; } close = 7; } double distToPlayer = this.storm.posGround.distanceTo(plPos); float volScaleFar = (float) ((far - distToPlayer) / far); float volScaleClose = (float) ((close - distToPlayer) / close); if (volScaleFar < 0F) { volScaleFar = 0.0F; } if (volScaleClose < 0F) { volScaleClose = 0.0F; } if (distToPlayer < close) { lastTickPlayerClose = true; } else { lastTickPlayerClose = false; } if (distToPlayer < far) { if (playFarSound) { if (mc.level.getGameTime() % 40 == 0) { isOutsideCached = WeatherUtilEntity.isPosOutside(mc.level, new Vec3(mc.player.getPosition(1).x()+0.5F, mc.player.getPosition(1).y()+0.5F, mc.player.getPosition(1).z()+0.5F)); } if (isOutsideCached) { tryPlaySound(WeatherUtilSound.snd_wind_far, 2, mc.player, (float)(volScaleFar * quietAmbientTweak * ConfigSound.windyStormVolume), far); } } if (playNearSound) tryPlaySound(WeatherUtilSound.snd_wind_close, 1, mc.player, (float)(volScaleClose * quietTornadoTweak * ConfigSound.tornadoWindVolume), close); if (storm.levelCurIntensityStage >= storm.STATE_FORMING && storm.stormType == storm.TYPE_LAND) { tryPlaySound(WeatherUtilSound.snd_tornado_dmg_close, 0, mc.player, (float)(volScaleClose * quietTornadoTweak * ConfigSound.tornadoDamageVolume), close); } } } public boolean tryPlaySound(String[] sound, int arrIndex, Entity source, float vol, float parCutOffRange) { Random rand = new Random(); if (WeatherUtilSound.soundTimer[arrIndex] <= System.currentTimeMillis()) { WeatherUtilSound.playMovingSound(storm, new StringBuilder().append("streaming." + sound[WeatherUtilSound.snd_rand[arrIndex]]).toString(), vol, 1.0F, parCutOffRange); int length = WeatherUtilSound.soundToLength.get(sound[WeatherUtilSound.snd_rand[arrIndex]]); //-500L, for blending WeatherUtilSound.soundTimer[arrIndex] = System.currentTimeMillis() + length - 500L; WeatherUtilSound.snd_rand[arrIndex] = rand.nextInt(3); } return false; } public boolean isBlockGrabbingBlocked(Level world, BlockState state, BlockPos pos) { int queryRate = 40; if (isBlockGrabbingBlockedCached_LastCheck + queryRate < world.getGameTime()) { isBlockGrabbingBlockedCached_LastCheck = world.getGameTime(); isBlockGrabbingBlockedCached = false; for (Long hash : storm.manager.getLookupWeatherBlockDamageDeflector().keySet()) { BlockPos posDeflect = BlockPos.of(hash); if (pos.distSqr(posDeflect) < ConfigStorm.Storm_Deflector_RadiusOfStormRemoval * ConfigStorm.Storm_Deflector_RadiusOfStormRemoval) { isBlockGrabbingBlockedCached = true; break; } } } return isBlockGrabbingBlockedCached; } public void cleanup() { listBlockUpdateQueue.clear(); storm = null; } } ================================================ FILE: src/main/java/weather2/weathersystem/storm/WeatherEntityConfig.java ================================================ package weather2.weathersystem.storm; public class WeatherEntityConfig { public static int TYPE_SPOUT = 0; public static int TYPE_TORNADO = 1; public static int TYPE_HURRICANE = 2; public int type = 1; public float tornadoInitialSpeed; public float tornadoPullRate; public float tornadoLiftRate; public int relTornadoSize; public int tornadoBaseSize; public float tornadoWidthScale; public double grabDist = 120.0D; public int tornadoTime = 2500; public boolean grabsBlocks = true; } ================================================ FILE: src/main/java/weather2/weathersystem/storm/WeatherObject.java ================================================ package weather2.weathersystem.storm; import net.minecraft.world.phys.Vec3; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.fml.LogicalSide; import net.minecraftforge.fml.util.thread.EffectiveSide; import weather2.util.CachedNBTTagCompound; import weather2.weathersystem.WeatherManager; public class WeatherObject { public static long lastUsedStormID = 0; //ID starts from 0 for each game start, no storm nbt disk reload for now public long ID; //loosely accurate ID for tracking, but we wanted to persist between world reloads..... need proper UUID??? I guess add in UUID later and dont persist, start from 0 per game run public boolean isDead = false; /** * used to count up to a threshold to finally remove weather objects, * solves issue of simbox cutoff removing storms for first few ticks as player is joining in singleplayer * helps with multiplayer, requiring 30 seconds of no players near before removal */ public int ticksSinceNoNearPlayer = 0; public WeatherManager manager; public Vec3 pos = Vec3.ZERO; public Vec3 posGround = Vec3.ZERO; public Vec3 motion = Vec3.ZERO; //used as radius public int size = 50; public int maxSize = 0; //unused public EnumWeatherObjectType weatherObjectType = EnumWeatherObjectType.CLOUD; private CachedNBTTagCompound nbtCache; //private NBTTagCompound cachedClientNBTState; public WeatherObject(WeatherManager parManager) { manager = parManager; nbtCache = new CachedNBTTagCompound(); } public void initFirstTime() { ID = lastUsedStormID++; } public void tick() { } @OnlyIn(Dist.CLIENT) public void tickRender(float partialTick) { } public void reset() { remove(); } public void remove() { //Weather.dbg("storm killed, ID: " + ID); isDead = true; //cleanup memory //if (FMLCommonHandler.instance().getEffectiveSide() == Dist.CLIENT/*manager.getWorld().isRemote*/) { if (EffectiveSide.get().equals(LogicalSide.CLIENT)) { cleanupClient(); } cleanup(); } public void cleanup() { manager = null; } @OnlyIn(Dist.CLIENT) public void cleanupClient() { } public int getUpdateRateForNetwork() { return 40; } public void read() { } public void write() { } public void nbtSyncFromServer() { CachedNBTTagCompound parNBT = this.getNbtCache(); ID = parNBT.getLong("ID"); //Weather.dbg("StormObject " + ID + " receiving sync"); pos = new Vec3(parNBT.getDouble("posX"), parNBT.getDouble("posY"), parNBT.getDouble("posZ")); //motion = new Vec3(parNBT.getDouble("motionX"), parNBT.getDouble("motionY"), parNBT.getDouble("motionZ")); motion = new Vec3(parNBT.getDouble("vecX"), parNBT.getDouble("vecY"), parNBT.getDouble("vecZ")); size = parNBT.getInt("size"); maxSize = parNBT.getInt("maxSize"); this.weatherObjectType = EnumWeatherObjectType.get(parNBT.getInt("weatherObjectType")); } public void nbtSyncForClient() { CachedNBTTagCompound nbt = this.getNbtCache(); nbt.putDouble("posX", pos.x); nbt.putDouble("posY", pos.y); nbt.putDouble("posZ", pos.z); /*nbt.putDouble("motionX", motion.xCoord); nbt.putDouble("motionY", motion.yCoord); nbt.putDouble("motionZ", motion.zCoord);*/ nbt.putDouble("vecX", motion.x); nbt.putDouble("vecY", motion.y); nbt.putDouble("vecZ", motion.z); nbt.putLong("ID", ID); //just blind set ID into non cached data so client always has it, no need to check for forced state and restore orig state nbt.getNewNBT().putLong("ID", ID); nbt.putInt("size", size); nbt.putInt("maxSize", maxSize); nbt.putInt("weatherObjectType", this.weatherObjectType.ordinal()); } public CachedNBTTagCompound getNbtCache() { return nbtCache; } public void setNbtCache(CachedNBTTagCompound nbtCache) { this.nbtCache = nbtCache; } public int getSize() { return size; } } ================================================ FILE: src/main/java/weather2/weathersystem/storm/WeatherObjectParticleStorm.java ================================================ package weather2.weathersystem.storm; import com.corosus.coroutil.util.CoroUtilBlock; import com.corosus.coroutil.util.CoroUtilCompatibility; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.world.level.Level; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.Biomes; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.phys.Vec3; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import weather2.WeatherBlocks; import weather2.config.ConfigSand; import weather2.config.ConfigSnow; import weather2.util.CachedNBTTagCompound; import weather2.util.WeatherUtilBlock; import weather2.weathersystem.WeatherManager; import weather2.weathersystem.wind.WindManager; import java.util.Random; public class WeatherObjectParticleStorm extends WeatherObject { public int age = 0; public int maxAge = 20*20; public Random rand = new Random(); public StormType type; public enum StormType { SANDSTORM("SANDSTORM"), SNOWSTORM("SNOWSTORM"); private final String key; public static final StormType[] VALUES = values(); StormType(String key) { this.key = key; } public String getKey() { return key; } } public WeatherObjectParticleStorm(WeatherManager parManager) { super(parManager); this.weatherObjectType = EnumWeatherObjectType.SAND; } public void initStormSpawn(Vec3 pos) { this.pos = pos; this.maxAge = 20*60*5; } public static boolean canSpawnHere(Level world, BlockPos pos, StormType type, boolean forSpawn) { Holder biomeIn = world.getBiome(pos); if (type == StormType.SANDSTORM) { return isDesert(biomeIn, forSpawn); } else if (type == StormType.SNOWSTORM) { return isColdForStorm(world, biomeIn, forSpawn, pos); } return false; } public static boolean isColdForStorm(Level world, Holder biome, boolean forSpawn, BlockPos pos) { //return biome.getPrecipitation() == Biome.Precipitation.SNOW; //adjusted to this way to make it work with serene seasons boolean canPrecip = biome.get().getPrecipitationAt(pos) == Biome.Precipitation.RAIN || biome.get().getPrecipitationAt(pos) == Biome.Precipitation.SNOW; return canPrecip && CoroUtilCompatibility.coldEnoughToSnow(biome.get(), pos, world); } public static boolean isDesert(Holder biome, boolean forSpawn) { return biome.get().equals(Biomes.DESERT) || (!forSpawn && biome.get().equals(Biomes.RIVER)) || biome.unwrap().left().toString().toLowerCase().contains("desert"); } @Override public int getSize() { return 250; } @Override public void tick() { super.tick(); if (!manager.getWorld().isClientSide()) { this.age++; //CULog.dbg("this.age: " + this.age); if (this.age > this.maxAge) { this.remove(); } if (getIntensity() > 0.2D) { tickBlockSandBuildup(); } if (manager != null && manager.getWindManager() != null && !manager.getWindManager().isHighWindEventActive()) { manager.getWindManager().stopLowWindEvent(); manager.getWindManager().startHighWindEvent(); } } posGround = pos; } /** * 0-1F for first half of age, 1-0F for second half of age * @return */ public float getIntensity() { float age = this.age; float maxAge = this.maxAge; if (age / maxAge <= 0.5F) { return age / (maxAge/2); } else { return 1F - (age / (maxAge/2) - 1F); } } @OnlyIn(Dist.CLIENT) public void tickClient() { } public Block getBlockForBuildup() { if (this.type == StormType.SANDSTORM) { return WeatherBlocks.BLOCK_SAND_LAYER.get(); } else if (this.type == StormType.SNOWSTORM) { return Blocks.SNOW; } return null; } public void tickBlockSandBuildup() { Level world = manager.getWorld(); WindManager windMan = manager.getWindManager(); float angle = windMan.getWindAngleForClouds(); //keep it set to do a lot of work only occasionally, prevents chunk render tick spam for client which kills fps int delay = ConfigSand.Sandstorm_Sand_Buildup_TickRate; int loop = (int)((float)ConfigSand.Sandstorm_Sand_Buildup_LoopAmountBase * getIntensity()); boolean buildupOutsideArea = ConfigSand.Sandstorm_Sand_Buildup_AllowOutsideDesert; int maxBlockStackingAllowed = ConfigSand.Sandstorm_Sand_Block_Max_Height; if (getType() == StormType.SNOWSTORM) { delay = ConfigSnow.Snowstorm_Snow_Buildup_TickRate; loop = (int)((float)ConfigSnow.Snowstorm_Snow_Buildup_LoopAmountBase * getIntensity()); buildupOutsideArea = ConfigSnow.Snowstorm_Snow_Buildup_AllowOutsideColdBiomes; maxBlockStackingAllowed = ConfigSnow.Snowstorm_Snow_Block_Max_Height; } //delay = 1; //sand block buildup if (!world.isClientSide) { if (getBlockForBuildup() != null) { if (world.getGameTime() % delay == 0) { for (int i = 0; i < loop; i++) { //rate of placement based on storm intensity if (rand.nextDouble() >= getIntensity()) continue; Vec3 vecPos = getRandomPosInStorm(); BlockPos blockPos = WeatherUtilBlock.getPrecipitationHeightSafe(world, CoroUtilBlock.blockPos(vecPos.x, 0, vecPos.z)); //avoid unloaded areas if (!world.hasChunkAt(blockPos)) continue; if (buildupOutsideArea || canSpawnHere(world, blockPos, getType(), false)) { WeatherUtilBlock.fillAgainstWallSmoothly(world, new Vec3(blockPos.getX(), blockPos.getY(), blockPos.getZ()), angle, 15, 2, getBlockForBuildup(), maxBlockStackingAllowed); } } } } } } public Vec3 getRandomPosInStorm() { Random rand = new Random(); int x = (int) Math.floor(posGround.x + rand.nextInt(getSize()) - rand.nextInt(getSize())); int z = (int) Math.floor(posGround.z + rand.nextInt(getSize()) - rand.nextInt(getSize())); int y = WeatherUtilBlock.getPrecipitationHeightSafe(manager.getWorld(), new BlockPos(x, 128, z)).getY(); Vec3 vec = new Vec3(x, y, z); return vec; } @Override public int getUpdateRateForNetwork() { return 1; } @Override public void nbtSyncForClient() { super.nbtSyncForClient(); CachedNBTTagCompound data = this.getNbtCache(); data.putInt("age", age); data.putInt("maxAge", maxAge); data.putString("type", type.key); data.putString("test", "WHAT"); } @Override public void nbtSyncFromServer() { super.nbtSyncFromServer(); CachedNBTTagCompound parNBT = this.getNbtCache(); this.age = parNBT.getInt("age"); this.maxAge = parNBT.getInt("maxAge"); if (parNBT.contains("type")) { this.type = StormType.valueOf(parNBT.getString("type").toUpperCase()); } } @Override public void read() { super.read(); nbtSyncFromServer(); CachedNBTTagCompound var1 = this.getNbtCache(); motion = new Vec3(var1.getDouble("vecX"), var1.getDouble("vecY"), var1.getDouble("vecZ")); } @Override public void write() { super.write(); nbtSyncForClient(); CachedNBTTagCompound nbt = this.getNbtCache(); nbt.putDouble("vecX", motion.x); nbt.putDouble("vecY", motion.y); nbt.putDouble("vecZ", motion.z); } @Override public void cleanup() { super.cleanup(); } @OnlyIn(Dist.CLIENT) @Override public void cleanupClient() { super.cleanupClient(); } public StormType getType() { return type; } public void setType(StormType type) { this.type = type; } } ================================================ FILE: src/main/java/weather2/weathersystem/storm/WeatherObjectSandstormOld.java ================================================ package weather2.weathersystem.storm; import java.util.Random; import com.corosus.coroutil.util.CoroUtilBlock; import net.minecraft.core.BlockPos; import net.minecraft.world.phys.Vec3; import net.minecraft.world.level.Level; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.Biomes; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.registries.ForgeRegistries; import weather2.WeatherBlocks; import weather2.config.ConfigSand; import weather2.util.CachedNBTTagCompound; import weather2.util.WeatherUtilBlock; import weather2.weathersystem.WeatherManager; import weather2.weathersystem.wind.WindManager; public class WeatherObjectSandstormOld extends WeatherObject { public int age = 0; public int maxAge = 20*20; public Random rand = new Random(); public WeatherObjectSandstormOld(WeatherManager parManager) { super(parManager); this.weatherObjectType = EnumWeatherObjectType.SAND; } public void initSandstormSpawn(Vec3 pos) { this.pos = pos; this.maxAge = 20*60*5; } public static boolean isDesert(Biome biome) { return isDesert(biome, false); } public static boolean isDesert(Biome biome, boolean forSpawn) { //TODO: make sure new comparison works if (ForgeRegistries.BIOMES.getKey(biome) == null) return false; return biome.equals(Biomes.DESERT) || (!forSpawn && biome.equals(Biomes.RIVER)) || ForgeRegistries.BIOMES.getKey(biome).toString().toLowerCase().contains("desert"); } public int getSize() { return 250; } @Override public void tick() { super.tick(); if (!manager.getWorld().isClientSide()) { this.age++; //CULog.dbg("this.age: " + this.age); if (this.age > this.maxAge) { this.remove(); } if (getIntensity() > 0.2D) { tickBlockSandBuildup(); } } posGround = pos; } /** * 0-1F for first half of age, 1-0F for second half of age * @return */ public float getIntensity() { float age = this.age; float maxAge = this.maxAge; if (age / maxAge <= 0.5F) { return age / (maxAge/2); } else { return 1F - (age / (maxAge/2) - 1F); } } @OnlyIn(Dist.CLIENT) public void tickClient() { } public void tickBlockSandBuildup() { Level world = manager.getWorld(); WindManager windMan = manager.getWindManager(); float angle = windMan.getWindAngleForClouds(); //keep it set to do a lot of work only occasionally, prevents chunk render tick spam for client which kills fps int delay = ConfigSand.Sandstorm_Sand_Buildup_TickRate; int loop = (int)((float)ConfigSand.Sandstorm_Sand_Buildup_LoopAmountBase * getIntensity()); //sand block buildup if (!world.isClientSide) { if (world.getGameTime() % delay == 0) { for (int i = 0; i < loop; i++) { //rate of placement based on storm intensity if (rand.nextDouble() >= getIntensity()) continue; Vec3 vecPos = getRandomPosInSandstorm(); int y = WeatherUtilBlock.getPrecipitationHeightSafe(world, CoroUtilBlock.blockPos(vecPos.x, 0, vecPos.z)).getY(); BlockPos blockPos = CoroUtilBlock.blockPos(vecPos.x, y, vecPos.z); //avoid unloaded areas if (!world.hasChunkAt(blockPos)) continue; Biome biomeIn = world.getBiome(blockPos).get(); if (ConfigSand.Sandstorm_Sand_Buildup_AllowOutsideDesert || isDesert(biomeIn)) { WeatherUtilBlock.fillAgainstWallSmoothly(world, new Vec3(blockPos.getX(), blockPos.getY(), blockPos.getZ()), angle, 15, 2, WeatherBlocks.BLOCK_SAND_LAYER.get(), 3); } } } } } public Vec3 getRandomPosInSandstorm() { Random rand = new Random(); int x = (int) Math.floor(posGround.x + rand.nextInt(getSize()) - rand.nextInt(getSize())); int z = (int) Math.floor(posGround.z + rand.nextInt(getSize()) - rand.nextInt(getSize())); int y = WeatherUtilBlock.getPrecipitationHeightSafe(manager.getWorld(), new BlockPos(x, 128, z)).getY(); Vec3 vec = new Vec3(x, y, z); return vec; } @Override public int getUpdateRateForNetwork() { return 1; } @Override public void nbtSyncForClient() { super.nbtSyncForClient(); CachedNBTTagCompound data = this.getNbtCache(); data.putInt("age", age); data.putInt("maxAge", maxAge); } @Override public void nbtSyncFromServer() { super.nbtSyncFromServer(); CachedNBTTagCompound parNBT = this.getNbtCache(); this.age = parNBT.getInt("age"); this.maxAge = parNBT.getInt("maxAge"); } @Override public void read() { super.read(); nbtSyncFromServer(); CachedNBTTagCompound var1 = this.getNbtCache(); motion = new Vec3(var1.getDouble("vecX"), var1.getDouble("vecY"), var1.getDouble("vecZ")); } @Override public void write() { super.write(); nbtSyncForClient(); CachedNBTTagCompound nbt = this.getNbtCache(); nbt.putDouble("vecX", motion.x); nbt.putDouble("vecY", motion.y); nbt.putDouble("vecZ", motion.z); } @Override public void cleanup() { super.cleanup(); } @OnlyIn(Dist.CLIENT) @Override public void cleanupClient() { super.cleanupClient(); } } ================================================ FILE: src/main/java/weather2/weathersystem/storm/WeatherTypes.java ================================================ package weather2.weathersystem.storm; import java.util.ArrayList; import java.util.List; public class WeatherTypes { public static List weatherEntTypes; static { initWeatherTypes(); } public static void initWeatherTypes() { weatherEntTypes = new ArrayList(); WeatherEntityConfig sConf = new WeatherEntityConfig(); //0 = spout //1 = F1 //2 = F3 //3 = F5 //4 = F6 //5 = Hurricane C1 //water spout sConf.tornadoInitialSpeed = 0.2F; sConf.tornadoPullRate = 0.04F; sConf.tornadoLiftRate = 0.05F; sConf.relTornadoSize = 0; sConf.tornadoBaseSize = 3; sConf.tornadoWidthScale = 1.0F; sConf.grabDist = 40D; sConf.tornadoTime = 4500; sConf.type = 0; sConf.grabsBlocks = false; weatherEntTypes.add(sConf); //F1 tornado sConf = new WeatherEntityConfig(); sConf.tornadoInitialSpeed = 0.2F; sConf.tornadoPullRate = 0.04F; sConf.tornadoLiftRate = 0.05F; sConf.relTornadoSize = -20; //sConf.tornadoBaseSize = 3; sConf.tornadoWidthScale = 1.5F; //sConf.grabDist = 100D; weatherEntTypes.add(sConf); //F2 tornado sConf = new WeatherEntityConfig(); sConf.tornadoInitialSpeed = 0.2F; sConf.tornadoPullRate = 0.04F; sConf.tornadoLiftRate = 0.06F; sConf.relTornadoSize = -30; //sConf.tornadoBaseSize = 6; sConf.tornadoWidthScale = 1.5F; //sConf.grabDist = 100D; weatherEntTypes.add(sConf); //F3 tornado sConf = new WeatherEntityConfig(); //sConf.tornadoInitialSpeed = 0.2F; sConf.tornadoPullRate = 0.04F; sConf.tornadoLiftRate = 0.07F; sConf.relTornadoSize = -40; //sConf.tornadoBaseSize = 10; sConf.tornadoWidthScale = 1.9F; weatherEntTypes.add(sConf); //F4 tornado sConf = new WeatherEntityConfig(); //sConf.tornadoInitialSpeed = 0.2F; sConf.tornadoPullRate = 0.04F; sConf.tornadoLiftRate = 0.08F; sConf.relTornadoSize = -50; //sConf.tornadoBaseSize = 10; sConf.tornadoWidthScale = 1.9F; weatherEntTypes.add(sConf); //F5 tornado sConf = new WeatherEntityConfig(); //sConf.tornadoInitialSpeed = 0.15F; sConf.tornadoPullRate = 0.04F; sConf.tornadoLiftRate = 0.09F; sConf.relTornadoSize = -60; //sConf.tornadoBaseSize = 25; sConf.tornadoWidthScale = 2.5F; weatherEntTypes.add(sConf); //F6 sConf = new WeatherEntityConfig(); //sConf.tornadoInitialSpeed = 0.15F; sConf.tornadoPullRate = 0.15F; sConf.tornadoLiftRate = 0.10F; sConf.relTornadoSize = -95; //sConf.tornadoBaseSize = 95; sConf.tornadoWidthScale = 3.5F; weatherEntTypes.add(sConf); //Hurricane /*sConf = new WeatherEntityConfig(); //sConf.tornadoInitialSpeed = 0.15F; sConf.tornadoPullRate = 0.15F; sConf.tornadoLiftRate = 0.04F; sConf.relTornadoSize = -105; //sConf.tornadoBaseSize = 155; sConf.tornadoWidthScale = 3.5F; //sConf.tornadoTime = 4500; sConf.type = 2; weatherEntTypes.add(sConf);*/ } } ================================================ FILE: src/main/java/weather2/weathersystem/tornado/ActiveTornadoConfig.java ================================================ package weather2.weathersystem.tornado; import net.minecraft.nbt.CompoundTag; /** * Defines the shape and other characteristics of a tornado */ public class ActiveTornadoConfig { private float radiusOfBase; //incremental size of radius per layer private float radiusIncreasePerLayer; private float height; private float spinSpeed; private float entityPullDistXZ; private float entityPullDistXZForY; public CompoundTag serialize() { CompoundTag tag = new CompoundTag(); tag.putFloat("radiusOfBase", radiusOfBase); tag.putFloat("radiusIncreasePerLayer", radiusIncreasePerLayer); tag.putFloat("height", height); tag.putFloat("spinSpeed", spinSpeed); tag.putFloat("entityPullDistXZ", entityPullDistXZ); tag.putFloat("entityPullDistXZForY", entityPullDistXZForY); return tag; } public static ActiveTornadoConfig deserialize(CompoundTag tag) { ActiveTornadoConfig config = new ActiveTornadoConfig(); config.setRadiusOfBase(tag.getFloat("radiusOfBase")); config.setRadiusIncreasePerLayer(tag.getFloat("radiusIncreasePerLayer")); config.setHeight(tag.getFloat("height")); config.setSpinSpeed(tag.getFloat("spinSpeed")); config.setEntityPullDistXZ(tag.getFloat("entityPullDistXZ")); config.setEntityPullDistXZForY(tag.getFloat("entityPullDistXZForY")); return config; } public float getRadiusOfBase() { return radiusOfBase; } public ActiveTornadoConfig setRadiusOfBase(float radiusOfBase) { this.radiusOfBase = radiusOfBase; return this; } public float getRadiusIncreasePerLayer() { return radiusIncreasePerLayer; } public ActiveTornadoConfig setRadiusIncreasePerLayer(float radiusIncreasePerLayer) { this.radiusIncreasePerLayer = radiusIncreasePerLayer; return this; } public float getHeight() { return height; } public ActiveTornadoConfig setHeight(float height) { this.height = height; return this; } public float getSpinSpeed() { return spinSpeed; } public ActiveTornadoConfig setSpinSpeed(float spinSpeed) { this.spinSpeed = spinSpeed; return this; } public float getEntityPullDistXZ() { return entityPullDistXZ; } public ActiveTornadoConfig setEntityPullDistXZ(float entityPullDistXZ) { this.entityPullDistXZ = entityPullDistXZ; return this; } public float getEntityPullDistXZForY() { return entityPullDistXZForY; } public ActiveTornadoConfig setEntityPullDistXZForY(float entityPullDistXZForY) { this.entityPullDistXZForY = entityPullDistXZForY; return this; } } ================================================ FILE: src/main/java/weather2/weathersystem/tornado/CatmullRomSpline.java ================================================ /******************************************************************************* * Copyright 2011 See AUTHORS file. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package weather2.weathersystem.tornado; import net.minecraft.util.Mth; /** @author Xoppa */ public class CatmullRomSpline> implements Path { /** Calculates the catmullrom value for the given position (t). * @param out The Vector to set to the result. * @param t The position (0<=t<=1) on the spline * @param points The control points * @param continuous If true the b-spline restarts at 0 when reaching 1 * @param tmp A temporary vector used for the calculation * @return The value of out */ public static > T calculate (final T out, final float t, final T[] points, final boolean continuous, final T tmp) { final int n = continuous ? points.length : points.length - 3; float u = t * n; int i = (t >= 1f) ? (n - 1) : (int)u; u -= i; return calculate(out, i, u, points, continuous, tmp); } /** Calculates the catmullrom value for the given span (i) at the given position (u). * @param out The Vector to set to the result. * @param i The span (0<=i> T calculate (final T out, final int i, final float u, final T[] points, final boolean continuous, final T tmp) { final int n = points.length; final float u2 = u * u; final float u3 = u2 * u; out.set(points[i]).scl(1.5f * u3 - 2.5f * u2 + 1.0f); if (continuous || i > 0) out.add(tmp.set(points[(n + i - 1) % n]).scl(-0.5f * u3 + u2 - 0.5f * u)); if (continuous || i < (n - 1)) out.add(tmp.set(points[(i + 1) % n]).scl(-1.5f * u3 + 2f * u2 + 0.5f * u)); if (continuous || i < (n - 2)) out.add(tmp.set(points[(i + 2) % n]).scl(0.5f * u3 - 0.5f * u2)); return out; } /** Calculates the derivative of the catmullrom spline for the given position (t). * @param out The Vector to set to the result. * @param t The position (0<=t<=1) on the spline * @param points The control points * @param continuous If true the b-spline restarts at 0 when reaching 1 * @param tmp A temporary vector used for the calculation * @return The value of out */ public static > T derivative (final T out, final float t, final T[] points, final boolean continuous, final T tmp) { final int n = continuous ? points.length : points.length - 3; float u = t * n; int i = (t >= 1f) ? (n - 1) : (int)u; u -= i; return derivative(out, i, u, points, continuous, tmp); } /** Calculates the derivative of the catmullrom spline for the given span (i) at the given position (u). * @param out The Vector to set to the result. * @param i The span (0<=i> T derivative (final T out, final int i, final float u, final T[] points, final boolean continuous, final T tmp) { /* * catmull'(u) = 0.5 *((-p0 + p2) + 2 * (2*p0 - 5*p1 + 4*p2 - p3) * u + 3 * (-p0 + 3*p1 - 3*p2 + p3) * u * u) */ final int n = points.length; final float u2 = u * u; // final float u3 = u2 * u; out.set(points[i]).scl(-u * 5 + u2 * 4.5f); if (continuous || i > 0) out.add(tmp.set(points[(n + i - 1) % n]).scl(-0.5f + u * 2 - u2 * 1.5f)); if (continuous || i < (n - 1)) out.add(tmp.set(points[(i + 1) % n]).scl(0.5f + u * 4 - u2 * 4.5f)); if (continuous || i < (n - 2)) out.add(tmp.set(points[(i + 2) % n]).scl(-u + u2 * 1.5f)); return out; } public T[] controlPoints; public boolean continuous; public int spanCount; private T tmp; private T tmp2; private T tmp3; public CatmullRomSpline () { } public CatmullRomSpline (final T[] controlPoints, final boolean continuous) { set(controlPoints, continuous); } public CatmullRomSpline set (final T[] controlPoints, final boolean continuous) { if (tmp == null) tmp = controlPoints[0].cpy(); if (tmp2 == null) tmp2 = controlPoints[0].cpy(); if (tmp3 == null) tmp3 = controlPoints[0].cpy(); this.controlPoints = controlPoints; this.continuous = continuous; this.spanCount = continuous ? controlPoints.length : controlPoints.length - 3; return this; } @Override public T valueAt (T out, float t) { final int n = spanCount; float u = t * n; int i = (t >= 1f) ? (n - 1) : (int)u; u -= i; return valueAt(out, i, u); } /** @return The value of the spline at position u of the specified span */ public T valueAt (final T out, final int span, final float u) { return calculate(out, continuous ? span : (span + 1), u, controlPoints, continuous, tmp); } @Override public T derivativeAt (T out, float t) { final int n = spanCount; float u = t * n; int i = (t >= 1f) ? (n - 1) : (int)u; u -= i; return derivativeAt(out, i, u); } /** @return The derivative of the spline at position u of the specified span */ public T derivativeAt (final T out, final int span, final float u) { return derivative(out, continuous ? span : (span + 1), u, controlPoints, continuous, tmp); } /** @return The span closest to the specified value */ public int nearest (final T in) { return nearest(in, 0, spanCount); } /** @return The span closest to the specified value, restricting to the specified spans. */ public int nearest (final T in, int start, final int count) { while (start < 0) start += spanCount; int result = start % spanCount; float dst = in.dst2(controlPoints[result]); for (int i = 1; i < count; i++) { final int idx = (start + i) % spanCount; final float d = in.dst2(controlPoints[idx]); if (d < dst) { dst = d; result = idx; } } return result; } @Override public float approximate (T v) { return approximate(v, nearest(v)); } public float approximate (final T in, int start, final int count) { return approximate(in, nearest(in, start, count)); } public float approximate (final T in, final int near) { int n = near; final T nearest = controlPoints[n]; final T previous = controlPoints[n > 0 ? n - 1 : spanCount - 1]; final T next = controlPoints[(n + 1) % spanCount]; final float dstPrev2 = in.dst2(previous); final float dstNext2 = in.dst2(next); T P1, P2, P3; if (dstNext2 < dstPrev2) { P1 = nearest; P2 = next; P3 = in; } else { P1 = previous; P2 = nearest; P3 = in; n = n > 0 ? n - 1 : spanCount - 1; } float L1Sqr = P1.dst2(P2); float L2Sqr = P3.dst2(P2); float L3Sqr = P3.dst2(P1); float L1 = (float)Math.sqrt(L1Sqr); float s = (L2Sqr + L1Sqr - L3Sqr) / (2f * L1); float u = Mth.clamp((L1 - s) / L1, 0f, 1f); return (n + u) / spanCount; } @Override public float locate (T v) { return approximate(v); } @Override public float approxLength (int samples) { float tempLength = 0; for (int i = 0; i < samples; ++i) { tmp2.set(tmp3); valueAt(tmp3, (i) / ((float)samples - 1)); if (i > 0) tempLength += tmp2.dst(tmp3); } return tempLength; } } ================================================ FILE: src/main/java/weather2/weathersystem/tornado/CubicBezierCurve.java ================================================ package weather2.weathersystem.tornado; import org.joml.Vector3f; /** * source: http://www.java2s.com/Code/Java/2D-Graphics-GUI/AclassthatmodelsaCubicBeziercurve.htm */ public class CubicBezierCurve { private static final long serialVersionUID = -5219859720055898005L; public Vector3f[] P; /** * a contructor * * @param pointsVector 4 points that are required to build the bezier curve */ public CubicBezierCurve(Vector3f[] pointsVector) { this.P = pointsVector; } /** * returns the point in 3d space that corresponds to the given value of t * * @param t curve's parameter that should be in the range [0, 1.0] * @return the point in 3d space that corresponds to the given value of t */ public Vector3f getValue(float t) { if (t > 1.0 || t < 0.0) { throw new IllegalArgumentException("The value of t is out of range: " + t + " ."); } float one_minus_t = 1 - t; Vector3f retValue = new Vector3f(0, 0, 0); Vector3f[] terms = new Vector3f[4]; terms[0] = calcNewVector(one_minus_t * one_minus_t * one_minus_t, P[0]); terms[1] = calcNewVector(3 * one_minus_t * one_minus_t * t, P[1]); terms[2] = calcNewVector(3 * one_minus_t * t * t, P[2]); terms[3] = calcNewVector(t * t * t, P[3]); for (int i = 0; i < 4; i++) { retValue.add(terms[i]); } return retValue; } public Vector3f getValueTest(float t) { if (t > 1.0 || t < 0.0) { throw new IllegalArgumentException("The value of t is out of range: " + t + " ."); } float one_minus_t = 1 - t; Vector3f retValue = new Vector3f(0, 0, 0); Vector3f[] terms = new Vector3f[6]; float magicnumber = 5; terms[0] = calcNewVector(one_minus_t * one_minus_t * one_minus_t * one_minus_t * one_minus_t, P[0]); terms[1] = calcNewVector(magicnumber * one_minus_t * one_minus_t * one_minus_t * one_minus_t * t, P[1]); terms[2] = calcNewVector(magicnumber * one_minus_t * one_minus_t * one_minus_t * t * t, P[2]); terms[3] = calcNewVector(magicnumber * one_minus_t * one_minus_t * t * t * t, P[3]); terms[4] = calcNewVector(magicnumber * one_minus_t * t * t * t * t, P[4]); terms[5] = calcNewVector(t * t * t * t * t, P[5]); for (int i = 0; i < 6; i++) { retValue.add(terms[i]); } return retValue; } public Vector3f getValueTest10(float t) { if (t > 1.0 || t < 0.0) { throw new IllegalArgumentException("The value of t is out of range: " + t + " ."); } float one_minus_t = 1 - t; Vector3f retValue = new Vector3f(0, 0, 0); Vector3f[] terms = new Vector3f[10]; float mn = 9; terms[0] = calcNewVector(one_minus_t * one_minus_t * one_minus_t * one_minus_t * one_minus_t * one_minus_t * one_minus_t * one_minus_t * one_minus_t, P[0]); terms[1] = calcNewVector(mn * one_minus_t * one_minus_t * one_minus_t * one_minus_t * one_minus_t * one_minus_t * one_minus_t * one_minus_t * t, P[1]); terms[2] = calcNewVector(mn * one_minus_t * one_minus_t * one_minus_t * one_minus_t * one_minus_t * one_minus_t * one_minus_t * t * t, P[2]); terms[3] = calcNewVector(mn * one_minus_t * one_minus_t * one_minus_t * one_minus_t * one_minus_t * one_minus_t * t * t * t, P[3]); terms[4] = calcNewVector(mn * one_minus_t * one_minus_t * one_minus_t * one_minus_t * one_minus_t * t * t * t * t, P[4]); terms[5] = calcNewVector(mn * one_minus_t * one_minus_t * one_minus_t * one_minus_t * t * t * t * t * t, P[5]); terms[6] = calcNewVector(mn * one_minus_t * one_minus_t * one_minus_t * t * t * t * t * t * t, P[6]); terms[7] = calcNewVector(mn * one_minus_t * one_minus_t * t * t * t * t * t * t * t, P[7]); terms[8] = calcNewVector(mn * one_minus_t * t * t * t * t * t * t * t * t, P[8]); terms[9] = calcNewVector(t * t * t * t * t * t * t * t * t, P[9]); if (t > 0.8F) { int awt = 0; } for (int i = 0; i < 10; i++) { retValue.add(terms[i]); } return retValue; } /*public Vector3f getValueTest10(float t) { if (t > 1.0 || t < 0.0) { throw new IllegalArgumentException("The value of t is out of range: " + t + " ."); } float one_minus_t = 1 - t; Vector3f retValue = new Vector3f(0, 0, 0); Vector3f[] terms = new Vector3f[10]; int mn = 9; terms[0] = calcNewVector(one_minus_t * one_minus_t * one_minus_t * one_minus_t * one_minus_t * one_minus_t * one_minus_t * one_minus_t * one_minus_t, P[0]); terms[1] = calcNewVector(mn * one_minus_t * one_minus_t * one_minus_t * one_minus_t * one_minus_t * one_minus_t * one_minus_t * one_minus_t * t, P[1]); terms[2] = calcNewVector(mn * one_minus_t * one_minus_t * one_minus_t * one_minus_t * one_minus_t * one_minus_t * one_minus_t * t * t, P[2]); terms[3] = calcNewVector(mn * one_minus_t * one_minus_t * one_minus_t * one_minus_t * one_minus_t * one_minus_t * t * t * t, P[3]); terms[4] = calcNewVector(mn * one_minus_t * one_minus_t * one_minus_t * one_minus_t * one_minus_t * t * t * t * t, P[4]); terms[5] = calcNewVector(mn * one_minus_t * one_minus_t * one_minus_t * one_minus_t * t * t * t * t * t, P[5]); terms[6] = calcNewVector(mn * one_minus_t * one_minus_t * one_minus_t * t * t * t * t * t * t, P[6]); terms[7] = calcNewVector(mn * one_minus_t * one_minus_t * t * t * t * t * t * t * t, P[7]); terms[8] = calcNewVector(mn * one_minus_t * t * t * t * t * t * t * t * t, P[8]); terms[9] = calcNewVector(t * t * t * t * t * t * t * t * t, P[9]); for (int i = 0; i < 10; i++) { retValue.add(terms[i]); } return retValue; }*/ /** * calculates and returns a new vector that is base * scaler * * @param scaler * @param base * @return */ private Vector3f calcNewVector(float scaler, Vector3f base) { //Vector3f retValue = new Vector3f(base); Vector3f retValue = new Vector3f(base.x(), base.y(), base.z()); retValue.mul(scaler); return retValue; } } ================================================ FILE: src/main/java/weather2/weathersystem/tornado/Path.java ================================================ /******************************************************************************* * Copyright 2011 See AUTHORS file. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package weather2.weathersystem.tornado; /** Interface that specifies a path of type T within the window 0.0<=t<=1.0. * @author Xoppa */ public interface Path { T derivativeAt (T out, float t); /** @return The value of the path at t where 0<=t<=1 */ T valueAt (T out, float t); /** @return The approximated value (between 0 and 1) on the path which is closest to the specified value. Note that the * implementation of this method might be optimized for speed against precision, see {@link #locate(Object)} for a more * precise (but more intensive) method. */ float approximate (T v); /** @return The precise location (between 0 and 1) on the path which is closest to the specified value. Note that the * implementation of this method might be CPU intensive, see {@link #approximate(Object)} for a faster (but less * precise) method. */ float locate (T v); /** @param samples The amount of divisions used to approximate length. Higher values will produce more precise results, but * will be more CPU intensive. * @return An approximated length of the spline through sampling the curve and accumulating the euclidean distances between the * sample points. */ float approxLength (int samples); } ================================================ FILE: src/main/java/weather2/weathersystem/tornado/TornadoFunnel.java ================================================ package weather2.weathersystem.tornado; import com.corosus.coroutil.util.CoroUtilBlock; import extendedrenderer.particle.ParticleRegistry; import extendedrenderer.particle.entity.ParticleTexFX; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.core.BlockPos; import net.minecraft.util.Mth; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; import org.joml.Matrix3f; import org.joml.Quaternionf; import org.joml.Vector3d; import org.joml.Vector3f; import java.util.*; /** * To contain the full funnel, with each component piece */ public class TornadoFunnel { public Vector3d pos = new Vector3d(0, 0, 0); public LinkedList listFunnel = new LinkedList(); //temp? public int amountPerLayer = 30; public int particleCount = amountPerLayer * 50; public int funnelPieces = 2; CubicBezierCurve bezierCurve; static class FunnelPiece { public List listParticles = new ArrayList<>(); public Vector3d posStart = new Vector3d(0, 0, 0); public Vector3d posEnd = new Vector3d(0, 20, 0); //public Vector3d vecDir = new Vector3d(0, 0, 0); public float vecDirX = 0; public float vecDirZ = 0; public boolean needInit = true; CubicBezierCurve bezierCurve; } public TornadoFunnel() { } public void tickGame() { amountPerLayer = 30; particleCount = amountPerLayer * 50; funnelPieces = 10; tickGameTestCreate(); tickUpdateFunnel(); } private void tickGameTestCreate() { Player entP = Minecraft.getInstance().player; Random rand = new Random(); //listFunnel.clear(); while (listFunnel.size() < funnelPieces) { addPieceToEnd(new FunnelPiece()); } //for (FunnelPiece piece : listFunnel) { for (int i = 0; i < listFunnel.size(); i++) { FunnelPiece piece = listFunnel.get(i); if (piece.needInit) { piece.needInit = false; int height = 10; //temp //TODO: LINK TO PREVIOUS OR NEXT PIECE IF THERE IS ONE if (i == 0) { piece.posStart = new Vector3d(entP.getX(), entP.getY(), entP.getZ()); piece.posEnd = new Vector3d(entP.getX(), entP.getY() + height, entP.getZ()); //piece.posEnd = new Vector3d(entP.posX, entP.posY + entP.getEyeHeight(), entP.posZ); } else { Vector3d prev = listFunnel.get(i-1).posEnd; piece.posStart = new Vector3d(prev.x, prev.y, prev.z); piece.posEnd = new Vector3d(piece.posStart.x, piece.posStart.y + height, piece.posStart.z); } if (i == funnelPieces - 1) { piece.posEnd = new Vector3d(piece.posStart.x, piece.posStart.y + height, piece.posStart.z); } piece.vecDirX = rand.nextBoolean() ? 1 : -1; piece.vecDirZ = rand.nextBoolean() ? 1 : -1; } double dist = distanceTo(piece.posStart, piece.posEnd); double sizeXYParticle = 1; double funnelRadius = 3; double circumference = funnelRadius * 2D * Math.PI; amountPerLayer = (int) (circumference / sizeXYParticle); int layers = (int) (dist / sizeXYParticle); particleCount = layers * amountPerLayer; /*while (piece.listParticles.size() > particleCount) { piece.listParticles.get(piece.listParticles.size() - 1).setExpired(); piece.listParticles.remove(piece.listParticles.size() - 1); }*/ /*while (piece.listParticles.size() > 0) { piece.listParticles.get(piece.listParticles.size() - 1).setExpired(); piece.listParticles.remove(piece.listParticles.size() - 1); }*/ if (piece.bezierCurve == null || entP.level().getGameTime() % 40 == 0) { Vector3f[] vecs = new Vector3f[4]; for (int ii = 0; ii < vecs.length; ii++) { vecs[ii] = new Vector3f(entP.level().random.nextFloat(), entP.level().random.nextFloat(), entP.level().random.nextFloat()); } piece.bezierCurve = new CubicBezierCurve(vecs); } if (bezierCurve == null || entP.level().getGameTime() % 40 == 0) { Vector3f[] vecs = new Vector3f[4]; for (int ii = 0; ii < vecs.length; ii++) { vecs[ii] = new Vector3f(entP.level().random.nextFloat(), entP.level().random.nextFloat(), entP.level().random.nextFloat()); } bezierCurve = new CubicBezierCurve(vecs); } while (piece.listParticles.size() < particleCount) { BlockPos pos = CoroUtilBlock.blockPos(piece.posEnd.x, piece.posEnd.y, piece.posEnd.z); //if (entP.getDistanceSq(pos) < 10D * 10D) continue; //pos = world.getPrecipitationHeight(pos).add(0, 1, 0); ClientLevel world = (ClientLevel)entP.level(); ParticleTexFX particleTest = new ParticleTexFX(world, pos.getX() + rand.nextFloat(), pos.getY(), pos.getZ() + rand.nextFloat(), 0, 0, 0, ParticleRegistry.square16); //particleTest.setSprite(); particleTest.setMaxAge(250); particleTest.setParticleSpeed(0, 0, 0); particleTest.setScale(0.1F); //particleTest.setColor(0.1F * (particles.size() % particleCountCircle), 0, 0); particleTest.setColor(world.random.nextFloat(), world.random.nextFloat(), world.random.nextFloat()); particleTest.setGravity(0); /*if (piece.listParticles.size() < particleCountCircle * 5) { particleTest.setColor(1, 1, 1); }*/ //particleTest.move(0, -0.1, 0); Minecraft.getInstance().particleEngine.add(particleTest); piece.listParticles.add(particleTest); } } //reset /*for (int i = 0; i < listFunnel.size(); i++) { FunnelPiece piece = listFunnel.get(i); while (piece.listParticles.size() > particleCount) { piece.listParticles.get(piece.listParticles.size() - 1).setExpired(); piece.listParticles.remove(piece.listParticles.size() - 1); } }*/ //listFunnel.clear(); } private void tickUpdateFunnel() { Level world = Minecraft.getInstance().level; Player player = Minecraft.getInstance().player; //for (FunnelPiece piece : listFunnel) { for (int ii = 0; ii < listFunnel.size(); ii++) { FunnelPiece piece = listFunnel.get(ii); /*if (ii == listFunnel.size() - 1) { piece.posEnd = new Vector3d(piece.posStart.x, piece.posStart.y + 20, piece.posStart.z); }*/ double rate = 0.2F/* + (ii * 0.1F)*/; double distMax = 5 + (listFunnel.size() - ii); Random rand = new Random(); piece.posEnd.add(new Vector3d(rate * piece.vecDirX, 0, rate * piece.vecDirZ * 0.7)); //piece.posEnd = piece.posEnd.add(rate * random.nextFloat() * piece.vecDirX, 0, rate * random.nextFloat() * piece.vecDirZ); int offset = 360 / listFunnel.size(); long timeC = (world.getGameTime() * (ii+1) + (offset * ii)) * 1; float range = 35F; //piece.posEnd = new Vector3d(piece.posStart.x + Math.sin(Math.toRadians(timeC % 360)) * range, piece.posStart.y + 3, piece.posStart.z + Math.cos(Math.toRadians(timeC % 360)) * range); //piece.posEnd. //piece.posEnd = piece.posEnd.addVector(-1, 0, 0); float speedAmp = 0.3F; double xx1 = piece.posEnd.x - piece.posStart.x; double zz1 = piece.posEnd.z - piece.posStart.z; double xzDist2 = (double) Mth.sqrt((float) (xx1 * xx1 + zz1 * zz1)); if (xzDist2 > distMax) { if (piece.posEnd.x - piece.posStart.x > 0) { piece.vecDirX = -1; piece.vecDirX *= (0.5F + rand.nextFloat()) + (ii * speedAmp); } if (piece.posEnd.x - piece.posStart.x < 0) { piece.vecDirX = 1; piece.vecDirX *= (0.5F + rand.nextFloat()) + (ii * speedAmp); } if (piece.posEnd.z - piece.posStart.z > 0) { piece.vecDirZ = -1; piece.vecDirZ *= (0.5F + rand.nextFloat()) + (ii * speedAmp); } if (piece.posEnd.z - piece.posStart.z < 0) { piece.vecDirZ = 1; piece.vecDirZ *= (0.5F + rand.nextFloat()) + (ii * speedAmp); } } /*if (Math.abs(piece.posStart.x - piece.posEnd.x) > distMax) { piece.vecDirX *= -1; } if (Math.abs(piece.posStart.z - piece.posEnd.z) > distMax) { piece.vecDirZ *= -1; }*/ if (ii > 0) { Vector3d prev = listFunnel.get(ii-1).posEnd; piece.posStart = new Vector3d(prev.x, prev.y, prev.z); } double dist = distanceTo(piece.posStart, piece.posEnd); double x1 = piece.posEnd.x - piece.posStart.x; double y1 = piece.posEnd.y - piece.posStart.y; double z1 = piece.posEnd.z - piece.posStart.z; Vector3d vec = new Vector3d(x1 / dist, y1 / dist, z1 / dist); double sizeXYParticle = 1; double funnelRadius = 3; double circumference = funnelRadius * 2D * Math.PI; amountPerLayer = (int) (circumference / sizeXYParticle); int layers = (int) (dist / sizeXYParticle); particleCount = layers * amountPerLayer; Iterator it = piece.listParticles.iterator(); int index = 0; while (it.hasNext()) { ParticleTexFX part = it.next(); if (!part.isAlive()) { it.remove(); } else { int particleCountCircle = 20; int particleCountLayers = 40; int yIndex = index / amountPerLayer; int rotIndex = index % amountPerLayer; int yCount = particleCount / amountPerLayer; float x = 0;//((world.getGameTime() * 0.5F) % 360); float y = /*((world.getGameTime() * 3) % 360) + */((index % particleCountCircle) * (360 / particleCountCircle)); float y2 = ((world.getGameTime() * 3) % 360) + ((index % particleCountCircle) * (360 / particleCountCircle)); float z = 0;//((world.getGameTime() * 0.3F) % 360); int testY = 100; //float dist2 = (float)Math.sqrt(player.getDistanceSq(0.5, testY, 0.5)); float dist2 = (float)Math.sqrt(distanceTo(piece.posStart, piece.posEnd)); Vector3f vecDiff = new Vector3f( (float)(piece.posStart.x - piece.posEnd.x) / dist2, (float)(piece.posStart.y - piece.posEnd.y) / dist2, (float)(piece.posStart.z - piece.posEnd.z) / dist2); Vector3f vecAngles = new Vector3f( (float)Math.atan2(vecDiff.y(), vecDiff.z()), (float)Math.atan2(vecDiff.z(), vecDiff.x()), //invert if needed (float)Math.atan2(vecDiff.x(), vecDiff.y())); //invert if needed //convert to degrees vecAngles = new Vector3f((float)Math.toDegrees(vecAngles.x()), (float)Math.toDegrees(vecAngles.y()), (float)Math.toDegrees(vecAngles.z())); double xx = piece.posStart.x - piece.posEnd.x; double zz = piece.posStart.z - piece.posEnd.z; double xzDist = Math.sqrt(xx * xx + zz * zz); float pitchAngle = (float)Math.toDegrees(Math.atan2(vecDiff.y(), xzDist / dist2)); pitchAngle += 90; y = vecAngles.y() - 90; double curvePoint = Math.min(1F, (float)(index / particleCountCircle) / (float)particleCountLayers); double curvePoint2 = (index / particleCountCircle); double yDiff = curvePoint2 * (dist / particleCountLayers)/* - (particleCountLayers / 2)*/; float yDiffDist = 2F; float curveAmp = 1F; Quaternionf quaternionY = new Quaternionf(0.0F, 1.0F, 0.0F, Math.toRadians(-y)); Quaternionf quaternionYCircle = new Quaternionf(0.0F, 1.0F, 0.0F, Math.toRadians(-y2)); Quaternionf quatPitch = new Quaternionf(1.0F, 0.0F, 0.0F, Math.toRadians(-pitchAngle)); Vector3f vecCurve = piece.bezierCurve.getValue((float)curvePoint); //Vector3f vecNew = new Vector3f((float)vecCurve.x * curveAmp, 1 + ((float)yDiff) * yDiffDist, (float)vecCurve.z * curveAmp); Vector3f vecNew = new Vector3f((float)vecCurve.x() * curveAmp, 1 + ((float)yDiff) * yDiffDist, (float)vecCurve.z() * curveAmp); //Vector3f vecNew = new Vector3f((float)0, 1 + ((float)yDiff) * yDiffDist, (float)0); float rotAroundPosX = 0; float rotAroundPosY = 0; float rotAroundPosZ = 0; Matrix3f matrix = new Matrix3f(); matrix.rotation(quaternionY); matrix.rotation(quatPitch); //multiply in the radial shape of the tornado matrix.rotation(quaternionYCircle); vecNew.mulTranspose(matrix); rotAroundPosX = vecNew.x(); rotAroundPosY = vecNew.y(); rotAroundPosZ = vecNew.z(); //part.setPosition(player.getX() + rotAroundPosX, player.getY() + rotAroundPosY, player.getZ() + rotAroundPosZ); //part.setPosition(pos.x + rotAroundPosX, pos.y + rotAroundPosY, pos.z + rotAroundPosZ); //part.setPosition(pos.x + x1, pos.y + y1, pos.z + z1); part.setPosition(piece.posStart.x + rotAroundPosX, piece.posStart.y + rotAroundPosY, piece.posStart.z + rotAroundPosZ); } index++; } } } public void addPieceToEnd(FunnelPiece piece) { listFunnel.addLast(piece); } public double distanceTo(Vector3d vec1, Vector3d p_82555_) { double d0 = p_82555_.x - vec1.x; double d1 = p_82555_.y - vec1.y; double d2 = p_82555_.z - vec1.z; return Math.sqrt(d0 * d0 + d1 * d1 + d2 * d2); } } ================================================ FILE: src/main/java/weather2/weathersystem/tornado/TornadoManagerTodoRenameMe.java ================================================ package weather2.weathersystem.tornado; import extendedrenderer.particle.ParticleRegistry; import extendedrenderer.particle.entity.ParticleTexFX; import net.minecraft.client.Minecraft; import net.minecraft.util.Mth; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec2; import net.minecraft.world.phys.Vec3; import org.joml.Matrix3f; import org.joml.Quaternionf; import org.joml.Vector3d; import org.joml.Vector3f; import weather2.weathersystem.tornado.simple.TornadoFunnelSimple; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class TornadoManagerTodoRenameMe { private Class lastScreenClass = null; private ParticleTexFX particleTest = null; private List particles = new ArrayList<>(); private TornadoFunnel funnel; private TornadoFunnelSimple funnelSimple; //public CubicBezierCurve bezierCurve; public List curves = new ArrayList<>(); public Vector3f[] vecSpeeds = new Vector3f[10]; public void tick(Level world) { Minecraft mc = Minecraft.getInstance(); if (mc.level == null || mc.player == null) return; if (mc.level.getGameTime() % 1 == 0) { for (Player playerEntity : mc.level.players()) { if (true || mc.player.distanceTo(playerEntity) < 20) { int particleCountCircle = 20; int particleCountLayers = 40; while (particles.size() < particleCountCircle * particleCountLayers) { particleTest = new ParticleTexFX(mc.level, playerEntity.getX(), playerEntity.getY() + 2.2, playerEntity.getZ(), 0, 0, 0, ParticleRegistry.square16); //particleTest.setSprite(ParticleRegistry.square16); particleTest.setMaxAge(250); //particleTest.setMotion(0, 0, 0); particleTest.setScale(0.2F); //particleTest.setColor(0.1F * (particles.size() % particleCountCircle), 0, 0); particleTest.setColor(world.random.nextFloat(), world.random.nextFloat(), world.random.nextFloat()); if (particles.size() < particleCountCircle * 5) { particleTest.setColor(1, 1, 1); } float randGrey = 0.4F + (world.random.nextFloat() * 0.4F); particleTest.setColor(randGrey, randGrey, randGrey); //particleTest.move(0, -0.1, 0); mc.particleEngine.add(particleTest); //particleTest.setAngle(world.randomom.nextInt(360)); particles.add(particleTest); } int testY = 100; Vector3f pos1 = new Vector3f(0.5F, 70, 0.5F); Vector3f pos2 = new Vector3f(0.5F, 120, 0.5F); /*float dist = (float)Math.sqrt(playerEntity.getDistanceSq(0.5, testY, 0.5)); Vector3f vecDiff = new Vector3f( (float)(playerEntity.getPosX() - 0.5) / dist, (float)(playerEntity.getPosY() - testY) / dist, (float)(playerEntity.getPosZ() - 0.5) / dist); Vector3f vecAngles = new Vector3f( (float)Math.atan2(vecDiff.y(), vecDiff.z()), (float)Math.atan2(vecDiff.z(), vecDiff.x()), //invert if needed (float)Math.atan2(vecDiff.x(), vecDiff.y())); //invert if needed*/ float dist = getDistance(pos1, pos2); Vector3f vecDiff = new Vector3f( (pos1.x() - pos2.x()) / dist, (pos1.y() - pos2.y()) / dist, (pos1.z() - pos2.z()) / dist); Vector3f vecAngles = new Vector3f( (float)Math.atan2(vecDiff.y(), vecDiff.z()), (float)Math.atan2(vecDiff.z(), vecDiff.x()), //invert if needed (float)Math.atan2(vecDiff.x(), vecDiff.y())); //invert if needed //convert to degrees vecAngles = new Vector3f((float)Math.toDegrees(vecAngles.x()), (float)Math.toDegrees(vecAngles.y()), (float)Math.toDegrees(vecAngles.z())); double xx = pos1.x() - pos2.x(); double zz = pos1.z() - pos2.z(); double xzDist = Math.sqrt(xx * xx + zz * zz); float pitchAngle = (float)Math.toDegrees(Math.atan2(vecDiff.y(), xzDist / dist)); pitchAngle += 90; /*if (playerEntity.isSprinting()) { curves.clear(); }*/ while (curves.size() < 2) { //Vector3f[] vecs = new Vector3f[4]; Vector3f[] vecs = new Vector3f[10]; for (int i = 0; i < vecs.length; i++) { vecs[i] = new Vector3f(world.random.nextFloat(), world.random.nextFloat(), world.random.nextFloat()); } curves.add(new CubicBezierCurve(vecs)); } //trying to smooth the 2 curves together... //match the end and start of each curve //tempoffcurves.get(1).P[0].set(curves.get(0).P[3].x(), curves.get(0).P[3].y(), curves.get(0).P[3].z()); //try to match the previous second last to the next second so it'd present a smooth curve //doesnt work, or im doing it wrong //curves.get(1).P[1].set(1F - curves.get(0).P[2].x(), 1F - curves.get(0).P[2].y(), 1F - curves.get(0).P[2].z()); //curves.get(1).P[0].set(1F-curves.get(0).P[3].x(), 1F-curves.get(0).P[3].y(), 1F-curves.get(0).P[3].z()); //curves.get(1).P[1].set(1F-curves.get(0).P[2].x(), 1F-curves.get(0).P[2].y(), 1F-curves.get(0).P[2].z()); //curves.get(1).P[2].set(1F-curves.get(0).P[1].x(), 1F-curves.get(0).P[1].y(), 1F-curves.get(0).P[1].z()); //curves.get(1).P[3].set(1F-curves.get(0).P[0].x(), 1F-curves.get(0).P[0].y(), 1F-curves.get(0).P[0].z()); CubicBezierCurve bezierCurve = curves.get(0); /*if (bezierCurve == null) { Vector3f[] vecs = new Vector3f[4]; for (int i = 0; i < vecs.length; i++) { vecs[i] = new Vector3f(world.random.nextFloat(), world.random.nextFloat(), world.random.nextFloat()); } bezierCurve = new CubicBezierCurve(vecs); }*/ /*if (bezierCurve != null) { float randScale = 0.1F; for (int i = 0; i < bezierCurve.P.length; i++) { bezierCurve.P[i].add((world.random.nextFloat() - world.random.nextFloat()) * randScale, (world.random.nextFloat() - world.random.nextFloat()) * randScale, (world.random.nextFloat() - world.random.nextFloat()) * randScale); bezierCurve.P[i].normalize(); } }*/ if (bezierCurve != null && true) { float randScale = 0.1F; for (int i = 0; i < bezierCurve.P.length; i++) { if (vecSpeeds[i] == null) { vecSpeeds[i] = new Vector3f(world.random.nextFloat(), world.random.nextFloat(), world.random.nextFloat()); } bezierCurve.P[i].add(vecSpeeds[i].x() * 0.01F, vecSpeeds[i].y() * 0.01F, vecSpeeds[i].z() * 0.01F); float maxY = 1F; float minY = 0F; /*if (i == 0) { maxY = 0.25F; minY = 0.0F; } else if (i == 1) { maxY = 0.9F; minY = 0.1F; } else if (i == 2) { maxY = 0.9F; minY = 0.1F; } else if (i == 3) { maxY = 1.0F; minY = 0.75F; }*/ //maxY += 2; float minXZ = 0; float maxXZ = 1; float randSpeed = 1.5F; if (bezierCurve.P[i].x() > maxXZ) { vecSpeeds[i].set(world.random.nextFloat() * -1 * randSpeed, vecSpeeds[i].y(), vecSpeeds[i].z()); } else if (bezierCurve.P[i].x() < minXZ) { vecSpeeds[i].set(world.random.nextFloat() * randSpeed, vecSpeeds[i].y(), vecSpeeds[i].z()); } if (bezierCurve.P[i].y() > maxY) { vecSpeeds[i].set(vecSpeeds[i].x(), world.random.nextFloat() * -1 * randSpeed, vecSpeeds[i].z()); } else if (bezierCurve.P[i].y() < minY) { vecSpeeds[i].set(vecSpeeds[i].x(), world.random.nextFloat() * randSpeed, vecSpeeds[i].z()); } if (bezierCurve.P[i].z() > maxXZ) { vecSpeeds[i].set(vecSpeeds[i].x(), vecSpeeds[i].y(), world.random.nextFloat() * -1 * randSpeed); } else if (bezierCurve.P[i].z() < minXZ) { vecSpeeds[i].set(vecSpeeds[i].x(), vecSpeeds[i].y(), world.random.nextFloat() * randSpeed); } //bezierCurve.P[i].normalize(); } //bezierCurve.P[0] = new Vector3f(0.5F, 0, 0.5F); //base of tornado //bezierCurve.P[1].set(bezierCurve.P[0].x(), bezierCurve.P[0].y() + 0.3F, bezierCurve.P[0].z()); //bezierCurve.P[0].set(bezierCurve.P[3].x(), 0, bezierCurve.P[3].z()); //bezierCurve.P[0].set(bezierCurve.P[0].x(), 0, bezierCurve.P[0].z()); //top of tornado //bezierCurve.P[2].set(bezierCurve.P[3].x(), bezierCurve.P[3].y() - 0.3F, bezierCurve.P[3].z()); //bezierCurve.P[3].set(bezierCurve.P[3].x(), 1, bezierCurve.P[3].z()); if (bezierCurve.P.length == 6) { //bezierCurve.P[5].set(bezierCurve.P[5].x(), 1, bezierCurve.P[5].z()); } //bezierCurve.P[0].set(0.5F, 0, 0.5F); //bezierCurve.P[1].set(0.5F, 0.8F, 0.5F); //bezierCurve.P[2].set(0.5F, 0.2F, 0.5F); //bezierCurve.P[3].set(0.5F, 0.99F, 0.5F); //bezierCurve.P[3] = new Vector3f(0.5F, 1, 0.5F); } /*if (mc.level.getGameTime() % 40 == 0 && WATUT.playerManagerClient.getPlayerStatus(mc.player.getUniqueID()).getStatusType() == PlayerStatus.StatusType.CHAT) { System.out.println("x: " + vecAngles.x()); System.out.println("y: " + vecAngles.y()); System.out.println("z: " + vecAngles.z()); //System.out.println("yDiff: " + (playerEntity.getPosY() - testY)); System.out.println("pitchAngle: " + pitchAngle); }*/ Iterator it = particles.iterator(); int index = 0; float adjustedCurvePos = 0; while (it.hasNext()) { ParticleTexFX particle = it.next(); if (!particle.isAlive()) { it.remove(); } else { //(index * (360 / particleCount)) float x = 0;//((world.getGameTime() * 0.5F) % 360); float y2 = ((world.getGameTime() * 2) % 360) + ((index % particleCountCircle) * (360 / particleCountCircle)); float y = /*((world.getGameTime() * 3) % 360) + */((index % particleCountCircle) * (360 / particleCountCircle)); float z = 0;//((world.getGameTime() * 0.3F) % 360); y = vecAngles.y() - 90; int yDiff = (index / particleCountCircle) - (particleCountLayers / 2); float yDiffDist = 0.01F; int curLayer = (index / particleCountCircle); float curvePoint = (float)curLayer / (float)particleCountLayers * 1F; float curvePoint2 = (float)Math.min(1D, (float)(curLayer+1) / (float)particleCountLayers) * 1F; float stretchCurveY = 4F; float curveAmp = 2F; y2 = ((world.getGameTime() * (7 + (particleCountLayers - curLayer) * (particleCountLayers - curLayer) * 0.02F)) % 360) + ((index % particleCountCircle) * (360 / particleCountCircle)); //y2 = ((index % particleCountCircle) * (360 / particleCountCircle)); float distFinal = dist / 2F; /*Vector3f vecCurve1 = bezierCurve.getValue(curvePoint); Vector3f vecCurve2 = bezierCurve.getValue((float)Math.min(1D, (float)(curLayer+1) / (float)particleCountLayers));*/ Vector3f vecCurve1 = getCurveValue(curvePoint); Vector3f vecCurve2 = getCurveValue(curvePoint2); Vec2 curvePointYawPitch = yawPitch(vecCurve2, vecCurve1); float curveDist = getDistance(vecCurve1, vecCurve2); if ((index % particleCountCircle) == 0) { adjustedCurvePos += curveDist; //System.out.println(getDistance(vecCurve1, vecCurve2)); //System.out.println(curvePointYawPitch.x + " - " + curvePointYawPitch.y); } //Quaternionf quaternionY = new Quaternionf(new Vector3f(0.0F, 1.0F, 0.0F), -y, true); Quaternionf quaternionY = new Quaternionf(0.0F, 1.0F, 0.0F, Math.toRadians(-curvePointYawPitch.x - 90)); //adding quaternionY here cancels out the unwanted rotations from the bezier curve adjustments Quaternionf quaternionYCircle = new Quaternionf(0.0F, 1.0F, 0.0F, Math.toRadians(-y2 + (curvePointYawPitch.x - 90))); Quaternionf quatPitch = new Quaternionf(1.0F, 0.0F, 0.0F, Math.toRadians(curvePointYawPitch.y)); //Vector3f vecNew = new Vector3f(1F, 1 + ((float)yDiff) * yDiffDist, 0); //Vector3d vecCurve = bezierCurve.getValue(curvePoint); Vector3f vecCurve = getCurveValue(curvePoint); //System.out.println("curvePoint: " + curvePoint + ", " + vecCurve); //Vector3f vecNew = new Vector3f((float)vecCurve.x * curveAmp, (float)vecCurve.y * curveAmp * stretchCurveY * (((float)yDiff) * yDiffDist) + 10F, (float)vecCurve.z * curveAmp); //Vector3f vecNew = new Vector3f((float)vecCurve.x() * curveAmp - curveAmp/2F, (1F + ((float)yDiff) * yDiffDist * (dist*2F)) - (dist/2F), (float)vecCurve.z() * curveAmp - curveAmp/2F); //Vector3f vecNew = new Vector3f(1F * curveAmp, (1F + ((float)yDiff) * yDiffDist * (dist*2F)) - (dist/2F), 0F); //Vector3f vecNew = new Vector3f(1F * curveAmp, (1F + ((float)yDiff) * yDiffDist * (dist*2F)) - (dist/2F), 1F * curveAmp); //Vector3f vecNew = new Vector3f(1F * curveAmp, (((float)yDiff) * distFinal) - (dist/2F), 0); //Vector3f vecNew = new Vector3f(1.3F + Math.min((curLayer * curLayer * 0.005F), 40)/* + (curLayer * 0.05F)*/, 0F, 0); //Vector3f vecNew = new Vector3f(1.3F + 1/* + (curLayer * 0.05F)*/, 0F, 0); Vector3f vecNew = new Vector3f(1/* + (curLayer * 0.05F)*/, 0F, 0); float rotAroundPosX = 0; float rotAroundPosY = 0; float rotAroundPosZ = 0; Matrix3f matrix = new Matrix3f(); matrix.rotation(quaternionY); matrix.rotation(quatPitch); matrix.rotation(quaternionYCircle); vecNew.mulTranspose(matrix); rotAroundPosX = vecNew.x(); rotAroundPosY = vecNew.y(); rotAroundPosZ = vecNew.z(); float tiltAdj = 1F; /*tiltAdj = curvePoint; if (tiltAdj > 0.5) { tiltAdj = 1F - tiltAdj; } else if (tiltAdj < 0.2) { //tiltAdj = tiltAdj; } else { tiltAdj = 1F; }*/ //particle.setPosition(pos1.x() + rotAroundPosX, pos1.y() + rotAroundPosY, pos1.z() + rotAroundPosZ); //particle.setPosition(pos1.x() + (vecCurve1.x()*distFinal) + rotAroundPosX, pos1.y() + (vecCurve1.y()*distFinal) + rotAroundPosY, pos1.z() + (vecCurve1.z()*distFinal) + rotAroundPosZ); //particle.setPosition(pos1.x() + (vecCurve1.x()*distFinal) + rotAroundPosX, pos1.y() + (curLayer) + rotAroundPosY, pos1.z() + (vecCurve1.z()*distFinal) + rotAroundPosZ); //particle.setPosition(pos1.x() + (vecCurve1.x()*distFinal) + rotAroundPosX, pos1.y() + (curLayer) + (rotAroundPosY * (tiltAdj)), pos1.z() + (vecCurve1.z()*distFinal) + rotAroundPosZ); particle.setPosition(pos1.x() + (vecCurve1.x()*distFinal) + rotAroundPosX, pos1.y() + (vecCurve1.y()*distFinal) + (rotAroundPosY * (tiltAdj)), pos1.z() + (vecCurve1.z()*distFinal) + rotAroundPosZ); //particle.setPosition(0, 100, 0); particle.setMotionX(0); particle.setMotionY(0); particle.setMotionZ(0); /*particle.setPrevPosX(particle.getPosX()); particle.setPrevPosY(particle.getPosY()); particle.setPrevPosZ(particle.getPosZ());*/ //particle.setPosition(pos1.x() + (vecCurve1.x()*distFinal) + rotAroundPosX, pos1.y() + 1 + curLayer, pos1.z() + (vecCurve1.z()*distFinal) + rotAroundPosZ); } index++; } } } } if (funnel == null) { funnel = new TornadoFunnel(); funnel.pos = new Vector3d(mc.player.getX(), mc.player.getY(), mc.player.getZ()); } //funnel.tickGame(); /*if (funnelSimple == null) { ActiveTornadoConfig activeTornadoConfig = new ActiveTornadoConfig().setHeight(10).setRadiusOfBase(3).setSpinSpeed(360F / 20F).setRadiusIncreasePerLayer(0.5F); funnelSimple = new TornadoFunnelSimple(activeTornadoConfig); funnelSimple.pos = new Vec3(mc.player.getX(), mc.player.getY(), mc.player.getZ()); }*/ //funnelSimple.tickClient(); } public float getDistance(Vector3f vec1, Vector3f vec2) { float f = (vec1.x() - vec2.x()); float f1 = (vec1.y() - vec2.y()); float f2 = (vec1.z() - vec2.z()); return Mth.sqrt(f * f + f1 * f1 + f2 * f2); } /** * * @param pos2 * @param pos1 * @return yaw and pitch in degrees */ public Vec2 yawPitch(Vector3f pos2, Vector3f pos1) { float dist = getDistance(pos1, pos2); Vector3f vecDiff = new Vector3f( (pos1.x() - pos2.x()) / dist, (pos1.y() - pos2.y()) / dist, (pos1.z() - pos2.z()) / dist); Vector3f vecAngles = new Vector3f( (float)Math.atan2(vecDiff.y(), vecDiff.z()), (float)Math.atan2(vecDiff.z(), vecDiff.x()), //invert if needed (float)Math.atan2(vecDiff.x(), vecDiff.y())); //invert if needed double xx = pos1.x() - pos2.x(); double zz = pos1.z() - pos2.z(); double xzDist = Math.sqrt(xx * xx + zz * zz); double wat = xzDist / dist; float pitchAngle = (float)Math.toDegrees(Math.atan2(vecDiff.y(), xzDist / dist)); vecAngles = new Vector3f((float)Math.toDegrees(vecAngles.x()), (float)Math.toDegrees(vecAngles.y()), (float)Math.toDegrees(vecAngles.z())); pitchAngle += 90; return new Vec2(vecAngles.y(), pitchAngle); } public Vector3f getCurveValue(float val) { int arrayEntry = (int)Math.floor(val); if (arrayEntry > curves.size()-1) { System.out.println("out of bounds on curve lookup, val: " + val + " curves: - " + curves.size()); return new Vector3f(1F, 1F, 1F); } CubicBezierCurve curve = curves.get(arrayEntry); return curve.getValue(val % 1F); } } ================================================ FILE: src/main/java/weather2/weathersystem/tornado/Vector.java ================================================ /******************************************************************************* * Copyright 2011 See AUTHORS file. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package weather2.weathersystem.tornado; /** Encapsulates a general vector. Allows chaining operations by returning a reference to itself in all modification methods. See * * @author Xoppa */ public interface Vector> { /** @return a copy of this vector */ T cpy (); /** @return The euclidean length */ float len (); /** This method is faster than {@link Vector#len()} because it avoids calculating a square root. It is useful for comparisons, * but not for getting exact lengths, as the return value is the square of the actual length. * @return The squared euclidean length */ float len2 (); /** Limits the length of this vector, based on the desired maximum length. * @param limit desired maximum length for this vector * @return this vector for chaining */ T limit (float limit); /** Limits the length of this vector, based on the desired maximum length squared. *

* This method is slightly faster than limit(). * @param limit2 squared desired maximum length for this vector * @return this vector for chaining * @see #len2() */ T limit2 (float limit2); /** Sets the length of this vector. Does nothing if this vector is zero. * @param len desired length for this vector * @return this vector for chaining */ T setLength (float len); /** Sets the length of this vector, based on the square of the desired length. Does nothing if this vector is zero. *

* This method is slightly faster than setLength(). * @param len2 desired square of the length for this vector * @return this vector for chaining * @see #len2() */ T setLength2 (float len2); /** Clamps this vector's length to given min and max values * @param min Min length * @param max Max length * @return This vector for chaining */ T clamp (float min, float max); /** Sets this vector from the given vector * @param v The vector * @return This vector for chaining */ T set (T v); /** Subtracts the given vector from this vector. * @param v The vector * @return This vector for chaining */ T sub (T v); /** Normalizes this vector. Does nothing if it is zero. * @return This vector for chaining */ T nor (); /** Adds the given vector to this vector * @param v The vector * @return This vector for chaining */ T add (T v); /** @param v The other vector * @return The dot product between this and the other vector */ float dot (T v); /** Scales this vector by a scalar * @param scalar The scalar * @return This vector for chaining */ T scl (float scalar); /** Scales this vector by another vector * @return This vector for chaining */ T scl (T v); /** @param v The other vector * @return the distance between this and the other vector */ float dst (T v); /** This method is faster than {@link Vector#dst(Vector)} because it avoids calculating a square root. It is useful for * comparisons, but not for getting accurate distances, as the return value is the square of the actual distance. * @param v The other vector * @return the squared distance between this and the other vector */ float dst2 (T v); /** Linearly interpolates between this vector and the target vector by alpha which is in the range [0,1]. The result is stored * in this vector. * @param target The target vector * @param alpha The interpolation coefficient * @return This vector for chaining. */ T lerp (T target, float alpha); /** Sets this vector to the unit vector with a random direction * @return This vector for chaining */ T setToRandomDirection (); /** @return Whether this vector is a unit length vector */ boolean isUnit (); /** @return Whether this vector is a unit length vector within the given margin. */ boolean isUnit (final float margin); /** @return Whether this vector is a zero vector */ boolean isZero (); /** @return Whether the length of this vector is smaller than the given margin */ boolean isZero (final float margin); /** @return true if this vector is in line with the other vector (either in the same or the opposite direction) */ boolean isOnLine (T other, float epsilon); /** @return true if this vector is in line with the other vector (either in the same or the opposite direction) */ boolean isOnLine (T other); /** @return true if this vector is collinear with the other vector ({@link #isOnLine(Vector, float)} && * {@link #hasSameDirection(Vector)}). */ boolean isCollinear (T other, float epsilon); /** @return true if this vector is collinear with the other vector ({@link #isOnLine(Vector)} && * {@link #hasSameDirection(Vector)}). */ boolean isCollinear (T other); /** @return true if this vector is opposite collinear with the other vector ({@link #isOnLine(Vector, float)} && * {@link #hasOppositeDirection(Vector)}). */ boolean isCollinearOpposite (T other, float epsilon); /** @return true if this vector is opposite collinear with the other vector ({@link #isOnLine(Vector)} && * {@link #hasOppositeDirection(Vector)}). */ boolean isCollinearOpposite (T other); /** @return Whether this vector is perpendicular with the other vector. True if the dot product is 0. */ boolean isPerpendicular (T other); /** @return Whether this vector is perpendicular with the other vector. True if the dot product is 0. * @param epsilon a positive small number close to zero */ boolean isPerpendicular (T other, float epsilon); /** @return Whether this vector has similar direction compared to the other vector. True if the normalized dot product is > * 0. */ boolean hasSameDirection (T other); /** @return Whether this vector has opposite direction compared to the other vector. True if the normalized dot product is < * 0. */ boolean hasOppositeDirection (T other); /** Compares this vector with the other vector, using the supplied epsilon for fuzzy equality testing. * @param other * @param epsilon * @return whether the vectors have fuzzy equality. */ boolean epsilonEquals (T other, float epsilon); /** First scale a supplied vector, then add it to this vector. * @param v addition vector * @param scalar for scaling the addition vector */ T mulAdd (T v, float scalar); /** First scale a supplied vector, then add it to this vector. * @param v addition vector * @param mulVec vector by whose values the addition vector will be scaled */ T mulAdd (T v, T mulVec); /** Sets the components of this vector to 0 * @return This vector for chaining */ T setZero (); } ================================================ FILE: src/main/java/weather2/weathersystem/tornado/simple/Layer.java ================================================ package weather2.weathersystem.tornado.simple; import extendedrenderer.particle.entity.PivotingParticle; import net.minecraft.world.phys.Vec3; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.fml.LogicalSide; import net.minecraftforge.fml.util.thread.EffectiveSide; import java.util.ArrayList; import java.util.List; public class Layer { @OnlyIn(Dist.CLIENT) private List listParticles; @OnlyIn(Dist.CLIENT) private List listParticlesExtra; private Vec3 pos = Vec3.ZERO; private float rotation; public Layer(Vec3 pos) { this.pos = new Vec3(pos.x, pos.y, pos.z); if (EffectiveSide.get() == LogicalSide.CLIENT) { initClient(); } } @OnlyIn(Dist.CLIENT) public void initClient() { listParticles = new ArrayList<>(); listParticlesExtra = new ArrayList<>(); } @OnlyIn(Dist.CLIENT) public List getListParticles() { return listParticles; } @OnlyIn(Dist.CLIENT) public void setListParticles(List listParticles) { this.listParticles = listParticles; } @OnlyIn(Dist.CLIENT) public List getListParticlesExtra() { return listParticlesExtra; } @OnlyIn(Dist.CLIENT) public void setListParticlesExtra(List listParticlesExtra) { this.listParticlesExtra = listParticlesExtra; } public Vec3 getPos() { return pos; } public void setPos(Vec3 pos) { this.pos = pos; } public float getRotation() { return rotation; } public void setRotation(float rotation) { this.rotation = rotation; } } ================================================ FILE: src/main/java/weather2/weathersystem/tornado/simple/TornadoFunnelSimple.java ================================================ package weather2.weathersystem.tornado.simple; import com.corosus.coroutil.util.CULog; import extendedrenderer.particle.ParticleRegistry; import extendedrenderer.particle.entity.PivotingParticle; import net.minecraft.client.Minecraft; import net.minecraft.client.ParticleStatus; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.animal.Dolphin; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import weather2.Weather; import weather2.weathersystem.storm.StormObject; import weather2.weathersystem.tornado.ActiveTornadoConfig; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Random; public class TornadoFunnelSimple { private ActiveTornadoConfig config; public Vec3 pos = new Vec3(0, 0, 0); public List listLayers = new ArrayList<>(); private float heightPerLayer = 1F; private StormObject stormObject; private float targetSizeRadius = 0; private float sizeRadiusRate = 0; private float renderDistCutoff = 50; //hack to fix client data coming in late private boolean wasFirenado = false; public TornadoFunnelSimple(ActiveTornadoConfig config, StormObject stormObject) { this.config = config; this.stormObject = stormObject; config.setRadiusOfBase(stormObject.tornadoHelper.getTornadoBaseSize() / 2); } public void init() { listLayers.clear(); } public void tick() { if (stormObject.isPet()) { heightPerLayer = 0.2F; } //TESTING //config.setEntityPullDistXZForY(90); //dynamic sizing targetSizeRadius = stormObject.tornadoHelper.getTornadoBaseSize() / 2; sizeRadiusRate = 0.01F; if (config.getRadiusOfBase() != targetSizeRadius) { //CULog.dbg("tornado size transitioning: " + config.getRadiusOfBase()); if (config.getRadiusOfBase() < targetSizeRadius) { config.setRadiusOfBase(config.getRadiusOfBase() + sizeRadiusRate); if (config.getRadiusOfBase() > targetSizeRadius) config.setRadiusOfBase(targetSizeRadius); } else { config.setRadiusOfBase(config.getRadiusOfBase() - sizeRadiusRate); if (config.getRadiusOfBase() < targetSizeRadius) config.setRadiusOfBase(targetSizeRadius); } } int layers = (int) (config.getHeight() / heightPerLayer); float radiusMax = config.getRadiusOfBase() + (config.getRadiusIncreasePerLayer() * (layers+1)); for (int i = 0; i < layers; i++) { //grow layer count as height increases if (i >= listLayers.size()) { listLayers.add(new Layer(stormObject.posBaseFormationPos)); } /** * get radius for current layer * convert to circumference (c = r2 * pi) * count = space per particle / circumference */ float radius = config.getRadiusOfBase() + (config.getRadiusIncreasePerLayer() * (i)); Vec3 posLayer = listLayers.get(i).getPos(); float relYDown1 = (heightPerLayer * (radius / radiusMax)); Vec3 posLayerLower; if (i == 0) { posLayerLower = new Vec3(pos.x, pos.y, pos.z); } else { Vec3 temp = listLayers.get(i-1).getPos(); posLayerLower = new Vec3(temp.x, temp.y + relYDown1, temp.z); } double dist = posLayer.distanceTo(posLayerLower); //easy way to fix the spawning at 0,0 issue if (dist > 50) { CULog.dbg("teleporting tornado layer to lower piece"); listLayers.get(i).setPos(new Vec3(posLayerLower.x, posLayerLower.y, posLayerLower.z)); } else if (dist > 0.1F * (radius / radiusMax)) { double dynamicSpeed = 15F * (Math.min(30F, dist) / 30F); double speed = dynamicSpeed;//0.01F; Vec3 moveVec = posLayer.vectorTo(posLayerLower).normalize().multiply(speed, speed * 1F, speed); Vec3 newPos = posLayer.add(moveVec); listLayers.get(i).setPos(new Vec3(newPos.x, newPos.y, newPos.z)); } } Level level = stormObject.manager.getWorld(); if (stormObject.isSharknado()) { if (!level.isClientSide()) { if (level.getGameTime() % 20 == 0) { Entity ent = null; if (Weather.isLoveTropicsInstalled()) { /** * TODO: for LT, turn back on when LT is needed, activates dependency on LTWeather / Tropicraft */ //ent = new SharkEntity(TropicraftEntities.HAMMERHEAD.get(), level); } else { ent = new Dolphin(EntityType.DOLPHIN, level); } if (ent == null) { CULog.log("SharkEntity not spawned, enable in weather mod"); ent = new Dolphin(EntityType.DOLPHIN, level); } Vec3 posRand = new Vec3(pos.x + 0, pos.y + 3, pos.z - 5); ent.setPos(posRand); ent.setDeltaMovement(3F, 0, 0); level.addFreshEntity(ent); } } } } @OnlyIn(Dist.CLIENT) public void tickClient() { long gameTime = stormObject.getAge(); Level level = stormObject.manager.getWorld(); renderDistCutoff = Minecraft.getInstance().gameRenderer.getRenderDistance() * 4; int layers = (int) (config.getHeight() / heightPerLayer); float radiusMax = config.getRadiusOfBase() + (config.getRadiusIncreasePerLayer() * (layers+1)); boolean isBaby = stormObject.isBaby(); boolean isPet = stormObject.isPet(); //cleanup layers beyond current size //while (listLayers.size() > layers) { for (int i = layers; i < listLayers.size(); i++) { List listLayer = listLayers.get(i).getListParticles(); Iterator it = listLayer.iterator(); while (it.hasNext()) { PivotingParticle particle = it.next(); it.remove(); particle.remove(); } } int particleCount = 0; float adjustedRate = 1F; if (!isPet) { if (Minecraft.getInstance().options.particles.get() == ParticleStatus.DECREASED) { adjustedRate = 0.6F; } else if (Minecraft.getInstance().options.particles.get() == ParticleStatus.MINIMAL) { adjustedRate = 0.3F; } } int layersWithDebris = stormObject.getAgeSinceTornadoTouchdown()/5; //CULog.dbg("layersWithDebris: " + layersWithDebris); for (int i = 0; i < layers; i++) { /** * get radius for current layer * convert to circumference (c = r2 * pi) * count = space per particle / circumference */ List listLayer = listLayers.get(i).getListParticles(); List listLayerExtra = listLayers.get(i).getListParticlesExtra(); float radius = config.getRadiusOfBase() + (config.getRadiusIncreasePerLayer() * (i)); float radiusAdjustedForParticleSize = radius * (radius / radiusMax); float circumference = radius * 2 * Mth.PI; //float particleSpaceOccupy = 0.5F * (radius / radiusMax); float particleSpaceOccupy = (15F / adjustedRate) * (radius / radiusMax); if (isBaby) particleSpaceOccupy = (2F / adjustedRate) * (radius / radiusMax); if (isPet) particleSpaceOccupy = (0.2F / adjustedRate) * (radius / radiusMax); float particlesPerLayer = (float) /*Math.floor(*/circumference / particleSpaceOccupy/*)*/; Iterator itt = listLayer.iterator(); float indexx = 0; while (itt.hasNext()) { PivotingParticle particle = itt.next(); if (!particle.isAlive() || indexx >= particlesPerLayer) { particle.remove(); itt.remove(); } else { indexx++; } } //cleanupList(listLayer, (int)particlesPerLayer); int firstLayerForParticles = 6; if (stormObject.isBaby()) { firstLayerForParticles = 0; } while (listLayer.size() < particlesPerLayer && i >= firstLayerForParticles) { PivotingParticle particle = createParticle((ClientLevel) level, pos.x, pos.y, pos.z); particle.spawnAsWeatherEffect(); listLayer.add(particle); } float particleSpacingDegrees = 360 / particlesPerLayer; float spinSpeedLayer = 1F - ((float)(i+1) / (float)layers) + 1F; if (isPet) { listLayers.get(i).setRotation(listLayers.get(i).getRotation() + (10F * spinSpeedLayer / (radiusAdjustedForParticleSize))); } else { listLayers.get(i).setRotation(listLayers.get(i).getRotation() + (50.22F * spinSpeedLayer / (radiusAdjustedForParticleSize))); } Iterator it = listLayer.iterator(); int index = 0; while (it.hasNext()) { particleCount++; PivotingParticle particle = it.next(); float rot = ((particleSpacingDegrees * index) + listLayers.get(i).getRotation()); particle.setPivotRotPrev(particle.getPivotRot()); particle.setPivotRot(new Vec3(0, rot, 0)); particle.setPivotPrev(particle.getPivot()); particle.setPivot(new Vec3(0, radiusAdjustedForParticleSize, 0)); Vec3 pivotedPosition = particle.getPivotedPosition(0); double vecX = 0 - pivotedPosition.x; double vecZ = 0 - pivotedPosition.z; particle.prevRotationYaw = particle.rotationYaw; particle.rotationYaw = -(float)(Mth.atan2(vecZ, vecX) * 180.0D / Math.PI) - 90.0F + 180F; int rotationVarianceSize = 90; particle.rotationYaw -= (particle.getEntityId() % rotationVarianceSize) - (rotationVarianceSize/2); particle.rotationPitch = -30; //fix interpolation when angle wraps around if (particle.rotationYaw > 0 && particle.prevRotationYaw < 0) { particle.prevRotationYaw += 360; }/* else if (particle.rotationYaw < 0 && particle.prevRotationYaw > 0) { particle.prevRotationYaw -= 360; }*/ Vec3 posLayer = listLayers.get(i).getPos(); particle.setPosition(posLayer.x, posLayer.y, posLayer.z); particle.setPrevPosX(particle.x); particle.setPrevPosY(particle.y); particle.setPrevPosZ(particle.z); particle.setScale(10F * (radius / radiusMax)); if (isBaby) particle.setScale(10F / 3F * (radius / radiusMax)); if (isPet) particle.setScale(10F / 3F / 7F * (radius / radiusMax)); //allow fade in but stop age after if (particle.getAge() > particle.getTicksFadeInMax()+1) particle.setAge((int)particle.getTicksFadeInMax()+1); /*particle.setScale(0.3F); if (i % 2 == 0) { particle.setColor(0, 0, 0); } else { particle.setColor(1, 1, 1); }*/ if (stormObject.isFirenado && !wasFirenado) { if (particle.getSprite() == ParticleRegistry.cloud256) { particle.setSprite(ParticleRegistry.cloud256_fire); } float baseBright = 0.8F; float randFloat = (level.random.nextFloat() * 0.2F); float finalBright = Math.min(1F, baseBright + randFloat); particle.setColor(finalBright, finalBright, finalBright); } index++; } //extra debris particlesPerLayer = (int) (20 * adjustedRate); if (isBaby) particlesPerLayer = (int) (10 * adjustedRate); if (isPet) particlesPerLayer = (int) (5 * adjustedRate); if (stormObject.levelCurIntensityStage == StormObject.STATE_FORMING) { particlesPerLayer = 0; } cleanupList(listLayerExtra, (int)particlesPerLayer); //int particlesPerLayerDynamic = stormObject.getAgeSinceTornadoTouchdown()/20; if (i <= layersWithDebris && i >= firstLayerForParticles + 1) { while (listLayerExtra.size() < particlesPerLayer) { PivotingParticle particle = createParticleDebris((ClientLevel) level, pos.x, pos.y, pos.z); particle.spawnAsWeatherEffect(); listLayerExtra.add(particle); } } particleSpacingDegrees = 360 / particlesPerLayer; //TODO: oh god stop the copypasta it = listLayerExtra.iterator(); index = 0; while (it.hasNext()) { particleCount++; PivotingParticle particle = it.next(); float radAdj = (particle.getEntityId() % particlesPerLayer) / particlesPerLayer; radAdj = 0.4F + (radAdj * 0.6F); float moar = i * 0.5F; if (isPet) moar = 0.5F; radiusAdjustedForParticleSize = (radius * (radius / radiusMax) + moar) * radAdj; float rot = (particleSpacingDegrees * index) + listLayers.get(i).getRotation(); particle.setPivotRotPrev(particle.getPivotRot()); particle.setPivotRot(new Vec3(0, rot, 0)); particle.setPivotPrev(particle.getPivot()); particle.setPivot(new Vec3(0, radiusAdjustedForParticleSize, 0)); particle.prevRotationYaw = particle.rotationYaw; particle.rotationYaw += 5F; particle.rotationPitch = -30; Vec3 posLayer = listLayers.get(i).getPos(); particle.setPosition(posLayer.x, posLayer.y, posLayer.z); particle.setPrevPosX(particle.x); particle.setPrevPosY(particle.y); particle.setPrevPosZ(particle.z); particle.setScale(8 * 0.15F); if (isPet) particle.setScale(10F / 3F / 15F * (radius / radiusMax)); if (particle.getAge() > particle.getTicksFadeInMax()+1) { particle.setAge((int)particle.getTicksFadeInMax()+1); } particle.setGravity(0); //particle.setAlpha(1); index++; } //listLayers.get(i).setPos(new Vector3d(pos.x, pos.y, pos.z)); } //CULog.dbg(particleCount + ""); wasFirenado = stormObject.isFirenado; } public void cleanupList(List list, int particlesPerLayer) { Iterator it = list.iterator(); float index = 0; while (it.hasNext()) { PivotingParticle particle = it.next(); if (!particle.isAlive() || index >= particlesPerLayer) { particle.remove(); it.remove(); } else { index++; } } } @OnlyIn(Dist.CLIENT) private PivotingParticle createParticle(ClientLevel world, double x, double y, double z) { //ParticleTexFX particle = new ParticleTexFX(world, x, y, z, 0, 0, 0, ParticleRegistry.square16); TextureAtlasSprite sprite = ParticleRegistry.cloud256; if (stormObject.isFirenado) { sprite = ParticleRegistry.cloud256_fire; } PivotingParticle particle = new PivotingParticle(world, x, y, z, 0, 0, 0, sprite); particle.setMaxAge(300); particle.setTicksFadeInMax(80); //particle.setTicksFadeOutMax(20); particle.setParticleSpeed(0, 0, 0); particle.setScale(0.1F); particle.setScale(5F); particle.setScale(15F); //particle.setColor(world.random.nextFloat(), world.random.nextFloat(), world.random.nextFloat()); if (!stormObject.isFirenado) { float baseBright = 0.3F; float randFloat = (world.random.nextFloat() * 0.6F); float finalBright = Math.min(1F, baseBright + randFloat); particle.setColor(finalBright - 0.2F, finalBright - 0.2F, finalBright - 0.2F); } else { float baseBright = 0.6F; float randFloat = (world.random.nextFloat() * 0.3F); float finalBright = Math.min(1F, baseBright + randFloat); particle.setColor(finalBright - 0.2F, finalBright - 0.2F, finalBright - 0.2F); } particle.setGravity(0); particle.rotationYaw = world.random.nextFloat() * 360; particle.setRenderDistanceCull(renderDistCutoff); return particle; } @OnlyIn(Dist.CLIENT) private PivotingParticle createParticleDebris(ClientLevel world, double x, double y, double z) { int chance = world.getRandom().nextInt(3); TextureAtlasSprite sprite = ParticleRegistry.debris_1; if (chance == 1) { sprite = ParticleRegistry.debris_2; } else if (chance == 2) { sprite = ParticleRegistry.debris_3; } PivotingParticle particle = new PivotingParticle(world, x, y, z, 0, 0, 0, sprite); particle.setMaxAge(25000); particle.setTicksFadeInMax(80); particle.setParticleSpeed(0, 0, 0); particle.setFacePlayer(false); particle.spinFast = true; particle.isTransparent = true; particle.rotationYaw = (float)world.getRandom().nextInt(360); particle.rotationPitch = (float)world.getRandom().nextInt(360); particle.setGravity(0F); float brightnessMulti = 1F - (world.getRandom().nextFloat() * 0.5F); particle.setColor(1F * brightnessMulti, 1F * brightnessMulti, 1F * brightnessMulti); particle.setScale(8 * 0.15F); particle.aboveGroundHeight = 0.5D; particle.collisionSpeedDampen = false; particle.bounceSpeed = 0.03D; particle.bounceSpeedAhead = 0.03D; particle.setKillOnCollide(false); particle.windWeight = 5F; particle.setRenderDistanceCull(renderDistCutoff); return particle; } public Vec3 getPosTop() { if (listLayers.size() == 0) return pos; return listLayers.get(listLayers.size()-1).getPos(); } public StormObject getStormObject() { return stormObject; } public void setStormObject(StormObject stormObject) { this.stormObject = stormObject; } public void cleanup() { listLayers.clear(); } /** * Dramatic version for effect */ public void cleanupClient() { for (int i = 0; i < listLayers.size(); i++) { listLayers.get(i).getListParticles().stream().forEach(pivotingParticle -> disperseParticleSmoothly(pivotingParticle, true)); listLayers.get(i).getListParticlesExtra().stream().forEach(pivotingParticle -> disperseParticleSmoothly(pivotingParticle, true)); listLayers.get(i).getListParticles().clear(); listLayers.get(i).getListParticlesExtra().clear(); } } public void fadeOut() { for (int i = 0; i < listLayers.size(); i++) { listLayers.get(i).getListParticles().stream().forEach(pivotingParticle -> disperseParticleSmoothly(pivotingParticle, false)); listLayers.get(i).getListParticlesExtra().stream().forEach(pivotingParticle -> disperseParticleSmoothly(pivotingParticle, false)); listLayers.get(i).getListParticles().clear(); listLayers.get(i).getListParticlesExtra().clear(); } } public void disperseParticleSmoothly(PivotingParticle pivotingParticle, boolean explode) { pivotingParticle.prevRotationYaw = pivotingParticle.rotationYaw; pivotingParticle.setPivotPrev(pivotingParticle.getPivot()); pivotingParticle.setPivotRotPrev(pivotingParticle.getPivotRot()); Random rand = new Random(); if (explode) { pivotingParticle.setMotionX((rand.nextFloat() - rand.nextFloat()) * 2F); pivotingParticle.setMotionZ((rand.nextFloat() - rand.nextFloat()) * 2F); } else { pivotingParticle.setMotionX((rand.nextFloat() - rand.nextFloat()) * 0.4F); pivotingParticle.setMotionZ((rand.nextFloat() - rand.nextFloat()) * 0.4F); } pivotingParticle.setAge(100); pivotingParticle.setMaxAge(200); pivotingParticle.setTicksFadeOutMax(80); pivotingParticle.spinFast = false; } public void cleanupClientQuick() { for (int i = 0; i < listLayers.size(); i++) { listLayers.get(i).getListParticles().stream().forEach(pivotingParticle -> pivotingParticle.remove()); listLayers.get(i).getListParticlesExtra().stream().forEach(pivotingParticle -> pivotingParticle.remove()); listLayers.get(i).getListParticles().clear(); listLayers.get(i).getListParticlesExtra().clear(); } } public ActiveTornadoConfig getConfig() { return config; } public void setConfig(ActiveTornadoConfig config) { this.config = config; } } ================================================ FILE: src/main/java/weather2/weathersystem/wind/WindInfoCache.java ================================================ package weather2.weathersystem.wind; public class WindInfoCache { public long cacheTimeWindSpeedEvent; public float windSpeedEvent; public long cacheTimeChunkHeight; public int averageChunkHeightAround; public long cacheTimeWindSpeedAtChunkHeight; public float windSpeedAtChunkHeight; } ================================================ FILE: src/main/java/weather2/weathersystem/wind/WindManager.java ================================================ package weather2.weathersystem.wind; import com.corosus.coroutil.util.CoroUtilBlock; import com.corosus.coroutil.util.CoroUtilEntOrParticle; import com.corosus.coroutil.util.CoroUtilMisc; import net.minecraft.client.Minecraft; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.levelgen.synth.PerlinNoise; import net.minecraft.world.phys.Vec3; import net.minecraft.server.level.ServerLevel; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import weather2.PerlinNoiseHelper; import weather2.ServerWeatherProxy; import weather2.Weather; import weather2.config.ConfigMisc; import weather2.config.ConfigWind; import weather2.util.WeatherUtilEntity; import weather2.weathersystem.WeatherManager; import weather2.weathersystem.WeatherManagerServer; import weather2.weathersystem.storm.StormObject; import javax.annotation.Nullable; import java.util.HashMap; import java.util.Random; public class WindManager { public WeatherManager manager; //global public float windAngleGlobal = 0; public float windSpeedGlobal = 0; public float windSpeedGlobalChangeRate = 0.05F; public int windSpeedGlobalRandChangeTimer = 0; public int windSpeedGlobalRandChangeDelay = 10; //generic? /*public float windSpeedMin = 0.00001F; public float windSpeedMax = 1F;*/ //events - design derp, we're making this client side, so its set based on closest storm to the client side player public float windAngleEvent = 0; public BlockPos windOriginEvent = BlockPos.ZERO; public float windSpeedEvent = 0; //client side only //its assumed this will get set by whatever initializes an event, and this class counts it down from a couple seconds, helps wind system know what takes priority public int windTimeEvent = 0; //gusts public float windAngleGust = 0; public float windSpeedGust = 0; public int windTimeGust = 0; //public float directionGust = 0; //public float directionBeforeGust = 0; public int windGustEventTimeRand = 60; public float chanceOfWindGustEvent = 0.5F; //low wind event public int lowWindTimer = 0; //high wind event public int highWindTimer = 0; public static boolean FORCE_ON_DEBUG_TESTING = false; public HashMap lookupChunkToWindInfo = new HashMap<>(); //this one specifically hashes with a y value, so different vertical heights within the same chunk can have different results still public HashMap lookupChunkWithHeightToWindInfo = new HashMap<>(); public int cachedWindInfoUpdateFrequency = 100; public int cachedChunkHeightUpdateFrequency = 20*60*5; //used by client particles, and off thread work public float cachedWindSpeedClient = 0; public WindManager(WeatherManager parManager) { manager = parManager; Random rand = new Random(); windAngleGlobal = rand.nextInt(360); } public float getWindSpeed() { return getWindSpeed(null, 1); } public float getWindSpeed(@Nullable BlockPos pos) { return getWindSpeed(pos, 1); } public float getWindSpeed(@Nullable BlockPos pos, float extraHeightAmpMax) { if (pos != null) { return getWindSpeedPositional(pos, extraHeightAmpMax); } if (windTimeEvent > 0 && (windSpeedEvent > windSpeedGust && windSpeedEvent > windSpeedGlobal)) { return windSpeedEvent; } else if (windTimeGust > 0) { return windSpeedGust; } else { return windSpeedGlobal; } } public float getWindSpeedPositional(BlockPos pos) { return getWindSpeedPositional(pos, 1); } public float getWindSpeedPositional(BlockPos pos, float extraHeightAmpMax) { return getWindSpeedPositional(pos, extraHeightAmpMax, true); } /** * Uses various caches to factor in event wind speed (server side only), height based wind speed amplifier (cached to 16x16x16 areas) * * @param pos * @param extraHeightAmpMax * @return */ //TODO: design flaw: use of extraHeightAmpMax gets cached into WindInfoCache, will mess with results if 2 sources use 2 different extraHeightAmpMax //workaround for now is that on server side, only turbine is using this cache, need to make the extraHeightAmpMax calculation outside the cache, by fixing the other hacks below public float getWindSpeedPositional(BlockPos pos, float extraHeightAmpMax, boolean useClientCache) { if (manager.getWorld().isClientSide() && useClientCache) { return cachedWindSpeedClient; } this.manager.getWorld().getProfiler().push("weather2_wind_calculation"); boolean eventFastest = false; float lastWindSpeed; //dont waste cpu on client using the cache system when we only need the info around the player, especially given all the particles using it if (manager.getWorld().isClientSide()) { lastWindSpeed = windTimeEvent > 0 ? windSpeedEvent : 0; } else { lastWindSpeed = getCachedWindSpeedEventForChunkPos(pos); } if (lastWindSpeed > windSpeedGlobal && lastWindSpeed > windSpeedGlobal) eventFastest = true; lastWindSpeed = Math.max(lastWindSpeed, windSpeedGlobal); if (windTimeGust > 0) lastWindSpeed = Math.max(lastWindSpeed, windSpeedGust); int averageHeight = getCachedAverageChunkHeightAround(pos); float windSpeedHeightAmp = getWindSpeedAmplifierForHeight(pos.getY(), averageHeight, extraHeightAmpMax); lastWindSpeed *= windSpeedHeightAmp; //give a constant speed buff if high enough if (windSpeedHeightAmp > 1.3F) { lastWindSpeed += (windSpeedHeightAmp-1F) * 1F; } //TODO: remove the need for this hack, and eventFastest //ok so i wanted the cap for turbines to be at 3, and everything else either 1 or 1.5 as shown above, this hacky if statement and event check will have to do for now float cap = 1F; if (eventFastest) { cap = 2F; } if (extraHeightAmpMax >= 2) { cap = extraHeightAmpMax + 1; } this.manager.getWorld().getProfiler().pop(); return Math.min(cap, lastWindSpeed); } public void startHighWindEvent() { highWindTimer = ConfigWind.highWindTimerEnableAmountBase + (new Random()).nextInt(ConfigWind.highWindTimerEnableAmountRnd); } public boolean isHighWindEventActive() { return highWindTimer > 0; } public void stopHighWindEvent() { highWindTimer = 0; } public void startLowWindEvent() { lowWindTimer = ConfigWind.lowWindTimerEnableAmountBase + (new Random()).nextInt(ConfigWind.lowWindTimerEnableAmountRnd); } public void stopLowWindEvent() { lowWindTimer = 0; } public float getWindSpeedForGusts() { return windSpeedGust; } public float getWindSpeedForClouds() { return windSpeedGlobal; } public float getWindAngle(Vec3 pos) { if (windTimeEvent > 0) { return getWindAngleForEvents(pos); } else if (windTimeGust > 0) { return windAngleGust; } else { return windAngleGlobal; } } /** * Returns angle in degrees, 0-360 * * @return */ public float getWindAngleForEvents() { return windAngleEvent; } public float getWindAngleForEvents(Vec3 pos) { if (pos != null && !windOriginEvent.equals(BlockPos.ZERO)) { double var11 = windOriginEvent.getX() + 0.5D - pos.x; double var15 = windOriginEvent.getZ() + 0.5D - pos.z; return (-((float)Math.atan2(var11, var15)) * 180.0F / (float)Math.PI) - 45; } else { return windAngleEvent; } } /** * Returns angle in degrees, 0-360 * * @return */ public float getWindAngleForGusts() { return windAngleGust; } /** * Returns angle in degrees, 0-360 * * @return */ public float getWindAngleForClouds() { return windAngleGlobal; } public void setWindTimeGust(int time) { windTimeGust = time; } public void setWindTimeEvent(int parVal) { windTimeEvent = parVal; //syncData(); - might be too often //Weather.dbg("Wind event time set: " + parVal); } public void tick() { Random rand = CoroUtilMisc.random(); //windSpeedGust = 0; if (!ConfigWind.Misc_windOn) { windSpeedGlobal = 0; windSpeedGust = 0; windTimeGust = 0; //windSpeedSmooth = 0; } else { if (!manager.getWorld().isClientSide()) { //WIND SPEED\\ //global random wind speed change if (!ConfigWind.Wind_LowWindEvents) { lowWindTimer = 0; } if (lowWindTimer <= 0) { if (windSpeedGlobalRandChangeTimer-- <= 0) { //standard wind adjustment if (highWindTimer <= 0) { windSpeedGlobal += (rand.nextDouble() * windSpeedGlobalChangeRate) - (windSpeedGlobalChangeRate / 2); //only increase for high wind } else { windSpeedGlobal += (rand.nextDouble() * windSpeedGlobalChangeRate)/* - (windSpeedGlobalChangeRate / 2)*/; } windSpeedGlobalRandChangeTimer = windSpeedGlobalRandChangeDelay; } //only allow for low wind if high wind not active if (highWindTimer <= 0) { if (ConfigWind.Wind_LowWindEvents) { if (rand.nextInt(ConfigWind.lowWindOddsTo1) == 0) { startLowWindEvent(); Weather.dbg("low wind event started, for ticks: " + lowWindTimer); } } } else { //fix edge case where if a high wind event is manually started, low wind could still be trying to take control stopLowWindEvent(); } if (ConfigWind.Wind_HighWindEvents && highWindTimer <= 0) { if (rand.nextInt(ConfigWind.highWindOddsTo1) == 0) { startHighWindEvent(); Weather.dbg("high wind event started, for ticks: " + highWindTimer); } } } else { lowWindTimer--; if (lowWindTimer <= 0) { Weather.dbg("low wind event ended"); } windSpeedGlobal -= 0.01F; } if (highWindTimer > 0) { highWindTimer--; if (highWindTimer <= 0) { Weather.dbg("high wind event ended"); } } //enforce mins and maxs of wind speed if (windSpeedGlobal < ConfigWind.windSpeedMin) { windSpeedGlobal = (float)ConfigWind.windSpeedMin; } if (windSpeedGlobal > ConfigWind.windSpeedMax) { windSpeedGlobal = (float)ConfigWind.windSpeedMax; } if (windTimeGust > 0) { windTimeGust--; if (windTimeGust == 0) { windSpeedGust = 0; syncData(); } } if (ConfigMisc.overcastMode && manager.getWorld().isRaining()) { if (windSpeedGlobal < ConfigWind.windSpeedMinGlobalOvercastRaining) { windSpeedGlobal = (float) ConfigWind.windSpeedMinGlobalOvercastRaining; } } float speedOverride = ServerWeatherProxy.getWindSpeed((ServerLevel) manager.getWorld()); if (speedOverride != -1) { windSpeedGlobal = speedOverride; } //smooth use /*if (windSpeed > windSpeedSmooth) { windSpeedSmooth += 0.01F; } else if (windSpeed < windSpeedSmooth) { windSpeedSmooth -= 0.01F; } if (windSpeedSmooth < 0) { windSpeedSmooth = 0F; }*/ //WIND SPEED // //WIND ANGLE\\ //windGustEventTimeRand = 100; float randGustWindFactor = 1F; //gust data if (this.windTimeGust == 0 && lowWindTimer <= 0/* && highWindTimer <= 0*/) { if (chanceOfWindGustEvent > 0F) { if (rand.nextInt((int)((100 - chanceOfWindGustEvent) * randGustWindFactor)) == 0) { windSpeedGust = windSpeedGlobal + rand.nextFloat() * 0.6F; boolean randomDirectionGust = false; if (randomDirectionGust) { windAngleGust = rand.nextInt(360) - 180; } else { windAngleGust = windAngleGlobal + rand.nextInt(120) - 60; } setWindTimeGust(rand.nextInt(windGustEventTimeRand * 3)); //windEventTime += windTime; //unneeded since priority system determines wind to use //directionBeforeGust = windAngleGlobal; } } } //global wind angle //windAngleGlobal += ((new Random()).nextInt(5) - 2) * 0.2F; windAngleGlobal += (rand.nextFloat() * ConfigWind.globalWindAngleChangeAmountRate) - (rand.nextFloat() * ConfigWind.globalWindAngleChangeAmountRate); //windAngleGlobal += 0.1; //windAngleGlobal = 0; if (windAngleGlobal < -180) { windAngleGlobal += 360; } if (windAngleGlobal > 180) { windAngleGlobal -= 360; } //WIND ANGLE // } else { tickClient(); } } /*windSpeedGlobal = 0.9F; windAngleGlobal = 270;*/ } @OnlyIn(Dist.CLIENT) public void tickClient() { Player entP = Minecraft.getInstance().player; if (windTimeEvent > 0) { windTimeEvent--; if (windTimeEvent == 0) { windTimeGust = 0; } } //event data if (entP != null) { if (entP != null && entP.level().getGameTime() % 5 == 0) { cachedWindSpeedClient = getWindSpeedPositional(entP.blockPosition(), 1, false); } if (manager.getWorld().getGameTime() % 20 == 0) { float maxDist = 512; StormObject so = manager.getClosestStorm(new Vec3(entP.getX(), StormObject.layers.get(0), entP.getZ()), maxDist, StormObject.STATE_HIGHWIND); if (so != null) { windOriginEvent = CoroUtilBlock.blockPos(so.posGround.x, so.posGround.y, so.posGround.z); setWindTimeEvent(80); //player pos aiming at storm double var11 = so.posGround.x - entP.getX(); double var15 = so.posGround.z - entP.getZ(); float yaw = -((float)Math.atan2(var11, var15)) * 180.0F / (float)Math.PI; windAngleEvent = yaw; double dist = entP.position().distanceTo(so.posGround); windSpeedEvent = getEventSpeedFactor(dist, maxDist); } } } } public float getEventSpeedFactor(double dist, double maxDist) { return (float) (1F - (dist / maxDist)) * 2F; } public WindInfoCache getWindInfoCacheForChunk(BlockPos blockPos, boolean forHeightChunks) { //note: the y value hashing here is used when we want data at that height level BlockPos chunkPos = new BlockPos(blockPos.getX() >> 4, forHeightChunks ? blockPos.getY() >> 4 : 0, blockPos.getZ() >> 4); HashMap lookup = forHeightChunks ? lookupChunkWithHeightToWindInfo : lookupChunkToWindInfo; long hash = chunkPos.asLong(); if (lookup.containsKey(hash)) { return lookup.get(hash); } else { WindInfoCache cache = new WindInfoCache(); lookup.put(hash, cache); return cache; } } public float getCachedWindSpeedForHeight(BlockPos blockPos, float extraHeightAmpMax) { WindInfoCache cache = getWindInfoCacheForChunk(blockPos, true); if (cache.cacheTimeWindSpeedAtChunkHeight == 0 || cache.cacheTimeWindSpeedAtChunkHeight + cachedWindInfoUpdateFrequency <= manager.getWorld().getGameTime()) { cache.cacheTimeWindSpeedAtChunkHeight = manager.getWorld().getGameTime(); cache.windSpeedAtChunkHeight = getWindSpeed(blockPos, extraHeightAmpMax); } return cache.windSpeedAtChunkHeight; } public float getCachedWindSpeedEventForChunkPos(BlockPos blockPos) { WindInfoCache cache = getWindInfoCacheForChunk(blockPos, false); if (cache.cacheTimeWindSpeedEvent == 0 || cache.cacheTimeWindSpeedEvent + cachedWindInfoUpdateFrequency <= manager.getWorld().getGameTime()) { cache.cacheTimeWindSpeedEvent = manager.getWorld().getGameTime(); cache.windSpeedEvent = calculateWindSpeedEventForPos(blockPos); } return cache.windSpeedEvent; } public float calculateWindSpeedEventForPos(BlockPos pos) { float maxDist = 512; Vec3 posVec = new Vec3(pos.getX(), pos.getY(), pos.getZ()); StormObject so = manager.getClosestStorm(posVec, maxDist, StormObject.STATE_HIGHWIND); if (so != null) { double dist = posVec.distanceTo(so.posGround); return getEventSpeedFactor(dist, maxDist); } return 0; } public int getCachedAverageChunkHeightAround(BlockPos blockPos) { WindInfoCache cache = getWindInfoCacheForChunk(blockPos, false); if (cache.cacheTimeChunkHeight == 0 || cache.cacheTimeChunkHeight + cachedChunkHeightUpdateFrequency <= manager.getWorld().getGameTime()) { cache.cacheTimeChunkHeight = manager.getWorld().getGameTime(); BlockPos chunkPos = new BlockPos(blockPos.getX() >> 4, 0, blockPos.getZ() >> 4); cache.averageChunkHeightAround = calculateAverageChunkHeightAround(chunkPos); } return cache.averageChunkHeightAround; } /** * Gets heights around the chunk in middle of each chunk checked, stepping out 'squareRadius' times, each check spaced out by 'scanSpacing' chunks * * @param chunkPos * @return */ public int calculateAverageChunkHeightAround(BlockPos chunkPos) { int squareRadius = 2; int scanSpacing = 3; int count = 0; int totalHeight = 0; for (int x = -squareRadius; x <= squareRadius; x++) { for (int z = -squareRadius; z <= squareRadius; z++) { BlockPos pos = new BlockPos((chunkPos.getX() + (x * scanSpacing)) * 16 + 8, 0, (chunkPos.getZ() + (z * scanSpacing)) * 16 + 8); int height = manager.getWorld().getHeightmapPos(Heightmap.Types.WORLD_SURFACE, pos).getY(); if (height > -64) { totalHeight += height; count++; } } } if (count == 0) return -64; int avgHeight = totalHeight / count; //System.out.println("avgHeight: " + avgHeight); return avgHeight; } /** * Get an amp between average height found in area and max build height * * averageHeight is the bottom * getMaxBuildHeight is top * height is somewhere in between * * dimensions with smaller range between base height and max build height are easier to benefit from, less height needed * this is the best option imo considering the alternatives * * @param height * @param averageHeight * @return */ public float getWindSpeedAmplifierForHeight(int height, int averageHeight, float extraHeightAmpMax) { //prevent weird math using negative numbers int maxSpeedHeight = manager.getWorld().getHeight(); if (manager.getWorld().getMinBuildHeight() < 0) { int heightAdj = Math.abs(manager.getWorld().getMinBuildHeight()); height += heightAdj; averageHeight += heightAdj; } int range = maxSpeedHeight - averageHeight; height -= averageHeight; return 1F + Math.max(0, ((float)height / (float)range) * extraHeightAmpMax); } public void applyWindForceNew(Object ent, float multiplier, float maxSpeed) { applyWindForceNew(ent, multiplier, maxSpeed, true); } /** * * To solve the problem of speed going overkill due to bad formulas * * end goal: make object move at speed of wind * - object has a weight that slows that adjustment * - conservation of momentum * * calculate force based on wind speed vs objects speed * - use that force to apply to weight of object * - profit */ public void applyWindForceNew(Object ent, float multiplier, float maxSpeed, boolean dynamicWind) { Vec3 pos = new Vec3(CoroUtilEntOrParticle.getPosX(ent), CoroUtilEntOrParticle.getPosY(ent), CoroUtilEntOrParticle.getPosZ(ent)); Vec3 motion = applyWindForceImpl(pos, new Vec3(CoroUtilEntOrParticle.getMotionX(ent), CoroUtilEntOrParticle.getMotionY(ent), CoroUtilEntOrParticle.getMotionZ(ent)), WeatherUtilEntity.getWeight(ent), multiplier, maxSpeed, dynamicWind); CoroUtilEntOrParticle.setMotionX(ent, motion.x); CoroUtilEntOrParticle.setMotionZ(ent, motion.z); } /** * Handle generic uses of wind force, for stuff like weather objects that arent entities or paticles */ public Vec3 applyWindForceImpl(Vec3 pos, Vec3 motion, float weight, float multiplier, float maxSpeed, boolean dynamicWind) { float windSpeed = 0; if (pos != null && ConfigWind.Wind_UsePerlinNoise) { /*if (windTimeGust > 0) { windSpeed = getWindSpeedPerlinNoise(pos); } else */{ windSpeed = (getWindSpeed(dynamicWind ? CoroUtilBlock.blockPos(pos) : null) * 0.5F) + (getWindSpeedPerlinNoise(pos) * 0.5F); } } else { windSpeed = getWindSpeed(dynamicWind ? CoroUtilBlock.blockPos(pos) : null); } float windAngle = getWindAngle(pos); float windX = (float) -Math.sin(Math.toRadians(windAngle)) * windSpeed; float windZ = (float) Math.cos(Math.toRadians(windAngle)) * windSpeed; float objX = (float) motion.x; float objZ = (float) motion.z; float windWeight = 1F; float objWeight = weight; //divide by zero protection if (objWeight <= 0) { objWeight = 0.001F; } float weightDiff = windWeight / objWeight; float vecX = (objX - windX) * weightDiff; float vecZ = (objZ - windZ) * weightDiff; vecX *= multiplier; vecZ *= multiplier; //copy over existing motion data Vec3 newMotion = motion; double speedCheck = (Math.abs(vecX) + Math.abs(vecZ)) / 2D; if (speedCheck < maxSpeed) { newMotion = new Vec3(objX - vecX, motion.y, objZ - vecZ); } else { float speedDampen = (float)(maxSpeed / speedCheck); newMotion = new Vec3(objX - vecX*speedDampen, motion.y, objZ - vecZ*speedDampen); } return newMotion; } public CompoundTag nbtSyncForClient() { CompoundTag data = new CompoundTag(); //idea: only sync the wind data client cares about (the active priority wind) data.putFloat("windSpeedGlobal", windSpeedGlobal); data.putFloat("windAngleGlobal", windAngleGlobal); data.putFloat("windSpeedGust", windSpeedGust); data.putFloat("windAngleGust", windAngleGust); /*data.putFloat("windSpeedEvent", windSpeedEvent); data.putFloat("windAngleEvent", windAngleEvent); data.putInt("windTimeEvent", windTimeEvent);*/ data.putInt("windTimeGust", windTimeGust); return data; } public void nbtSyncFromServer(CompoundTag parNBT) { windSpeedGlobal = parNBT.getFloat("windSpeedGlobal"); windAngleGlobal = parNBT.getFloat("windAngleGlobal"); windSpeedGust = parNBT.getFloat("windSpeedGust"); windAngleGust = parNBT.getFloat("windAngleGust"); /*windSpeedEvent = parNBT.getFloat("windSpeedEvent"); windAngleEvent = parNBT.getFloat("windAngleEvent"); windTimeEvent = parNBT.getInt("windTimeEvent");*/ windTimeGust = parNBT.getInt("windTimeGust"); } public Vec3 getWindForce(@Nullable BlockPos pos) { float windSpeed = this.getWindSpeed(pos); float windAngle = this.getWindAngle(null); float windX = (float) -Math.sin(Math.toRadians(windAngle)) * windSpeed; float windZ = (float) Math.cos(Math.toRadians(windAngle)) * windSpeed; return new Vec3(windX, 0, windZ); } public void syncData() { if (manager instanceof WeatherManagerServer) { ((WeatherManagerServer) manager).syncWindUpdate(this); } } public void reset() { manager = null; } public void read(CompoundTag data) { windSpeedGlobal = data.getFloat("windSpeedGlobal"); windAngleGlobal = data.getFloat("windAngleGlobal"); windSpeedGust = data.getFloat("windSpeedGust"); windAngleGust = data.getFloat("windAngleGust"); windTimeGust = data.getInt("windTimeGust"); windSpeedEvent = data.getFloat("windSpeedEvent"); windAngleEvent = data.getFloat("windAngleEvent"); windTimeEvent = data.getInt("windTimeEvent"); lowWindTimer = data.getInt("lowWindTimer"); highWindTimer = data.getInt("highWindTimer"); } public CompoundTag write(CompoundTag data) { data.putFloat("windSpeedGlobal", windSpeedGlobal); data.putFloat("windAngleGlobal", windAngleGlobal); data.putFloat("windSpeedGust", windSpeedGust); data.putFloat("windAngleGust", windAngleGust); data.putInt("windTimeGust", windTimeGust); data.putFloat("windSpeedEvent", windSpeedEvent); data.putFloat("windAngleEvent", windAngleEvent); data.putInt("windTimeEvent", windTimeEvent); data.putInt("lowWindTimer", lowWindTimer); data.putInt("highWindTimer", highWindTimer); return data; } public float getWindSpeedPerlinNoise(Vec3 pos) { PerlinNoise perlinNoise = PerlinNoiseHelper.get().getPerlinNoise(); /*int indexX = index % xWide; int indexZ = index / xWide;*/ int indexX = (int) Math.floor(pos.x); int indexZ = (int) Math.floor(pos.z); double scale = 10; long time = Minecraft.getInstance().level.getGameTime() * 2; double posYAdj = 0; double noiseVal = perlinNoise.getValue(((indexX) * scale) + time, ((indexZ) * scale) + time, posYAdj)/* + 0.2F*/; return (float) Math.max(-1.5F, Math.min(1.5F, noiseVal * 4F)); } } ================================================ FILE: src/main/resources/META-INF/accesstransformer.cfg ================================================ # Model hacks for item frames, need forge patch public-f net.minecraft.client.renderer.model.ModelBakery field_209607_C # STATE_CONTAINER_OVERRIDES # Fixing vanilla stupidity with flower pots protected net.minecraft.block.FlowerPotBlock field_196451_b # Tweaking canyon gen height protected net.minecraft.world.gen.carver.CanyonWorldCarver func_222729_a(Lnet/minecraft/world/chunk/IChunk;JIIIDDDFFFIIDLjava/util/BitSet;)V # func_222729_a # Tree helpers public net.minecraft.world.gen.feature.AbstractTreeFeature func_214572_g(Lnet/minecraft/world/gen/IWorldGenerationBaseReader;Lnet/minecraft/util/math/BlockPos;)Z # isAirOrLeaves # Map of DyeColor to wool block, just useful public net.minecraft.entity.passive.SheepEntity field_200206_bz # WOOL_BY_COLOR # Overriding chest container name public net.minecraft.block.ChestBlock$InventoryFactory public net.minecraft.block.ChestBlock field_220110_j # field_220110_j # Loot Tables protected net.minecraft.data.loot.BlockLootTables field_218573_a protected net.minecraft.data.loot.BlockLootTables field_218574_b protected net.minecraft.data.loot.BlockLootTables field_218575_c protected net.minecraft.data.loot.BlockLootTables field_218576_d protected net.minecraft.data.loot.BlockLootTables field_218577_e protected net.minecraft.data.loot.BlockLootTables field_218579_g protected net.minecraft.data.loot.BlockLootTables field_218580_h # Umbrella Shadow protected net.minecraft.client.renderer.entity.EntityRenderer func_76975_c(Lnet/minecraft/entity/Entity;DDDFF)V # renderShadow protected net.minecraft.client.renderer.entity.EntityRenderer func_76977_a(Lnet/minecraft/entity/Entity;DDDF)V # renderEntityOnFire # Minigame dimension public net.minecraft.world.GameRules func_234901_a_(Lcom/mojang/serialization/DynamicLike;)V # decode # Chase camera public net.minecraft.client.renderer.ActiveRenderInfo func_216779_a(D)D # calcCameraDistance public net.minecraft.client.renderer.ActiveRenderInfo func_216782_a(DDD)V # movePosition public net.minecraft.client.renderer.ActiveRenderInfo func_216776_a(FF)V # setDirection public net.minecraft.client.renderer.ActiveRenderInfo func_216775_b(DDD)V # setPosition # Runtime worlds public net.minecraft.server.MinecraftServer field_71305_c # worlds public net.minecraft.server.MinecraftServer field_71310_m # anvilConverterForAnvilFile # Creepers public net.minecraft.entity.monster.CreeperEntity func_146077_cc()V # explode public net.minecraft.client.particle.Particle field_187126_f # posX public net.minecraft.client.particle.Particle field_187127_g # posY public net.minecraft.client.particle.Particle field_187128_h # posZ public net.minecraft.client.particle.Particle field_187129_i # motionX public net.minecraft.client.particle.Particle field_187130_j # motionY public net.minecraft.client.particle.Particle field_187131_k # motionZ public net.minecraft.client.particle.Particle field_70552_h # particleRed public net.minecraft.client.particle.Particle field_70553_i # particleGreen public net.minecraft.client.particle.Particle field_70551_j # particleBlue public net.minecraft.client.particle.Particle field_70546_d # age public net.minecraft.world.server.ChunkManager func_223491_f()Ljava/lang/Iterable; # getLoadedChunksIterable public net.minecraft.client.world.ClientWorld field_217428_a # globalEntities # Acid rain public-f net.minecraft.client.renderer.WorldRenderer field_228413_h_ # RAIN_TEXTURES #public net.minecraft.client.renderer.GameRenderer * #all fields public #public net.minecraft.client.renderer.GameRenderer func_215311_a(Lnet/minecraft/client/renderer/ActiveRenderInfo;FZ)D # getFOVModifier #public net.minecraft.client.renderer.entity.EntityRendererManager * #all fields public #public net.minecraft.client.world.ClientWorld field_217428_a #globalEntities #public net.minecraft.world.ServerWorld field_73068_P # allPlayersSleeping #public-f net.minecraft.world.World field_72986_A # worldInfo #NEW FOR MOJANG MAPPINGS public net.minecraft.client.particle.Particle f_107227_ # rCol public net.minecraft.client.particle.Particle f_107228_ # gCol public net.minecraft.client.particle.Particle f_107229_ # bCol public net.minecraft.client.particle.Particle f_107212_ # x public net.minecraft.client.particle.Particle f_107213_ # y public net.minecraft.client.particle.Particle f_107214_ # z public net.minecraft.client.particle.Particle f_107215_ # xd public net.minecraft.client.particle.Particle f_107216_ # yd public net.minecraft.client.particle.Particle f_107217_ # zd public net.minecraft.client.particle.Particle f_107224_ # age public net.minecraft.world.entity.player.Player f_36077_ # abilities public net.minecraft.server.level.ChunkMap m_140416_()Ljava/lang/Iterable; # getChunks public net.minecraft.client.color.block.BlockColors f_92571_ # blockColors public com.mojang.math.Matrix4f * # all fields public net.minecraft.world.level.biome.Biome m_47505_(Lnet/minecraft/core/BlockPos;)F # getTemperature public net.minecraft.server.level.ServerLevel f_8549_ # serverLevelData public net.minecraft.server.level.ServerLevel m_143248_(Lnet/minecraft/core/BlockPos;)Ljava/util/Optional; # findLightningRod public net.minecraft.client.Options f_92073_ # particles public net.minecraft.client.renderer.LevelRenderer m_109703_(Lnet/minecraft/client/renderer/LightTexture;FDDD)V # renderSnowAndRain public net.minecraft.client.particle.ParticleEngine m_263560_()V # clearParticles public net.minecraft.client.particle.ParticleEngine f_107289_ # particles public net.minecraft.client.particle.ParticleEngine f_107290_ # trackingEmitters ================================================ FILE: src/main/resources/META-INF/mods.toml ================================================ # This is an example mods.toml file. It contains the data relating to the loading mods. # There are several mandatory fields (#mandatory), and many more that are optional (#optional). # The overall format is standard TOML format, v0.5.0. # Note that there are a couple of TOML lists in this file. # Find more information on toml format here: https://github.com/toml-lang/toml # The name of the mod loader type to load - for regular FML @Mod mods it should be javafml modLoader="javafml" #mandatory # A version range to match for said mod loader - for regular FML @Mod it will be the forge version loaderVersion="${loader_version_range}" #mandatory This is typically bumped every Minecraft version by Forge. See our download page for lists of versions. # The license for you mod. This is mandatory metadata and allows for easier comprehension of your redistributive properties. # Review your options at https://choosealicense.com/. All rights reserved is the default copyright stance, and is thus the default here. license="${mod_license}" # A URL to refer people to when problems occur with this mod #issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #optional # A list of mods - how many allowed here is determined by the individual mod loader [[mods]] #mandatory # The modid of the mod modId="${mod_id}" #mandatory # The version number of the mod version="${mod_version}" #mandatory # A display name for the mod displayName="${mod_name}" #mandatory # A URL to query for updates for this mod. See the JSON update specification https://docs.minecraftforge.net/en/latest/misc/updatechecker/ #updateJSONURL="https://change.me.example.invalid/updates.json" #optional # A URL for the "homepage" for this mod, displayed in the mod UI #displayURL="https://change.me.to.your.mods.homepage.example.invalid/" #optional # A file name (in the root of the mod JAR) containing a logo for display #logoFile="examplemod.png" #optional # A text field displayed in the mod UI #credits="" #optional # A text field displayed in the mod UI authors="${mod_authors}" #optional # Display Test controls the display for your mod in the server connection screen # MATCH_VERSION means that your mod will cause a red X if the versions on client and server differ. This is the default behaviour and should be what you choose if you have server and client elements to your mod. # IGNORE_SERVER_VERSION means that your mod will not cause a red X if it's present on the server but not on the client. This is what you should use if you're a server only mod. # IGNORE_ALL_VERSION means that your mod will not cause a red X if it's present on the client or the server. This is a special case and should only be used if your mod has no server component. # NONE means that no display test is set on your mod. You need to do this yourself, see IExtensionPoint.DisplayTest for more information. You can define any scheme you wish with this value. # IMPORTANT NOTE: this is NOT an instruction as to which environments (CLIENT or DEDICATED SERVER) your mod loads on. Your mod should load (and maybe do nothing!) whereever it finds itself. #displayTest="MATCH_VERSION" # MATCH_VERSION is the default if nothing is specified (#optional) # The description text for the mod (multi line!) (#mandatory) description='''${mod_description}''' # A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional. [[dependencies.${mod_id}]] #optional # the modid of the dependency modId="forge" #mandatory # Does this dependency have to exist - if not, ordering below must be specified mandatory=true #mandatory # The version range of the dependency versionRange="${forge_version_range}" #mandatory # An ordering relationship for the dependency - BEFORE or AFTER required if the dependency is not mandatory # BEFORE - This mod is loaded BEFORE the dependency # AFTER - This mod is loaded AFTER the dependency ordering="NONE" # Side this dependency is applied on - BOTH, CLIENT, or SERVER side="BOTH" # Here's another dependency [[dependencies.${mod_id}]] modId="minecraft" mandatory=true # This version range declares a minimum of the current minecraft version up to but not including the next major version versionRange="${minecraft_version_range}" ordering="NONE" side="BOTH" [[dependencies.${mod_id}]] modId="coroutil" mandatory=true versionRange="[1.20.1-1.3.7,)" ordering="NONE" side="BOTH" # Features are specific properties of the game environment, that you may want to declare you require. This example declares # that your mod requires GL version 3.2 or higher. Other features will be added. They are side aware so declaring this won't # stop your mod loading on the server for example. #[features.${mod_id}] #openGLVersion="[3.2,)" ================================================ FILE: src/main/resources/assets/coroutil/blockstates/blank.json ================================================ { "variants": { "normal": { "model": "coroutil:blank" } } } ================================================ FILE: src/main/resources/assets/coroutil/blockstates/repairing_block.json ================================================ { "variants": { "normal": { "model": "coroutil:repairing_block" } } } ================================================ FILE: src/main/resources/assets/coroutil/config/loot_tables/testloot.json ================================================ { "pools": [ { "rolls": 1, "entries": [ { "type": "item", "name": "minecraft:rotten_flesh", "weight": 1, "functions": [ { "function": "set_count", "count": { "min": 0, "max": 2 } }, { "function": "looting_enchant", "count": { "min": 0, "max": 1 } } ] } ] }, { "conditions": [ { "condition": "killed_by_player" }, { "condition": "random_chance_with_looting", "chance": 0.025, "looting_multiplier": 0.01 } ], "rolls": 1, "entries": [ { "type": "item", "name": "minecraft:iron_ingot", "weight": 1 }, { "type": "item", "name": "minecraft:carrot", "weight": 1 }, { "type": "item", "name": "minecraft:potato", "weight": 1 } ] } ] } ================================================ FILE: src/main/resources/assets/coroutil/config/loot_tables/testlootboss.json ================================================ { "pools": [ { "rolls": 1, "entries": [ { "type": "item", "name": "minecraft:stick", "weight": 1, "functions": [ { "function": "set_count", "count": { "min": 1, "max": 3 } }, { "function": "looting_enchant", "count": { "min": 0, "max": 1 } } ] } ] }, { "conditions": [ { "condition": "killed_by_player" } ], "rolls": 1, "entries": [ { "type": "item", "name": "minecraft:diamond", "weight": 3 }, { "type": "item", "name": "minecraft:emerald", "weight": 3 } ] } ] } ================================================ FILE: src/main/resources/assets/coroutil/config/templates/actions/mob_spawns.json ================================================ { "wiki": "//looking to customize your own invasions? see http://coros.us/wiki/index.php?title=Hostile_Worlds_-_Invasions_-_Customizing for helpfull info", "format": "mob_spawns", "templates": [ { "name": "invasion_stage_1", "wave_message": "§cAn invasion has started! Zombie Miners!", "conditions": [ { "condition": "invasion_number", "min": 1, "max": 1 } ], "spawns": [ { "entities": [ "minecraft:zombie" ], "count": 3, "count_max": 5, "count_difficulty_multiplier": 2, "spawn_type": "ground", "cmods": [ { "cmod": "template", "template": "invader_miner" }, { "cmod": "template", "template": "inventory_all_scales" } ] }, { "entities": [ "minecraft:zombie" ], "count": 5, "count_max": 15, "count_difficulty_multiplier": 2, "spawn_type": "ground", "cmods": [ { "cmod": "template", "template": "invader_soldier" }, { "cmod": "template", "template": "inventory_all_scales" } ] } ] }, { "name": "invasion_stage_2", "wave_message": "§cAn invasion has started! Miners and Skeletons!", "conditions": [ { "condition": "invasion_number", "min": 2, "max": 2 } ], "spawns": [ { "entities": [ "minecraft:zombie" ], "count": 3, "count_max": 5, "count_difficulty_multiplier": 2, "cmods": [ { "cmod": "template", "template": "invader_miner" }, { "cmod": "template", "template": "inventory_all_scales" } ] }, { "entities": [ "minecraft:skeleton" ], "count": 5, "count_max": 10, "count_difficulty_multiplier": 2, "cmods": [ { "cmod": "template", "template": "invader_soldier" }, { "cmod": "template", "template": "inventory_all_scales_no_weapon" } ] } ] }, { "name": "invasion_stage_3_opt_1_zombies", "wave_message": "§cAn invasion has started! Miners and Zombies!", "conditions": [ { "condition": "invasion_number", "min": 3 }, { "condition": "random", "weight": 10 } ], "spawns": [ { "entities": [ "minecraft:zombie" ], "count": 5, "count_max": 10, "count_difficulty_multiplier": 2, "cmods": [ { "cmod": "template", "template": "invader_miner" }, { "cmod": "template", "template": "inventory_all_scales" }, { "cmod": "template", "template": "buffed_attributes" } ] }, { "entities": [ "minecraft:zombie" ], "count": 5, "count_max": 30, "count_difficulty_multiplier": 0.5, "cmods": [ { "cmod": "template", "template": "invader_soldier" }, { "cmod": "template", "template": "inventory_all_scales" }, { "cmod": "template", "template": "buffed_attributes" } ] } ] }, { "name": "invasion_stage_3_opt_2_skeletons", "wave_message": "§cAn invasion has started! Miners and Skeletons!", "conditions": [ { "condition": "invasion_number", "min": 3 }, { "condition": "random", "weight": 10 } ], "spawns": [ { "entities": [ "minecraft:zombie" ], "count": 5, "count_max": 10, "count_difficulty_multiplier": 2, "cmods": [ { "cmod": "template", "template": "invader_miner" }, { "cmod": "template", "template": "inventory_all_scales" }, { "cmod": "template", "template": "buffed_attributes" } ] }, { "entities": [ "minecraft:skeleton" ], "count": 5, "count_max": 30, "count_difficulty_multiplier": 1, "cmods": [ { "cmod": "template", "template": "invader_soldier" }, { "cmod": "template", "template": "inventory_all_scales_no_weapon" }, { "cmod": "template", "template": "buffed_attributes" } ] } ] }, { "name": "invasion_stage_3_opt_3_endermen", "wave_message": "§cAn invasion has started! Miners and Endermen!", "conditions": [ { "condition": "invasion_number", "min": 3 }, { "condition": "random", "weight": 10 } ], "spawns": [ { "entities": [ "minecraft:zombie" ], "count": 5, "count_max": 10, "count_difficulty_multiplier": 2, "cmods": [ { "cmod": "template", "template": "invader_miner" }, { "cmod": "template", "template": "inventory_all_scales" }, { "cmod": "template", "template": "buffed_attributes" } ] }, { "entities": [ "minecraft:enderman" ], "count": 5, "count_max": 30, "count_difficulty_multiplier": 0.5, "cmods": [ { "cmod": "template", "template": "invader_soldier" }, { "cmod": "template", "template": "buffed_attributes" } ] } ] }, { "name": "invasion_stage_3_opt_4_creepers", "wave_message": "§cAn invasion has started! Miners and Creepers!", "conditions": [ { "condition": "invasion_number", "min": 3 }, { "condition": "random", "weight": 10 } ], "spawns": [ { "entities": [ "minecraft:zombie" ], "count": 5, "count_max": 10, "count_difficulty_multiplier": 2, "cmods": [ { "cmod": "template", "template": "invader_miner" }, { "cmod": "template", "template": "inventory_all_scales" }, { "cmod": "template", "template": "buffed_attributes" } ] }, { "entities": [ "minecraft:creeper" ], "count": 5, "count_max": 30, "count_difficulty_multiplier": 0.5, "cmods": [ { "cmod": "template", "template": "invader_soldier" }, { "cmod": "template", "template": "buffed_attributes" } ] } ] }, { "name": "invasion_stage_3_opt_5_bats", "wave_message": "§cAn invasion has started! Miners and Bats!", "conditions": [ { "condition": "invasion_number", "min": 3 }, { "condition": "random", "weight": 10 } ], "spawns": [ { "entities": [ "minecraft:zombie" ], "count": 5, "count_max": 10, "count_difficulty_multiplier": 2, "cmods": [ { "cmod": "template", "template": "invader_miner" }, { "cmod": "template", "template": "inventory_all_scales" }, { "cmod": "template", "template": "buffed_attributes" } ] }, { "entities": [ "minecraft:bat" ], "count": 15, "count_max": 40, "count_difficulty_multiplier": 0.5, "cmods": [ { "cmod": "template", "template": "invader_soldier" }, { "cmod": "template", "template": "buffed_attributes" }, { "cmod": "ai_attack_melee" } ] } ] }, { "name": "invasion_stage_3_opt_6_spiders", "wave_message": "§cAn invasion has started! Miners and Spiders!", "conditions": [ { "condition": "invasion_number", "min": 3 }, { "condition": "random", "weight": 10 } ], "spawns": [ { "entities": [ "minecraft:zombie" ], "count": 5, "count_max": 10, "count_difficulty_multiplier": 2, "cmods": [ { "cmod": "template", "template": "invader_miner" }, { "cmod": "template", "template": "inventory_all_scales" }, { "cmod": "template", "template": "buffed_attributes" } ] }, { "entities": [ "minecraft:spider" ], "count": 15, "count_max": 40, "count_difficulty_multiplier": 0.5, "cmods": [ { "cmod": "template", "template": "invader_soldier" }, { "cmod": "template", "template": "buffed_attributes" } ] } ] }, { "name": "invasion_stage_3_opt_7_infernal", "wave_message": "§cAn invasion has started! Miners and Infernal Mobs!", "conditions": [ { "condition": "invasion_number", "min": 7 }, { "condition": "random", "weight": 3 }, { "condition": "mod_loaded", "mod_id": "infernalmobs" } ], "spawns": [ { "entities": [ "minecraft:zombie" ], "count": 5, "count_max": 10, "count_difficulty_multiplier": 2, "cmods": [ { "cmod": "template", "template": "invader_miner" }, { "cmod": "template", "template": "inventory_all_scales" }, { "cmod": "template", "template": "buffed_attributes" } ] }, { "entities": [ "minecraft:zombie", "minecraft:skeleton" ], "count": 3, "count_max": 8, "count_difficulty_multiplier": 0.5, "cmods": [ { "cmod": "template", "template": "invader_soldier" }, { "cmod": "template", "template": "inventory_all_scales_no_weapon" }, { "cmod": "attribute_attackdamage", "base_value": 5, "max_value": 10, "difficulty_multiplier": 0.5 }, { "cmod": "attribute_health", "base_value": 50, "max_value": 150, "difficulty_multiplier": 1.0 }, { "cmod": "attribute_speed", "base_value": 0.23, "max_value": 0.3, "difficulty_multiplier": 0.5 }, { "cmod": "attribute_speed_flying", "base_value": 3.0, "max_value": 5, "difficulty_multiplier": 0.5 }, { "cmod": "xp", "base_value": 50, "difficulty_multiplier": 1.5 }, { "cmod": "ai_infernal", "randomly_choose_count": "10", "difficulty_multiplier": 1.0, "modifiers": [ "1UP", "Alchemist", "Berserk", "Blastoff", "Bulwark", "Choke", "Cloaking", "Darkness", "Ender", "Exhaust", "Fiery", "Ghastly", "Gravity", "LifeSteal", "Ninja", "Poisonous", "Quicksand", "Regen", "Rust", "Sapper", "Sprint", "Sticky", "Storm", "Vengeance", "Weakness", "Webber", "Wither" ] } ] } ] }, { "name": "invasion_stage_3_opt_8_zombie_players", "wave_message": "§cAn invasion has started! Your zombie player friends have arrived and want to check out your base!", "conditions": [ { "condition": "invasion_number", "min": 5 }, { "condition": "random", "weight": 10 }, { "condition": "mod_loaded", "mod_id": "zombie_players" } ], "spawns": [ { "entities": [ "zombie_players:zombie_player" ], "count": 5, "count_max": 20, "count_difficulty_multiplier": 2, "cmods": [ { "cmod": "template", "template": "invader_miner" }, { "cmod": "template", "template": "buffed_attributes" } ] } ] }, { "name": "invasion_stage_3_opt_9_crazy_animals", "wave_message": "§cAn invasion has started! Animals have gone crazy! Also miners!", "conditions": [ { "condition": "invasion_number", "min": 3 }, { "condition": "random", "weight": 10 } ], "spawns": [ { "entities": [ "minecraft:zombie" ], "count": 3, "count_max": 6, "count_difficulty_multiplier": 2, "cmods": [ { "cmod": "template", "template": "invader_miner" }, { "cmod": "template", "template": "inventory_all_scales" }, { "cmod": "template", "template": "buffed_attributes" } ] }, { "entities": [ "cow", "pig", "sheep", "chicken", "wolf", "ocelot", "parrot", "polar_bear", "rabbit", "horse", "donkey", "mule", "llama" ], "count": 20, "count_max": 30, "count_difficulty_multiplier": 2, "cmods": [ { "cmod": "ai_omniscience" }, { "cmod": "template", "template": "buffed_attributes" }, { "cmod": "ai_attack_melee" }, { "cmod": "attribute_attackdamage", "base_value": 5, "max_value": 10, "difficulty_multiplier": 0.5 } ] } ] }, { "name": "invasion_stage_3_opt_10_illagers", "wave_message": "§cAn invasion has started! The illagers are coming! Also miners!", "conditions": [ { "condition": "invasion_number", "min": 5 }, { "condition": "random", "weight": 10 } ], "spawns": [ { "entities": [ "minecraft:zombie" ], "count": 5, "count_max": 10, "count_difficulty_multiplier": 2, "cmods": [ { "cmod": "template", "template": "invader_miner" }, { "cmod": "template", "template": "inventory_all_scales" }, { "cmod": "template", "template": "buffed_attributes" } ] }, { "entities": [ "vindication_illager", "illusion_illager" ], "count": 4, "count_max": 15, "count_difficulty_multiplier": 2, "cmods": [ { "cmod": "template", "template": "invader_soldier" }, { "cmod": "template", "template": "buffed_attributes" } ] }, { "entities": [ "evocation_illager" ], "count": 1, "count_max": 2, "count_difficulty_multiplier": 2, "cmods": [ { "cmod": "template", "template": "invader_soldier" }, { "cmod": "template", "template": "buffed_attributes" } ] } ] } ] } ================================================ FILE: src/main/resources/assets/coroutil/config/templates/actions/mob_spawns_example_commented.json ================================================ { "wiki": "//looking to customize your own invasions? see http://coros.us/wiki/index.php?title=Hostile_Worlds_-_Invasions_-_Customizing for helpfull info", "format": "mob_spawns_example_commented", "comment": "//for your main file, normally this should always be named mob_spawns, if you want to test and switch between json files, name the above field differently and change the config option mobSpawnsProfile in CoroUtil developer config file to the name", "templates": [ { "name": "invasion_stage_1_opt_1", "comment": "//name of this wave option, should be uniquely named for troubleshooting, not used anywhere else", "wave_message": "miners!", "comment": "//optional, custom invasion start message", "conditions": [ { "condition": "random", "weight": 3 }, { "condition": "difficulty", "min": 0, "max": 0.1 } ], "comment": "//conditions are optional, but should be structured like this above, see json file all_conditions for everything you can use", "spawns": [ { "comment0": "//you can have multiple object entries in the spawns array, this is the first, both the first and second and so on will be used if all the conditions matched", "entities": [ "minecraft:zombie", "minecraft:skeleton" ], "comment": "//a list of entities spawnable for this profile, minimum entry of 1 required, will randomize between them", "count": 2, "comment": "//the minimum base amount that will spawn at difficulty of 0, if the entities list above has multiple entries, it will not increase the amount spawned", "count_max": 5, "comment": "//optional, a safety hard limit on the max spawnable, even if difficulty multiplier is over this amount", "count_difficulty_multiplier": 2, "comment": "//multiplies the amount that will spawn by local dynamic difficulty * this multiplier", "comment": "//the formula is: count + (count * difficulty * count_difficulty_multiplier)", "comment": "//example: with a count of 5, difficulty of 0.5 and count_difficulty_multiplier of 2: 5 + (5 * 0.5 * 2) = 10", "comment": "//setting count_difficulty_multiplier to 0 will make the amount of mobs spawned always equal the count field", "spawn_type": "ground", "comment": "//optional, specifies how to spawn the entities in the list, choices are: ground, surface, cave, air, water", "comment": "//if ground: will try to spawn either on the surface or in a cave depending on where the player is", "comment": "//if surface: will try to spawn on the surface, a place that can see the sky", "comment": "//if cave: will try to spawn in a cave, a dark stoney place that cant see the sky", "comment": "//if air: will try to spawn in open air, not requiring a block under it", "comment": "//if water: will try to spawn underwater", "cmods": [ { "cmod": "template", "template": "invader_miner" }, { "cmod": "attribute_health", "base_value": 40, "difficulty_multiplier": 1.5 } ], "comment": "//cmods are optional, but should be structured like this above, see json file all_cmods for everything you can use" }, { "comment": "//you can have multiple object entries in the spawns array, this is the second, this is the last comment, everything below is just more examples of the same structures", "entities": [ "minecraft:zombie" ], "count": 5, "count_max": 15, "count_difficulty_multiplier": 2, "cmods": [ { "cmod": "template", "template": "invader_soldier" }, { "cmod": "template", "template": "inventory_leather" } ] } ] }, { "name": "invasion_stage_1_opt_2", "conditions": [ { "condition": "random", "weight": 1 } ], "spawns": [ { "entities": [ "minecraft:zombie" ], "count": 2, "count_max": 5, "count_difficulty_multiplier": 2, "cmods": [ { "cmod": "template", "template": "invader_miner" }, { "cmod": "template", "template": "inventory_leather" } ] }, { "entities": [ "minecraft:zombie" ], "count": 2, "count_max": 10, "count_difficulty_multiplier": 2, "cmods": [ { "cmod": "template", "template": "invader_soldier" }, { "cmod": "template", "template": "inventory_leather" } ] }, { "entities": [ "minecraft:skeleton" ], "count": 2, "count_max": 10, "count_difficulty_multiplier": 2, "cmods": [ { "cmod": "template", "template": "invader_soldier" }, { "cmod": "template", "template": "inventory_leather_no_weapon" } ] } ] }, { "name": "invasion_wave_override_1", "conditions": [ { "condition": "random", "weight": 99999 }, { "condition": "invasion_rate", "rate": 5 }, { "condition": "mod_loaded", "mod_id": "infernalmobs", "mode_boolean": "invert" } ], "spawns": [ { "entities": [ "minecraft:zombie" ], "count": 1, "cmods": [ { "cmod": "template", "template": "invader_miner" }, { "cmod": "attribute_health", "base_value": 150, "difficulty_multiplier": 1.5 }, { "cmod": "xp", "base_value": 100, "difficulty_multiplier": 1.5 }, { "cmod": "mob_drops", "loot_table": "testlootboss" }, { "cmod": "ai_counterattack" }, { "cmod": "ai_lunge" }, { "cmod": "template", "template": "inventory_iron" } ] } ] }, { "name": "invasion_wave_override_2", "conditions": [ { "condition": "difficulty", "min": 0, "max": 999 }, { "condition": "invasion_rate", "rate": 5 }, { "condition": "random", "weight": 99999 }, { "condition": "mod_loaded", "mod_id": "infernalmobs" } ], "spawns": [ { "entities": [ "minecraft:zombie" ], "count": 1, "cmods": [ { "cmod": "template", "template": "invader_miner" }, { "cmod": "attribute_health", "base_value": 30, "difficulty_multiplier": 1.5 }, { "cmod": "xp", "base_value": 100, "difficulty_multiplier": 1.5 }, { "cmod": "mob_drops", "loot_table": "testlootboss" }, { "cmod": "ai_counterattack" }, { "cmod": "ai_lunge" }, { "cmod": "ai_infernal", "randomly_choose_count": "10", "difficulty_multiplier": 1.0, "modifiers": [ "1UP", "Alchemist", "Berserk", "Blastoff", "Bulwark", "Choke", "Cloaking", "Darkness", "Ender", "Exhaust", "Fiery", "Ghastly", "Gravity", "LifeSteal", "Ninja", "Poisonous", "Quicksand", "Regen", "Rust", "Sapper", "Sprint", "Sticky", "Storm", "Vengeance", "Weakness", "Webber", "Wither" ] } ] } ] } ] } ================================================ FILE: src/main/resources/assets/coroutil/config/templates/actions/mob_spawns_testing_miners.json ================================================ { "format": "mob_spawns_testing_miners", "templates": [ { "name": "invasion_stage_1_opt_1", "wave_message": "miners!", "spawns": [ { "entities": [ "minecraft:zombie" ], "count": 10, "count_difficulty_multiplier": 0, "spawn_type": "ground", "cmods": [ { "cmod": "template", "template": "invader_miner" }, { "cmod": "template", "template": "inventory_all_scales" }, { "cmod": "ai_attack_melee" }, { "cmod": "attribute_attackdamage", "base_value": 5, "max_value": 10, "difficulty_multiplier": 1.0 }, { "cmod": "attribute_health", "base_value": 20, "max_value": 50, "difficulty_multiplier": 1.0 }, { "cmod": "attribute_speed", "base_value": 0.28, "max_value": 0.3, "difficulty_multiplier": 1.0 }, { "cmod": "attribute_speed_flying", "base_value": 3.0, "max_value": 5, "difficulty_multiplier": 1.0 } ] } ] } ] } ================================================ FILE: src/main/resources/assets/coroutil/config/templates/cmods/all_cmods.json ================================================ { "format": "cmods", "templates": [ { "name": "all_cmods", "cmods": [ { "cmod": "template", "template": "boringvanilla", "comment": "//to save on copying and pasting, you can define templates with a set of cmods and refer to them using the template cmod, see invasions_cmods for my actual uses of them, and mob_spawns_example_commented for usage of them", "comment": "//you cant do templates within templates, so this entry isnt technically valid its just here to explain things, but within the mob_spawns json it would be valid" }, { "cmod": "inventory", "inv_hand_main": "minecraft:diamond_sword", "inv_hand_off": "minecraft:shield", "inv_head": "minecraft:diamond_helmet", "inv_chest": "minecraft:diamond_chestplate", "inv_legs": "minecraft:diamond_leggings", "inv_feet": "minecraft:diamond_boots", "comment": "//give inventory to a mob, up to you to make sure the mob you are giving it to supports it, could crash if they dont, supports modded items but cant be sure how they'll work out" }, { "cmod": "inventory_difficulty_scaled", "stages": [ { "min": 0, "max": 0.3, "inv_hand_off": "minecraft:shield", "inv_hand_main": "minecraft:stone_sword", "inv_head": "minecraft:leather_helmet", "inv_chest": "minecraft:leather_chestplate", "inv_legs": "minecraft:leather_leggings", "inv_feet": "minecraft:leather_boots" }, { "min": 0.3, "max": 6, "inv_hand_off": "minecraft:shield", "inv_hand_main": "minecraft:diamond_sword", "inv_head": "minecraft:diamond_helmet", "inv_chest": "minecraft:diamond_chestplate", "inv_legs": "minecraft:diamond_leggings", "inv_feet": "minecraft:diamond_boots" } ], "comment": "//give inventory depending on difficulty of area, for easy re-use within mob spawn profiles" }, { "cmod": "mob_drops", "loot_table": "testloot", "comment": "//give a mob extra loot table drops, supports vanilla eg minecraft:zombie or custom ones you put in the config/loot_tables folder, currently does not override existing drops of a mob" }, { "cmod": "attribute_health", "base_value": 40, "max_value": 80, "difficulty_multiplier": 1.5, "comment": "//set the base health of a mob, difficulty_multiplier is used to multiply their health based on the rated difficulty of the area, so if difficulty in area was 2.0, the math would be: base value 40 + (base health 40 * local difficulty 2.0 * multiplier 1.5) = 120 health, set difficulty_multiplier to 0 or dont include the tag to make the value always be the base value", "comment": "//max_value is optional, if local dynamic difficulty combined with difficulty_multiplier puts the health over what max_value is set to, it will cap the health to max_value" }, { "cmod": "attribute_attackdamage", "base_value": 5, "max_value": 10, "difficulty_multiplier": 1.5, "comment": "//set the base damage of a mob, difficulty_multiplier is used to multiply their damage based on the rated difficulty of the area, so if difficulty in area was 2.0, the math would be: base value 5 + (base value 5 * local difficulty 2.0 * multiplier 1.5) = 15 damage" }, { "cmod": "attribute_speed", "base_value": 0.23, "max_value": 0.3, "difficulty_multiplier": 1.1, "comment": "//set the base ground movement speed of the mob, multiplication formula works the same as attribute_health, be carefull with this one so you dont get hyper speed mobs, vanilla zombie speed is 0.23 for reference" }, { "cmod": "attribute_speed_flying", "base_value": 0.4, "max_value": 0.5, "difficulty_multiplier": 0.0, "comment": "//same as attribute_speed but for flying entities when they arent touching the ground, because mojang, youll generally want a value 2-3x higher than the attribute_speed which is used for ground" }, { "cmod": "xp", "base_value": 0, "difficulty_multiplier": 1.5, "comment": "//set the base xp given of a mob, multiplication formula works the same as attribute_health" }, { "cmod": "ai_antiair", "comment": "//gives a mob the ability to perform mean things to flying players depending on ConfigHWMonsters.antiAirType setting, they either leap very far and grab the player by mounting them in their head, or magically pull them down when they see them, pretty mean, antiAirType mode 0 is a bit experimental still" }, { "cmod": "ai_mining", "comment": "//gives a mob the ability to dig towards their target, only use for mobs the size of a zombie" }, { "cmod": "ai_explodeonstuck", "comment": "//gives a mob the ability to explode once they cant get any closer to their target" }, { "cmod": "ai_counterattack", "comment": "//gives a mob the ability to counter attack with a leap towards the target after theyre hit" }, { "cmod": "ai_lunge", "comment": "//gives a mob the ability to move faster towards the target when they are close" }, { "cmod": "ai_attack_melee", "comment": "//gives a mob the ability to chase and hurt a target, use on mobs that didnt have a melee attack like passive mobs, use attribute_attackdamage cmod to set their attack damage" }, { "cmod": "ai_infernal", "randomly_choose_count": "10", "randomly_choose_count_max": "15", "difficulty_multiplier": 1.0, "modifiers": [ "1UP", "Alchemist", "Berserk", "Blastoff", "Bulwark", "Choke", "Cloaking", "Darkness", "Ender", "Exhaust", "Fiery", "Ghastly", "Gravity", "LifeSteal", "Ninja", "Poisonous", "Quicksand", "Regen", "Rust", "Sapper", "Sprint", "Sticky", "Storm", "Vengeance", "Weakness", "Webber", "Wither" ], "comment": "//if atomicstrykers Infernal Mobs mod is installed, this gives a mob a random amount of the abilities you list above, the above list is all the options available from the mod", "comment": "//you can specify the modifiers and a number of them to randomly choose from, in the above example config, at a local difficulty of 0.1, it will choose 1 modifier from the list, at 0.5, it will choose 5, etc", "comment": "//randomly_choose_count_max is optional, since its set, itll never choose more than 15 no matter the difficulty", "comment": "//this functionality depends on my code knowing how to interact with their mods code, if they change theirs, this cmod may break", "comment": "//you can safely try to use this even if Infernal Mobs isnt installed, it will just not be used" } ] } ] } ================================================ FILE: src/main/resources/assets/coroutil/config/templates/cmods/invasions_cmods.json ================================================ { "format": "cmods", "templates": [ { "name": "invader_miner", "cmods": [ { "cmod": "ai_mining" }, { "cmod": "ai_omniscience" } ] }, { "name": "invader_soldier", "cmods": [ { "cmod": "ai_omniscience" }, { "cmod": "ai_counterattack" }, { "cmod": "ai_lunge" }, { "cmod": "ai_hoist" } ] }, { "name": "buffed_attributes", "cmods": [ { "cmod": "attribute_attackdamage", "base_value": 3, "max_value": 5, "difficulty_multiplier": 0.5 }, { "cmod": "attribute_health", "base_value": 20, "max_value": 50, "difficulty_multiplier": 1.0 }, { "cmod": "attribute_speed", "base_value": 0.23, "max_value": 0.3, "difficulty_multiplier": 0.5 }, { "cmod": "attribute_speed_flying", "base_value": 3.0, "max_value": 5, "difficulty_multiplier": 0.5 }, { "cmod": "xp", "base_value": 5, "difficulty_multiplier": 1.5 } ] }, { "name": "infernal_skeleton_1", "cmods": [ { "cmod": "ai_infernal", "modifiers": [ "Regen", "Cloaking", "Storm" ] } ] }, { "name": "inventory_all_scales", "cmods": [ { "cmod": "inventory_difficulty_scaled", "stages": [ { "min": 0, "max": 0.25, "inv_hand_off": "minecraft:shield", "inv_hand_main": "minecraft:stone_sword", "inv_head": "minecraft:leather_helmet", "inv_chest": "minecraft:leather_chestplate", "inv_legs": "minecraft:leather_leggings", "inv_feet": "minecraft:leather_boots" }, { "min": 0.25, "max": 0.5, "inv_hand_off": "minecraft:shield", "inv_hand_main": "minecraft:stone_sword", "inv_head": "minecraft:chainmail_helmet", "inv_chest": "minecraft:chainmail_chestplate", "inv_legs": "minecraft:chainmail_leggings", "inv_feet": "minecraft:chainmail_boots" }, { "min": 0.5, "max": 0.75, "inv_hand_off": "minecraft:shield", "inv_hand_main": "minecraft:iron_sword", "inv_head": "minecraft:iron_helmet", "inv_chest": "minecraft:iron_chestplate", "inv_legs": "minecraft:iron_leggings", "inv_feet": "minecraft:iron_boots" }, { "min": 0.75, "max": 999, "inv_hand_off": "minecraft:shield", "inv_hand_main": "minecraft:diamond_sword", "inv_head": "minecraft:diamond_helmet", "inv_chest": "minecraft:diamond_chestplate", "inv_legs": "minecraft:diamond_leggings", "inv_feet": "minecraft:diamond_boots" } ] } ] }, { "name": "inventory_all_scales_no_weapon", "cmods": [ { "cmod": "inventory_difficulty_scaled", "stages": [ { "min": 0, "max": 0.25, "inv_hand_off": "minecraft:shield", "inv_head": "minecraft:leather_helmet", "inv_chest": "minecraft:leather_chestplate", "inv_legs": "minecraft:leather_leggings", "inv_feet": "minecraft:leather_boots" }, { "min": 0.25, "max": 0.5, "inv_hand_off": "minecraft:shield", "inv_head": "minecraft:chainmail_helmet", "inv_chest": "minecraft:chainmail_chestplate", "inv_legs": "minecraft:chainmail_leggings", "inv_feet": "minecraft:chainmail_boots" }, { "min": 0.5, "max": 0.75, "inv_hand_off": "minecraft:shield", "inv_head": "minecraft:iron_helmet", "inv_chest": "minecraft:iron_chestplate", "inv_legs": "minecraft:iron_leggings", "inv_feet": "minecraft:iron_boots" }, { "min": 0.75, "max": 999, "inv_hand_off": "minecraft:shield", "inv_head": "minecraft:diamond_helmet", "inv_chest": "minecraft:diamond_chestplate", "inv_legs": "minecraft:diamond_leggings", "inv_feet": "minecraft:diamond_boots" } ] } ] }, { "name": "inventory_leather", "cmods": [ { "cmod": "inventory", "inv_hand_main": "minecraft:stone_sword", "inv_head": "minecraft:leather_helmet", "inv_chest": "minecraft:leather_chestplate", "inv_legs": "minecraft:leather_leggings", "inv_feet": "minecraft:leather_boots" } ] }, { "name": "inventory_leather_no_weapon", "cmods": [ { "cmod": "inventory", "inv_hand_main": "minecraft:stone_sword", "inv_head": "minecraft:leather_helmet", "inv_chest": "minecraft:leather_chestplate", "inv_legs": "minecraft:leather_leggings", "inv_feet": "minecraft:leather_boots" } ] }, { "name": "inventory_chainmail", "cmods": [ { "cmod": "inventory", "inv_hand_main": "minecraft:stone_sword", "inv_head": "minecraft:chainmail_helmet", "inv_chest": "minecraft:chainmail_chestplate", "inv_legs": "minecraft:chainmail_leggings", "inv_feet": "minecraft:chainmail_boots" } ] }, { "name": "inventory_iron", "cmods": [ { "cmod": "inventory", "inv_hand_main": "minecraft:iron_sword", "inv_hand_off": "minecraft:shield", "inv_head": "minecraft:iron_helmet", "inv_chest": "minecraft:iron_chestplate", "inv_legs": "minecraft:iron_leggings", "inv_feet": "minecraft:iron_boots" } ] }, { "name": "inventory_diamond", "cmods": [ { "cmod": "inventory", "inv_hand_main": "minecraft:diamond_sword", "inv_hand_off": "minecraft:shield", "inv_head": "minecraft:diamond_helmet", "inv_chest": "minecraft:diamond_chestplate", "inv_legs": "minecraft:diamond_leggings", "inv_feet": "minecraft:diamond_boots" } ] }, { "name": "infernal_skeleton_1_bad", "cmods": [ { "cmod": "ai_infernal", "modifiers": [ "Regen", "Claking", "Storm" ] } ] }, { "name": "inventory_chainmail_bad", "cmods": [ { "cmod": "inventory", "inv_hand_main": "minecraft:diamond_swod", "inv_hand_off": "minecraft:shield", "inv_head": "minecraft:diamond_helmet", "inv_chest": "minecraft:diamond_chestplate", "inv_legs": "minecraft:diamond_leggings", "inv_feet": "minecraft:diamond_boots" } ] } ] } ================================================ FILE: src/main/resources/assets/coroutil/config/templates/conditions/all_conditions.json ================================================ { "format": "conditions", "templates": [ { "name": "all_conditions", "conditions": [ { "condition": "context", "type": "invasion/regular/all", "comment": "//placeholder for future stuff, dont use for now, dev note: is 'all' even needed? its the same as not using this condition, maybe if recursive templates were a thing, top level one could override lower one" }, { "condition": "difficulty", "min": 0, "max": 0.1, "comment": "//only use when local dynamic difficulty as at or between these numbers, difficulty determined by many things, see CoroUtils DynamicDifficulty.cfg for configuring that", "comment": "//things that can affect difficulty: damage per second, armor rating, server time (default off), vanilla local chunk occupancy time, max health, distance from spawn, buffed location (unused), debuffed location (unused)", "comment": "//difficulty starts at 0, and goes up to 1 for vanilla and default config, if difficulty goes above 1 its likely due to other mods (like a high damage weapon or stronger than diamond armor, or health over 20)" }, { "condition": "invasion_number", "min": 1, "max": 5, "comment": "//only use when the active invasion number/count is at or between these numbers, the counter is global and not per player" }, { "condition": "invasion_rate", "rate": 5, "comment": "//only use every x invasions, in this example, this will be true every 5th invasion" }, { "condition": "random", "weight": 5, "comment": "//when you want to randomize between multiple invasion wave profiles, more weight = more likely, if this isnt set, a default weight of 1 is set", "comment": "//random is evaluated last after all other conditions, if there is more than 1 invasion profile still usable after all other conditions met, it will then randomize between them using these weights", "comment": "//if there is more than 1 invasion profile still usable after all other conditions met, it will then randomize between them using these weights" }, { "condition": "filter_mobs", "mode": "whitelist/blacklist or allow/deny?", "entities": [ "minecraft:zombie" ], "comment": "//currently unused" }, { "condition": "template", "template": "condition_set_1", "comment": "//since this is a template within a template, maybe dont allow it for now" }, { "condition": "mod_loaded", "mod_id": "infernalmobs", "mode_boolean": "invert", "comment": "//filter waves to only be used when a certain mod is or isnt installed, depending on if mode_boolean is set to normal or invert, normal = is mod loaded, invert = is mod not loaded" } ] } ] } ================================================ FILE: src/main/resources/assets/coroutil/config/templates/conditions/invasions_stages.json ================================================ { "format": "conditions", "templates": [ { "name": "invasion_stage_1", "conditions": [ { "condition": "context", "type": "invasion" }, { "condition": "difficulty", "min": 0, "max": 0.1 } ] }, { "name": "invasion_stage_2", "conditions": [ { "condition": "context", "type": "invasion" }, { "condition": "difficulty", "min": 0.1, "max": 0.2 } ] }, { "name": "invasion_stage_3", "conditions": [ { "condition": "context", "type": "invasion" }, { "condition": "difficulty", "min": 0.2, "max": 5.0 } ] } ] } ================================================ FILE: src/main/resources/assets/coroutil/lang/en_us.json ================================================ { "itemGroup.weather2": "Weather2 Items" } ================================================ FILE: src/main/resources/assets/coroutil/models/block/blank.json ================================================ { } ================================================ FILE: src/main/resources/assets/coroutil/models/block/repairing_block.json ================================================ { "parent": "block/cube_all", "textures": { "all": "coroutil:blocks/repairing_block" } } ================================================ FILE: src/main/resources/assets/coroutil/models/item/item_repairing_gel.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "coroutil:items/item_repairing_gel" } } ================================================ FILE: src/main/resources/assets/coroutil/models/item/repairing_block.json ================================================ { "parent": "coroutil:block/repairing_block", "display": { "thirdperson": { "rotation": [ 10, -45, 170 ], "translation": [ 0, 1.5, -2.75 ], "scale": [ 0.375, 0.375, 0.375 ] } } } ================================================ FILE: src/main/resources/assets/coroutil/shaders/foliage.fs ================================================ #version 130 uniform sampler2D texture_sampler; uniform int fogmode; //uniform int stipple[64]; varying vec2 outTexCoord; //flat varying float outBrightness; varying vec4 outRGBA; //varying float outAlphaInt; void main() { //considering range 0.9 to 1.0 is quite costly, and provides minimal visual difference, this is more efficient //scratch that, stipple is crazy expensive in many scenarios, damn /*if (outRGBA.w < 0.9) { ivec2 coord = ivec2(gl_FragCoord.xy - 0.5); if (stipple[int(mod(coord.x, 8) + mod(coord.y, 8) * 8)] < outAlphaInt - 1) { discard; } }*/ float fogFactor = 0; if (fogmode == 0) { // Linear fog fogFactor = (gl_Fog.end - gl_FogFragCoord) * gl_Fog.scale; } else if (fogmode == 1) { // Exp fog fogFactor = exp(-gl_Fog.density * gl_FogFragCoord); } //0 = full fog //1 = no fog /*int kk = lightmapColors[i] >> 16 & 255; int ll = lightmapColors[i] >> 8 & 255; int ii = lightmapColors[i] & 255;*/ //int lightMap = int(outBrightness); //full 1 1 1 //lightMap = -1; //mostly blue //lightMap = -13421569; /*float r = float((lightMap >> 16) & 255) / 255.0; float g = float((lightMap >> 8) & 255) / 255.0; float b = float(lightMap & 255) / 255.0;*/ /*r = 0.2F; g = 0.2F; b = 1F;*/ /*float r = 1F; float g = 1F; float b = 1F;*/ vec4 fragColor = texture2D(texture_sampler, outTexCoord); fragColor.x *= outRGBA.x; fragColor.y *= outRGBA.y; fragColor.z *= outRGBA.z; fragColor.w *= outRGBA.w; /*if (stipple[1] == 0) { fragColor.w = 1; }*/ if (outRGBA.w > 0) { fogFactor = clamp(fogFactor, 0.0, 1.0); gl_FragColor = mix(gl_Fog.color, fragColor, fogFactor); gl_FragColor.w = fragColor.w; //gl_FragColor = fragColor; } else { gl_FragColor = fragColor; } //gl_FragColor = fragColor; } ================================================ FILE: src/main/resources/assets/coroutil/shaders/foliage.vs ================================================ #version 130 #extension GL_EXT_gpu_shader4 : enable //in int gl_VertexID; //in int gl_InstanceID; //seldom changing or 1 time use data - non instanced: attribute vec3 position; //mesh pos attribute vec2 texCoord; attribute vec3 vertexNormal; //unused //seldom - instanced attribute mat4 modelMatrix; //used to be modelViewMatrix, separate from view matrix attribute vec4 rgba; //4th entry, alpha not used here, might as well leave vec4 unless more efficient to separate things to per float/attrib entries attribute vec4 meta; //often changed data - instanced attribute vec2 alphaBrightness; varying vec2 outTexCoord; //flat varying float outBrightness; varying vec4 outRGBA; varying float outAlphaInt; uniform mat4 modelViewMatrixCamera; uniform int time; uniform float partialTick; uniform float windDir; uniform float windSpeed; vec3 computeCorner(vec3 sway, vec3 angle, vec3 center) { return center + normalize(cross(sway, angle)) * 0.5; } mat4 rotationMatrix(vec3 axis, float angle) { axis = normalize(axis); float s = sin(angle); float c = cos(angle); float oc = 1.0 - c; return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0, oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0, oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0, 0.0, 0.0, 0.0, 1.0); } void main() { float radian = 0.0174533; int swayLag = 20; float index = meta.x; float animationID = meta.y; float heightIndex = meta.z; float antiStiffness = meta.w; float rotation = rgba.w; float baseTimeChangeRate = 60.0 * windSpeed; float timeSmooth = (time-baseTimeChangeRate) + (baseTimeChangeRate * partialTick); timeSmooth += index * 200; vec3 pos = vec3(0, 0, 0); mat4 finalMat = modelViewMatrixCamera * modelMatrix; vec3 posTestAdj = position; posTestAdj.y = posTestAdj.y + heightIndex + 0.5; vec4 posTest = finalMat * vec4(posTestAdj.x, posTestAdj.y, posTestAdj.z, 1.0); if (windSpeed > 0.00001 && posTest.w < 999) { //wind hit foliage, 1 high for now if (animationID == 0) { //BETTER CODE START float variance = 0.6; //try offsetting mesh so bottom is 0 vec3 usePos = position; usePos.y = usePos.y + 0.5; float heightFromBase = heightIndex + usePos.y; swayLag = int(heightFromBase * -baseTimeChangeRate * 0.2); //swayLag = int(heightFromBase * -1); float windSpeedAdj = windSpeed * 0.05 * (heightFromBase); windSpeedAdj = windSpeedAdj * (antiStiffness * 2.0); //a bit of hack to make all but reeds be influenced by wind more lower down if (antiStiffness == 1.0) { windSpeedAdj = windSpeed * 0.5; } //disable for more variance per height //windSpeedAdj = windSpeed * 0.2; float adjDir = windDir/* - rotation*/; vec3 windAdj = vec3(-sin(adjDir * radian) * windSpeedAdj, 0, cos(adjDir * radian) * windSpeedAdj); float yAdj = windAdj.y; if (antiStiffness == 1.0) { //yAdj = cross(windAdj, vec3(1, 0, 1)).y; } //semi hacky fix for rotation being done before we apply sway logic if (rotation == 45.0) { windAdj = vec3(-cos(adjDir * radian) * windSpeedAdj, 0, -sin(adjDir * radian) * windSpeedAdj); } //maybe correct, added gap between mesh connections though if (antiStiffness == 1.0) { //windAdj.y = yAdj; } //windAdj.y = windAdj.y - 0.2; //this.rotationYaw is quaternion is both required but messing with the sway math, rework when its quat rotated? //timeModTop = int(mod((((timeSmooth + ((1) * swayLag))) * 60.0 * windSpeed), 360)); //timeModTop = int(mod((((timeSmooth + ((1) * swayLag))) * 60.0), 360)); //timeModTop = int((((timeSmooth + ((0.001) * swayLag))) * 1.0)); int timeModTop = int(mod((timeSmooth * 0.2/* * antiStiffness*/) + swayLag, 360)); //timeModTop = int(mod(int(timeSmooth * windSpeed * 10), 360)); //timeModTop = int(mod(90, 360)); variance = 0.02 + (0.05 * windSpeed); variance = variance * antiStiffness; //enable for more variance per height //variance = 0.06 * (heightFromBase * heightFromBase * 0.02); vec3 chaosAdj = vec3(-sin(timeModTop * radian) * variance, 0, cos(timeModTop * radian) * variance); //semi hacky fix for rotation being done before we apply sway logic if (rotation == 45.0) { chaosAdj = vec3(-cos(timeModTop * radian) * variance, 0, -sin(timeModTop * radian) * variance); } windAdj = windAdj * heightFromBase; chaosAdj = chaosAdj * heightFromBase * 1.0; pos = usePos; pos = pos + windAdj + chaosAdj; pos.y = pos.y + heightIndex; //seaweed } else if (false && animationID == 1) { //timeSmooth = 1; float variance = 0.6; vec3 angle = vec3(-1, 0, 1); if (rotation == 1) { angle = vec3(1, 0, 1); } //more performant but less accurate algorithm, use unless crazy mesh warping needed vec3 baseHeight = vec3(0, heightIndex-1, 0); vec3 baseHeight2 = vec3(0, heightIndex, 0); int timeModBottom = int(mod(((timeSmooth + ((heightIndex - 1 + 1) * swayLag)) * 2) + rotation, 360)); vec3 swayBottom = vec3(sin(timeModBottom * radian) * variance, 1, cos(timeModBottom * radian) * variance); vec3 prevSway = swayBottom; vec3 bottom = baseHeight + swayBottom; int timeModTop = int(mod(((timeSmooth + ((heightIndex + 1) * swayLag)) * 2) + rotation, 360)); vec3 sway = vec3(sin(timeModTop * radian) * variance, 1, cos(timeModTop * radian) * variance); vec3 top = baseHeight2 + sway; if (heightIndex == 0) { bottom = vec3(0, 0, 0); prevSway = vec3(0, 1, 0); } //more accurate but more expensive loop /* vec3 top = vec3(0, 0, 0); vec3 bottom = vec3(0, 0, 0); vec3 bottomNext = bottom; //verify vec3 sway = vec3(0, 0, 0); for (int i = 0; i <= heightIndex; i++) { prevSway = sway; timeMod = int(mod(((timeSmooth + ((i + 1) * swayLag)) * 2) + rotation, 360)); sway = vec3(sin(timeMod * radian) * variance, 1, cos(timeMod * radian) * variance); sway = normalize(sway); top = bottomNext + sway; bottom = bottomNext; bottomNext = top; }*/ if (gl_VertexID == 0) { pos = computeCorner(sway, angle, top); } else if (gl_VertexID == 1) { pos = computeCorner(prevSway, angle, bottom); } else if (gl_VertexID == 2) { angle = angle * -1; pos = computeCorner(prevSway, angle, bottom); } else if (gl_VertexID == 3) { angle = angle * -1; pos = computeCorner(sway, angle, top); } } gl_Position = finalMat * vec4(pos.x, pos.y, pos.z, 1.0); } else { gl_Position = posTest;//finalMat * vec4(posTestAdj.x, posTestAdj.y, posTestAdj.z, 1.0); } //lazy, cheap dist to camera gl_FogFragCoord = abs(gl_Position.z); outTexCoord = texCoord; //outBrightness = alphaBrightness.y; int lightMap = int(alphaBrightness.y); vec3 texMap = vec3(float((lightMap >> 16) & 255) / 255.0, float((lightMap >> 8) & 255) / 255.0, float(lightMap & 255) / 255.0); outRGBA = vec4(rgba.x * texMap.x, rgba.y * texMap.y, rgba.z * texMap.z, alphaBrightness.x); //outAlphaInt = 255 - int(outRGBA.w * 255); } ================================================ FILE: src/main/resources/assets/coroutil/shaders/particle.fs ================================================ #version 130 uniform sampler2D texture_sampler; uniform int fogmode; varying vec2 outTexCoord; //varying float outBrightness; varying vec4 outRGBA; void main() { float fogFactor = 0; if (fogmode == 0) { // Linear fog fogFactor = (gl_Fog.end - gl_FogFragCoord) * gl_Fog.scale; } else if (fogmode == 1) { // Exp fog fogFactor = exp(-gl_Fog.density * gl_FogFragCoord); } vec4 fragColor = texture2D(texture_sampler, outTexCoord); fragColor.x *= outRGBA.x; fragColor.y *= outRGBA.y; fragColor.z *= outRGBA.z; fragColor.w *= outRGBA.w; if (outRGBA.w > 0) { fogFactor = clamp(fogFactor, 0.0, 1.0); gl_FragColor = mix(gl_Fog.color, fragColor, fogFactor); gl_FragColor.w = fragColor.w; //gl_FragColor = fragColor; } else { gl_FragColor = fragColor; } //gl_FragColor.w = 0.1; //gl_FragColor = fragColor; } ================================================ FILE: src/main/resources/assets/coroutil/shaders/particle.vs ================================================ #version 130 #extension GL_EXT_gpu_shader4 : enable attribute vec3 position; attribute vec2 texCoord; attribute vec3 vertexNormal; attribute mat4 modelMatrix; attribute float brightness; attribute vec4 rgba; //attribute vec4 rgbaTest; //in vec2 texOffset; varying vec2 outTexCoord; //flat varying float outBrightness; varying vec4 outRGBA; uniform mat4 modelViewMatrixCamera; //uniform mat4 projectionMatrix; //uniform int numCols; //uniform int numRows; void main() { gl_Position = modelViewMatrixCamera * modelMatrix * vec4(position, 1.0); //vec4 eyePos = gl_ModelViewMatrix * gl_Position; //gl_FogFragCoord = abs(eyePos.z/eyePos.w); gl_FogFragCoord = abs(gl_Position.z); // Support for texture atlas, update texture coordinates //float x = (texCoord.x / numCols + texOffset.x); //float y = (texCoord.y / numRows + texOffset.y); outTexCoord = texCoord; //outBrightness = brightness; int lightMap = int(brightness); vec3 texMap = vec3(float((lightMap >> 16) & 255) / 255.0, float((lightMap >> 8) & 255) / 255.0, float(lightMap & 255) / 255.0); //temp //rgba.x = 1; //rgba.y = 1; //rgba.z = 1; //rgba.w = 1; outRGBA = rgba; outRGBA.x = rgba.x * texMap.x; outRGBA.y = rgba.y * texMap.y; outRGBA.z = rgba.z * texMap.z; } ================================================ FILE: src/main/resources/assets/weather2/blockstates/anemometer.json ================================================ { "variants": { "": { "model": "weather2:block/anemometer" } } } ================================================ FILE: src/main/resources/assets/weather2/blockstates/sand_layer.json ================================================ { "variants": { "layers=1": { "model": "weather2:block/sand_height2" }, "layers=2": { "model": "weather2:block/sand_height4" }, "layers=3": { "model": "weather2:block/sand_height6" }, "layers=4": { "model": "weather2:block/sand_height8" }, "layers=5": { "model": "weather2:block/sand_height10" }, "layers=6": { "model": "weather2:block/sand_height12" }, "layers=7": { "model": "weather2:block/sand_height14" }, "layers=8": { "model": "block/sand" } } } ================================================ FILE: src/main/resources/assets/weather2/blockstates/tornado_sensor.json ================================================ { "variants": { "powered=true": { "model": "weather2:block/tornado_sensor" }, "powered=false": { "model": "weather2:block/tornado_sensor" } } } ================================================ FILE: src/main/resources/assets/weather2/blockstates/tornado_sensor_new.json ================================================ { "forge_marker": 1, "defaults": { "textures": { "all": "weather2:blocks/tornado_sensor" }, "model": "weather2:block/tornado_sensor", "uvlock": true }, "variants": { "power": { "true": { "textures": { "all": "weather2:blocks/tornado_sensor" } }, "false": { "textures": { "all": "weather2:blocks/tornado_sensor" } } }, "normal": [{ }] } } ================================================ FILE: src/main/resources/assets/weather2/blockstates/tornado_siren.json ================================================ { "variants": { "": { "model": "weather2:block/tornado_siren" } } } ================================================ FILE: src/main/resources/assets/weather2/blockstates/tornado_siren_manual.json ================================================ { "forge_marker": 1, "defaults": { "textures": { "all": "weather2:blocks/tornado_siren" }, "model": "weather2:block/tornado_siren", "uvlock": true }, "variants": { "enabled": { "true": { "textures": { "all": "weather2:blocks/tornado_siren_manual_on" } }, "false": { "textures": { "all": "weather2:blocks/tornado_siren_manual" } } }, "normal": [{ }] } } ================================================ FILE: src/main/resources/assets/weather2/blockstates/tornado_siren_old.json ================================================ { "variants": { "normal": { "model": "weather2:block/tornado_siren" } } } ================================================ FILE: src/main/resources/assets/weather2/blockstates/weather_deflector.json ================================================ { "variants": { "": { "model": "weather2:block/weather_deflector" } } } ================================================ FILE: src/main/resources/assets/weather2/blockstates/weather_forecast.json ================================================ { "variants": { "": { "model": "weather2:block/weather_forecast" } } } ================================================ FILE: src/main/resources/assets/weather2/blockstates/weather_machine.json ================================================ { "variants": { "normal": { "model": "weather2:block/weather_machine" } } } ================================================ FILE: src/main/resources/assets/weather2/blockstates/wind_turbine.json ================================================ { "variants": { "": { "model": "weather2:block/wind_turbine" } } } ================================================ FILE: src/main/resources/assets/weather2/blockstates/wind_vane.json ================================================ { "variants": { "": { "model": "weather2:block/wind_vane" } } } ================================================ FILE: src/main/resources/assets/weather2/lang/en_us.json ================================================ { "itemGroup.weather2": "Weather2 Items", "block.weather2.tornado_sensor": "Tornado Sensor", "block.weather2.tornado_siren": "Weather Siren", "block.weather2.tornado_siren_manual": "Manual Redstone Siren", "block.weather2.wind_vane": "Wind Vane", "block.weather2.wind_turbine": "Wind Turbine", "block.weather2.weather_forecast": "Weather Forecast", "block.weather2.weather_machine": "Weather Machine (right click to cycle)", "block.weather2.weather_deflector": "Weather Deflector", "block.weather2.anemometer": "Anemometer", "block.weather2.sand_layer": "Placeable Sand Layer", "item.weather2.weather_item": "Weather Item", "item.weather2.sand_layer_placeable": "Placeable Sand Layer", "item.weather2.pocket_sand": "Pocket Sand!" } ================================================ FILE: src/main/resources/assets/weather2/models/block/anemometer.json ================================================ { "parent": "forge:item/default", "display": { "gui": { "rotation": [ 30, 225, 0 ], "translation": [ 0, 0, 0], "scale":[ 0.725, 0.725, 0.725 ] } }, "credit": "Made with Blockbench", "textures": { "particle": "block/stone", "0": "weather2:blocks/anemometer" }, "elements": [ { "from": [7.5, 0, 7.5], "to": [8.5, 11, 8.5], "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, "faces": { "north": {"uv": [2, 2, 3, 13], "texture": "#0"}, "east": {"uv": [1, 2, 2, 13], "texture": "#0"}, "south": {"uv": [4, 2, 5, 13], "texture": "#0"}, "west": {"uv": [3, 2, 4, 13], "texture": "#0"}, "up": {"uv": [3, 2, 2, 1], "texture": "#0"}, "down": {"uv": [4, 1, 3, 2], "texture": "#0"} } }, { "from": [7, 10.5, 7], "to": [9, 12.5, 9], "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, "faces": { "north": {"uv": [2, 2, 4, 4], "texture": "#0"}, "east": {"uv": [0, 2, 2, 4], "texture": "#0"}, "south": {"uv": [6, 2, 8, 4], "texture": "#0"}, "west": {"uv": [4, 2, 6, 4], "texture": "#0"}, "up": {"uv": [4, 2, 2, 0], "texture": "#0"}, "down": {"uv": [6, 0, 4, 2], "texture": "#0"} } }, { "from": [7.5, 11, 0], "to": [8.5, 12, 7], "rotation": {"angle": 0, "axis": "y", "origin": [16, 0, 0]}, "faces": { "north": {"uv": [7, 7, 8, 8], "texture": "#0"}, "east": {"uv": [0, 7, 7, 8], "texture": "#0"}, "south": {"uv": [15, 7, 16, 8], "texture": "#0"}, "west": {"uv": [8, 7, 15, 8], "texture": "#0"}, "up": {"uv": [8, 7, 7, 0], "texture": "#0"}, "down": {"uv": [9, 0, 8, 7], "texture": "#0"} } }, { "from": [6.5, 11, -0.5], "to": [7.5, 12, 0.5], "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, "faces": { "north": {"uv": [2, 2, 3, 3], "texture": "#0"}, "east": {"uv": [1, 2, 2, 3], "texture": "#0"}, "south": {"uv": [4, 2, 5, 3], "texture": "#0"}, "west": {"uv": [3, 2, 4, 3], "texture": "#0"}, "up": {"uv": [3, 2, 2, 1], "texture": "#0"}, "down": {"uv": [4, 1, 3, 2], "texture": "#0"} } }, { "from": [6.5, 12, -0.5], "to": [7.5, 13, 2.5], "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, "faces": { "north": {"uv": [8, 6, 9, 7], "texture": "#0"}, "east": {"uv": [5, 6, 8, 7], "texture": "#0"}, "south": {"uv": [12, 6, 13, 7], "texture": "#0"}, "west": {"uv": [9, 6, 12, 7], "texture": "#0"}, "up": {"uv": [9, 6, 8, 3], "texture": "#0"}, "down": {"uv": [10, 3, 9, 6], "texture": "#0"} } }, { "from": [6.5, 11, 1.5], "to": [7.5, 12, 2.5], "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, "faces": { "north": {"uv": [2, 2, 3, 3], "texture": "#0"}, "east": {"uv": [1, 2, 2, 3], "texture": "#0"}, "south": {"uv": [4, 2, 5, 3], "texture": "#0"}, "west": {"uv": [3, 2, 4, 3], "texture": "#0"}, "up": {"uv": [3, 2, 2, 1], "texture": "#0"}, "down": {"uv": [4, 1, 3, 2], "texture": "#0"} } }, { "from": [6.5, 10, -0.5], "to": [7.5, 11, 2.5], "rotation": {"angle": 0, "axis": "y", "origin": [8, -2, 8]}, "faces": { "north": {"uv": [5, 5, 6, 6], "texture": "#0"}, "east": {"uv": [2, 5, 5, 6], "texture": "#0"}, "south": {"uv": [9, 5, 10, 6], "texture": "#0"}, "west": {"uv": [6, 5, 9, 6], "texture": "#0"}, "up": {"uv": [6, 5, 5, 2], "texture": "#0"}, "down": {"uv": [7, 2, 6, 5], "texture": "#0"} } }, { "from": [0, 11, 7.5], "to": [7, 12, 8.5], "rotation": {"angle": 0, "axis": "y", "origin": [8, 11.5, 8]}, "faces": { "north": {"uv": [1, 2, 8, 3], "texture": "#0"}, "east": {"uv": [0, 2, 1, 3], "texture": "#0"}, "south": {"uv": [9, 2, 16, 3], "texture": "#0"}, "west": {"uv": [8, 2, 9, 3], "texture": "#0"}, "up": {"uv": [8, 2, 1, 1], "texture": "#0"}, "down": {"uv": [15, 1, 8, 2], "texture": "#0"} } }, { "from": [-0.5, 11, 8.5], "to": [0.5, 12, 9.5], "rotation": {"angle": 0, "axis": "y", "origin": [8, 11.5, 8]}, "faces": { "north": {"uv": [11, 12, 12, 13], "texture": "#0"}, "east": {"uv": [10, 12, 11, 13], "texture": "#0"}, "south": {"uv": [13, 12, 14, 13], "texture": "#0"}, "west": {"uv": [12, 12, 13, 13], "texture": "#0"}, "up": {"uv": [12, 12, 11, 11], "texture": "#0"}, "down": {"uv": [13, 11, 12, 12], "texture": "#0"} } }, { "from": [-0.5, 12, 8.5], "to": [2.5, 13, 9.5], "rotation": {"angle": 0, "axis": "y", "origin": [8, 11.5, 8]}, "faces": { "north": {"uv": [9, 10, 12, 11], "texture": "#0"}, "east": {"uv": [8, 10, 9, 11], "texture": "#0"}, "south": {"uv": [13, 10, 16, 11], "texture": "#0"}, "west": {"uv": [12, 10, 13, 11], "texture": "#0"}, "up": {"uv": [12, 10, 9, 9], "texture": "#0"}, "down": {"uv": [15, 9, 12, 10], "texture": "#0"} } }, { "from": [1.5, 11, 8.5], "to": [2.5, 12, 9.5], "rotation": {"angle": 0, "axis": "y", "origin": [8, 11.5, 8]}, "faces": { "north": {"uv": [11, 12, 12, 13], "texture": "#0"}, "east": {"uv": [10, 12, 11, 13], "texture": "#0"}, "south": {"uv": [13, 12, 14, 13], "texture": "#0"}, "west": {"uv": [12, 12, 13, 13], "texture": "#0"}, "up": {"uv": [12, 12, 11, 11], "texture": "#0"}, "down": {"uv": [13, 11, 12, 12], "texture": "#0"} } }, { "from": [-0.5, 10, 8.5], "to": [2.5, 11, 9.5], "rotation": {"angle": 0, "axis": "y", "origin": [8, 11.5, 8]}, "faces": { "north": {"uv": [9, 10, 12, 11], "texture": "#0"}, "east": {"uv": [8, 10, 9, 11], "texture": "#0"}, "south": {"uv": [13, 10, 16, 11], "texture": "#0"}, "west": {"uv": [12, 10, 13, 11], "texture": "#0"}, "up": {"uv": [12, 10, 9, 9], "texture": "#0"}, "down": {"uv": [15, 9, 12, 10], "texture": "#0"} } }, { "from": [7.5, 11, 9], "to": [8.5, 12, 16], "rotation": {"angle": 0, "axis": "y", "origin": [8, 11.5, 8]}, "faces": { "north": {"uv": [7, 7, 8, 8], "texture": "#0"}, "east": {"uv": [0, 7, 7, 8], "texture": "#0"}, "south": {"uv": [15, 7, 16, 8], "texture": "#0"}, "west": {"uv": [8, 7, 15, 8], "texture": "#0"}, "up": {"uv": [8, 7, 7, 0], "texture": "#0"}, "down": {"uv": [9, 0, 8, 7], "texture": "#0"} } }, { "from": [8.5, 11, 15.5], "to": [9.5, 12, 16.5], "rotation": {"angle": 0, "axis": "y", "origin": [8, 11.5, 8]}, "faces": { "north": {"uv": [2, 2, 3, 3], "texture": "#0"}, "east": {"uv": [1, 2, 2, 3], "texture": "#0"}, "south": {"uv": [4, 2, 5, 3], "texture": "#0"}, "west": {"uv": [3, 2, 4, 3], "texture": "#0"}, "up": {"uv": [3, 2, 2, 1], "texture": "#0"}, "down": {"uv": [4, 1, 3, 2], "texture": "#0"} } }, { "from": [8.5, 12, 13.5], "to": [9.5, 13, 16.5], "rotation": {"angle": 0, "axis": "y", "origin": [8, 11.5, 8]}, "faces": { "north": {"uv": [7, 7, 8, 8], "texture": "#0"}, "east": {"uv": [4, 7, 7, 8], "texture": "#0"}, "south": {"uv": [11, 7, 12, 8], "texture": "#0"}, "west": {"uv": [8, 7, 11, 8], "texture": "#0"}, "up": {"uv": [8, 7, 7, 4], "texture": "#0"}, "down": {"uv": [9, 4, 8, 7], "texture": "#0"} } }, { "from": [8.5, 11, 13.5], "to": [9.5, 12, 14.5], "rotation": {"angle": 0, "axis": "y", "origin": [8, 11.5, 8]}, "faces": { "north": {"uv": [2, 2, 3, 3], "texture": "#0"}, "east": {"uv": [1, 2, 2, 3], "texture": "#0"}, "south": {"uv": [4, 2, 5, 3], "texture": "#0"}, "west": {"uv": [3, 2, 4, 3], "texture": "#0"}, "up": {"uv": [3, 2, 2, 1], "texture": "#0"}, "down": {"uv": [4, 1, 3, 2], "texture": "#0"} } }, { "from": [8.5, 10, 13.5], "to": [9.5, 11, 16.5], "rotation": {"angle": 0, "axis": "y", "origin": [8, 11.5, 8]}, "faces": { "north": {"uv": [5, 6, 6, 7], "texture": "#0"}, "east": {"uv": [2, 6, 5, 7], "texture": "#0"}, "south": {"uv": [9, 6, 10, 7], "texture": "#0"}, "west": {"uv": [6, 6, 9, 7], "texture": "#0"}, "up": {"uv": [6, 6, 5, 3], "texture": "#0"}, "down": {"uv": [7, 3, 6, 6], "texture": "#0"} } }, { "from": [9, 11, 7.5], "to": [16, 12, 8.5], "rotation": {"angle": 0, "axis": "y", "origin": [8, 11.5, 8]}, "faces": { "north": {"uv": [1, 1, 8, 2], "texture": "#0"}, "east": {"uv": [0, 1, 1, 2], "texture": "#0"}, "south": {"uv": [9, 1, 16, 2], "texture": "#0"}, "west": {"uv": [8, 1, 9, 2], "texture": "#0"}, "up": {"uv": [8, 1, 1, 0], "texture": "#0"}, "down": {"uv": [15, 0, 8, 1], "texture": "#0"} } }, { "from": [15.5, 11, 6.5], "to": [16.5, 12, 7.5], "rotation": {"angle": 0, "axis": "y", "origin": [8, 11.5, 8]}, "faces": { "north": {"uv": [2, 2, 3, 3], "texture": "#0"}, "east": {"uv": [1, 2, 2, 3], "texture": "#0"}, "south": {"uv": [4, 2, 5, 3], "texture": "#0"}, "west": {"uv": [3, 2, 4, 3], "texture": "#0"}, "up": {"uv": [3, 2, 2, 1], "texture": "#0"}, "down": {"uv": [4, 1, 3, 2], "texture": "#0"} } }, { "from": [13.5, 12, 6.5], "to": [16.5, 13, 7.5], "rotation": {"angle": 0, "axis": "y", "origin": [8, 11.5, 8]}, "faces": { "north": {"uv": [4, 4, 7, 5], "texture": "#0"}, "east": {"uv": [3, 4, 4, 5], "texture": "#0"}, "south": {"uv": [8, 4, 11, 5], "texture": "#0"}, "west": {"uv": [7, 4, 8, 5], "texture": "#0"}, "up": {"uv": [7, 4, 4, 3], "texture": "#0"}, "down": {"uv": [10, 3, 7, 4], "texture": "#0"} } }, { "from": [13.5, 11, 6.5], "to": [14.5, 12, 7.5], "rotation": {"angle": 0, "axis": "y", "origin": [8, 11.5, 8]}, "faces": { "north": {"uv": [2, 2, 3, 3], "texture": "#0"}, "east": {"uv": [1, 2, 2, 3], "texture": "#0"}, "south": {"uv": [4, 2, 5, 3], "texture": "#0"}, "west": {"uv": [3, 2, 4, 3], "texture": "#0"}, "up": {"uv": [3, 2, 2, 1], "texture": "#0"}, "down": {"uv": [4, 1, 3, 2], "texture": "#0"} } }, { "from": [13.5, 10, 6.5], "to": [16.5, 11, 7.5], "rotation": {"angle": 0, "axis": "y", "origin": [8, 11.5, 8]}, "faces": { "north": {"uv": [3, 3, 6, 4], "texture": "#0"}, "east": {"uv": [2, 3, 3, 4], "texture": "#0"}, "south": {"uv": [7, 3, 10, 4], "texture": "#0"}, "west": {"uv": [6, 3, 7, 4], "texture": "#0"}, "up": {"uv": [6, 3, 3, 2], "texture": "#0"}, "down": {"uv": [9, 2, 6, 3], "texture": "#0"} } }, { "from": [6.5, 0, 6.5], "to": [9.5, 1, 9.5], "faces": { "north": {"uv": [3, 3, 6, 4], "texture": "#0"}, "east": {"uv": [0, 3, 3, 4], "texture": "#0"}, "south": {"uv": [9, 3, 12, 4], "texture": "#0"}, "west": {"uv": [6, 3, 9, 4], "texture": "#0"}, "up": {"uv": [6, 3, 3, 0], "texture": "#0"}, "down": {"uv": [9, 0, 6, 3], "texture": "#0"} } } ], "groups": [ { "name": "root", "origin": [8, 0, 8], "color": 0, "children": [ { "name": "base", "origin": [8, 0, 8], "color": 0, "children": [ 0, { "name": "top", "origin": [8, 11.5, 8], "color": 0, "children": [ 1, { "name": "arm1", "origin": [8, 11.5, 8], "color": 0, "children": [ 2, { "name": "cup1", "origin": [8, 0, 8], "color": 0, "children": [3, 4, 5, 6] } ] }, { "name": "arm2", "origin": [8, 11.5, 8], "color": 0, "children": [ 7, { "name": "cup2", "origin": [8, 0, 8], "color": 0, "children": [8, 9, 10, 11] } ] }, { "name": "arm3", "origin": [8, 11.5, 8], "color": 0, "children": [ 12, { "name": "cup3", "origin": [8, 0, 8], "color": 0, "children": [13, 14, 15, 16] } ] }, { "name": "arm4", "origin": [8, 11.5, 8], "color": 0, "children": [ 17, { "name": "cup4", "origin": [8, 0, 8], "color": 0, "children": [18, 19, 20, 21] } ] } ] } ] } ] } ] } ================================================ FILE: src/main/resources/assets/weather2/models/block/sand_height10.json ================================================ { "textures": { "particle": "block/sand", "texture": "block/sand" }, "elements": [ { "from": [ 0, 0, 0 ], "to": [ 16, 10, 16 ], "faces": { "down": { "uv": [ 0, 0, 16, 16 ], "texture": "#texture", "cullface": "down" }, "up": { "uv": [ 0, 0, 16, 16 ], "texture": "#texture" }, "north": { "uv": [ 0, 6, 16, 16 ], "texture": "#texture", "cullface": "north" }, "south": { "uv": [ 0, 6, 16, 16 ], "texture": "#texture", "cullface": "south" }, "west": { "uv": [ 0, 6, 16, 16 ], "texture": "#texture", "cullface": "west" }, "east": { "uv": [ 0, 6, 16, 16 ], "texture": "#texture", "cullface": "east" } } } ] } ================================================ FILE: src/main/resources/assets/weather2/models/block/sand_height12.json ================================================ { "textures": { "particle": "block/sand", "texture": "block/sand" }, "elements": [ { "from": [ 0, 0, 0 ], "to": [ 16, 12, 16 ], "faces": { "down": { "uv": [ 0, 0, 16, 16 ], "texture": "#texture", "cullface": "down" }, "up": { "uv": [ 0, 0, 16, 16 ], "texture": "#texture" }, "north": { "uv": [ 0, 4, 16, 16 ], "texture": "#texture", "cullface": "north" }, "south": { "uv": [ 0, 4, 16, 16 ], "texture": "#texture", "cullface": "south" }, "west": { "uv": [ 0, 4, 16, 16 ], "texture": "#texture", "cullface": "west" }, "east": { "uv": [ 0, 4, 16, 16 ], "texture": "#texture", "cullface": "east" } } } ] } ================================================ FILE: src/main/resources/assets/weather2/models/block/sand_height14.json ================================================ { "textures": { "particle": "block/sand", "texture": "block/sand" }, "elements": [ { "from": [ 0, 0, 0 ], "to": [ 16, 14, 16 ], "faces": { "down": { "uv": [ 0, 0, 16, 16 ], "texture": "#texture", "cullface": "down" }, "up": { "uv": [ 0, 0, 16, 16 ], "texture": "#texture" }, "north": { "uv": [ 0, 2, 16, 16 ], "texture": "#texture" }, "south": { "uv": [ 0, 2, 16, 16 ], "texture": "#texture" }, "west": { "uv": [ 0, 2, 16, 16 ], "texture": "#texture" }, "east": { "uv": [ 0, 2, 16, 16 ], "texture": "#texture" } } } ] } ================================================ FILE: src/main/resources/assets/weather2/models/block/sand_height2.json ================================================ { "parent": "block/thin_block", "textures": { "particle": "block/sand", "texture": "block/sand" }, "elements": [ { "from": [ 0, 0, 0 ], "to": [ 16, 2, 16 ], "faces": { "down": { "uv": [ 0, 0, 16, 16 ], "texture": "#texture", "cullface": "down" }, "up": { "uv": [ 0, 0, 16, 16 ], "texture": "#texture" }, "north": { "uv": [ 0, 14, 16, 16 ], "texture": "#texture", "cullface": "north" }, "south": { "uv": [ 0, 14, 16, 16 ], "texture": "#texture", "cullface": "south" }, "west": { "uv": [ 0, 14, 16, 16 ], "texture": "#texture", "cullface": "west" }, "east": { "uv": [ 0, 14, 16, 16 ], "texture": "#texture", "cullface": "east" } } } ] } ================================================ FILE: src/main/resources/assets/weather2/models/block/sand_height4.json ================================================ { "textures": { "particle": "block/sand", "texture": "block/sand" }, "elements": [ { "from": [ 0, 0, 0 ], "to": [ 16, 4, 16 ], "faces": { "down": { "uv": [ 0, 0, 16, 16 ], "texture": "#texture", "cullface": "down" }, "up": { "uv": [ 0, 0, 16, 16 ], "texture": "#texture" }, "north": { "uv": [ 0, 12, 16, 16 ], "texture": "#texture", "cullface": "north" }, "south": { "uv": [ 0, 12, 16, 16 ], "texture": "#texture", "cullface": "south" }, "west": { "uv": [ 0, 12, 16, 16 ], "texture": "#texture", "cullface": "west" }, "east": { "uv": [ 0, 12, 16, 16 ], "texture": "#texture", "cullface": "east" } } } ] } ================================================ FILE: src/main/resources/assets/weather2/models/block/sand_height6.json ================================================ { "textures": { "particle": "block/sand", "texture": "block/sand" }, "elements": [ { "from": [ 0, 0, 0 ], "to": [ 16, 6, 16 ], "faces": { "down": { "uv": [ 0, 0, 16, 16 ], "texture": "#texture", "cullface": "down" }, "up": { "uv": [ 0, 0, 16, 16 ], "texture": "#texture" }, "north": { "uv": [ 0, 10, 16, 16 ], "texture": "#texture", "cullface": "north" }, "south": { "uv": [ 0, 10, 16, 16 ], "texture": "#texture", "cullface": "south" }, "west": { "uv": [ 0, 10, 16, 16 ], "texture": "#texture", "cullface": "west" }, "east": { "uv": [ 0, 10, 16, 16 ], "texture": "#texture", "cullface": "east" } } } ] } ================================================ FILE: src/main/resources/assets/weather2/models/block/sand_height8.json ================================================ { "textures": { "particle": "block/sand", "texture": "block/sand" }, "elements": [ { "from": [ 0, 0, 0 ], "to": [ 16, 8, 16 ], "faces": { "down": { "uv": [ 0, 0, 16, 16 ], "texture": "#texture", "cullface": "down" }, "up": { "uv": [ 0, 0, 16, 16 ], "texture": "#texture" }, "north": { "uv": [ 0, 8, 16, 16 ], "texture": "#texture", "cullface": "north" }, "south": { "uv": [ 0, 8, 16, 16 ], "texture": "#texture", "cullface": "south" }, "west": { "uv": [ 0, 8, 16, 16 ], "texture": "#texture", "cullface": "west" }, "east": { "uv": [ 0, 8, 16, 16 ], "texture": "#texture", "cullface": "east" } } } ] } ================================================ FILE: src/main/resources/assets/weather2/models/block/tornado_sensor.json ================================================ { "parent": "minecraft:block/cube_all", "textures": { "all": "weather2:blocks/tornado_sensor" } } ================================================ FILE: src/main/resources/assets/weather2/models/block/tornado_siren.json ================================================ { "parent": "minecraft:block/cube_all", "textures": { "all": "weather2:blocks/tornado_siren" } } ================================================ FILE: src/main/resources/assets/weather2/models/block/tornado_siren_manual.json ================================================ { "parent": "minecraft:block/cube_all", "textures": { "all": "weather2:blocks/tornado_siren_manual" } } ================================================ FILE: src/main/resources/assets/weather2/models/block/weather_deflector.json ================================================ { "parent": "minecraft:block/cube_all", "textures": { "all": "weather2:blocks/weather_deflector" } } ================================================ FILE: src/main/resources/assets/weather2/models/block/weather_forecast.json ================================================ { "parent": "minecraft:block/cube_all", "textures": { "all": "weather2:blocks/weather_forecast" } } ================================================ FILE: src/main/resources/assets/weather2/models/block/weather_machine.json ================================================ { "parent": "minecraft:block/cube_all", "textures": { "all": "weather2:blocks/weather_machine" } } ================================================ FILE: src/main/resources/assets/weather2/models/block/wind_turbine.json ================================================ { "parent": "forge:item/default", "display": { "gui": { "rotation": [ 30, 225, 0 ], "translation": [ 0, -3, 0], "scale":[ 0.425, 0.425, 0.425 ] } }, "credit": "Made with Blockbench", "texture_size": [128, 128], "textures": { "particle": "block/stone", "2": "weather2:blocks/wind_turbine" }, "elements": [ { "from": [2, 0, 2], "to": [14, 2, 14], "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, "faces": { "north": {"uv": [1.5, 1.5, 3, 1.75], "texture": "#2"}, "east": {"uv": [0, 1.5, 1.5, 1.75], "texture": "#2"}, "south": {"uv": [4.5, 1.5, 6, 1.75], "texture": "#2"}, "west": {"uv": [3, 1.5, 4.5, 1.75], "texture": "#2"}, "up": {"uv": [3, 1.5, 1.5, 0], "texture": "#2"}, "down": {"uv": [4.5, 0, 3, 1.5], "texture": "#2"} } }, { "from": [5, 2, 5], "to": [11, 4, 11], "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, "faces": { "north": {"uv": [5.25, 1, 6, 1.25], "texture": "#2"}, "east": {"uv": [4.5, 1, 5.25, 1.25], "texture": "#2"}, "south": {"uv": [6.75, 1, 7.5, 1.25], "texture": "#2"}, "west": {"uv": [6, 1, 6.75, 1.25], "texture": "#2"}, "up": {"uv": [6, 1, 5.25, 0.25], "texture": "#2"}, "down": {"uv": [6.75, 0.25, 6, 1], "texture": "#2"} } }, { "from": [7, 2, 7], "to": [9, 31, 9], "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, "faces": { "north": {"uv": [0.25, 4.625, 0.5, 8.25], "texture": "#2"}, "east": {"uv": [0, 4.625, 0.25, 8.25], "texture": "#2"}, "south": {"uv": [0.75, 4.625, 1, 8.25], "texture": "#2"}, "west": {"uv": [0.5, 4.625, 0.75, 8.25], "texture": "#2"}, "up": {"uv": [0.5, 4.625, 0.25, 4.375], "texture": "#2"}, "down": {"uv": [0.75, 4.375, 0.5, 4.625], "texture": "#2"} } }, { "from": [6, 28, 6], "to": [10, 30, 10], "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, "faces": { "north": {"uv": [0.5, 3, 1, 3.25], "texture": "#2"}, "east": {"uv": [0, 3, 0.5, 3.25], "texture": "#2"}, "south": {"uv": [1.5, 3, 2, 3.25], "texture": "#2"}, "west": {"uv": [1, 3, 1.5, 3.25], "texture": "#2"}, "up": {"uv": [1, 3, 0.5, 2.5], "texture": "#2"}, "down": {"uv": [1.5, 2.5, 1, 3], "texture": "#2"} } }, { "from": [-1, 28.5, 7.5], "to": [17, 29.5, 8.5], "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, "faces": { "north": {"uv": [0.125, 4.25, 2.375, 4.375], "texture": "#2"}, "east": {"uv": [0, 4.25, 0.125, 4.375], "texture": "#2"}, "south": {"uv": [2.5, 4.25, 4.75, 4.375], "texture": "#2"}, "west": {"uv": [2.375, 4.25, 2.5, 4.375], "texture": "#2"}, "up": {"uv": [2.375, 4.25, 0.125, 4.125], "texture": "#2"}, "down": {"uv": [4.625, 4.125, 2.375, 4.25], "texture": "#2"} } }, { "name": "upper", "from": [7.5, 28.5, -1], "to": [8.5, 29.5, 17], "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, "faces": { "north": {"uv": [2.25, 4, 2.375, 4.125], "texture": "#2"}, "east": {"uv": [0, 4, 2.25, 4.125], "texture": "#2"}, "south": {"uv": [4.625, 4, 4.75, 4.125], "texture": "#2"}, "west": {"uv": [2.375, 4, 4.625, 4.125], "texture": "#2"}, "up": {"uv": [2.375, 4, 2.25, 1.75], "texture": "#2"}, "down": {"uv": [2.5, 1.75, 2.375, 4], "texture": "#2"} } }, { "name": "upper", "from": [7.5, 12.5, 0], "to": [8.5, 13.5, 16], "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, "faces": { "north": {"uv": [2.25, 4, 2.375, 4.125], "texture": "#2"}, "east": {"uv": [0.25, 4, 2.25, 4.125], "texture": "#2"}, "south": {"uv": [4.375, 4, 4.5, 4.125], "texture": "#2"}, "west": {"uv": [2.375, 4, 4.375, 4.125], "texture": "#2"}, "up": {"uv": [2.375, 4, 2.25, 2], "texture": "#2"}, "down": {"uv": [2.5, 2, 2.375, 4], "texture": "#2"} } }, { "name": "lower", "from": [0, 12.5, 7.5], "to": [16, 13.5, 8.5], "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, "faces": { "north": {"uv": [4.625, 0.125, 6.625, 0.25], "texture": "#2"}, "east": {"uv": [4.5, 0.125, 4.625, 0.25], "texture": "#2"}, "south": {"uv": [6.75, 0.125, 8.75, 0.25], "texture": "#2"}, "west": {"uv": [6.625, 0.125, 6.75, 0.25], "texture": "#2"}, "up": {"uv": [6.625, 0.125, 4.625, 0], "texture": "#2"}, "down": {"uv": [8.625, 0, 6.625, 0.125], "texture": "#2"} } }, { "name": "fin", "from": [-2, 10, 6], "to": [0, 32, 10], "rotation": {"angle": 0, "axis": "x", "origin": [0, 13, 8]}, "faces": { "north": {"uv": [4.75, 4.375, 5, 7.125], "texture": "#2"}, "east": {"uv": [4.25, 4.375, 4.75, 7.125], "texture": "#2"}, "south": {"uv": [5.5, 4.375, 5.75, 7.125], "texture": "#2"}, "west": {"uv": [5, 4.375, 5.5, 7.125], "texture": "#2"}, "up": {"uv": [5, 4.375, 4.75, 3.875], "texture": "#2"}, "down": {"uv": [5.25, 3.875, 5, 4.375], "texture": "#2"} } }, { "name": "fin", "from": [16, 10, 6], "to": [18, 32, 10], "rotation": {"angle": 0, "axis": "x", "origin": [16, 13, 8]}, "faces": { "north": {"uv": [4.75, 4.375, 5, 7.125], "texture": "#2"}, "east": {"uv": [4.25, 4.375, 4.75, 7.125], "texture": "#2"}, "south": {"uv": [5.5, 4.375, 5.75, 7.125], "texture": "#2"}, "west": {"uv": [5, 4.375, 5.5, 7.125], "texture": "#2"}, "up": {"uv": [5, 4.375, 4.75, 3.875], "texture": "#2"}, "down": {"uv": [5.25, 3.875, 5, 4.375], "texture": "#2"} } }, { "name": "fin", "from": [6, 10, 16], "to": [10, 32, 18], "rotation": {"angle": 0, "axis": "x", "origin": [8, 13, 16]}, "faces": { "north": {"uv": [1.25, 4.625, 1.75, 7.375], "texture": "#2"}, "east": {"uv": [1, 4.625, 1.25, 7.375], "texture": "#2"}, "south": {"uv": [2, 4.625, 2.5, 7.375], "texture": "#2"}, "west": {"uv": [1.75, 4.625, 2, 7.375], "texture": "#2"}, "up": {"uv": [1.75, 4.625, 1.25, 4.375], "texture": "#2"}, "down": {"uv": [2.25, 4.375, 1.75, 4.625], "texture": "#2"} } }, { "name": "fin", "from": [6, 10, -2], "to": [10, 32, 0], "rotation": {"angle": 0, "axis": "x", "origin": [8, 13, 0]}, "faces": { "north": {"uv": [1.25, 4.625, 1.75, 7.375], "texture": "#2"}, "east": {"uv": [1, 4.625, 1.25, 7.375], "texture": "#2"}, "south": {"uv": [2, 4.625, 2.5, 7.375], "texture": "#2"}, "west": {"uv": [1.75, 4.625, 2, 7.375], "texture": "#2"}, "up": {"uv": [1.75, 4.625, 1.25, 4.375], "texture": "#2"}, "down": {"uv": [2.25, 4.375, 1.75, 4.625], "texture": "#2"} } }, { "name": "upper", "from": [7.5, 12.5, 0], "to": [8.5, 13.5, 16], "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, "faces": { "north": {"uv": [2.25, 4, 2.375, 4.125], "texture": "#2"}, "east": {"uv": [0.25, 4, 2.25, 4.125], "texture": "#2"}, "south": {"uv": [4.375, 4, 4.5, 4.125], "texture": "#2"}, "west": {"uv": [2.375, 4, 4.375, 4.125], "texture": "#2"}, "up": {"uv": [2.375, 4, 2.25, 2], "texture": "#2"}, "down": {"uv": [2.5, 2, 2.375, 4], "texture": "#2"} } }, { "from": [6, 12, 6], "to": [10, 14, 10], "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, "faces": { "north": {"uv": [0.5, 2.25, 1, 2.5], "texture": "#2"}, "east": {"uv": [0, 2.25, 0.5, 2.5], "texture": "#2"}, "south": {"uv": [1.5, 2.25, 2, 2.5], "texture": "#2"}, "west": {"uv": [1, 2.25, 1.5, 2.5], "texture": "#2"}, "up": {"uv": [1, 2.25, 0.5, 1.75], "texture": "#2"}, "down": {"uv": [1.5, 1.75, 1, 2.25], "texture": "#2"} } } ], "groups": [ { "name": "root", "origin": [8, 0, 8], "color": 0, "children": [ 0, 1, { "name": "shaft", "origin": [8, 0, 8], "color": 0, "children": [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] } ] } ] } ================================================ FILE: src/main/resources/assets/weather2/models/block/wind_vane.json ================================================ { "parent": "forge:item/default", "display": { "gui": { "rotation": [ 30, 225, 0 ], "translation": [ 0, 0, 0], "scale":[ 1, 1, 1 ] } }, "credit": "Made with Blockbench", "texture_size": [32, 32], "textures": { "particle": "block/stone", "0": "weather2:blocks/wind_vane" }, "elements": [ { "from": [7.75, 0, 7.75], "to": [8.25, 7, 8.25], "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, "faces": { "north": {"uv": [4.5, 3, 4.5, 6.5], "texture": "#0"}, "east": {"uv": [4.5, 3, 4.5, 6.5], "texture": "#0"}, "south": {"uv": [4.5, 3, 4.5, 6.5], "texture": "#0"}, "west": {"uv": [4.5, 3, 4.5, 6.5], "texture": "#0"}, "up": {"uv": [4.5, 3, 4.5, 3], "texture": "#0"}, "down": {"uv": [4.5, 3, 4.5, 3], "texture": "#0"} } }, { "from": [7.25, 0, 7.25], "to": [8.75, 0.5, 8.75], "rotation": {"angle": 0, "axis": "y", "origin": [8, 7.5, 8]}, "faces": { "north": {"uv": [0.5, 0.5, 1, 0.5], "texture": "#0"}, "east": {"uv": [0, 0.5, 0.5, 0.5], "texture": "#0"}, "south": {"uv": [1.5, 0.5, 2, 0.5], "texture": "#0"}, "west": {"uv": [1, 0.5, 1.5, 0.5], "texture": "#0"}, "up": {"uv": [1, 0.5, 0.5, 0], "texture": "#0"}, "down": {"uv": [1.5, 0, 1, 0.5], "texture": "#0"} } }, { "from": [7.75, 8, 7.75], "to": [8.25, 11, 8.25], "rotation": {"angle": 0, "axis": "y", "origin": [8, 7.5, 8]}, "faces": { "north": {"uv": [8.5, 7, 8.5, 8.5], "texture": "#0"}, "east": {"uv": [8.5, 7, 8.5, 8.5], "texture": "#0"}, "south": {"uv": [8.5, 7, 8.5, 8.5], "texture": "#0"}, "west": {"uv": [8.5, 7, 8.5, 8.5], "texture": "#0"}, "up": {"uv": [8.5, 7, 8.5, 7], "texture": "#0"}, "down": {"uv": [8.5, 7, 8.5, 7], "texture": "#0"} } }, { "from": [7.25, 6.75, 7.25], "to": [8.75, 8.25, 8.75], "rotation": {"angle": 0, "axis": "y", "origin": [8, 7.5, 8]}, "faces": { "north": {"uv": [0.5, 0.5, 1, 1], "texture": "#0"}, "east": {"uv": [0, 0.5, 0.5, 1], "texture": "#0"}, "south": {"uv": [1.5, 0.5, 2, 1], "texture": "#0"}, "west": {"uv": [1, 0.5, 1.5, 1], "texture": "#0"}, "up": {"uv": [1, 0.5, 0.5, 0], "texture": "#0"}, "down": {"uv": [1.5, 0, 1, 0.5], "texture": "#0"} } }, { "from": [7.25, 6.75, 7.25], "to": [8.75, 8.25, 8.75], "rotation": {"angle": 45, "axis": "x", "origin": [8, 7.5, 8]}, "faces": { "north": {"uv": [0.5, 0.5, 1, 1], "texture": "#0"}, "east": {"uv": [0, 0.5, 0.5, 1], "texture": "#0"}, "south": {"uv": [1.5, 0.5, 2, 1], "texture": "#0"}, "west": {"uv": [1, 0.5, 1.5, 1], "texture": "#0"}, "up": {"uv": [1, 0.5, 0.5, 0], "texture": "#0"}, "down": {"uv": [1.5, 0, 1, 0.5], "texture": "#0"} } }, { "from": [7.25, 6.75, 7.25], "to": [8.75, 8.25, 8.75], "rotation": {"angle": 45, "axis": "y", "origin": [8, 7.5, 8]}, "faces": { "north": {"uv": [0.5, 0.5, 1, 1], "texture": "#0"}, "east": {"uv": [0, 0.5, 0.5, 1], "texture": "#0"}, "south": {"uv": [1.5, 0.5, 2, 1], "texture": "#0"}, "west": {"uv": [1, 0.5, 1.5, 1], "texture": "#0"}, "up": {"uv": [1, 0.5, 0.5, 0], "texture": "#0"}, "down": {"uv": [1.5, 0, 1, 0.5], "texture": "#0"} } }, { "from": [7.25, 6.75, 7.25], "to": [8.75, 8.25, 8.75], "rotation": {"angle": 45, "axis": "z", "origin": [8, 7.5, 8]}, "faces": { "north": {"uv": [0.5, 0.5, 1, 1], "texture": "#0"}, "east": {"uv": [0, 0.5, 0.5, 1], "texture": "#0"}, "south": {"uv": [1.5, 0.5, 2, 1], "texture": "#0"}, "west": {"uv": [1, 0.5, 1.5, 1], "texture": "#0"}, "up": {"uv": [1, 0.5, 0.5, 0], "texture": "#0"}, "down": {"uv": [1.5, 0, 1, 0.5], "texture": "#0"} } }, { "from": [7.75, 7.25, 4], "to": [8.25, 7.75, 8], "rotation": {"angle": 0, "axis": "y", "origin": [16, -4, 0]}, "faces": { "north": {"uv": [4, 8.5, 4, 8.5], "texture": "#0"}, "east": {"uv": [2, 8.5, 4, 8.5], "texture": "#0"}, "south": {"uv": [6, 8.5, 6, 8.5], "texture": "#0"}, "west": {"uv": [4, 8.5, 6, 8.5], "texture": "#0"}, "up": {"uv": [4, 8.5, 4, 6.5], "texture": "#0"}, "down": {"uv": [4, 6.5, 4, 8.5], "texture": "#0"} } }, { "from": [8, 6.5, 2], "to": [8.05, 8.5, 4], "rotation": {"angle": 0, "axis": "y", "origin": [8, 7.5, 8]}, "faces": { "north": {"uv": [1, 15, 1, 16], "texture": "#0"}, "east": {"uv": [0, 15, 1, 16], "texture": "#0"}, "south": {"uv": [2, 15, 2, 16], "texture": "#0"}, "west": {"uv": [1, 15, 2, 16], "texture": "#0"}, "up": {"uv": [1, 15, 1, 14], "texture": "#0"}, "down": {"uv": [1, 14, 1, 15], "texture": "#0"} } }, { "from": [7.25, 10.75, 7.25], "to": [8.75, 12.25, 8.75], "rotation": {"angle": 45, "axis": "z", "origin": [8, 11.5, 8]}, "faces": { "north": {"uv": [0.5, 0.5, 1, 1], "texture": "#0"}, "east": {"uv": [0, 0.5, 0.5, 1], "texture": "#0"}, "south": {"uv": [1.5, 0.5, 2, 1], "texture": "#0"}, "west": {"uv": [1, 0.5, 1.5, 1], "texture": "#0"}, "up": {"uv": [1, 0.5, 0.5, 0], "texture": "#0"}, "down": {"uv": [1.5, 0, 1, 0.5], "texture": "#0"} } }, { "from": [7.25, 10.75, 7.25], "to": [8.75, 12.25, 8.75], "rotation": {"angle": 0, "axis": "y", "origin": [8, 11.5, 8]}, "faces": { "north": {"uv": [0.5, 0.5, 1, 1], "texture": "#0"}, "east": {"uv": [0, 0.5, 0.5, 1], "texture": "#0"}, "south": {"uv": [1.5, 0.5, 2, 1], "texture": "#0"}, "west": {"uv": [1, 0.5, 1.5, 1], "texture": "#0"}, "up": {"uv": [1, 0.5, 0.5, 0], "texture": "#0"}, "down": {"uv": [1.5, 0, 1, 0.5], "texture": "#0"} } }, { "from": [7.25, 10.75, 7.25], "to": [8.75, 12.25, 8.75], "rotation": {"angle": 45, "axis": "x", "origin": [8, 11.5, 8]}, "faces": { "north": {"uv": [0.5, 0.5, 1, 1], "texture": "#0"}, "east": {"uv": [0, 0.5, 0.5, 1], "texture": "#0"}, "south": {"uv": [1.5, 0.5, 2, 1], "texture": "#0"}, "west": {"uv": [1, 0.5, 1.5, 1], "texture": "#0"}, "up": {"uv": [1, 0.5, 0.5, 0], "texture": "#0"}, "down": {"uv": [1.5, 0, 1, 0.5], "texture": "#0"} } }, { "from": [7.25, 10.75, 7.25], "to": [8.75, 12.25, 8.75], "rotation": {"angle": 45, "axis": "y", "origin": [8, 11.5, 8]}, "faces": { "north": {"uv": [0.5, 0.5, 1, 1], "texture": "#0"}, "east": {"uv": [0, 0.5, 0.5, 1], "texture": "#0"}, "south": {"uv": [1.5, 0.5, 2, 1], "texture": "#0"}, "west": {"uv": [1, 0.5, 1.5, 1], "texture": "#0"}, "up": {"uv": [1, 0.5, 0.5, 0], "texture": "#0"}, "down": {"uv": [1.5, 0, 1, 0.5], "texture": "#0"} } }, { "from": [7.75, 11.25, 4], "to": [8.25, 11.75, 8], "rotation": {"angle": 0, "axis": "y", "origin": [8, 11.5, 8]}, "faces": { "north": {"uv": [8, 2, 8, 2], "texture": "#0"}, "east": {"uv": [6, 2, 8, 2], "texture": "#0"}, "south": {"uv": [10, 2, 10, 2], "texture": "#0"}, "west": {"uv": [8, 2, 10, 2], "texture": "#0"}, "up": {"uv": [8, 2, 8, 0], "texture": "#0"}, "down": {"uv": [8, 0, 8, 2], "texture": "#0"} } }, { "from": [8, 10.5, 2], "to": [8.05, 12.5, 4], "rotation": {"angle": 0, "axis": "y", "origin": [8, 11.5, 8]}, "faces": { "north": {"uv": [11, 15, 11, 16], "texture": "#0"}, "east": {"uv": [10, 15, 11, 16], "texture": "#0"}, "south": {"uv": [12, 15, 12, 16], "texture": "#0"}, "west": {"uv": [11, 15, 12, 16], "texture": "#0"}, "up": {"uv": [11, 15, 11, 14], "texture": "#0"}, "down": {"uv": [11, 14, 11, 15], "texture": "#0"} } }, { "from": [7.75, 11.25, 8], "to": [8.25, 11.75, 12], "rotation": {"angle": 0, "axis": "y", "origin": [8, 11.5, 8]}, "faces": { "north": {"uv": [4, 6.5, 4, 6.5], "texture": "#0"}, "east": {"uv": [2, 6.5, 4, 6.5], "texture": "#0"}, "south": {"uv": [6, 6.5, 6, 6.5], "texture": "#0"}, "west": {"uv": [4, 6.5, 6, 6.5], "texture": "#0"}, "up": {"uv": [4, 6.5, 4, 4.5], "texture": "#0"}, "down": {"uv": [4, 4.5, 4, 6.5], "texture": "#0"} } }, { "from": [8, 10.5, 12], "to": [8.05, 12.5, 14], "rotation": {"angle": 0, "axis": "y", "origin": [8, 11.5, 13]}, "faces": { "north": {"uv": [13.5, 15, 13.5, 16], "texture": "#0"}, "east": {"uv": [14.5, 15, 13.5, 16], "texture": "#0"}, "south": {"uv": [14.5, 15, 14.5, 16], "texture": "#0"}, "west": {"uv": [13.5, 15, 12.5, 16], "texture": "#0"}, "up": {"uv": [13.5, 15, 13.5, 14], "texture": "#0"}, "down": {"uv": [13.5, 14, 13.5, 15], "texture": "#0"} } }, { "from": [8, 12.5, 5.5], "to": [8.05, 16.5, 9.5], "rotation": {"angle": 0, "axis": "y", "origin": [8, 13.5, 12]}, "faces": { "north": {"uv": [13.5, 2, 13.5, 4], "texture": "#0"}, "east": {"uv": [11.5, 2, 13.5, 4], "texture": "#0"}, "south": {"uv": [15.5, 2, 15.5, 4], "texture": "#0"}, "west": {"uv": [13.5, 2, 15.5, 4], "texture": "#0"}, "up": {"uv": [13.5, 2, 13.5, 0], "texture": "#0"}, "down": {"uv": [13.5, 0, 13.5, 2], "texture": "#0"} } }, { "from": [8, 7.25, 7.75], "to": [12, 7.75, 8.25], "rotation": {"angle": 0, "axis": "y", "origin": [8, 7, 8]}, "faces": { "north": {"uv": [1, 5.5, 3, 5.5], "texture": "#0"}, "east": {"uv": [1, 5.5, 1, 5.5], "texture": "#0"}, "south": {"uv": [3, 5.5, 5, 5.5], "texture": "#0"}, "west": {"uv": [3, 5.5, 3, 5.5], "texture": "#0"}, "up": {"uv": [3, 5.5, 1, 5.5], "texture": "#0"}, "down": {"uv": [5, 5.5, 3, 5.5], "texture": "#0"} } }, { "from": [12, 6.5, 8], "to": [14, 8.5, 8.05], "rotation": {"angle": 0, "axis": "y", "origin": [13, 7.5, 8]}, "faces": { "north": {"uv": [3.5, 15, 2.5, 16], "texture": "#0"}, "east": {"uv": [3.5, 15, 3.5, 16], "texture": "#0"}, "south": {"uv": [4.5, 15, 3.5, 16], "texture": "#0"}, "west": {"uv": [2.5, 15, 2.5, 16], "texture": "#0"}, "up": {"uv": [2.5, 15, 3.5, 15], "texture": "#0"}, "down": {"uv": [3.5, 15, 4.5, 15], "texture": "#0"} } }, { "from": [7.75, 7.25, 8], "to": [8.25, 7.75, 12], "rotation": {"angle": 0, "axis": "y", "origin": [8, 7, 8]}, "faces": { "north": {"uv": [6.5, 7, 6.5, 7], "texture": "#0"}, "east": {"uv": [4.5, 7, 6.5, 7], "texture": "#0"}, "south": {"uv": [8.5, 7, 8.5, 7], "texture": "#0"}, "west": {"uv": [6.5, 7, 8.5, 7], "texture": "#0"}, "up": {"uv": [6.5, 7, 6.5, 5], "texture": "#0"}, "down": {"uv": [6.5, 5, 6.5, 7], "texture": "#0"} } }, { "from": [8, 6.5, 12], "to": [8.05, 8.5, 14], "rotation": {"angle": 0, "axis": "y", "origin": [8, 7.5, 13]}, "faces": { "north": {"uv": [6, 15, 6, 16], "texture": "#0"}, "east": {"uv": [7, 15, 6, 16], "texture": "#0"}, "south": {"uv": [7, 15, 7, 16], "texture": "#0"}, "west": {"uv": [6, 15, 5, 16], "texture": "#0"}, "up": {"uv": [6, 15, 6, 14], "texture": "#0"}, "down": {"uv": [6, 14, 6, 15], "texture": "#0"} } }, { "from": [4, 7.25, 7.75], "to": [8, 7.75, 8.25], "rotation": {"angle": 0, "axis": "y", "origin": [8, 7, 8]}, "faces": { "north": {"uv": [5, 3, 7, 3], "texture": "#0"}, "east": {"uv": [5, 3, 5, 3], "texture": "#0"}, "south": {"uv": [7, 3, 9, 3], "texture": "#0"}, "west": {"uv": [7, 3, 7, 3], "texture": "#0"}, "up": {"uv": [7, 3, 5, 3], "texture": "#0"}, "down": {"uv": [9, 3, 7, 3], "texture": "#0"} } }, { "from": [2, 6.5, 7.95], "to": [4, 8.5, 8], "rotation": {"angle": 0, "axis": "y", "origin": [8, 7, 8]}, "faces": { "north": {"uv": [7.5, 15, 8.5, 16], "texture": "#0"}, "east": {"uv": [7.5, 15, 7.5, 16], "texture": "#0"}, "south": {"uv": [8.5, 15, 9.5, 16], "texture": "#0"}, "west": {"uv": [8.5, 15, 8.5, 16], "texture": "#0"}, "up": {"uv": [8.5, 15, 7.5, 15], "texture": "#0"}, "down": {"uv": [9.5, 15, 8.5, 15], "texture": "#0"} } } ], "groups": [ { "name": "root", "origin": [8, 0, 8], "color": 0, "children": [ { "name": "base", "origin": [8, 0, 8], "color": 0, "children": [ 0, 1, { "name": "middle", "origin": [8, 11.5, 8], "color": 0, "children": [ 2, 3, 4, 5, 6, { "name": "arm1", "origin": [8, 7.5, 8], "color": 0, "children": [7, 8] }, { "name": "top", "origin": [8, 11.5, 8], "color": 0, "children": [ 9, 10, 11, 12, { "name": "toparm1", "origin": [8, 11.5, 8], "color": 0, "children": [13, 14] }, { "name": "toparm2", "origin": [8, 11.5, 8], "color": 0, "children": [15, 16] }, 17 ] }, { "name": "arm2", "origin": [8, 7.5, 8], "color": 0, "children": [18, 19] }, { "name": "arm3", "origin": [8, 7.5, 8], "color": 0, "children": [20, 21] }, { "name": "arm4", "origin": [8, 7.5, 8], "color": 0, "children": [22, 23] } ] } ] } ] } ] } ================================================ FILE: src/main/resources/assets/weather2/models/item/anemometer.json ================================================ { "parent": "weather2:block/anemometer", "display": { "thirdperson": { "rotation": [ 10, -45, 170 ], "translation": [ 0, 1.5, -2.75 ], "scale": [ 0.375, 0.375, 0.375 ] } } } ================================================ FILE: src/main/resources/assets/weather2/models/item/pocket_sand.json ================================================ { "parent": "item/generated", "textures": { "layer0": "weather2:items/pocket_sand" } } ================================================ FILE: src/main/resources/assets/weather2/models/item/sand_layer.json ================================================ { "parent": "item/generated", "textures": { "layer0": "weather2:items/sand_layer" } } ================================================ FILE: src/main/resources/assets/weather2/models/item/sand_layer_placeable.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "weather2:items/sand_layer_placeable" } } ================================================ FILE: src/main/resources/assets/weather2/models/item/tornado_sensor.json ================================================ { "parent": "weather2:block/tornado_sensor", "display": { "thirdperson": { "rotation": [ 10, -45, 170 ], "translation": [ 0, 1.5, -2.75 ], "scale": [ 0.375, 0.375, 0.375 ] } } } ================================================ FILE: src/main/resources/assets/weather2/models/item/tornado_siren.json ================================================ { "parent": "weather2:block/tornado_siren", "display": { "thirdperson": { "rotation": [ 10, -45, 170 ], "translation": [ 0, 1.5, -2.75 ], "scale": [ 0.375, 0.375, 0.375 ] } } } ================================================ FILE: src/main/resources/assets/weather2/models/item/tornado_siren_manual.json ================================================ { "parent": "weather2:block/tornado_siren_manual", "display": { "thirdperson": { "rotation": [ 10, -45, 170 ], "translation": [ 0, 1.5, -2.75 ], "scale": [ 0.375, 0.375, 0.375 ] } } } ================================================ FILE: src/main/resources/assets/weather2/models/item/weather_deflector.json ================================================ { "parent": "weather2:block/weather_deflector", "display": { "thirdperson": { "rotation": [ 10, -45, 170 ], "translation": [ 0, 1.5, -2.75 ], "scale": [ 0.375, 0.375, 0.375 ] } } } ================================================ FILE: src/main/resources/assets/weather2/models/item/weather_forecast.json ================================================ { "parent": "weather2:block/weather_forecast", "display": { "thirdperson": { "rotation": [ 10, -45, 170 ], "translation": [ 0, 1.5, -2.75 ], "scale": [ 0.375, 0.375, 0.375 ] } } } ================================================ FILE: src/main/resources/assets/weather2/models/item/weather_item.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "weather2:items/weather_item" } } ================================================ FILE: src/main/resources/assets/weather2/models/item/weather_machine.json ================================================ { "parent": "weather2:block/weather_machine", "display": { "thirdperson": { "rotation": [ 10, -45, 170 ], "translation": [ 0, 1.5, -2.75 ], "scale": [ 0.375, 0.375, 0.375 ] } } } ================================================ FILE: src/main/resources/assets/weather2/models/item/wind_turbine.json ================================================ { "parent": "weather2:block/wind_turbine", "display": { "thirdperson": { "rotation": [ 10, -45, 170 ], "translation": [ 0, 1.5, -2.75 ], "scale": [ 0.375, 0.375, 0.375 ] } } } ================================================ FILE: src/main/resources/assets/weather2/models/item/wind_vane.json ================================================ { "parent": "weather2:block/wind_vane", "display": { "thirdperson": { "rotation": [ 10, -45, 170 ], "translation": [ 0, 1.5, -2.75 ], "scale": [ 0.375, 0.375, 0.375 ] } } } ================================================ FILE: src/main/resources/assets/weather2/particles/acidrain_splash.json ================================================ { "textures": [ "weather2:splash_0", "weather2:splash_1", "weather2:splash_2", "weather2:splash_3" ] } ================================================ FILE: src/main/resources/assets/weather2/shaders/core/rendertype_clouds.fsh ================================================ #version 150 #moj_import #moj_import uniform sampler2D Sampler0; uniform vec4 ColorModulator; uniform float FogStart; uniform float FogEnd; uniform vec4 FogColor; in vec2 texCoord0; in float vertexDistance; in vec4 vertexColor; in vec4 normal; out vec4 fragColor; void main() { vec4 color = texture(Sampler0, texCoord0) * vertexColor * ColorModulator; //vec4 color = vertexColor; if (color.a < 0.1) { discard; } //if (color.a == normal.x) { //discard; //} fragColor = linear_fog(color, vertexDistance, FogStart, FogEnd, FogColor); //fragColor = linear_fog(color, vertexDistance, 200, 1200, FogColor); //fragColor = linear_fog(color, vertexDistance, 0, 150, FogColor); //fragColor = color; } ================================================ FILE: src/main/resources/assets/weather2/shaders/core/rendertype_clouds.json ================================================ { "blend": { "func": "add", "srcrgb": "srcalpha", "dstrgb": "1-srcalpha" }, "vertex": "weather2:rendertype_clouds", "fragment": "weather2:rendertype_clouds", "attributes": [ "Position", "UV0", "Normal", "ModelMatrix", "Brightness", "Color" ], "samplers": [ { "name": "Sampler0" } ], "uniforms": [ { "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, { "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, { "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] }, { "name": "Light0_Direction", "type": "float", "count": 3, "values": [0.0, 0.0, 0.0] }, { "name": "Light1_Direction", "type": "float", "count": 3, "values": [0.0, 0.0, 0.0] }, { "name": "FogStart", "type": "float", "count": 1, "values": [ 0.0 ] }, { "name": "FogEnd", "type": "float", "count": 1, "values": [ 1.0 ] }, { "name": "FogColor", "type": "float", "count": 4, "values": [ 0.0, 0.0, 0.0, 0.0 ] }, { "name": "CustomTime", "type": "float", "count": 1, "values": [ 0.0 ] } ] } ================================================ FILE: src/main/resources/assets/weather2/shaders/core/rendertype_clouds.vsh ================================================ #version 150 #moj_import in vec3 Position; in vec2 UV0; in vec3 Normal; in mat4 ModelMatrix; in float Brightness; in vec4 Color; uniform mat4 ModelViewMat; uniform mat4 ProjMat; out vec2 texCoord0; out float vertexDistance; out vec4 vertexColor; out vec4 normal; void main() { gl_Position = ProjMat * ModelViewMat * ModelMatrix * vec4(Position * Brightness, 1.0); texCoord0 = UV0; vertexDistance = length((ModelViewMat * ModelMatrix * vec4(Position, 1.0)).xyz); //vertexColor = Color; vec3 Light0_Direction = vec3(0.16145112, 0.80725557, -0.5650789); vec3 Light1_Direction = vec3(-0.16145112, 0.80725557, 0.5650789); //normal = ProjMat * ModelMatrix * vec4(Normal, 0.0); normal = ProjMat * ModelMatrix * vec4(Normal, 0.0); //normal = vec4(Normal, 0.0); vertexColor = minecraft_mix_light(Light0_Direction, Light1_Direction, normal.xyz, Color); } ================================================ FILE: src/main/resources/assets/weather2/shaders/core/rendertype_clouds_orig.vsh ================================================ #version 150 #moj_import in vec3 Position; in vec2 UV0; in vec4 Color; in vec3 Normal; uniform mat4 ModelViewMat; uniform mat4 ProjMat; uniform vec3 Light0_Direction; uniform vec3 Light1_Direction; uniform float CustomTime; out vec2 texCoord0; out float vertexDistance; out vec4 vertexColor; out vec4 normal; mat4 rotationX( in float angle ) { return mat4( 1.0, 0, 0, 0, 0, cos(angle), -sin(angle), 0, 0, sin(angle), cos(angle), 0, 0, 0, 0, 1); } mat4 rotationY( in float angle ) { return mat4( cos(angle), 0, sin(angle), 0, 0, 1.0, 0, 0, -sin(angle), 0, cos(angle), 0, 0, 0, 0, 1); } mat4 rotationZ( in float angle ) { return mat4( cos(angle), -sin(angle), 0, 0, sin(angle), cos(angle), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } // // GLSL textureless classic 3D noise "cnoise", // with an RSL-style periodic variant "pnoise". // Author: Stefan Gustavson (stefan.gustavson@liu.se) // Version: 2011-10-11 // // Many thanks to Ian McEwan of Ashima Arts for the // ideas for permutation and gradient selection. // // Copyright (c) 2011 Stefan Gustavson. All rights reserved. // Distributed under the MIT license. See LICENSE file. // https://github.com/stegu/webgl-noise // vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } vec4 mod289(vec4 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } vec4 permute(vec4 x) { return mod289(((x*34.0)+10.0)*x); } vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; } vec3 fade(vec3 t) { return t*t*t*(t*(t*6.0-15.0)+10.0); } // Classic Perlin noise float cnoise(vec3 P) { vec3 Pi0 = floor(P); // Integer part for indexing vec3 Pi1 = Pi0 + vec3(1.0); // Integer part + 1 Pi0 = mod289(Pi0); Pi1 = mod289(Pi1); vec3 Pf0 = fract(P); // Fractional part for interpolation vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0 vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x); vec4 iy = vec4(Pi0.yy, Pi1.yy); vec4 iz0 = Pi0.zzzz; vec4 iz1 = Pi1.zzzz; vec4 ixy = permute(permute(ix) + iy); vec4 ixy0 = permute(ixy + iz0); vec4 ixy1 = permute(ixy + iz1); vec4 gx0 = ixy0 * (1.0 / 7.0); vec4 gy0 = fract(floor(gx0) * (1.0 / 7.0)) - 0.5; gx0 = fract(gx0); vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0); vec4 sz0 = step(gz0, vec4(0.0)); gx0 -= sz0 * (step(0.0, gx0) - 0.5); gy0 -= sz0 * (step(0.0, gy0) - 0.5); vec4 gx1 = ixy1 * (1.0 / 7.0); vec4 gy1 = fract(floor(gx1) * (1.0 / 7.0)) - 0.5; gx1 = fract(gx1); vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1); vec4 sz1 = step(gz1, vec4(0.0)); gx1 -= sz1 * (step(0.0, gx1) - 0.5); gy1 -= sz1 * (step(0.0, gy1) - 0.5); vec3 g000 = vec3(gx0.x,gy0.x,gz0.x); vec3 g100 = vec3(gx0.y,gy0.y,gz0.y); vec3 g010 = vec3(gx0.z,gy0.z,gz0.z); vec3 g110 = vec3(gx0.w,gy0.w,gz0.w); vec3 g001 = vec3(gx1.x,gy1.x,gz1.x); vec3 g101 = vec3(gx1.y,gy1.y,gz1.y); vec3 g011 = vec3(gx1.z,gy1.z,gz1.z); vec3 g111 = vec3(gx1.w,gy1.w,gz1.w); vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110))); g000 *= norm0.x; g010 *= norm0.y; g100 *= norm0.z; g110 *= norm0.w; vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111))); g001 *= norm1.x; g011 *= norm1.y; g101 *= norm1.z; g111 *= norm1.w; float n000 = dot(g000, Pf0); float n100 = dot(g100, vec3(Pf1.x, Pf0.yz)); float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z)); float n110 = dot(g110, vec3(Pf1.xy, Pf0.z)); float n001 = dot(g001, vec3(Pf0.xy, Pf1.z)); float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z)); float n011 = dot(g011, vec3(Pf0.x, Pf1.yz)); float n111 = dot(g111, Pf1); vec3 fade_xyz = fade(Pf0); vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z); vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y); float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x); return 2.2 * n_xyz; } // Classic Perlin noise, periodic variant float pnoise(vec3 P, vec3 rep) { vec3 Pi0 = mod(floor(P), rep); // Integer part, modulo period vec3 Pi1 = mod(Pi0 + vec3(1.0), rep); // Integer part + 1, mod period Pi0 = mod289(Pi0); Pi1 = mod289(Pi1); vec3 Pf0 = fract(P); // Fractional part for interpolation vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0 vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x); vec4 iy = vec4(Pi0.yy, Pi1.yy); vec4 iz0 = Pi0.zzzz; vec4 iz1 = Pi1.zzzz; vec4 ixy = permute(permute(ix) + iy); vec4 ixy0 = permute(ixy + iz0); vec4 ixy1 = permute(ixy + iz1); vec4 gx0 = ixy0 * (1.0 / 7.0); vec4 gy0 = fract(floor(gx0) * (1.0 / 7.0)) - 0.5; gx0 = fract(gx0); vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0); vec4 sz0 = step(gz0, vec4(0.0)); gx0 -= sz0 * (step(0.0, gx0) - 0.5); gy0 -= sz0 * (step(0.0, gy0) - 0.5); vec4 gx1 = ixy1 * (1.0 / 7.0); vec4 gy1 = fract(floor(gx1) * (1.0 / 7.0)) - 0.5; gx1 = fract(gx1); vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1); vec4 sz1 = step(gz1, vec4(0.0)); gx1 -= sz1 * (step(0.0, gx1) - 0.5); gy1 -= sz1 * (step(0.0, gy1) - 0.5); vec3 g000 = vec3(gx0.x,gy0.x,gz0.x); vec3 g100 = vec3(gx0.y,gy0.y,gz0.y); vec3 g010 = vec3(gx0.z,gy0.z,gz0.z); vec3 g110 = vec3(gx0.w,gy0.w,gz0.w); vec3 g001 = vec3(gx1.x,gy1.x,gz1.x); vec3 g101 = vec3(gx1.y,gy1.y,gz1.y); vec3 g011 = vec3(gx1.z,gy1.z,gz1.z); vec3 g111 = vec3(gx1.w,gy1.w,gz1.w); vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110))); g000 *= norm0.x; g010 *= norm0.y; g100 *= norm0.z; g110 *= norm0.w; vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111))); g001 *= norm1.x; g011 *= norm1.y; g101 *= norm1.z; g111 *= norm1.w; float n000 = dot(g000, Pf0); float n100 = dot(g100, vec3(Pf1.x, Pf0.yz)); float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z)); float n110 = dot(g110, vec3(Pf1.xy, Pf0.z)); float n001 = dot(g001, vec3(Pf0.xy, Pf1.z)); float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z)); float n011 = dot(g011, vec3(Pf0.x, Pf1.yz)); float n111 = dot(g111, Pf1); vec3 fade_xyz = fade(Pf0); vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z); vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y); float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x); return 2.2 * n_xyz; } float turbulence( vec3 p ) { float w = 100.0; float t = -.5; for (float f = 1.0 ; f <= 10.0 ; f++ ){ float power = pow( 2.0, f ); t += abs( pnoise( vec3( power * p ), vec3( 10.0, 10.0, 10.0 ) ) / power ); } return t; } float rand(vec2 co){ return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453); } vec4 perm(vec4 x){return mod289(((x * 34.0) + 1.0) * x);} float noise2(vec3 p){ vec3 a = floor(p); vec3 d = p - a; d = d * d * (3.0 - 2.0 * d); vec4 b = a.xxyy + vec4(0.0, 1.0, 0.0, 1.0); vec4 k1 = perm(b.xyxy); vec4 k2 = perm(k1.xyxy + b.zzww); vec4 c = k2 + a.zzzz; vec4 k3 = perm(c); vec4 k4 = perm(c + 1.0); vec4 o1 = fract(k3 * (1.0 / 41.0)); vec4 o2 = fract(k4 * (1.0 / 41.0)); vec4 o3 = o2 * d.z + o1 * (1.0 - d.z); vec2 o4 = o3.yw * d.x + o3.xz * (1.0 - d.x); return o4.y * d.y + o4.x * (1.0 - d.y); } void main() { vec3 test = vec3(1, 1, 1); normal = ProjMat * ModelViewMat * vec4(Normal, 0.0); float noise = 30.0 * -.10 * turbulence( .5 * test + CustomTime ); //float noise = 30.0 * -.10 * turbulence( .5 * normal.xyz + CustomTime ); float b = 5.0 * pnoise( 0.05 * Position + vec3( 2.0 * CustomTime ), vec3( 100.0 ) ); //float b = 5.0 * noise2( 0.05 * Position + vec3( 2.0 * CustomTime ) ); float displacement = - noise + b; //float randomSeed = rand(normal.xy); //vec3 newPosition = Position + vec3(sin(CustomTime) * 3, cos(CustomTime) * 3, 0); //vec3 newPosition = Position + normal.xyz * displacement; vec3 newPosition = Position + test; //vec3 newPosition = Position + test * displacement; gl_Position = ProjMat * ModelViewMat * vec4(newPosition, 1.0); texCoord0 = UV0; vertexDistance = length((ModelViewMat * vec4(newPosition, 1.0)).xyz); vertexColor = Color; //vec3 TestNormal = vec3(0, Normal.y * cos(CustomTime), 0); //mat4 NormalRotate = TestNormal * rotationZ(CustomTime); //normal = vec4(Normal, 0.0); //vec4 normal2 = ModelViewMat * vec4(Normal, 0.0); //vertexColor = minecraft_mix_light(Light0_Direction, Light1_Direction, normal.xyz, Color); //[0.16145112, 0.80725557, -0.5650789] //[-0.16145112, 0.80725557, 0.5650789] vec4 l0 = vec4(Light0_Direction, 0) * -ModelViewMat; vec4 l1 = vec4(Light1_Direction, 0) * -ModelViewMat; vec3 l00 = vec3(0.16145112, 0.80725557, -0.5650789); vec3 l11 = vec3(-0.16145112, 0.80725557, 0.5650789); if (CustomTime == 1F) { //vertexColor = minecraft_mix_light(l0.xyz, l1.xyz, TestNormal, Color); } else { } vertexColor = minecraft_mix_light(l00, l11, Normal, Color); //vertexColor = minecraft_mix_light(l00, l11, Normal, Color); } ================================================ FILE: src/main/resources/assets/weather2/shaders/core/rendertype_clouds_wip.json ================================================ { "blend": { "func": "add", "srcrgb": "srcalpha", "dstrgb": "1-srcalpha" }, "vertex": "weather2:rendertype_clouds", "fragment": "weather2:rendertype_clouds", "attributes_instanced": [ { "name": "Position", "index": 0 }, { "name": "UV0", "index": 1 }, { "name": "Color", "index": 2 }, { "name": "Normal", "index": 3 } ], "samplers": [ { "name": "Sampler0" } ], "uniforms": [ { "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, { "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, { "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] }, { "name": "Light0_Direction", "type": "float", "count": 3, "values": [0.0, 0.0, 0.0] }, { "name": "Light1_Direction", "type": "float", "count": 3, "values": [0.0, 0.0, 0.0] }, { "name": "FogStart", "type": "float", "count": 1, "values": [ 0.0 ] }, { "name": "FogEnd", "type": "float", "count": 1, "values": [ 1.0 ] }, { "name": "FogColor", "type": "float", "count": 4, "values": [ 0.0, 0.0, 0.0, 0.0 ] }, { "name": "CustomTime", "type": "float", "count": 1, "values": [ 0.0 ] } ] } ================================================ FILE: src/main/resources/assets/weather2/shaders/include/classicnoise3d.glsl ================================================ // // GLSL textureless classic 3D noise "cnoise", // with an RSL-style periodic variant "pnoise". // Author: Stefan Gustavson (stefan.gustavson@liu.se) // Version: 2011-10-11 // // Many thanks to Ian McEwan of Ashima Arts for the // ideas for permutation and gradient selection. // // Copyright (c) 2011 Stefan Gustavson. All rights reserved. // Distributed under the MIT license. See LICENSE file. // https://github.com/stegu/webgl-noise // vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } vec4 mod289(vec4 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } vec4 permute(vec4 x) { return mod289(((x*34.0)+10.0)*x); } vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; } vec3 fade(vec3 t) { return t*t*t*(t*(t*6.0-15.0)+10.0); } // Classic Perlin noise float cnoise(vec3 P) { vec3 Pi0 = floor(P); // Integer part for indexing vec3 Pi1 = Pi0 + vec3(1.0); // Integer part + 1 Pi0 = mod289(Pi0); Pi1 = mod289(Pi1); vec3 Pf0 = fract(P); // Fractional part for interpolation vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0 vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x); vec4 iy = vec4(Pi0.yy, Pi1.yy); vec4 iz0 = Pi0.zzzz; vec4 iz1 = Pi1.zzzz; vec4 ixy = permute(permute(ix) + iy); vec4 ixy0 = permute(ixy + iz0); vec4 ixy1 = permute(ixy + iz1); vec4 gx0 = ixy0 * (1.0 / 7.0); vec4 gy0 = fract(floor(gx0) * (1.0 / 7.0)) - 0.5; gx0 = fract(gx0); vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0); vec4 sz0 = step(gz0, vec4(0.0)); gx0 -= sz0 * (step(0.0, gx0) - 0.5); gy0 -= sz0 * (step(0.0, gy0) - 0.5); vec4 gx1 = ixy1 * (1.0 / 7.0); vec4 gy1 = fract(floor(gx1) * (1.0 / 7.0)) - 0.5; gx1 = fract(gx1); vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1); vec4 sz1 = step(gz1, vec4(0.0)); gx1 -= sz1 * (step(0.0, gx1) - 0.5); gy1 -= sz1 * (step(0.0, gy1) - 0.5); vec3 g000 = vec3(gx0.x,gy0.x,gz0.x); vec3 g100 = vec3(gx0.y,gy0.y,gz0.y); vec3 g010 = vec3(gx0.z,gy0.z,gz0.z); vec3 g110 = vec3(gx0.w,gy0.w,gz0.w); vec3 g001 = vec3(gx1.x,gy1.x,gz1.x); vec3 g101 = vec3(gx1.y,gy1.y,gz1.y); vec3 g011 = vec3(gx1.z,gy1.z,gz1.z); vec3 g111 = vec3(gx1.w,gy1.w,gz1.w); vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110))); g000 *= norm0.x; g010 *= norm0.y; g100 *= norm0.z; g110 *= norm0.w; vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111))); g001 *= norm1.x; g011 *= norm1.y; g101 *= norm1.z; g111 *= norm1.w; float n000 = dot(g000, Pf0); float n100 = dot(g100, vec3(Pf1.x, Pf0.yz)); float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z)); float n110 = dot(g110, vec3(Pf1.xy, Pf0.z)); float n001 = dot(g001, vec3(Pf0.xy, Pf1.z)); float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z)); float n011 = dot(g011, vec3(Pf0.x, Pf1.yz)); float n111 = dot(g111, Pf1); vec3 fade_xyz = fade(Pf0); vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z); vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y); float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x); return 2.2 * n_xyz; } // Classic Perlin noise, periodic variant float pnoise(vec3 P, vec3 rep) { vec3 Pi0 = mod(floor(P), rep); // Integer part, modulo period vec3 Pi1 = mod(Pi0 + vec3(1.0), rep); // Integer part + 1, mod period Pi0 = mod289(Pi0); Pi1 = mod289(Pi1); vec3 Pf0 = fract(P); // Fractional part for interpolation vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0 vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x); vec4 iy = vec4(Pi0.yy, Pi1.yy); vec4 iz0 = Pi0.zzzz; vec4 iz1 = Pi1.zzzz; vec4 ixy = permute(permute(ix) + iy); vec4 ixy0 = permute(ixy + iz0); vec4 ixy1 = permute(ixy + iz1); vec4 gx0 = ixy0 * (1.0 / 7.0); vec4 gy0 = fract(floor(gx0) * (1.0 / 7.0)) - 0.5; gx0 = fract(gx0); vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0); vec4 sz0 = step(gz0, vec4(0.0)); gx0 -= sz0 * (step(0.0, gx0) - 0.5); gy0 -= sz0 * (step(0.0, gy0) - 0.5); vec4 gx1 = ixy1 * (1.0 / 7.0); vec4 gy1 = fract(floor(gx1) * (1.0 / 7.0)) - 0.5; gx1 = fract(gx1); vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1); vec4 sz1 = step(gz1, vec4(0.0)); gx1 -= sz1 * (step(0.0, gx1) - 0.5); gy1 -= sz1 * (step(0.0, gy1) - 0.5); vec3 g000 = vec3(gx0.x,gy0.x,gz0.x); vec3 g100 = vec3(gx0.y,gy0.y,gz0.y); vec3 g010 = vec3(gx0.z,gy0.z,gz0.z); vec3 g110 = vec3(gx0.w,gy0.w,gz0.w); vec3 g001 = vec3(gx1.x,gy1.x,gz1.x); vec3 g101 = vec3(gx1.y,gy1.y,gz1.y); vec3 g011 = vec3(gx1.z,gy1.z,gz1.z); vec3 g111 = vec3(gx1.w,gy1.w,gz1.w); vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110))); g000 *= norm0.x; g010 *= norm0.y; g100 *= norm0.z; g110 *= norm0.w; vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111))); g001 *= norm1.x; g011 *= norm1.y; g101 *= norm1.z; g111 *= norm1.w; float n000 = dot(g000, Pf0); float n100 = dot(g100, vec3(Pf1.x, Pf0.yz)); float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z)); float n110 = dot(g110, vec3(Pf1.xy, Pf0.z)); float n001 = dot(g001, vec3(Pf0.xy, Pf1.z)); float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z)); float n011 = dot(g011, vec3(Pf0.x, Pf1.yz)); float n111 = dot(g111, Pf1); vec3 fade_xyz = fade(Pf0); vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z); vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y); float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x); return 2.2 * n_xyz; } ================================================ FILE: src/main/resources/assets/weather2/sounds.json ================================================ { "env.waterfall": { "category": "weather", "sounds": [ "weather2:env/waterfall" ] }, "env.wind_calm": { "category": "weather", "sounds": [ "weather2:env/wind_calm" ] }, "env.wind_calmfade": { "category": "weather", "sounds": [ "weather2:env/wind_calmfade" ] }, "streaming.destruction": { "category": "weather", "sounds": [ { "name": "weather2:streaming/destruction", "stream": true } ] }, "streaming.destruction_0_": { "category": "weather", "sounds": [ { "name": "weather2:streaming/destruction_0_", "stream": true } ] }, "streaming.destruction_1_": { "category": "weather", "sounds": [ { "name": "weather2:streaming/destruction_1_", "stream": true } ] }, "streaming.destruction_2_": { "category": "weather", "sounds": [ { "name": "weather2:streaming/destruction_2_", "stream": true } ] }, "streaming.destruction_s": { "category": "weather", "sounds": [ { "name": "weather2:streaming/destruction_s", "stream": true } ] }, "streaming.destructionb": { "category": "weather", "sounds": [ { "name": "weather2:streaming/destructionb", "stream": true } ] }, "streaming.siren": { "category": "weather", "sounds": [ { "name": "weather2:streaming/siren", "stream": true } ] }, "streaming.wind_close": { "category": "weather", "sounds": [ { "name": "weather2:streaming/wind_close", "stream": true } ] }, "streaming.wind_close_0_": { "category": "weather", "sounds": [ { "name": "weather2:streaming/wind_close_0_", "stream": true } ] }, "streaming.wind_close_1_": { "category": "weather", "sounds": [ { "name": "weather2:streaming/wind_close_1_", "stream": true } ] }, "streaming.wind_close_2_": { "category": "weather", "sounds": [ { "name": "weather2:streaming/wind_close_2_", "stream": true } ] }, "streaming.wind_far": { "category": "weather", "sounds": [ { "name": "weather2:streaming/wind_far", "stream": true } ] }, "streaming.wind_far_0_": { "category": "weather", "sounds": [ { "name": "weather2:streaming/wind_far_0_", "stream": true } ] }, "streaming.wind_far_1_": { "category": "weather", "sounds": [ { "name": "weather2:streaming/wind_far_1_", "stream": true } ] }, "streaming.wind_far_2_": { "category": "weather", "sounds": [ { "name": "weather2:streaming/wind_far_2_", "stream": true } ] } , "streaming.sandstorm_high1": { "category": "weather", "sounds": [ { "name": "weather2:streaming/sandstorm_high1", "stream": true } ] }, "streaming.sandstorm_med1": { "category": "weather", "sounds": [ { "name": "weather2:streaming/sandstorm_med1", "stream": true } ] }, "streaming.sandstorm_med2": { "category": "weather", "sounds": [ { "name": "weather2:streaming/sandstorm_med2", "stream": true } ] }, "streaming.sandstorm_low1": { "category": "weather", "sounds": [ { "name": "weather2:streaming/sandstorm_low1", "stream": true } ] }, "streaming.sandstorm_low2": { "category": "weather", "sounds": [ { "name": "weather2:streaming/sandstorm_low2", "stream": true } ] }, "streaming.siren_sandstorm_1": { "category": "weather", "sounds": [ { "name": "weather2:streaming/siren_sandstorm_1", "stream": true } ] }, "streaming.siren_sandstorm_2": { "category": "weather", "sounds": [ { "name": "weather2:streaming/siren_sandstorm_2", "stream": true } ] }, "streaming.siren_sandstorm_3": { "category": "weather", "sounds": [ { "name": "weather2:streaming/siren_sandstorm_3", "stream": true } ] }, "streaming.siren_sandstorm_4": { "category": "weather", "sounds": [ { "name": "weather2:streaming/siren_sandstorm_4", "stream": true } ] }, "streaming.siren_sandstorm_5_extra": { "category": "weather", "sounds": [ { "name": "weather2:streaming/siren_sandstorm_5_extra", "stream": true } ] }, "streaming.siren_sandstorm_6_extra": { "category": "weather", "sounds": [ { "name": "weather2:streaming/siren_sandstorm_6_extra", "stream": true } ] } } ================================================ FILE: src/main/resources/pack.mcmeta ================================================ { "pack": { "description": "weather2 resources", "pack_format": 4, "_comment": "A pack_format of 4 requires json lang files. Note: we require v4 pack meta for all mods." } } ================================================ FILE: src/main/resources/weather2.mixins.json ================================================ { "required": true, "package": "weather2.mixin", "compatibilityLevel": "JAVA_17", "refmap": "weather2.refmap.json", "client": ["client.RenderParticlesOverride"], "injectors": { "defaultRequire": 1 }, "minVersion": "0.8" }