diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBlockColorOverrideEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBlockColorOverrideEvent.java
new file mode 100644
index 000000000..742919e2b
--- /dev/null
+++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBlockColorOverrideEvent.java
@@ -0,0 +1,148 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL v3 License.
+ *
+ * Copyright (C) 2020 James Seibel
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.api.methods.events.abstractEvents;
+
+import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
+import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper;
+import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper;
+import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent;
+import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEventParam;
+import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam;
+import com.seibel.distanthorizons.coreapi.util.ColorUtil;
+
+import java.awt.*;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Performance note: this event will be fired thousands of times on concurrent threads,
+ * make it thread safe and as fast as possible.
+ *
+ * This event is fired when DH needs to convert a {@link IDhApiBlockStateWrapper}
+ * into a color for rendering. This event is fired after DH attempts to determine
+ * the color itself (using the base color, tinting, etc.).
+ * Using this event will override the tinting config for this block
+ * unless you re-implement that logic yourself.
+ *
+ *
+ * This event will only trigger for {@link IDhApiBlockStateWrapper}s that have been registered
+ * via {@link DhApiBlockStateWrapperCreatedEvent.EventParam#setAllowApiColorOverride(boolean)}.
+ *
+ * @author James Seibel
+ * @version 2026-04-14
+ * @since API 6.0.0
+ * @see IDhApiBlockStateWrapper
+ */
+public abstract class DhApiBlockColorOverrideEvent implements IDhApiEvent
+{
+ public abstract void blockStateWrapperCreated(DhApiEventParam event);
+
+
+ //=========================//
+ // internal DH API methods //
+ //=========================//
+
+ @Override
+ public final void fireEvent(DhApiEventParam event) { this.blockStateWrapperCreated(event); }
+
+
+ //==================//
+ // parameter object //
+ //==================//
+
+ public static class EventParam implements IDhApiEventParam
+ {
+ private IDhApiLevelWrapper levelWrapper;
+ private IDhApiBlockStateWrapper blockStateWrapper = null;
+ private int colorAsInt = -1;
+ private int blockPosX = 0, blockPosY = 0, blockPosZ = 0;
+
+
+
+ //=============//
+ // constructor //
+ //=============//
+
+ public EventParam() {}
+
+ public void update(
+ IDhApiLevelWrapper levelWrapper,
+ IDhApiBlockStateWrapper blockStateWrapper,
+ int colorAsInt,
+ int blockPosX, int blockPosY, int blockPosZ)
+ {
+ this.levelWrapper = levelWrapper;
+ this.blockStateWrapper = blockStateWrapper;
+ this.colorAsInt = colorAsInt;
+
+ this.blockPosX = blockPosX;
+ this.blockPosY = blockPosY;
+ this.blockPosZ = blockPosZ;
+ }
+
+
+
+ //=================//
+ // getters/setters //
+ //=================//
+
+ public IDhApiBlockStateWrapper getBlockStateWrapper() { return this.blockStateWrapper; }
+
+ public IDhApiLevelWrapper getLevelWrapper() { return levelWrapper; }
+
+ public int getColorAsInt() { return this.colorAsInt; }
+ public int getRed() { return ColorUtil.getRed(this.colorAsInt); }
+ public int getGreen() { return ColorUtil.getGreen(this.colorAsInt); }
+ public int getBlue() { return ColorUtil.getBlue(this.colorAsInt); }
+ public void setColor(int red, int green, int blue) throws IllegalArgumentException
+ {
+ ColorUtil.throwIfColorValueOutOfIntRange("red", red);
+ ColorUtil.throwIfColorValueOutOfIntRange("green", green);
+ ColorUtil.throwIfColorValueOutOfIntRange("blue", blue);
+
+ this.colorAsInt = ColorUtil.rgbToInt(red, green, blue);
+ }
+
+ /** @return the block's X value in the world */
+ public int getBlockPosX() { return blockPosX; }
+ /** @return the block's Y value in the world */
+ public int getBlockPosY() { return blockPosY; }
+ /** @return the block's Z value in the world */
+ public int getBlockPosZ() { return blockPosZ; }
+
+
+
+ /**
+ * Returns the same instance of this event.
+ * Copying this event isn't supported
+ * since the internal parameters must be mutated
+ * by API users in order to be tracked by DH's internal
+ * logic.
+ */
+ @Override
+ public EventParam copy() { return this; }
+
+ @Override
+ public boolean getCopyBeforeFire() { return false; }
+
+
+
+ }
+
+}
\ No newline at end of file
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBlockStateWrapperCreatedEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBlockStateWrapperCreatedEvent.java
index 8b0953019..c5de74b30 100644
--- a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBlockStateWrapperCreatedEvent.java
+++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBlockStateWrapperCreatedEvent.java
@@ -61,11 +61,12 @@ public abstract class DhApiBlockStateWrapperCreatedEvent implements IDhApiEvent<
* Note: modifying this object won't change anything
* a new wrapper will be created after this event finishes.
*/
- public final IDhApiBlockStateWrapper blockStateWrapper;
+ private final IDhApiBlockStateWrapper blockStateWrapper;
private boolean overridesSet = false;
private EDhApiBlockMaterial blockMaterial = null;
private Integer opacity = null;
+ private Boolean allowApiColorOverride = null;
@@ -73,10 +74,7 @@ public abstract class DhApiBlockStateWrapperCreatedEvent implements IDhApiEvent<
// constructor //
//=============//
- public EventParam(IDhApiBlockStateWrapper blockStateWrapper)
- {
- this.blockStateWrapper = blockStateWrapper;
- }
+ public EventParam(IDhApiBlockStateWrapper blockStateWrapper) { this.blockStateWrapper = blockStateWrapper; }
@@ -84,6 +82,8 @@ public abstract class DhApiBlockStateWrapperCreatedEvent implements IDhApiEvent<
// getters/setters //
//=================//
+ public IDhApiBlockStateWrapper getBlockStateWrapper() { return this.blockStateWrapper; }
+
/** if set this will override the value currently set in the given {@link IDhApiBlockStateWrapper} */
public void setBlockMaterial(EDhApiBlockMaterial blockMaterial)
{
@@ -100,6 +100,14 @@ public abstract class DhApiBlockStateWrapperCreatedEvent implements IDhApiEvent<
}
public Integer getOpacity() { return this.opacity; }
+ /** if set to true this {@link IDhApiBlockStateWrapper} will trigger {@link DhApiBlockColorOverrideEvent} */
+ public void setAllowApiColorOverride(boolean allowApiColorOverride)
+ {
+ this.allowApiColorOverride = allowApiColorOverride;
+ this.overridesSet = true;
+ }
+ public Boolean getAllowApiColorOverride() { return this.allowApiColorOverride; }
+
/** If true then one or more options for this block were set to be changed */
public boolean getOverridesSet() { return this.overridesSet; }
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/ColorUtil.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/util/ColorUtil.java
similarity index 95%
rename from core/src/main/java/com/seibel/distanthorizons/core/util/ColorUtil.java
rename to api/src/main/java/com/seibel/distanthorizons/coreapi/util/ColorUtil.java
index 8f5479401..b46910349 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/util/ColorUtil.java
+++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/util/ColorUtil.java
@@ -17,7 +17,7 @@
* along with this program. If not, see .
*/
-package com.seibel.distanthorizons.core.util;
+package com.seibel.distanthorizons.coreapi.util;
import java.awt.*;
@@ -89,6 +89,16 @@ public class ColorUtil
/** @param newBlue should be a value between 0 and 255 */
public static int setBlue(int color, int newBlue) { return (getAlpha(color) << 24) | (getRed(color) << 16) | (getGreen(color) << 8) | newBlue; }
+ /** @throws IllegalArgumentException if the given int value is out of the range 0 - 255 (exclusive) */
+ public static void throwIfColorValueOutOfIntRange(String colorName, int value) throws IllegalArgumentException
+ {
+ if (value < 0
+ || value > 255)
+ {
+ throw new IllegalArgumentException("["+colorName+"] with the value ["+value+"] is out of the expected range 0 - 255 (exclusive).");
+ }
+ }
+
public static int applyShade(int color, int shade)
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnBox.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnBox.java
index 726880feb..cfa0082e2 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnBox.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnBox.java
@@ -24,7 +24,7 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.enums.EDhDirection;
import com.seibel.distanthorizons.core.level.IDhClientLevel;
import com.seibel.distanthorizons.core.util.objects.pooling.PhantomArrayListCheckout;
-import com.seibel.distanthorizons.core.util.ColorUtil;
+import com.seibel.distanthorizons.coreapi.util.ColorUtil;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.RenderDataPointUtil;
import com.seibel.distanthorizons.core.dataObjects.render.columnViews.ColumnRenderView;
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java
index 686262d33..3f4fe08a2 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java
@@ -31,7 +31,7 @@ import com.seibel.distanthorizons.core.util.objects.pooling.PhantomArrayListChec
import com.seibel.distanthorizons.core.util.objects.pooling.PhantomArrayListPool;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
-import com.seibel.distanthorizons.core.util.ColorUtil;
+import com.seibel.distanthorizons.coreapi.util.ColorUtil;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.RenderDataPointUtil;
import com.seibel.distanthorizons.core.dataObjects.render.columnViews.ColumnRenderView;
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/LodQuadBuilder.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/LodQuadBuilder.java
index 61450859b..f97f67da3 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/LodQuadBuilder.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/LodQuadBuilder.java
@@ -30,7 +30,7 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.enums.EDhDirection;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
-import com.seibel.distanthorizons.core.util.ColorUtil;
+import com.seibel.distanthorizons.coreapi.util.ColorUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import org.lwjgl.system.MemoryUtil;
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java
index 12157e112..3af137531 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java
@@ -37,6 +37,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrappe
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
+import com.seibel.distanthorizons.coreapi.util.ColorUtil;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import com.seibel.distanthorizons.core.logging.DhLogger;
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/LodUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/LodUtil.java
index 5554fc005..a18e89549 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/util/LodUtil.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/util/LodUtil.java
@@ -22,6 +22,7 @@ package com.seibel.distanthorizons.core.util;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.logging.DhLogger;
+import com.seibel.distanthorizons.coreapi.util.ColorUtil;
/**
* This class holds methods and constants that may be used in multiple places.
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/RenderDataPointUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/RenderDataPointUtil.java
index 0d7bac19b..3b91954da 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/util/RenderDataPointUtil.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/util/RenderDataPointUtil.java
@@ -24,6 +24,7 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.logging.DhLogger;
+import com.seibel.distanthorizons.coreapi.util.ColorUtil;
/**
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java
index d9caddb31..4cc97245c 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java
@@ -20,6 +20,7 @@
package com.seibel.distanthorizons.core.wrapperInterfaces.block;
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper;
+import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBlockColorOverrideEvent;
import com.seibel.distanthorizons.core.util.LodUtil;
import java.awt.*;
@@ -59,6 +60,11 @@ public interface IBlockStateWrapper extends IDhApiBlockStateWrapper
* IE Iron, diamond, gold, etc.
*/
boolean isBeaconBaseBlock();
+ /**
+ * if true this block can have its color overridden
+ * by {@link DhApiBlockColorOverrideEvent}
+ */
+ boolean allowApiColorOverride();
Color getMapColor();
Color getBeaconTintColor();