From 4ba31e106acd820d2728ae657d89c8b72b040b48 Mon Sep 17 00:00:00 2001
From: RealDarkStudios <94769968+RealDarkStudios@users.noreply.github.com>
Date: Sat, 19 Jul 2025 21:41:42 -0500
Subject: [PATCH 1/3] 0.15.0.0+rc1 - Update to PMWeather 0.15 - Added
ColorMap#getWithBiome - Added ColorMaps#IR - ID Overlay now shows all enabled
overlays - Added StormType filtering to NearbyStorms - PixelRenderData now
has a Vec3 worldPos parameter - RenderData now has a sizeRenderDiameter
property - Added RadarMode#IR - Added new parameters smoothWidth,
cycloneWindspeed, and smoothWindspeed to StormBuilder - Added StormType util
enum
Took 1 hour 55 minutes
---
gradle.properties | 4 +-
.../pmweatherapi/client/render/IDOverlay.java | 27 +-
.../client/render/PixelRenderData.java | 5 +-
.../client/render/RenderData.java | 3 +-
.../pmweatherapi/config/PMWClientConfig.java | 4 +-
.../mixin/RadarRendererMixin.java | 304 +++++++++++++-----
.../nullved/pmweatherapi/radar/RadarMode.java | 29 +-
.../pmweatherapi/storm/NearbyStorms.java | 100 ++++++
.../pmweatherapi/storm/StormBuilder.java | 84 +++--
.../nullved/pmweatherapi/util/ColorMap.java | 32 +-
.../nullved/pmweatherapi/util/ColorMaps.java | 20 +-
.../nullved/pmweatherapi/util/StormType.java | 34 ++
12 files changed, 520 insertions(+), 126 deletions(-)
create mode 100644 src/main/java/net/nullved/pmweatherapi/util/StormType.java
diff --git a/gradle.properties b/gradle.properties
index 99e15c3..c8b93fc 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -17,10 +17,10 @@ parchment_mappings_version=2024.11.17
mod_id=pmweatherapi
mod_name=PMWeatherAPI
mod_license=GNU GPL 3.0
-mod_version=0.14.16.2
+mod_version=0.15.0.0+rc1
mod_group_id=net.nullved
mod_authors=NullVed
mod_description=An API for interfacing with ProtoManly's Weather Mod
# Dependencies
-pmweather_version=0.14.16
\ No newline at end of file
+pmweather_version=0.15.0
\ No newline at end of file
diff --git a/src/main/java/net/nullved/pmweatherapi/client/render/IDOverlay.java b/src/main/java/net/nullved/pmweatherapi/client/render/IDOverlay.java
index 9a0203b..4cd4a29 100644
--- a/src/main/java/net/nullved/pmweatherapi/client/render/IDOverlay.java
+++ b/src/main/java/net/nullved/pmweatherapi/client/render/IDOverlay.java
@@ -3,6 +3,7 @@
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Axis;
+import net.minecraft.client.Minecraft;
import net.minecraft.network.chat.Component;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
@@ -10,6 +11,8 @@
import net.nullved.pmweatherapi.config.PMWClientConfig;
import net.nullved.pmweatherapi.radar.RadarMode;
+import java.util.function.Supplier;
+
/**
* The overlay for {@link RadarMode} IDs.
*
@@ -23,16 +26,36 @@ public class IDOverlay implements IRadarOverlay {
@Override
public void render(boolean canRender, RenderData renderData, BufferBuilder bufferBuilder, Object... args) {
if (!PMWClientConfig.showRadarModeId) return;
+ if (!Minecraft.getInstance().player.isCrouching()) return;
+
RadarMode mode = getRadarMode(renderData);
PoseStack poseStack = renderData.poseStack();
PMWClientConfig.RadarModeIDSide side = PMWClientConfig.radarModeIDSide;
- poseStack.translate(side.x, 1.055f, side.z);
- poseStack.mulPose(Axis.ZP.rotationDegrees(side.rotation));
+ float scale = renderData.sizeRenderDiameter() / 3.0F;
+
+ poseStack.pushPose();
+ poseStack.translate((side.x * scale) - 0.5F * (scale - 1), 1.055f, (side.z * scale) - 0.5F * (scale - 1));
+ poseStack.mulPose(Axis.YN.rotationDegrees(side.rotation));
poseStack.scale(0.01f, 0.01f, 0.01f);
renderText(Component.literal(mode.getId().toString()), renderData, poseStack);
+ float lineHeight = 8.0f;
+ float offset = lineHeight;
+ for (Supplier extends IRadarOverlay> overlay: RadarOverlays.getOverlays()) {
+ poseStack.pushPose();
+ poseStack.translate(0, 0, offset);
+ poseStack.scale(0.6f, 0.6f, 0.6f);
+
+ renderText(Component.literal(overlay.get().getID().toString()).withColor(0x888888), renderData, poseStack);
+
+ poseStack.popPose();
+ offset += lineHeight * 0.6f;
+ }
+
+ poseStack.popPose();
+
// While I could reset the pose, it is not strictly necessary
// poseStack.scale(100f, 100f, 100f);
// poseStack.mulPose(Axis.ZP.rotationDegrees(-side.rotation));
diff --git a/src/main/java/net/nullved/pmweatherapi/client/render/PixelRenderData.java b/src/main/java/net/nullved/pmweatherapi/client/render/PixelRenderData.java
index a99ff47..32dc116 100644
--- a/src/main/java/net/nullved/pmweatherapi/client/render/PixelRenderData.java
+++ b/src/main/java/net/nullved/pmweatherapi/client/render/PixelRenderData.java
@@ -1,5 +1,7 @@
package net.nullved.pmweatherapi.client.render;
+import net.minecraft.world.phys.Vec3;
+
/**
* Specific rendering data for a pixel on the radar
* @param canRender {@code true} if either the server doesn't require WSR-88D or a WSR-88D is complete within 4 chunks of the radar
@@ -9,8 +11,9 @@
* @param x The x-position of the pixel (from {@code -resolution} to {@code resolution})
* @param z The z-position of the pixel (from {@code -resolution} to {@code resolution})
* @param resolution The resolution of the radar
+ * @param worldPos The world position of the pixel
* @param renderData The associated {@link RenderData}
* @since 0.14.15.6
*/
-public record PixelRenderData(boolean canRender, float rdbz, float velocity, float temp, int x, int z, int resolution, RenderData renderData) {
+public record PixelRenderData(boolean canRender, float rdbz, float velocity, float temp, int x, int z, int resolution, Vec3 worldPos, RenderData renderData) {
}
diff --git a/src/main/java/net/nullved/pmweatherapi/client/render/RenderData.java b/src/main/java/net/nullved/pmweatherapi/client/render/RenderData.java
index d86554a..2bd1616 100644
--- a/src/main/java/net/nullved/pmweatherapi/client/render/RenderData.java
+++ b/src/main/java/net/nullved/pmweatherapi/client/render/RenderData.java
@@ -7,6 +7,7 @@
/**
* A wrapper class to be passed to {@link RenderData}
* @param blockEntity The {@link BlockEntity} associated with the render call
+ * @param sizeRenderDiameter The size in blocks of the radar
* @param partialTicks The time, in partial ticks, since last full tick
* @param poseStack The {@link PoseStack}
* @param multiBufferSource The {@link MultiBufferSource}
@@ -14,5 +15,5 @@
* @param combinedOverlayIn The current overlay of the block entity
* @since 0.14.15.2
*/
-public record RenderData(BlockEntity blockEntity, float partialTicks, PoseStack poseStack, MultiBufferSource multiBufferSource, int combinedLightIn, int combinedOverlayIn) {
+public record RenderData(BlockEntity blockEntity, float sizeRenderDiameter, float partialTicks, PoseStack poseStack, MultiBufferSource multiBufferSource, int combinedLightIn, int combinedOverlayIn) {
}
diff --git a/src/main/java/net/nullved/pmweatherapi/config/PMWClientConfig.java b/src/main/java/net/nullved/pmweatherapi/config/PMWClientConfig.java
index a778d7b..252213a 100644
--- a/src/main/java/net/nullved/pmweatherapi/config/PMWClientConfig.java
+++ b/src/main/java/net/nullved/pmweatherapi/config/PMWClientConfig.java
@@ -41,9 +41,9 @@ public enum RadarModeIDSide {
NORTH(0, -1, -1),
EAST(90, 2, -1),
SOUTH(180, 2, 2),
- WEST(-90, -1, 2),;
+ WEST(-90, -1, 2);
- public int rotation, x, z;
+ public final int rotation, x, z;
RadarModeIDSide(int rotation, int x, int z) {
this.rotation = rotation;
diff --git a/src/main/java/net/nullved/pmweatherapi/mixin/RadarRendererMixin.java b/src/main/java/net/nullved/pmweatherapi/mixin/RadarRendererMixin.java
index 6ca332c..28d4ff3 100644
--- a/src/main/java/net/nullved/pmweatherapi/mixin/RadarRendererMixin.java
+++ b/src/main/java/net/nullved/pmweatherapi/mixin/RadarRendererMixin.java
@@ -13,19 +13,19 @@
import dev.protomanly.pmweather.render.RadarRenderer;
import dev.protomanly.pmweather.util.ColorTables;
import dev.protomanly.pmweather.util.Util;
-import dev.protomanly.pmweather.weather.Sounding;
-import dev.protomanly.pmweather.weather.Storm;
-import dev.protomanly.pmweather.weather.ThermodynamicEngine;
-import dev.protomanly.pmweather.weather.WindEngine;
+import dev.protomanly.pmweather.weather.*;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.core.BlockPos;
+import net.minecraft.core.Holder;
import net.minecraft.util.Mth;
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.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.phys.Vec2;
import net.minecraft.world.phys.Vec3;
import net.neoforged.api.distmarker.Dist;
@@ -37,41 +37,60 @@
import net.nullved.pmweatherapi.config.PMWClientConfig;
import net.nullved.pmweatherapi.data.PMWExtras;
import net.nullved.pmweatherapi.radar.RadarMode;
+import net.nullved.pmweatherapi.util.StormType;
import org.joml.Matrix4fStack;
+import org.joml.Vector2f;
import org.joml.Vector3f;
import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
import java.awt.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import static dev.protomanly.pmweather.render.RadarRenderer.FBM;
+
@OnlyIn(Dist.CLIENT)
@Mixin(RadarRenderer.class)
public class RadarRendererMixin {
+ @Shadow
+ public static int RenderedRadars = 0;
+
@WrapMethod(method = "render")
private void render(BlockEntity blockEntity, float partialTicks, PoseStack poseStack, MultiBufferSource multiBufferSource, int combinedLightIn, int combinedOverlayIn, Operation original) {
if (!(blockEntity instanceof RadarBlockEntity radarBlockEntity)) return;
- if (Minecraft.getInstance().player.position().distanceTo(blockEntity.getBlockPos().getCenter()) > (double)25.0F) return;
+ if (Minecraft.getInstance().player.position().distanceTo(blockEntity.getBlockPos().getCenter()) > (double) 20.0F || RenderedRadars > 2) return;
+ ++RenderedRadars;
boolean canRender = true;
BlockPos pos = radarBlockEntity.getBlockPos();
float sizeRenderDiameter = 3.0F;
float simSize = 2048.0F;
+
+ if (radarBlockEntity.hasRangeUpgrade) {
+ simSize *= 4.0F;
+ if (!ClientConfig._3X3Radar) {
+ sizeRenderDiameter = 6.0F;
+ }
+ }
+
int resolution = ClientConfig.radarResolution;
Matrix4fStack matrix4fStack = RenderSystem.getModelViewStack();
matrix4fStack.pushMatrix();
matrix4fStack.mul(poseStack.last().pose());
matrix4fStack.translate(0.5F, 1.05F, 0.5F);
+
RenderSystem.applyModelViewMatrix();
RenderSystem.enableBlend();
RenderSystem.depthMask(true);
RenderSystem.enableDepthTest();
RenderSystem.setShader(GameRenderer::getPositionColorShader);
RenderSystem.defaultBlendFunc();
+
Tesselator tesselator = Tesselator.getInstance();
BufferBuilder bufferBuilder = tesselator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR);
- List storms = new ArrayList(radarBlockEntity.storms);
+ List storms = new ArrayList<>(radarBlockEntity.storms);
boolean update = false;
ClientConfig.RadarMode clientRadarMode = ClientConfig.radarMode;
@@ -114,9 +133,9 @@ private void render(BlockEntity blockEntity, float partialTicks, PoseStack poseS
}
}
- float size = sizeRenderDiameter / (float)resolution;
+ float size = sizeRenderDiameter / (float) resolution;
- RenderData renderData = new RenderData(blockEntity, partialTicks, poseStack, multiBufferSource, combinedLightIn, combinedOverlayIn);
+ RenderData renderData = new RenderData(blockEntity, sizeRenderDiameter, partialTicks, poseStack, multiBufferSource, combinedLightIn, combinedOverlayIn);
RadarMode radarMode = blockEntity.getBlockState().getValue(PMWExtras.RADAR_MODE);
if (!PMWClientStorages.RADAR_MODE_COLORS.containsKey(radarMode)) update = true;
@@ -134,10 +153,12 @@ private void render(BlockEntity blockEntity, float partialTicks, PoseStack poseS
Color color = PMWClientStorages.RADAR_MODE_COLORS.computeIfAbsent(radarMode, rm -> new HashMap<>()).getOrDefault(id, new Color(1.0F, 0, 1.0F));
Color dbg = radarBlockEntity.debugMap.getOrDefault(longID, new Color(0, 0, 0));
- Vector3f pixelPos = (new Vector3f((float)x, 0.0F, (float)z)).mul(1.0F / (float)resolution).mul(sizeRenderDiameter / 2.0F);
- Vec3 worldPos = (new Vec3(x, 0.0F, z)).multiply(1.0F / (float)resolution, 0.0F, (1.0F / (float)resolution)).multiply(simSize, 0.0F, simSize).add(pos.getCenter());
+ Vector3f pixelPos = (new Vector3f((float) x, 0.0F, (float) z)).mul(1.0F / (float) resolution).mul(sizeRenderDiameter / 2.0F);
+ Vec3 worldPos = (new Vec3(x, 0.0F, z)).multiply(1.0F / (float) resolution, 0.0F, (1.0F / (float) resolution)).multiply(simSize, 0.0F, simSize).add(pos.getCenter());
if (update) {
+ float clouds = Clouds.getCloudDensity(GameBusClientEvents.weatherHandler, new Vector2f((float)worldPos.x, (float)worldPos.z), 0.0F);
+
dbz = 0.0F;
temp = 0.0F;
Vec2 f = (new Vec2((float)x, (float)z)).normalized();
@@ -147,34 +168,105 @@ private void render(BlockEntity blockEntity, float partialTicks, PoseStack poseS
for(Storm storm : storms) {
if (!storm.visualOnly) {
- double stormSize = ServerConfig.stormSize * (double)2.0F;
+ double stormSize = ServerConfig.stormSize * (double) 2.0F;
if (storm.stormType == 0) {
stormSize *= 1.5F;
}
double scale = stormSize / (double)1200.0F;
- double shapeNoise = radarBlockEntity.noise.getValue((float)radarBlockEntity.tickCount / 1200.0F, worldPos.x / ((double)750.0F * scale), worldPos.z / ((double)750.0F * scale));
- double shapeNoise2 = radarBlockEntity.noise.getValue((float)radarBlockEntity.tickCount / 1200.0F, worldPos.z / ((double)750.0F * scale), worldPos.x / ((double)750.0F * scale));
- double shapeNoise4 = radarBlockEntity.noise.getValue((float)radarBlockEntity.tickCount / 1200.0F, worldPos.z / ((double)250.0F * scale), worldPos.x / ((double)250.0F * scale));
- shapeNoise *= 0.5F;
- shapeNoise2 *= 0.5F;
- shapeNoise4 *= 0.5F;
- shapeNoise += 0.5F;
- shapeNoise2 += 0.5F;
- shapeNoise4 += 0.5F;
+ if (storm.stormType == 2) {
+ scale = (float) storm.maxWidth / 3000.0F;
+ scale *= 0.5F;
+ }
+
+ double shapeNoise = radarBlockEntity.noise.getValue(((float)radarBlockEntity.tickCount / 8000.0F), worldPos.x / ((double)750.0F * scale), worldPos.z / ((double)750.0F * scale));
+ float fineShapeNoise = FBM(radarBlockEntity.noise, new Vec3((float)radarBlockEntity.tickCount / 8000.0F, worldPos.x / ((double)500.0F * scale), worldPos.z / ((double)500.0F * scale)), 10, 2.0F, 0.75F, 1.0F);
+ double shapeNoise2 = radarBlockEntity.noise.getValue((float)radarBlockEntity.tickCount / 8000.0F, worldPos.z / ((double)750.0F * scale), worldPos.x / ((double)750.0F * scale));
+ double shapeNoise3 = radarBlockEntity.noise.getValue(((float)radarBlockEntity.tickCount / 16000.0F), worldPos.x / ((double)4000.0F * scale), worldPos.z / ((double)4000.0F * scale));
+ double shapeNoise4 = radarBlockEntity.noise.getValue(((float)radarBlockEntity.tickCount / 8000.0F), worldPos.z / ((double)250.0F * scale), worldPos.x / ((double)250.0F * scale));
+ shapeNoise *= (double)0.5F;
+ shapeNoise2 *= (double)0.5F;
+ shapeNoise4 *= (double)0.5F;
+ shapeNoise += (double)0.5F;
+ shapeNoise2 += (double)0.5F;
+ shapeNoise4 += (double)0.5F;
float localDBZ = 0.0F;
float smoothStage = (float)storm.stage + (float)storm.energy / 100.0F;
- if (storm.stormType == 1) {
- double rawDist = worldPos.distanceTo(storm.position.multiply(1.0F, 0.0F, 1.0F));
- Vec2 v2fWorldPos = new Vec2((float)worldPos.x, (float)worldPos.z);
- Vec2 stormVel = new Vec2((float)storm.velocity.x, (float)storm.velocity.z);
- Vec2 v2fStormPos = new Vec2((float)storm.position.x, (float)storm.position.z);
+ if (storm.stormType == StormType.CYCLONE.idx()) {
+ Vec3 wPos = worldPos;
+ Vec3 cPos = storm.position.multiply(1.0F, 0.0F, 1.0F);
+
+ for(Vorticy vorticy : storm.vorticies) {
+ Vec3 vPos = vorticy.getPosition();
+ float width = vorticy.getWidth() * 0.35F;
+ double d = wPos.multiply(1.0F, 0.0F, 1.0F).distanceTo(vPos.multiply(1.0F, 0.0F, 1.0F));
+ if (d < (double) width) {
+ double angle = Math.pow((double) 1.0F - Math.clamp(d / (double) width, 0.0F, 1.0F), 3.75F);
+ angle *= ((float) Math.PI / 10F);
+ angle *= Math.min(vorticy.windspeedMult * (float) storm.windspeed, 6.0F);
+ wPos = Util.rotatePoint(wPos, vPos, angle);
+ }
+ }
+
+ double rawDist = wPos.multiply(1.0F, 0.0F, 1.0F).distanceTo(storm.position.multiply(1.0F, 0.0F, 1.0F));
+ rawDist *= (double) 1.0F + shapeNoise3 * (double) 0.2F;
+ float intensity = (float) Math.pow(Math.clamp((float) storm.windspeed / 65.0F, 0.0F, 1.0F), 0.25F);
+ Vec3 relPos = cPos.subtract(wPos).multiply(scale, 0.0F, scale);
+ double d = ((float) storm.maxWidth / (3.0F + (float) storm.windspeed / 12.0F));
+ double d2 = ((float) storm.maxWidth / (1.15F + (float) storm.windspeed / 12.0F));
+ double dE = ((float) storm.maxWidth * 0.65F / (1.75F + (float) storm.windspeed / 12.0F));
+ double fac = (double) 1.0F + Math.max((rawDist - (double) ((float) storm.maxWidth * 0.2F)) / (double) storm.maxWidth, 0.0F) * (double) 2.0F;
+ d *= fac;
+ d2 *= fac;
+ double angle = Math.atan2(relPos.z, relPos.x) - rawDist / d;
+ double angle2 = Math.atan2(relPos.z, relPos.x) - rawDist / d2;
+ double angleE = Math.atan2(relPos.z, relPos.x) - rawDist / dE;
+ float weak = 0.0F;
+ float strong = 0.0F;
+ float intense = 0.0F;
+ float staticBands = (float) Math.sin(angle - (Math.PI / 2D));
+ staticBands *= (float) Math.pow(Math.clamp(rawDist / (double) ((float) storm.maxWidth * 0.25F), 0.0F, 1.0F), 0.1F);
+ staticBands *= 1.25F * (float) Math.pow(intensity, 0.75F);
+ if (staticBands < 0.0F) {
+ weak += Math.abs(staticBands);
+ } else {
+ weak += Math.abs(staticBands) * (float) Math.pow((double) 1.0F - Math.clamp(rawDist / (double) ((float) storm.maxWidth * 0.65F), 0.0F, 1.0F), 0.5F);
+ weak *= Math.clamp(((float) storm.windspeed - 70.0F) / 40.0F, 0.0F, 1.0F);
+ }
+
+ float rotatingBands = (float) Math.sin((angle2 + Math.toRadians(((float) storm.tickCount / 8.0F))) * (double) 6.0F);
+ rotatingBands *= (float) Math.pow(Math.clamp(rawDist / (double) ((float) storm.maxWidth * 0.25F), 0.0F, 1.0F), 0.1F);
+ rotatingBands *= 1.25F * (float) Math.pow(intensity, 0.75F);
+ strong += Mth.lerp(0.45F, Math.abs(rotatingBands) * 0.3F + 0.7F, weak);
+ intense += Mth.lerp(0.3F, Math.abs(rotatingBands) * 0.2F + 0.8F, weak);
+ weak = (Math.abs(rotatingBands) * 0.3F + 0.6F) * weak;
+ localDBZ += Mth.lerp(Math.clamp(((float) storm.windspeed - 120.0F) / 60.0F, 0.0F, 1.0F), Mth.lerp(Math.clamp(((float) storm.windspeed - 40.0F) / 90.0F, 0.0F, 1.0F), weak, strong), intense);
+ float eye = (float) Math.sin((angleE + Math.toRadians(((float) storm.tickCount / 4.0F))) * (double) 2.0F);
+ float efc = Mth.lerp(Math.clamp(((float) storm.windspeed - 100.0F) / 50.0F, 0.0F, 1.0F), 0.15F, 0.4F);
+ localDBZ = Math.max((float) Math.pow((double) 1.0F - Math.clamp(rawDist / (double) ((float) storm.maxWidth * efc), 0.0F, 1.0F), 0.5F) * (Math.abs(eye * 0.1F) + 0.9F) * 1.35F * intensity, localDBZ);
+ localDBZ *= (float) Math.pow((double) 1.0F - Math.clamp(rawDist / (double) storm.maxWidth, 0.0F, 1.0F), 0.5F);
+ localDBZ *= Mth.lerp(0.5F + Math.clamp(((float) storm.windspeed - 65.0F) / 40.0F, 0.0F, 1.0F) * 0.5F, 1.0F, (float) Math.pow(Math.clamp(rawDist / (double)((float) storm.maxWidth * 0.1F), 0.0F, 1.0F), 2.0F));
+ localDBZ *= Mth.lerp(Math.clamp(((float) storm.windspeed - 75.0F) / 50.0F, 0.0F, 1.0F), 0.8F + (float) shapeNoise2 * 0.4F, 1.0F);
+ localDBZ *= 0.8F + (float) shapeNoise * 0.4F;
+ localDBZ *= 1.0F + fineShapeNoise * Mth.lerp((float)Math.pow(Math.clamp(rawDist / (double) storm.maxWidth, 0.0F, 1.0F), 1.5F), 0.05F, 0.15F);
+ localDBZ = (float) Math.pow(localDBZ, 1.75F);
+ if (localDBZ > 0.8F) {
+ float dif = (localDBZ - 0.8F) / 1.25F;
+ localDBZ -= dif;
+ }
+ }
+
+ if (storm.stormType == StormType.SQUALL.idx()) {
+ double rawDist = worldPos.multiply(1.0F, 0.0F, 1.0F).distanceTo(storm.position.multiply(1.0F, 0.0F, 1.0F));
+ Vec2 v2fWorldPos = new Vec2((float) worldPos.x, (float) worldPos.z);
+ Vec2 stormVel = new Vec2((float) storm.velocity.x, (float) storm.velocity.z);
+ Vec2 v2fStormPos = new Vec2((float) storm.position.x, (float) storm.position.z);
Vec2 right = (new Vec2(stormVel.y, -stormVel.x)).normalized();
Vec2 fwd = stormVel.normalized();
- Vec2 le = Util.mulVec2(right, -3000.0F * (float)scale);
- Vec2 ri = Util.mulVec2(right, 3000.0F * (float)scale);
- Vec2 off = Util.mulVec2(fwd, -((float)Math.pow(Mth.clamp(rawDist / ((double)3000.0F * scale), 0.0F, 1.0F), 2.0F)) * 900.0F * (float)scale);
+ Vec2 le = Util.mulVec2(right, -3000.0F * (float) scale);
+ Vec2 ri = Util.mulVec2(right, 3000.0F * (float) scale);
+ Vec2 off = Util.mulVec2(fwd, -((float) Math.pow(Mth.clamp(rawDist / ((double) 3000.0F * scale), 0.0F, 1.0F), 2.0F)) * 900.0F * (float) scale);
le = le.add(off);
ri = ri.add(off);
le = le.add(v2fStormPos);
@@ -182,10 +274,10 @@ private void render(BlockEntity blockEntity, float partialTicks, PoseStack poseS
float dist = Util.minimumDistance(le, ri, v2fWorldPos);
float intensity = switch (storm.stage) {
- case 1 -> 0.1F + (float)storm.energy / 100.0F * 0.7F;
- case 2 -> 0.8F + (float)storm.energy / 100.0F * 0.4F;
- case 3 -> 1.2F + (float)storm.energy / 100.0F;
- default -> (float)storm.energy / 100.0F * 0.1F;
+ case 1 -> 0.1F + (float) storm.energy / 100.0F * 0.7F;
+ case 2 -> 0.8F + (float) storm.energy / 100.0F * 0.4F;
+ case 3 -> 1.2F + (float) storm.energy / 100.0F;
+ default -> (float) storm.energy / 100.0F * 0.1F;
};
if (intensity > 0.8F) {
@@ -195,62 +287,59 @@ private void render(BlockEntity blockEntity, float partialTicks, PoseStack poseS
Vec2 nearPoint = Util.nearestPoint(le, ri, v2fWorldPos);
Vec2 facing = v2fWorldPos.add(nearPoint.negated());
float behind = -facing.dot(fwd);
- behind += (float)shapeNoise * 600.0F * (float)scale * 0.2F;
- float sze = 600.0F * (float)scale * 1.5F * 3.0F;
- behind += (float)stormSize / 2.0F;
+ behind += (float) shapeNoise * 600.0F * (float) scale * 0.2F;
+ float sze = 600.0F * (float) scale * 1.5F * 3.0F;
+ behind += (float) stormSize / 2.0F;
if (behind > 0.0F) {
sze *= Mth.lerp(Mth.clamp(smoothStage - 1.0F, 0.0F, 1.0F), 1.0F, 4.0F);
float p = Mth.clamp(Math.abs(behind) / sze, 0.0F, 1.0F);
float start = 0.06F;
if (p <= start) {
p /= start;
- localDBZ += (float)Math.pow(p, 2.0F);
+ localDBZ += (float) Math.pow(p, 2.0F);
} else {
p = 1.0F - (p - start) / (1.0F - start);
- localDBZ += (float)Math.pow(p, 4.0F);
+ localDBZ += (float) Math.pow(p, 4.0F);
}
}
localDBZ *= Mth.sqrt(1.0F - Mth.clamp(dist / sze, 0.0F, 1.0F));
if (smoothStage > 3.0F) {
float p = Mth.clamp((smoothStage - 3.0F) / 2.0F, 0.0F, 0.5F);
- localDBZ *= 0.8F + (float)shapeNoise2 * 0.4F * (1.0F - p);
- localDBZ *= 0.8F + (float)shapeNoise * 0.4F * (1.0F - p);
+ localDBZ *= 0.8F + (float) shapeNoise2 * 0.4F * (1.0F - p);
+ localDBZ *= 0.8F + (float) shapeNoise * 0.4F * (1.0F - p);
localDBZ *= 1.0F + p * 0.25F;
} else {
- localDBZ *= 0.8F + (float)shapeNoise2 * 0.4F;
- localDBZ *= 0.8F + (float)shapeNoise * 0.4F;
+ localDBZ *= 0.8F + (float) shapeNoise2 * 0.4F;
+ localDBZ *= 0.8F + (float) shapeNoise * 0.4F;
}
localDBZ *= Mth.sqrt(intensity);
}
- if (storm.stormType == 0) {
- double dist = worldPos.distanceTo(storm.position.multiply(1.0F, 0.0F, 1.0F));
- if (dist > stormSize * (double)4.0F) {
+ if (storm.stormType == StormType.SUPERCELL.idx()) {
+ double dist = worldPos.multiply(1.0F, 0.0F, 1.0F).distanceTo(storm.position.multiply(1.0F, 0.0F, 1.0F));
+ if (dist > stormSize * (double) 4.0F) {
continue;
}
- float var178;
- switch (storm.stage) {
- case 1 -> var178 = 0.1F + (float)storm.energy / 100.0F * 0.7F;
- case 2 -> var178 = 0.8F + (float)storm.energy / 100.0F * 0.4F;
- case 3 -> var178 = 1.2F + (float)storm.windspeed / 100.0F;
- default -> var178 = (float)Math.pow((float)storm.energy / 100.0F, 2.0F) * 0.1F;
- }
+ float intensity = switch (storm.stage) {
+ case 1 -> 0.1F + (float) storm.energy / 100.0F * 0.7F;
+ case 2 -> 0.8F + (float) storm.energy / 100.0F * 0.4F;
+ case 3 -> 1.2F + (float) storm.energy / 100.0F;
+ default -> (float) Math.pow(storm.energy / 100.0F, 2.0F) * 0.1F;
+ };
- float intensity = var178;
if (intensity > 0.8F) {
intensity = 0.8F + (intensity - 0.8F) / 4.0F;
}
- switch (storm.stage) {
- case 2 -> var178 = (float)storm.energy / 100.0F * 40.0F;
- case 3 -> var178 = 40.0F + (float)storm.windspeed;
- default -> var178 = 0.0F;
- }
+ float windspeed = switch (storm.stage) {
+ case 2 -> (float) storm.energy / 100.0F * 40.0F;
+ case 3 -> 40.0F + (float) storm.windspeed;
+ default -> 0.0F;
+ };
- float windspeed = var178;
if (windspeed > 60.0F) {
windspeed -= (windspeed - 60.0F) * 0.2F;
}
@@ -262,54 +351,61 @@ private void render(BlockEntity blockEntity, float partialTicks, PoseStack poseS
xM = 1.0F;
}
- double coreDist = Math.sqrt(Math.pow((worldPos.x - corePos.x) * (double)xM, 2.0F) + Math.pow((worldPos.z - corePos.z) * (double)1.5F, 2.0F)) / scale;
+ double coreDist = Math.sqrt(Math.pow((worldPos.x - corePos.x) * (double) xM, 2.0F) + Math.pow((worldPos.z - corePos.z) * (double) 1.5F, 2.0F)) / scale;
dist /= scale;
coreDist *= 0.9 + shapeNoise * 0.3;
Vec3 relPos = torPos.subtract(worldPos).multiply(scale, 0.0F, scale);
- double d = (double)150.0F + dist / (double)3.0F;
- double d2 = (double)75.0F + dist / (double)3.0F;
+ double d = (double) 150.0F + dist / (double) 3.0F;
+ double d2 = (double) 75.0F + dist / (double) 3.0F;
double angle = Math.atan2(relPos.z, relPos.x) - dist / d;
double angle2 = Math.atan2(relPos.z, relPos.x) - dist / d2;
- double angle3 = Math.atan2(relPos.z, relPos.x) - dist / d2 / (double)2.0F;
+ double angle3 = Math.atan2(relPos.z, relPos.x) - dist / d2 / (double) 2.0F;
angle += Math.toRadians(180.0F);
angle2 += Math.toRadians(180.0F);
angle3 += Math.toRadians(180.0F);
- double angleMod = Math.toRadians(40.0F) * ((double)1.0F - Math.clamp(Math.pow((double)windspeed / (double)100.0F, 2.0F), 0.0F, 0.9));
- double noise = (shapeNoise4 - (double)0.5F) * Math.toRadians(10.0F);
+ double angleMod = Math.toRadians(40.0F) * ((double) 1.0F - Math.clamp(Math.pow((double) windspeed / (double) 100.0F, 2.0F), 0.0F, 0.9));
+ double noise = (shapeNoise4 - (double) 0.5F) * Math.toRadians(10.0F);
angle += angleMod + noise;
angle2 += angleMod + noise;
angle3 += angleMod + noise;
double inflow = Math.sin(angle - Math.toRadians(15.0F));
inflow = Math.pow(Math.abs(inflow), 0.5F) * Math.sin(inflow);
- inflow *= (double)1.0F - Math.clamp(dist / (double)2400.0F, 0.0F, 1.0F);
- if (inflow < (double)0.0F) {
- localDBZ += (float)(inflow * (double)2.0F * Math.pow(Math.clamp((double)(windspeed - 15.0F) / (double)50.0F, 0.0F, 1.0F), 2.0F));
+ inflow *= (double) 1.0F - Math.clamp(dist / (double) 2400.0F, 0.0F, 1.0F);
+ if (inflow < (double) 0.0F) {
+ localDBZ += (float) (inflow * (double) 2.0F * Math.pow(Math.clamp((double) (windspeed - 15.0F) / (double) 50.0F, 0.0F, 1.0F), 2.0F));
}
double surge = Math.sin(angle2 - Math.toRadians(60.0F));
surge = Math.abs(surge) * Math.sin(surge);
- surge *= ((double)1.0F - Math.pow(Math.clamp(dist / (double)1200.0F, 0.0F, 1.0F), 1.5F)) * ((double)1.0F - Math.clamp(dist / (double)200.0F, 0.0F, 0.3));
- if (surge > (double)0.0F) {
- double n = 0.8 * ((double)1.0F - Math.clamp(Math.pow((double)windspeed / (double)80.0F, 2.0F), 0.0F, 1.0F));
- double m = (double)1.0F - shapeNoise4 * n;
- localDBZ += (float)(surge * (double)1.5F * Math.clamp(dist / (double)500.0F, 0.0F, 1.0F) * Math.sqrt(Math.clamp((double)(windspeed - 20.0F) / (double)50.0F, 0.0F, 1.0F)) * m);
+ surge *= ((double) 1.0F - Math.pow(Math.clamp(dist / (double) 1200.0F, 0.0F, 1.0F), 1.5F)) * ((double) 1.0F - Math.clamp(dist / (double) 200.0F, 0.0F, 0.3));
+ if (surge > (double) 0.0F) {
+ double n = 0.8 * ((double) 1.0F - Math.clamp(Math.pow((double) windspeed / (double) 80.0F, 2.0F), 0.0F, 1.0F));
+ double m = (double) 1.0F - shapeNoise4 * n;
+ localDBZ += (float) (surge * (double)1.5F * Math.clamp(dist / (double) 500.0F, 0.0F, 1.0F) * Math.sqrt(Math.clamp((double) (windspeed - 20.0F) / (double) 50.0F, 0.0F, 1.0F)) * m);
}
double shield = Math.sin(angle3 - Math.toRadians(60.0F));
shield = Math.abs(shield) * Math.sin(shield);
- shield *= (double)1.0F - Math.pow(Math.clamp(dist / (double)2400.0F, 0.0F, 1.0F), 2.0F);
- if (shield > (double)0.0F) {
- localDBZ -= (float)(shield * (double)2.0F * Math.clamp(dist / (double)1000.0F, 0.0F, 1.0F) * Math.sqrt(Math.clamp((double)(windspeed - 30.0F) / (double)80.0F, 0.0F, 1.0F)));
+ shield *= (double) 1.0F - Math.pow(Math.clamp(dist / (double) 2400.0F, 0.0F, 1.0F), 2.0F);
+ if (shield > (double) 0.0F) {
+ localDBZ -= (float) (shield * (double) 2.0F * Math.clamp(dist / (double) 1000.0F, 0.0F, 1.0F) * Math.sqrt(Math.clamp((double) (windspeed - 30.0F) / (double) 80.0F, 0.0F, 1.0F)));
}
- double coreIntensity = ((double)1.0F - Math.clamp(coreDist / (double)1800.0F, 0.0F, 1.0F)) * ((double)1.5F - shapeNoise2 * (double)0.5F) * Math.sqrt(Math.clamp((double)intensity / (double)2.0F, 0.0F, 1.0F)) * Math.clamp(dist / (double)300.0F, 0.5F, 1.0F) * 1.2;
- localDBZ += (float)Math.pow(coreIntensity, 0.65);
+ double coreIntensity = ((double) 1.0F - Math.clamp(coreDist / (double) 1800.0F, 0.0F, 1.0F)) * ((double) 1.5F - shapeNoise2 * (double) 0.5F) * Math.sqrt(Math.clamp((double) intensity / (double) 2.0F, 0.0F, 1.0F)) * Math.clamp(dist / (double) 300.0F, 0.5F, 1.0F) * 1.2;
+ localDBZ += (float) Math.pow(coreIntensity, 0.65);
}
dbz = Math.max(dbz, localDBZ);
}
}
+ float v = Math.max(clouds - 0.15F, 0.0F) * 4.0F;
+ if (v > 0.4F) {
+ float dif = (v - 0.4F) / 2.0F;
+ v -= dif;
+ }
+
+ dbz = Math.max(dbz, v);
dbz += (PMWeather.RANDOM.nextFloat() - 0.5F) * 5.0F / 60.0F;
vel += (PMWeather.RANDOM.nextFloat() - 0.5F) * 3.0F;
if (dbz > 1.0F) {
@@ -330,21 +426,54 @@ private void render(BlockEntity blockEntity, float partialTicks, PoseStack poseS
// PMWeatherAPI: Support custom radar modes
if (!PMWClientConfig.disableCustomRadarModeRendering) {
- PixelRenderData pixelRenderData = new PixelRenderData(canRender, dbz * 60.0F, vel, temp, x, z, resolution, renderData);
+ PixelRenderData pixelRenderData = new PixelRenderData(canRender, dbz * 60.0F, vel, temp, x, z, resolution, worldPos, renderData);
color = radarMode.getColorForPixel(pixelRenderData);
PMWClientStorages.RADAR_MODE_COLORS.get(radarMode).put(id, color);
}
}
float rdbz = dbz * 60.0F;
+ Color startColor = radarBlockEntity.terrainMap.getOrDefault(id, Color.BLACK);
+ if (radarBlockEntity.init && update) {
+ Holder biome = radarBlockEntity.getNearestBiome(new BlockPos((int) worldPos.x, (int) worldPos.y, (int) worldPos.z));
+ String rn = biome.getRegisteredName().toLowerCase();
+ if (rn.contains("ocean") || rn.contains("river")) startColor = new Color(biome.value().getWaterColor());
+ else if (rn.contains("beach") || rn.contains("desert")) {
+ if (rn.contains("badlands")) startColor = new Color(214, 111, 42);
+ else startColor = new Color(biome.value().getGrassColor(worldPos.x, worldPos.z));
+ } else startColor = new Color(227, 198, 150);
+ }
if (PMWClientConfig.disableCustomRadarModeRendering) {
- color = ColorTables.getReflectivity(rdbz);
+ color = ColorTables.getReflectivity(rdbz, startColor);
+
+ if (rdbz > 5.0F && !radarBlockEntity.hasRangeUpgrade) {
+ if (temp < 3.0F && temp > -1.0F) {
+ color = ColorTables.getMixedReflectivity(rdbz);
+ } else if (temp <= -1.0F) {
+ color = ColorTables.getSnowReflectivity(rdbz);
+ }
+ }
+
if (radarMode == RadarMode.VELOCITY) {
color = new Color(0, 0, 0);
vel /= 1.75F;
color = ColorTables.lerp(Mth.clamp(Math.max(rdbz, (Mth.abs(vel) - 18.0F) / 0.65F) / 12.0F, 0.0F, 1.0F), color, ColorTables.getVelocity(vel));
}
+
+ if (radarMode == RadarMode.IR) {
+ float ir = rdbz * 10.0F;
+ if (rdbz > 10.0F) {
+ ir = 100.0F + (rdbz - 10.0F) * 2.5F;
+ }
+
+ if (rdbz > 50.0F) {
+ ir += (rdbz - 50.0F) * 5.0F;
+ }
+
+ color = ColorTables.getIR(ir);
+ }
+
}
if (ClientConfig.radarDebugging && update) {
@@ -359,6 +488,15 @@ private void render(BlockEntity blockEntity, float partialTicks, PoseStack poseS
}
}
+ if (clientRadarMode == ClientConfig.RadarMode.SST) {
+ Float t = ThermodynamicEngine.GetSST(GameBusClientEvents.weatherHandler, worldPos, blockEntity.getLevel(), radarBlockEntity, 0);
+ if (t == null) {
+ dbg = new Color(0, 0, 0);
+ } else {
+ dbg = ColorTables.getSST(t);
+ }
+ }
+
if (clientRadarMode == ClientConfig.RadarMode.WINDFIELDS && GameBusClientEvents.weatherHandler != null) {
Vec3 wP = (new Vec3(x, 0.0F, z)).multiply(1.0F / (float)resolution, 0.0F, 1.0F / (float)resolution).multiply(256.0F, 0.0F, 256.0F).add(pos.getCenter());
float wind = 0.0F;
@@ -370,6 +508,12 @@ private void render(BlockEntity blockEntity, float partialTicks, PoseStack poseS
dbg = ColorTables.getWindspeed(wind);
}
+ if (clientRadarMode == ClientConfig.RadarMode.GLOBALWINDS && GameBusClientEvents.weatherHandler != null) {
+ int height = GameBusClientEvents.weatherHandler.getWorld().getHeight(Heightmap.Types.MOTION_BLOCKING, (int)worldPos.x, (int)worldPos.z);
+ float wind = (float)WindEngine.getWind(new Vec3(worldPos.x, (double)height, worldPos.z), GameBusClientEvents.weatherHandler.getWorld(), false, false, false, true).length();
+ dbg = ColorTables.getHurricaneWindspeed(wind);
+ }
+
if (clientRadarMode == ClientConfig.RadarMode.CAPE) {
Sounding sounding = new Sounding(GameBusClientEvents.weatherHandler, worldPos, blockEntity.getLevel(), 500, 12000, radarBlockEntity);
Sounding.CAPE CAPE = sounding.getCAPE(sounding.getSBParcel());
@@ -428,7 +572,7 @@ private void render(BlockEntity blockEntity, float partialTicks, PoseStack poseS
}
}
- int color = radarMode.getDotColor().hashCode();
+ int color = radarMode.getDotColor().getRGB();
Vector3f topLeft = (new Vector3f(-1.0F, 0.0F, -1.0F)).mul(0.015F).add(0.0F, 0.01F, 0.0F);
Vector3f bottomLeft = (new Vector3f(-1.0F, 0.0F, 1.0F)).mul(0.015F).add(0.0F, 0.01F, 0.0F);
Vector3f bottomRight = (new Vector3f(1.0F, 0.0F, 1.0F)).mul(0.015F).add(0.0F, 0.01F, 0.0F);
diff --git a/src/main/java/net/nullved/pmweatherapi/radar/RadarMode.java b/src/main/java/net/nullved/pmweatherapi/radar/RadarMode.java
index dbcdf1d..af90357 100644
--- a/src/main/java/net/nullved/pmweatherapi/radar/RadarMode.java
+++ b/src/main/java/net/nullved/pmweatherapi/radar/RadarMode.java
@@ -1,9 +1,13 @@
package net.nullved.pmweatherapi.radar;
import dev.protomanly.pmweather.PMWeather;
+import dev.protomanly.pmweather.block.entity.RadarBlockEntity;
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.Holder;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
import net.minecraft.util.StringRepresentable;
+import net.minecraft.world.level.biome.Biome;
import net.nullved.pmweatherapi.client.render.PixelRenderData;
import net.nullved.pmweatherapi.client.render.RadarOverlays;
import net.nullved.pmweatherapi.data.PMWExtras;
@@ -53,7 +57,11 @@ public class RadarMode implements StringRepresentable, Comparable {
* A Radar Mode that is a copy of PMWeather's Reflectivity
* @since 0.14.15.6
*/
- public static final RadarMode REFLECTIVITY = create(PMWeather.getPath("reflectivity"), prd -> ColorMaps.REFLECTIVITY.get(prd.rdbz()));
+ public static final RadarMode REFLECTIVITY = create(PMWeather.getPath("reflectivity"), prd -> {
+ Holder biome = ((RadarBlockEntity) prd.renderData().blockEntity()).getNearestBiome(new BlockPos((int) prd.worldPos().x, (int) prd.worldPos().y, (int) prd.worldPos().z));
+ if (biome != null) return ColorMaps.REFLECTIVITY.getWithBiome(prd.rdbz(), biome, prd.worldPos());
+ else return ColorMaps.REFLECTIVITY.get(prd.rdbz());
+ });
/**
* A Radar Mode that is a copy of PMWeather's Velocity
@@ -65,6 +73,25 @@ public class RadarMode implements StringRepresentable, Comparable {
return ColorMap.lerp(Mth.clamp(Math.max(prd.rdbz(), (Mth.abs(prd.velocity() / 1.75F) - 18.0F) / 0.65F) / 12.0F, 0.0F, 1.0F), Color.BLACK, velCol);
});
+ /**
+ * A Radar Mode that is a copy of PMWeather's IR
+ * @since 0.15.0.0
+ */
+ public static final RadarMode IR = create(PMWeather.getPath("ir"), prd -> {
+ float rdbz = prd.rdbz();
+ float ir = rdbz * 10.0F;
+
+ if (rdbz > 10.0F) {
+ ir = 100.0F + (rdbz - 10.0F) * 2.5F;
+ }
+
+ if (rdbz > 50.0F) {
+ ir += (rdbz - 50.0F) * 5.0F;
+ }
+
+ return ColorMaps.IR.get(ir);
+ });
+
private final ResourceLocation id;
private final Function colorFunction;
private final Color dotColor;
diff --git a/src/main/java/net/nullved/pmweatherapi/storm/NearbyStorms.java b/src/main/java/net/nullved/pmweatherapi/storm/NearbyStorms.java
index befd3dc..6440ea9 100644
--- a/src/main/java/net/nullved/pmweatherapi/storm/NearbyStorms.java
+++ b/src/main/java/net/nullved/pmweatherapi/storm/NearbyStorms.java
@@ -10,6 +10,7 @@
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
+import net.nullved.pmweatherapi.util.StormType;
import java.util.ArrayList;
import java.util.HashMap;
@@ -152,4 +153,103 @@ public void forStormNearPlayer(Player player, double radius, Consumer con
List storms = stormsNearPlayer(player, radius);
for (Storm storm: storms) consumer.accept(storm);
}
+
+ /**
+ * Returns a {@link List} of {@link Storm}s that meet the {@link StormType} in a defined radius around the block
+ * @param block The {@link BlockPos} of the block at the center of the search area
+ * @param radius The radius of the search area
+ * @param type The {@link StormType} to match
+ * @return A {@link List} of {@link Storm}s
+ * @since 0.15.0.0
+ */
+ public List stormsNearBlock(BlockPos block, double radius, StormType type) {
+ List allStorms = handler.getStorms();
+ List nearStorms = new ArrayList<>();
+
+ for (Storm storm: allStorms) {
+ Vec3 pos = storm.position;
+ if (pos.distanceTo(block.getCenter()) <= radius && type.matches(storm)) nearStorms.add(storm);
+ }
+
+ return nearStorms;
+ }
+
+ /**
+ * Returns a {@link List} of {@link Storm}s that meet the {@link StormType} in a defined radius around the center of the chunk
+ * @param chunk The {@link ChunkPos} of the chunk at the center of the search area.
+ * @param radius The radius of the search area
+ * @param type The {@link StormType} to match
+ * @return A {@link List} of {@link Storm}s
+ * @since 0.15.0.0
+ */
+ public List stormsNearChunk(ChunkPos chunk, double radius, StormType type) {
+ List allStorms = handler.getStorms();
+ List nearStorms = new ArrayList<>();
+
+ for (Storm storm: allStorms) {
+ Vec3 pos = storm.position;
+ if (pos.distanceTo(chunk.getWorldPosition().getCenter()) <= radius && type.matches(storm)) nearStorms.add(storm);
+ }
+
+ return nearStorms;
+ }
+
+ /**
+ * Returns a {@link List} of {@link Storm}s that meet the {@link StormType} in a defined radius around the given {@link Player}
+ * @param player The {@link Player} to search around
+ * @param radius The radius of the search area
+ * @param type The {@link StormType} to match
+ * @return A {@link List} of {@link Storm}s
+ * @since 0.15.0.0
+ */
+ public List stormsNearPlayer(Player player, double radius, StormType type) {
+ List allStorms = handler.getStorms();
+ List nearStorms = new ArrayList<>();
+
+ for (Storm storm: allStorms) {
+ Vec3 pos = storm.position;
+ if (pos.distanceTo(player.position()) <= radius && type.matches(storm)) nearStorms.add(storm);
+ }
+
+ return nearStorms;
+ }
+
+ /**
+ * Executes the given {@link Consumer} for each {@link Storm} that meets the {@link StormType} in a defined radius around the block
+ * @param block The {@link BlockPos} of the block at the center of the search area
+ * @param radius The radius of the search area
+ * @param type The {@link StormType} to match
+ * @param consumer The {@link Consumer} to execute for each {@link Storm}
+ * @since 0.15.0.0
+ */
+ public void forStormNearBlock(BlockPos block, double radius, StormType type, Consumer consumer) {
+ List storms = stormsNearBlock(block, radius, type);
+ for (Storm storm: storms) consumer.accept(storm);
+ }
+
+ /**
+ * Executes the given {@link Consumer} for each {@link Storm} that meets the {@link StormType} in a defined radius around the center of the chunk
+ * @param chunk The {@link ChunkPos} of the chunk at the center of the search area.
+ * @param radius The radius of the search area
+ * @param type The {@link StormType} to match
+ * @param consumer The {@link Consumer} to execute for each {@link Storm}
+ * @since 0.15.0.0
+ */
+ public void forStormNearChunk(ChunkPos chunk, double radius, StormType type, Consumer consumer) {
+ List storms = stormsNearChunk(chunk, radius, type);
+ for (Storm storm: storms) consumer.accept(storm);
+ }
+
+ /**
+ * Executes the given {@link Consumer} for each {@link Storm} that meets the {@link StormType} in a defined radius around the given {@link Player}
+ * @param player The {@link Player} to search around
+ * @param radius The radius of the search area
+ * @param type The {@link StormType} to match
+ * @param consumer The {@link Consumer} to execute for each {@link Storm}
+ * @since 0.15.0.0
+ */
+ public void forStormNearPlayer(Player player, double radius, StormType type, Consumer consumer) {
+ List storms = stormsNearPlayer(player, radius, type);
+ for (Storm storm: storms) consumer.accept(storm);
+ }
}
diff --git a/src/main/java/net/nullved/pmweatherapi/storm/StormBuilder.java b/src/main/java/net/nullved/pmweatherapi/storm/StormBuilder.java
index 0a1ce98..5843c13 100644
--- a/src/main/java/net/nullved/pmweatherapi/storm/StormBuilder.java
+++ b/src/main/java/net/nullved/pmweatherapi/storm/StormBuilder.java
@@ -10,19 +10,20 @@
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
+import net.nullved.pmweatherapi.util.StormType;
/**
* A builder for {@link Storm}s that makes it easy to create and spawn them.
- * Create a StormBuilder with any constructor or {@link #atPlayer(Type, Player)}
+ * Create a StormBuilder with any constructor or {@link #atPlayer(StormType, Player)}
*
* To spawn the storm in the world, use {@link #buildAndSpawn()}. To only create the storm instance, use {@link #build()}
* @since 0.14.15.5
*/
public class StormBuilder {
private final WeatherHandler weatherHandler;
- private final Type type;
+ private final StormType type;
private final Vec3 position;
- private float risk = 0.5f, rankineFactor = 4.5F, width = 15.0F;
+ private float risk = 0.5f, rankineFactor = 4.5F, width = 15.0F, smoothWidth = 15.0F, cycloneWindspeed = 0.0F, smoothWindspeed = 0.0F;
private int windspeed = 0, maxWindspeed = 0, stage = 0, maxStage = 2, energy = 0, coldEnergy = 0, maxColdEnergy = 300, maxWidth = 15;
private Vec3 velocity = Vec3.ZERO;
private boolean visualOnly = false, aimAtAnyPlayer = false;
@@ -31,11 +32,11 @@ public class StormBuilder {
/**
* Create a new {@link StormBuilder}
* @param weatherHandler The {@link WeatherHandler} to use
- * @param type The {@link Type} of storm
+ * @param type The {@link StormType} of storm
* @param position The {@link Vec3} representing the position of the {@link Storm}
* @since 0.14.15.5
*/
- public StormBuilder(WeatherHandler weatherHandler, Type type, Vec3 position) {
+ public StormBuilder(WeatherHandler weatherHandler, StormType type, Vec3 position) {
this.weatherHandler = weatherHandler;
this.type = type;
this.position = position;
@@ -44,33 +45,33 @@ public StormBuilder(WeatherHandler weatherHandler, Type type, Vec3 position) {
/**
* Create a new {@link StormBuilder}
* @param level The {@link Level} to spawn in
- * @param type The {@link Type} of storm
+ * @param type The {@link StormType} of storm
* @param position The {@link Vec3} representing the position of the {@link Storm}
* @since 0.14.15.5
*/
- public StormBuilder(Level level, Type type, Vec3 position) {
+ public StormBuilder(Level level, StormType type, Vec3 position) {
this(GameBusEvents.MANAGERS.get(level.dimension()), type, position);
}
/**
* Create a new {@link StormBuilder}
* @param dimension The {@link ResourceKey} of the dimension to spawn in
- * @param type The {@link Type} of storm
+ * @param type The {@link StormType} of storm
* @param position The {@link Vec3} representing the position of the {@link Storm}
* @since 0.14.15.5
*/
- public StormBuilder(ResourceKey dimension, Type type, Vec3 position) {
+ public StormBuilder(ResourceKey dimension, StormType type, Vec3 position) {
this(GameBusEvents.MANAGERS.get(dimension), type, position);
}
/**
* Creates a {@link StormBuilder} at a {@link Player}'s dimension and position
- * @param type The {@link Type} of storm
+ * @param type The {@link StormType} of storm
* @param player The {@link Player} to grab the dimension and position from
* @return A new {@link StormBuilder}
* @since 0.14.15.5
*/
- public static StormBuilder atPlayer(Type type, Player player) {
+ public static StormBuilder atPlayer(StormType type, Player player) {
return new StormBuilder(player.level(), type, player.position());
}
@@ -142,6 +143,17 @@ public StormBuilder width(float width) {
return this;
}
+ /**
+ * Sets the smooth width of the {@link Storm}
+ * @param smoothWidth The smooth width
+ * @return The {@link StormBuilder} instance
+ * @since 0.15.0.0
+ */
+ public StormBuilder smoothWidth(float smoothWidth) {
+ this.smoothWidth = smoothWidth;
+ return this;
+ }
+
/**
* Sets the max width of the {@link Storm}
* @param maxWidth The max width
@@ -219,6 +231,28 @@ public StormBuilder windspeed(int windspeed) {
return this;
}
+ /**
+ * Sets the cyclone windspeed of the {@link Storm}
+ * @param cycloneWindspeed The cyclone windspeed
+ * @return The {@link StormBuilder} instance
+ * @since 0.15.0.0
+ */
+ public StormBuilder cycloneWindspeed(float cycloneWindspeed) {
+ this.cycloneWindspeed = cycloneWindspeed;
+ return this;
+ }
+
+ /**
+ * Sets the smooth windspeed of the {@link Storm}
+ * @param smoothWindspeed The smooth windspeed
+ * @return The {@link StormBuilder} instance
+ * @since 0.15.0.0
+ */
+ public StormBuilder smoothWindspeed(float smoothWindspeed) {
+ this.smoothWindspeed = smoothWindspeed;
+ return this;
+ }
+
/**
* Sets the max windspeed of the {@link Storm}
* @param maxWindspeed The max windspeed
@@ -249,12 +283,12 @@ public StormBuilder velocity(Vec3 velocity) {
* @since 0.14.15.5
*/
public Storm build() {
- Storm storm = new Storm(weatherHandler, weatherHandler.getWorld(), risk, type.ordinal());
+ Storm storm = new Storm(weatherHandler, weatherHandler.getWorld(), risk, type.idx());
storm.initFirstTime();
storm.visualOnly = visualOnly;
storm.velocity = velocity;
if (aimAtPlayer != null) {
- if (storm.stormType != Type.SQUALL.ordinal()) {
+ if (storm.stormType != StormType.SQUALL.idx()) {
Vec3 aimPos = aimAtPlayer.position().add(new Vec3((double)(PMWeather.RANDOM.nextFloat() - 0.5F) * ServerConfig.aimAtPlayerOffset, 0.0F, (double)(PMWeather.RANDOM.nextFloat() - 0.5F) * ServerConfig.aimAtPlayerOffset));
if (storm.position.distanceTo(aimPos) >= ServerConfig.aimAtPlayerOffset) {
Vec3 toward = storm.position.subtract(new Vec3(aimPos.x, storm.position.y, aimPos.z)).multiply(1.0F, 0.0F, 1.0F).normalize();
@@ -266,7 +300,7 @@ public Storm build() {
}
}
if (aimAtAnyPlayer) {
- if (storm.stormType != Type.SQUALL.ordinal()) {
+ if (storm.stormType != StormType.SQUALL.idx()) {
Player nearest = storm.level.getNearestPlayer(this.position.x, this.position.y, this.position.z, 4096.0F, false);
if (nearest != null) {
Vec3 aimPos = aimAtPlayer.position().add(new Vec3((double) (PMWeather.RANDOM.nextFloat() - 0.5F) * ServerConfig.aimAtPlayerOffset, 0.0F, (double) (PMWeather.RANDOM.nextFloat() - 0.5F) * ServerConfig.aimAtPlayerOffset));
@@ -282,15 +316,18 @@ public Storm build() {
}
}
storm.position = position;
- storm.stage = stage;
+ storm.stage = Math.max(stage, type.stage());
storm.maxStage = maxStage;
storm.energy = energy;
storm.width = width;
+ storm.smoothWidth = smoothWidth;
storm.maxWidth = maxWidth;
storm.rankineFactor = rankineFactor;
storm.coldEnergy = coldEnergy;
storm.maxColdEnergy = maxColdEnergy;
storm.windspeed = windspeed;
+ storm.cycloneWindspeed = cycloneWindspeed;
+ storm.smoothWindspeed = smoothWindspeed;
storm.maxWindspeed = maxWindspeed;
return storm;
}
@@ -309,21 +346,4 @@ public Storm buildAndSpawn() {
}
return storm;
}
-
- /**
- * An enum with types of {@link Storm}s
- * @since 0.14.15.5
- */
- public enum Type {
- /**
- * Represents both supercells and tornados
- * @since 0.14.15.5
- */
- SUPERCELL,
- /**
- * Represents squall lines
- * @since 0.14.15.5
- */
- SQUALL
- }
}
diff --git a/src/main/java/net/nullved/pmweatherapi/util/ColorMap.java b/src/main/java/net/nullved/pmweatherapi/util/ColorMap.java
index b088ac1..bf7e50d 100644
--- a/src/main/java/net/nullved/pmweatherapi/util/ColorMap.java
+++ b/src/main/java/net/nullved/pmweatherapi/util/ColorMap.java
@@ -1,6 +1,9 @@
package net.nullved.pmweatherapi.util;
import dev.protomanly.pmweather.util.ColorTables;
+import net.minecraft.core.Holder;
+import net.minecraft.world.level.biome.Biome;
+import net.minecraft.world.phys.Vec3;
import java.awt.Color;
import java.util.*;
@@ -20,14 +23,14 @@ public class ColorMap {
private final NavigableMap overridePoints;
private final Color base;
- private final float min;
- private final float max;
+ private final float min, max, firstThreshold;
private Color[] lookup;
private float resolution;
private ColorMap(Color base, boolean overrideModeGreater, List segments, NavigableMap overridePoints, float resolution) {
this.min = Math.round(segments.getFirst().start / resolution) * resolution;
this.max = Math.round(segments.getLast().end / resolution) * resolution;
+ this.firstThreshold = segments.getFirst().end;
this.base = base;
this.overrideModeGreater = overrideModeGreater;
@@ -90,6 +93,20 @@ public Color get(float val) {
return lookup[idx];
}
+ public Color getWithBiome(float val, Holder biome, Vec3 worldPos) {
+ Color startColor = Color.BLACK;
+ String rn = biome.getRegisteredName().toLowerCase();
+ if (rn.contains("ocean") || rn.contains("river")) startColor = new Color(biome.value().getWaterColor());
+ else if (rn.contains("beach") || rn.contains("desert")) {
+ if (rn.contains("badlands")) startColor = new Color(214, 111, 42);
+ else startColor = new Color(biome.value().getGrassColor(worldPos.x, worldPos.z));
+ } else startColor = new Color(227, 198, 150);
+
+ if (val < firstThreshold) {
+ return lerp(Math.clamp(val / (firstThreshold - min), 0.0F, 1.0F), startColor, segments.firstEntry().getValue().to);
+ } else return get(val);
+ }
+
/**
* Gets the {@link Color} for the specific value.
*
@@ -173,6 +190,15 @@ public static Builder of(Color base) {
return new Builder(base);
}
+ /**
+ * Creates a new {@link ColorMap.Builder} that has a default {@link Color#BLACK} base
+ * @return The created {@link ColorMap.Builder}
+ * @since 0.15.0.0
+ */
+ public static Builder biome() {
+ return new Builder(Color.BLACK);
+ }
+
/**
* Sets the step size between each value in the lookup table.
* A value too small may be storing the same color multiple times!
@@ -235,7 +261,7 @@ public Builder override(Color color, float threshold) {
*/
public ColorMap build(Color finalColor, float finalThreshold) {
if (!segments.isEmpty()) {
- LerpSegment first = segments.get(0);
+ LerpSegment first = segments.getFirst();
if (first.start > 0.0F) {
segments.add(0, new LerpSegment(0.0F, base, first.start, first.from));
}
diff --git a/src/main/java/net/nullved/pmweatherapi/util/ColorMaps.java b/src/main/java/net/nullved/pmweatherapi/util/ColorMaps.java
index 5a6eb93..41a38bc 100644
--- a/src/main/java/net/nullved/pmweatherapi/util/ColorMaps.java
+++ b/src/main/java/net/nullved/pmweatherapi/util/ColorMaps.java
@@ -11,10 +11,10 @@
*/
public class ColorMaps {
/**
- * A {@link ColorMap} equivalent to {@link ColorTables#getReflectivity(float)}
+ * A {@link ColorMap} equivalent to {@link ColorTables#getReflectivity(float, Color)}
* @since 0.14.15.6
*/
- public static final ColorMap REFLECTIVITY = ColorMap.Builder.of(Color.BLACK)
+ public static final ColorMap REFLECTIVITY = ColorMap.Builder.biome()
.addPoint(new Color(0x5C9DAE), 19.0F)
.addPoint(new Color(0x0B6409), 27.0F)
.addPoint(new Color(0xC5B300), 40.0F)
@@ -67,4 +67,20 @@ public class ColorMaps {
.addPoint(new Color(246, 53, 53), 200.0F)
.addPoint(new Color(240, 53, 246), 250.0F)
.build(new Color(255, 255, 255), 300.0F);
+
+ /**
+ * A {@link ColorMap} equivalent to {@link ColorTables#getIR(float)}
+ * @since 0.15.0.0
+ */
+ public static final ColorMap IR = ColorMap.Builder.of(Color.BLACK)
+ .addPoint(Color.WHITE, 100.0F)
+ .addPoint(new Color(0, 16, 116), 120.0F)
+ .addPoint(new Color(105, 248, 251), 140.0F)
+ .addPoint(new Color(0, 253, 0), 150.0F)
+ .addPoint(new Color(253, 251, 71), 160.0F)
+ .addPoint(new Color(235, 55, 23), 180.0F)
+ .addPoint(new Color(110, 26, 10), 200.0F)
+ .addPoint(Color.BLACK, 220.0F)
+ .build(Color.WHITE, 260.0F);
+
}
diff --git a/src/main/java/net/nullved/pmweatherapi/util/StormType.java b/src/main/java/net/nullved/pmweatherapi/util/StormType.java
new file mode 100644
index 0000000..4413a57
--- /dev/null
+++ b/src/main/java/net/nullved/pmweatherapi/util/StormType.java
@@ -0,0 +1,34 @@
+package net.nullved.pmweatherapi.util;
+
+import dev.protomanly.pmweather.weather.Storm;
+
+public enum StormType {
+ SUPERCELL(0),
+ TORNADO(0, 3),
+ SQUALL(1),
+ CYCLONE(2);
+
+ public final int idx, stage;
+
+ StormType(int idx, int stage) {
+ this.idx = idx;
+ this.stage = stage;
+ }
+
+ StormType(int idx) {
+ this.idx = idx;
+ this.stage = -1;
+ }
+
+ public int idx() {
+ return idx;
+ }
+
+ public int stage() {
+ return stage;
+ }
+
+ public boolean matches(Storm storm) {
+ return storm.stormType == idx && storm.stage >= stage;
+ }
+}
From 4151ffc239a8dc98f797f74eb64f28e2cab6aec7 Mon Sep 17 00:00:00 2001
From: RealDarkStudios <94769968+RealDarkStudios@users.noreply.github.com>
Date: Sun, 20 Jul 2025 09:22:21 -0500
Subject: [PATCH 2/3] 0.15.1.0 - Update to PMWeather 0.15.1 - Made 0.15.X.X
able to load with any PMWeather 0.15.X version
Took 25 minutes
---
build.gradle | 3 ++-
gradle.properties | 5 +++--
src/main/templates/META-INF/neoforge.mods.toml | 2 +-
3 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/build.gradle b/build.gradle
index ff80184..333d29d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -147,7 +147,8 @@ var generateModMetadata = tasks.register("generateModMetadata", ProcessResources
mod_version : mod_version,
mod_authors : mod_authors,
mod_description : mod_description,
- pmweather_version : pmweather_version]
+ pmweather_version : pmweather_version,
+ pmweather_version_range: pmweather_version_range]
inputs.properties replaceProperties
expand replaceProperties
from "src/main/templates"
diff --git a/gradle.properties b/gradle.properties
index c8b93fc..2c38b49 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -17,10 +17,11 @@ parchment_mappings_version=2024.11.17
mod_id=pmweatherapi
mod_name=PMWeatherAPI
mod_license=GNU GPL 3.0
-mod_version=0.15.0.0+rc1
+mod_version=0.15.1.0
mod_group_id=net.nullved
mod_authors=NullVed
mod_description=An API for interfacing with ProtoManly's Weather Mod
# Dependencies
-pmweather_version=0.15.0
\ No newline at end of file
+pmweather_version=0.15.1
+pmweather_version_range=[0.15.0-1.21.1-alpha,0.15.999-1.21.1-alpha)
\ No newline at end of file
diff --git a/src/main/templates/META-INF/neoforge.mods.toml b/src/main/templates/META-INF/neoforge.mods.toml
index 5138c4a..16b6f88 100644
--- a/src/main/templates/META-INF/neoforge.mods.toml
+++ b/src/main/templates/META-INF/neoforge.mods.toml
@@ -38,7 +38,7 @@ side = "BOTH"
[[dependencies."${mod_id}"]]
modId = "pmweather"
type = "required"
-versionRange = "[${pmweather_version}-${minecraft_version}-alpha]"
+versionRange = "${pmweather_version_range}"
ordering = "BEFORE"
side = "BOTH"
From 9c803fd65e68c71fafb94527c47ed8cc64a8d26f Mon Sep 17 00:00:00 2001
From: RealDarkStudios <94769968+RealDarkStudios@users.noreply.github.com>
Date: Mon, 21 Jul 2025 09:59:25 -0500
Subject: [PATCH 3/3] v0.15.1.0 - Add some missing docs - Add docs for
ColorMap#getWithBiome - Add docs for ColorMap.Builder#biome - Add docs for
StormType
Took 23 minutes
---
.../nullved/pmweatherapi/util/ColorMap.java | 13 ++++++++++--
.../nullved/pmweatherapi/util/StormType.java | 21 +++++++++++++++++++
2 files changed, 32 insertions(+), 2 deletions(-)
diff --git a/src/main/java/net/nullved/pmweatherapi/util/ColorMap.java b/src/main/java/net/nullved/pmweatherapi/util/ColorMap.java
index bf7e50d..4b9bc58 100644
--- a/src/main/java/net/nullved/pmweatherapi/util/ColorMap.java
+++ b/src/main/java/net/nullved/pmweatherapi/util/ColorMap.java
@@ -93,8 +93,16 @@ public Color get(float val) {
return lookup[idx];
}
+ /**
+ * Retrieves the color value using a color derived from the biome
+ * @param val The value to get a color for
+ * @param biome The biome to derive the starting color from
+ * @param worldPos The world position (for grass color checks)
+ * @return The approximate color for this value
+ * @since 0.15.0.0
+ */
public Color getWithBiome(float val, Holder biome, Vec3 worldPos) {
- Color startColor = Color.BLACK;
+ Color startColor;
String rn = biome.getRegisteredName().toLowerCase();
if (rn.contains("ocean") || rn.contains("river")) startColor = new Color(biome.value().getWaterColor());
else if (rn.contains("beach") || rn.contains("desert")) {
@@ -191,7 +199,8 @@ public static Builder of(Color base) {
}
/**
- * Creates a new {@link ColorMap.Builder} that has a default {@link Color#BLACK} base
+ * Creates a new {@link ColorMap.Builder} that has a default {@link Color#BLACK} base.
+ * This method has no effect unless you use {@link ColorMap#getWithBiome(float, Holder, Vec3)}
* @return The created {@link ColorMap.Builder}
* @since 0.15.0.0
*/
diff --git a/src/main/java/net/nullved/pmweatherapi/util/StormType.java b/src/main/java/net/nullved/pmweatherapi/util/StormType.java
index 4413a57..c63135d 100644
--- a/src/main/java/net/nullved/pmweatherapi/util/StormType.java
+++ b/src/main/java/net/nullved/pmweatherapi/util/StormType.java
@@ -2,6 +2,10 @@
import dev.protomanly.pmweather.weather.Storm;
+/**
+ * An enum holding different storm types
+ * @since 0.15.0.0
+ */
public enum StormType {
SUPERCELL(0),
TORNADO(0, 3),
@@ -20,14 +24,31 @@ public enum StormType {
this.stage = -1;
}
+ /**
+ * Gets the index of the {@link StormType}
+ * @return The {@link StormType} index
+ * @since 0.15.0.0
+ */
public int idx() {
return idx;
}
+ /**
+ * Gets the minimum stage of the {@link StormType}
+ * @return The minimum stage of the {@link StormType}
+ * @since 0.15.0.0
+ */
public int stage() {
return stage;
}
+ /**
+ * Determines if the {@link Storm} meets this {@link StormType}'s specification.
+ * If this {@link StormType} defines a `stage` (such as {@link StormType#TORNADO}), the {@link Storm} must be equal to or above that stage
+ * @param storm The {@link Storm} to check
+ * @return {@code true} if this {@link Storm} meets the {@link StormType} specification
+ * @since 0.15.0.0
+ */
public boolean matches(Storm storm) {
return storm.stormType == idx && storm.stage >= stage;
}