diff --git a/common/src/main/java/dev/latvian/mods/kubejs/block/BlockBuilder.java b/common/src/main/java/dev/latvian/mods/kubejs/block/BlockBuilder.java index b14163848..d1a4fd8d6 100644 --- a/common/src/main/java/dev/latvian/mods/kubejs/block/BlockBuilder.java +++ b/common/src/main/java/dev/latvian/mods/kubejs/block/BlockBuilder.java @@ -1,6 +1,5 @@ package dev.latvian.mods.kubejs.block; -import com.google.gson.JsonArray; import com.google.gson.JsonObject; import dev.latvian.mods.kubejs.block.callbacks.AfterEntityFallenOnBlockCallbackJS; import dev.latvian.mods.kubejs.block.callbacks.BlockExplodedCallbackJS; @@ -24,9 +23,7 @@ import dev.latvian.mods.kubejs.typings.Info; import dev.latvian.mods.kubejs.util.ConsoleJS; import dev.latvian.mods.kubejs.util.UtilsJS; -import dev.latvian.mods.rhino.mod.util.color.Color; import dev.latvian.mods.rhino.util.HideFromJS; -import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import net.minecraft.core.Direction; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ItemStack; @@ -45,10 +42,8 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; -import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.function.Consumer; import java.util.function.Function; @@ -71,7 +66,7 @@ public abstract class BlockBuilder extends BuilderBase { public transient boolean fullBlock; public transient boolean requiresTool; public transient String renderType; - public transient Int2IntOpenHashMap color; + public transient BlockTintFunction tint; public transient final JsonObject textures; public transient String model; public transient BlockItemBuilder itemBuilder; @@ -115,8 +110,6 @@ public BlockBuilder(ResourceLocation i) { fullBlock = false; requiresTool = false; renderType = "solid"; - color = new Int2IntOpenHashMap(); - color.defaultReturnValue(0xFFFFFFFF); textures = new JsonObject(); textureAll(id.getNamespace() + ":block/" + id.getPath()); model = ""; @@ -243,7 +236,7 @@ protected void generateBlockModelJsons(AssetJsonGenerator generator) { mg.textures(textures); } - if (!color.isEmpty() || !customShape.isEmpty()) { + if (tint != null || !customShape.isEmpty()) { List boxes = new ArrayList<>(customShape); if (boxes.isEmpty()) { @@ -259,7 +252,7 @@ protected void generateBlockModelJsons(AssetJsonGenerator generator) { face.tex("#" + direction.getSerializedName()); face.cull(); - if (!color.isEmpty()) { + if (tint != null) { face.tintindex(0); } }); @@ -274,61 +267,6 @@ protected void generateBlockStateJson(VariantBlockStateGenerator bs) { bs.simpleVariant("", model.isEmpty() ? (id.getNamespace() + ":block/" + id.getPath()) : model); } - public Map generateBlockModels(BlockBuilder builder) { - Map map = new HashMap<>(); - - if (builder.modelJson != null) { - map.put(builder.newID("models/block/", ""), builder.modelJson); - } else { - var modelJson = new JsonObject(); - - var particle = builder.textures.get("particle").getAsString(); - - if (areAllTexturesEqual(builder.textures, particle)) { - modelJson.addProperty("parent", "block/cube_all"); - var textures = new JsonObject(); - textures.addProperty("all", particle); - modelJson.add("textures", textures); - } else { - modelJson.addProperty("parent", "block/cube"); - modelJson.add("textures", builder.textures); - } - - if (!builder.color.isEmpty()) { - var cube = new JsonObject(); - var from = new JsonArray(); - from.add(0); - from.add(0); - from.add(0); - cube.add("from", from); - var to = new JsonArray(); - to.add(16); - to.add(16); - to.add(16); - cube.add("to", to); - var faces = new JsonObject(); - - for (var direction : Direction.values()) { - var f = new JsonObject(); - f.addProperty("texture", "#" + direction.getSerializedName()); - f.addProperty("cullface", direction.getSerializedName()); - f.addProperty("tintindex", 0); - faces.add(direction.getSerializedName(), f); - } - - cube.add("faces", faces); - - var elements = new JsonArray(); - elements.add(cube); - modelJson.add("elements", elements); - } - - map.put(builder.newID("models/block/", ""), modelJson); - } - - return map; - } - protected boolean areAllTexturesEqual(JsonObject tex, String t) { for (var direction : Direction.values()) { if (!tex.get(direction.getSerializedName()).getAsString().equals(t)) { @@ -466,8 +404,20 @@ public BlockBuilder renderType(String l) { @Info(""" Set the color of a specific layer of the block. """) - public BlockBuilder color(int index, Color c) { - color.put(index, c.getArgbJS()); + public BlockBuilder color(int index, BlockTintFunction color) { + if (!(tint instanceof BlockTintFunction.Mapped)) { + tint = new BlockTintFunction.Mapped(); + } + + ((BlockTintFunction.Mapped) tint).map.put(index, color); + return this; + } + + @Info(""" + Set the color of a specific layer of the block. + """) + public BlockBuilder color(BlockTintFunction color) { + tint = color; return this; } diff --git a/common/src/main/java/dev/latvian/mods/kubejs/block/BlockTintFunction.java b/common/src/main/java/dev/latvian/mods/kubejs/block/BlockTintFunction.java new file mode 100644 index 000000000..677a8a16b --- /dev/null +++ b/common/src/main/java/dev/latvian/mods/kubejs/block/BlockTintFunction.java @@ -0,0 +1,103 @@ +package dev.latvian.mods.kubejs.block; + +import dev.latvian.mods.rhino.BaseFunction; +import dev.latvian.mods.rhino.Context; +import dev.latvian.mods.rhino.NativeJavaObject; +import dev.latvian.mods.rhino.Undefined; +import dev.latvian.mods.rhino.mod.util.color.Color; +import dev.latvian.mods.rhino.mod.util.color.SimpleColor; +import dev.latvian.mods.rhino.mod.util.color.SimpleColorWithAlpha; +import dev.latvian.mods.rhino.mod.wrapper.ColorWrapper; +import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import net.minecraft.client.renderer.BiomeColors; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.BlockAndTintGetter; +import net.minecraft.world.level.FoliageColor; +import net.minecraft.world.level.GrassColor; +import net.minecraft.world.level.block.RedStoneWireBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +@FunctionalInterface +public interface BlockTintFunction { + Color getColor(BlockState state, @Nullable BlockAndTintGetter level, @Nullable BlockPos pos, int index); + + record Fixed(Color color) implements BlockTintFunction { + @Override + public Color getColor(BlockState state, @Nullable BlockAndTintGetter level, @Nullable BlockPos pos, int index) { + return color; + } + } + + class Mapped implements BlockTintFunction { + public final Int2ObjectMap map = new Int2ObjectArrayMap<>(1); + + @Override + public Color getColor(BlockState state, @Nullable BlockAndTintGetter level, @Nullable BlockPos pos, int index) { + var f = map.get(index); + return f == null ? null : f.getColor(state, level, pos, index); + } + } + + BlockTintFunction GRASS = (s, l, p, i) -> new SimpleColor(l == null || p == null ? GrassColor.get(0.5, 1.0) : BiomeColors.getAverageGrassColor(l, p)); + Color DEFAULT_FOLIAGE_COLOR = new SimpleColor(FoliageColor.getDefaultColor()); + BlockTintFunction FOLIAGE = (s, l, p, i) -> l == null || p == null ? DEFAULT_FOLIAGE_COLOR : new SimpleColor(BiomeColors.getAverageFoliageColor(l, p)); + Fixed EVERGREEN_FOLIAGE = new Fixed(new SimpleColor(FoliageColor.getEvergreenColor())); + Fixed BIRCH_FOLIAGE = new Fixed(new SimpleColor(FoliageColor.getBirchColor())); + Fixed MANGROVE_FOLIAGE = new Fixed(new SimpleColor(FoliageColor.getMangroveColor())); + BlockTintFunction WATER = (s, l, p, i) -> l == null || p == null ? null : new SimpleColorWithAlpha(BiomeColors.getAverageWaterColor(l, p)); + Color[] REDSTONE_COLORS = new Color[16]; + BlockTintFunction REDSTONE = (state, level, pos, index) -> { + if (REDSTONE_COLORS[0] == null) { + for (int i = 0; i < REDSTONE_COLORS.length; i++) { + REDSTONE_COLORS[i] = new SimpleColor(RedStoneWireBlock.getColorForPower(i)); + } + } + + return REDSTONE_COLORS[state.getValue(BlockStateProperties.POWER)]; + }; + + @Nullable + static BlockTintFunction of(Context cx, Object o) { + if (o == null || Undefined.isUndefined(o)) { + return null; + } else if (o instanceof BlockTintFunction f) { + return f; + } else if (o instanceof List list) { + var map = new Mapped(); + + for (int i = 0; i < list.size(); i++) { + var f = of(cx, list.get(i)); + + if (f != null) { + map.map.put(i, f); + } + } + + return map; + } else if (o instanceof CharSequence) { + var f = switch (o.toString()) { + case "grass" -> GRASS; + case "foliage" -> FOLIAGE; + case "evergreen_foliage" -> EVERGREEN_FOLIAGE; + case "birch_foliage" -> BIRCH_FOLIAGE; + case "mangrove_foliage" -> MANGROVE_FOLIAGE; + case "water" -> WATER; + case "redstone" -> REDSTONE; + default -> null; + }; + + if (f != null) { + return f; + } + } else if (o instanceof BaseFunction function) { + return (BlockTintFunction) NativeJavaObject.createInterfaceAdapter(cx, BlockTintFunction.class, function); + } + + return new Fixed(ColorWrapper.of(o)); + } +} diff --git a/common/src/main/java/dev/latvian/mods/kubejs/client/BlockTintFunctionWrapper.java b/common/src/main/java/dev/latvian/mods/kubejs/client/BlockTintFunctionWrapper.java new file mode 100644 index 000000000..153a0ce02 --- /dev/null +++ b/common/src/main/java/dev/latvian/mods/kubejs/client/BlockTintFunctionWrapper.java @@ -0,0 +1,16 @@ +package dev.latvian.mods.kubejs.client; + +import dev.latvian.mods.kubejs.block.BlockTintFunction; +import net.minecraft.client.color.block.BlockColor; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.BlockAndTintGetter; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.Nullable; + +public record BlockTintFunctionWrapper(BlockTintFunction function) implements BlockColor { + @Override + public int getColor(BlockState state, @Nullable BlockAndTintGetter level, @Nullable BlockPos pos, int index) { + var c = function.getColor(state, level, pos, index); + return c == null ? 0xFFFFFFFF : c.getArgbJS(); + } +} diff --git a/common/src/main/java/dev/latvian/mods/kubejs/client/ItemTintFunctionWrapper.java b/common/src/main/java/dev/latvian/mods/kubejs/client/ItemTintFunctionWrapper.java new file mode 100644 index 000000000..744345576 --- /dev/null +++ b/common/src/main/java/dev/latvian/mods/kubejs/client/ItemTintFunctionWrapper.java @@ -0,0 +1,13 @@ +package dev.latvian.mods.kubejs.client; + +import dev.latvian.mods.kubejs.item.ItemTintFunction; +import net.minecraft.client.color.item.ItemColor; +import net.minecraft.world.item.ItemStack; + +public record ItemTintFunctionWrapper(ItemTintFunction function) implements ItemColor { + @Override + public int getColor(ItemStack stack, int index) { + var c = function.getColor(stack, index); + return c == null ? 0xFFFFFFFF : c.getArgbJS(); + } +} diff --git a/common/src/main/java/dev/latvian/mods/kubejs/item/ItemBuilder.java b/common/src/main/java/dev/latvian/mods/kubejs/item/ItemBuilder.java index 484d75230..5bb92b9b8 100644 --- a/common/src/main/java/dev/latvian/mods/kubejs/item/ItemBuilder.java +++ b/common/src/main/java/dev/latvian/mods/kubejs/item/ItemBuilder.java @@ -13,9 +13,6 @@ import dev.latvian.mods.kubejs.typings.Param; import dev.latvian.mods.kubejs.util.ConsoleJS; import dev.latvian.mods.rhino.mod.util.color.Color; -import dev.latvian.mods.rhino.mod.wrapper.ColorWrapper; -import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; -import net.minecraft.Util; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.InteractionHand; @@ -101,7 +98,7 @@ public static Tier toToolTier(Object o) { public transient boolean glow; public transient final List tooltip; @Nullable - public transient ItemColorJS colorCallback; + public transient ItemTintFunction tint; public transient FoodBuilder foodBuilder; public transient Function barColor; public transient ToIntFunction barWidth; @@ -174,6 +171,7 @@ public void generateAssetJsons(AssetJsonGenerator generator) { if (textureJson.size() == 0) { texture(newID("item/", "").toString()); } + m.textures(textureJson); }); } @@ -242,21 +240,18 @@ public ItemBuilder group(@Nullable String g) { } @Info("Colorizes item's texture of the given index. Index is used when you have multiple layers, e.g. a crushed ore (of rock + ore).") - public ItemBuilder color(int index, Color c) { - if (!(colorCallback instanceof IndexedItemColor indexed)) { - if (colorCallback != null) { - ConsoleJS.STARTUP.warnf("Overwriting existing dynamic item color for {} with an indexed color", id); - } - color(Util.make(new IndexedItemColor(), col -> col.add(index, c))); - } else { - indexed.add(index, c); + public ItemBuilder color(int index, ItemTintFunction color) { + if (!(tint instanceof ItemTintFunction.Mapped)) { + tint = new ItemTintFunction.Mapped(); } + + ((ItemTintFunction.Mapped) tint).map.put(index, color); return this; } @Info("Colorizes item's texture of the given index. Useful for coloring items, like GT ores ore dusts.") - public ItemBuilder color(ItemColorJS callback) { - colorCallback = callback; + public ItemBuilder color(ItemTintFunction callback) { + tint = callback; return this; } @@ -432,28 +427,6 @@ public ItemBuilder releaseUsing(ReleaseUsingCallback releaseUsing) { return this; } - public static class IndexedItemColor implements ItemColorJS { - Int2IntOpenHashMap colors = new Int2IntOpenHashMap(); - - public IndexedItemColor() { - colors.defaultReturnValue(0xFFFFFFFF); - } - - @Override - public Color getColor(ItemStack stack, int tintIndex) { - return ColorWrapper.of(colors.get(tintIndex)); - } - - public void add(int tintIndex, Color color) { - colors.put(tintIndex, color.getRgbJS()); - } - } - - @FunctionalInterface - public interface ItemColorJS { - Color getColor(ItemStack stack, int tintIndex); - } - @FunctionalInterface public interface UseCallback { boolean use(Level level, Player player, InteractionHand interactionHand); diff --git a/common/src/main/java/dev/latvian/mods/kubejs/item/ItemTintFunction.java b/common/src/main/java/dev/latvian/mods/kubejs/item/ItemTintFunction.java new file mode 100644 index 000000000..40d7eae42 --- /dev/null +++ b/common/src/main/java/dev/latvian/mods/kubejs/item/ItemTintFunction.java @@ -0,0 +1,102 @@ +package dev.latvian.mods.kubejs.item; + +import dev.latvian.mods.rhino.BaseFunction; +import dev.latvian.mods.rhino.Context; +import dev.latvian.mods.rhino.NativeJavaObject; +import dev.latvian.mods.rhino.Undefined; +import dev.latvian.mods.rhino.mod.util.color.Color; +import dev.latvian.mods.rhino.mod.util.color.SimpleColor; +import dev.latvian.mods.rhino.mod.wrapper.ColorWrapper; +import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.MapItem; +import net.minecraft.world.item.alchemy.PotionUtils; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +@FunctionalInterface +public interface ItemTintFunction { + Color getColor(ItemStack stack, int index); + + record Fixed(Color color) implements ItemTintFunction { + @Override + public Color getColor(ItemStack stack, int index) { + return color; + } + } + + class Mapped implements ItemTintFunction { + public final Int2ObjectMap map = new Int2ObjectArrayMap<>(1); + + @Override + public Color getColor(ItemStack stack, int index) { + var f = map.get(index); + return f == null ? null : f.getColor(stack, index); + } + } + + ItemTintFunction BLOCK = (stack, index) -> { + if (stack.getItem() instanceof BlockItem block) { + var s = block.getBlock().defaultBlockState(); + var internal = s.getBlock().kjs$getBlockBuilder(); + + if (internal != null && internal.tint != null) { + return internal.tint.getColor(s, null, null, index); + } + } + + return null; + }; + + ItemTintFunction POTION = (stack, index) -> new SimpleColor(PotionUtils.getColor(stack)); + ItemTintFunction MAP = (stack, index) -> new SimpleColor(MapItem.getColor(stack)); + ItemTintFunction DISPLAY_COLOR_NBT = (stack, index) -> { + var tag = stack.getTagElement("display"); + + if (tag != null && tag.contains("color", 99)) { + return new SimpleColor(tag.getInt("color")); + } + + return null; + }; + + @Nullable + static ItemTintFunction of(Context cx, Object o) { + if (o == null || Undefined.isUndefined(o)) { + return null; + } else if (o instanceof ItemTintFunction f) { + return f; + } else if (o instanceof List list) { + var map = new Mapped(); + + for (int i = 0; i < list.size(); i++) { + var f = of(cx, list.get(i)); + + if (f != null) { + map.map.put(i, f); + } + } + + return map; + } else if (o instanceof CharSequence) { + var f = switch (o.toString()) { + case "block" -> BLOCK; + case "potion" -> POTION; + case "map" -> MAP; + case "display_color_nbt" -> DISPLAY_COLOR_NBT; + default -> null; + }; + + if (f != null) { + return f; + } + } else if (o instanceof BaseFunction function) { + return (ItemTintFunction) NativeJavaObject.createInterfaceAdapter(cx, ItemTintFunction.class, function); + } + + return new Fixed(ColorWrapper.of(o)); + } +} \ No newline at end of file diff --git a/fabric/src/main/java/dev/latvian/mods/kubejs/fabric/KubeJSFabricClient.java b/fabric/src/main/java/dev/latvian/mods/kubejs/fabric/KubeJSFabricClient.java index 9ec258ab5..14efcc2b8 100644 --- a/fabric/src/main/java/dev/latvian/mods/kubejs/fabric/KubeJSFabricClient.java +++ b/fabric/src/main/java/dev/latvian/mods/kubejs/fabric/KubeJSFabricClient.java @@ -1,6 +1,8 @@ package dev.latvian.mods.kubejs.fabric; import dev.latvian.mods.kubejs.block.BlockBuilder; +import dev.latvian.mods.kubejs.client.BlockTintFunctionWrapper; +import dev.latvian.mods.kubejs.client.ItemTintFunctionWrapper; import dev.latvian.mods.kubejs.fluid.FluidBucketItemBuilder; import dev.latvian.mods.kubejs.fluid.FluidBuilder; import dev.latvian.mods.kubejs.item.ItemBuilder; @@ -19,8 +21,8 @@ public static void registry() { case "translucent" -> BlockRenderLayerMap.INSTANCE.putBlocks(RenderType.translucent(), b.get()); } - if (!b.color.isEmpty()) { - ColorProviderRegistry.BLOCK.register((state, level, pos, index) -> b.color.get(index), b.get()); + if (b.tint != null) { + ColorProviderRegistry.BLOCK.register(new BlockTintFunctionWrapper(b.tint), b.get()); } } } @@ -39,8 +41,8 @@ public static void registry() { // ClientSpriteRegistryCallback.EVENT.register((atlasTexture, registry) -> ClientEvents.ATLAS_SPRITE_REGISTRY.post(new AtlasSpriteRegistryEventJS(registry::register), atlasTexture.location())); for (var builder : RegistryInfo.ITEM) { - if (builder instanceof ItemBuilder b && b.colorCallback != null) { - ColorProviderRegistry.ITEM.register((stack, tintIndex) -> b.colorCallback.getColor(stack, tintIndex).getArgbJS(), b.get()); + if (builder instanceof ItemBuilder b && b.tint != null) { + ColorProviderRegistry.ITEM.register(new ItemTintFunctionWrapper(b.tint), b.get()); } if (builder instanceof FluidBucketItemBuilder b && b.fluidBuilder.bucketColor != 0xFFFFFFFF) { diff --git a/forge/src/main/java/dev/latvian/mods/kubejs/forge/KubeJSForgeClient.java b/forge/src/main/java/dev/latvian/mods/kubejs/forge/KubeJSForgeClient.java index 68907a5fc..f79408fd5 100644 --- a/forge/src/main/java/dev/latvian/mods/kubejs/forge/KubeJSForgeClient.java +++ b/forge/src/main/java/dev/latvian/mods/kubejs/forge/KubeJSForgeClient.java @@ -2,6 +2,8 @@ import dev.latvian.mods.kubejs.KubeJS; import dev.latvian.mods.kubejs.block.BlockBuilder; +import dev.latvian.mods.kubejs.client.BlockTintFunctionWrapper; +import dev.latvian.mods.kubejs.client.ItemTintFunctionWrapper; import dev.latvian.mods.kubejs.fluid.FluidBucketItemBuilder; import dev.latvian.mods.kubejs.fluid.FluidBuilder; import dev.latvian.mods.kubejs.item.ItemBuilder; @@ -48,16 +50,16 @@ private void setupClient(FMLClientSetupEvent event) { private void blockColors(RegisterColorHandlersEvent.Block event) { for (var builder : RegistryInfo.BLOCK) { - if (builder instanceof BlockBuilder b && !b.color.isEmpty()) { - event.register((state, level, pos, index) -> b.color.get(index), b.get()); + if (builder instanceof BlockBuilder b && b.tint != null) { + event.register(new BlockTintFunctionWrapper(b.tint), b.get()); } } } private void itemColors(RegisterColorHandlersEvent.Item event) { for (var builder : RegistryInfo.ITEM) { - if (builder instanceof ItemBuilder b && b.colorCallback != null) { - event.register((stack, tintIndex) -> b.colorCallback.getColor(stack, tintIndex).getArgbJS(), b.get()); + if (builder instanceof ItemBuilder b && b.tint != null) { + event.register(new ItemTintFunctionWrapper(b.tint), b.get()); } if (builder instanceof FluidBucketItemBuilder b && b.fluidBuilder.bucketColor != 0xFFFFFFFF) {