diff --git a/gradle.properties b/gradle.properties index 02cb9fa7..338fbe58 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ org.gradle.daemon=false mod_id=sophisticatedstorage mod_group_id=sophisticatedstorage -mod_version=0.11.3 +mod_version=1.0.0 sonar_project_key=sophisticatedstorage:SophisticatedStorage github_package_url=https://maven.pkg.github.com/P3pp3rF1y/SophisticatedStorage @@ -30,6 +30,6 @@ jade_cf_file_id=4614153 chipped_cf_file_id=5077656 resourcefullib_cf_file_id=5070629 athena_cf_file_id=4764357 -sc_version=[1.20.1-0.7.12,1.20.4) +sc_version=[1.20.1-1.0.0,1.20.4) sb_version=[1.20.1-3.20.5,1.20.4) parchment_version=2023.09.03-1.20.1 diff --git a/src/generated/resources/data/sophisticatedstorage/advancements/recipes/misc/paintbrush.json b/src/generated/resources/data/sophisticatedstorage/advancements/recipes/misc/paintbrush.json new file mode 100644 index 00000000..a2defe60 --- /dev/null +++ b/src/generated/resources/data/sophisticatedstorage/advancements/recipes/misc/paintbrush.json @@ -0,0 +1,33 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_base_tier_wooden_storage": { + "conditions": { + "items": [ + { + "tag": "sophisticatedstorage:base_tier_wooden_storage" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "sophisticatedstorage:paintbrush" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_base_tier_wooden_storage", + "has_the_recipe" + ] + ], + "rewards": { + "recipes": [ + "sophisticatedstorage:paintbrush" + ] + }, + "sends_telemetry_event": false +} \ No newline at end of file diff --git a/src/generated/resources/data/sophisticatedstorage/recipes/paintbrush.json b/src/generated/resources/data/sophisticatedstorage/recipes/paintbrush.json new file mode 100644 index 00000000..21e0cc4e --- /dev/null +++ b/src/generated/resources/data/sophisticatedstorage/recipes/paintbrush.json @@ -0,0 +1,21 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "key": { + "S": { + "item": "minecraft:stick" + }, + "W": { + "tag": "minecraft:wool" + } + }, + "pattern": [ + " W ", + " SW", + "S " + ], + "result": { + "item": "sophisticatedstorage:paintbrush" + }, + "show_notification": true +} \ No newline at end of file diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/block/BarrelBlock.java b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/block/BarrelBlock.java index 578ee793..05809ef9 100644 --- a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/block/BarrelBlock.java +++ b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/block/BarrelBlock.java @@ -52,6 +52,7 @@ import net.p3pp3rf1y.sophisticatedstorage.item.WoodStorageBlockItem; import javax.annotation.Nullable; +import java.util.EnumMap; import java.util.Map; import java.util.function.Consumer; import java.util.function.Function; @@ -141,7 +142,7 @@ public void setPlacedBy(Level level, BlockPos pos, BlockState state, @org.jetbra WorldHelper.getBlockEntity(level, pos, BarrelBlockEntity.class).ifPresent(barrel -> { Map materials = BarrelBlockItem.getMaterials(stack); if (!materials.isEmpty()) { - barrel.setMaterials(materials); + barrel.setMaterials(new EnumMap<>(materials)); } }); } diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/block/ChestBlockEntity.java b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/block/ChestBlockEntity.java index fef663b6..b12567b5 100644 --- a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/block/ChestBlockEntity.java +++ b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/block/ChestBlockEntity.java @@ -363,6 +363,14 @@ public StorageWrapper getMainStorageWrapper() { return getStorageWrapper(); } + @Nullable + public ChestBlockEntity getMainChestBlockEntity() { + if (doubleMainPos != null) { + return level.getBlockEntity(doubleMainPos, ModBlocks.CHEST_BLOCK_ENTITY_TYPE.get()).orElse(null); + } + return this; + } + @Override public boolean canBeLinked() { return isMainChest() && super.canBeLinked(); diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/block/DecorationTableBlockEntity.java b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/block/DecorationTableBlockEntity.java index b959bef2..976d3a44 100644 --- a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/block/DecorationTableBlockEntity.java +++ b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/block/DecorationTableBlockEntity.java @@ -1,13 +1,11 @@ package net.p3pp3rf1y.sophisticatedstorage.block; import net.minecraft.core.BlockPos; -import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.nbt.Tag; import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.FastColor; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; @@ -19,10 +17,13 @@ import net.p3pp3rf1y.sophisticatedcore.util.WorldHelper; import net.p3pp3rf1y.sophisticatedstorage.init.ModBlocks; import net.p3pp3rf1y.sophisticatedstorage.item.BarrelBlockItem; +import net.p3pp3rf1y.sophisticatedstorage.item.PaintbrushItem; import net.p3pp3rf1y.sophisticatedstorage.item.StorageBlockItem; +import net.p3pp3rf1y.sophisticatedstorage.util.DecorationHelper; import javax.annotation.Nullable; import java.util.*; +import java.util.stream.Collectors; public class DecorationTableBlockEntity extends BlockEntity { public static final int TOP_INNER_TRIM_SLOT = 0; @@ -35,21 +36,7 @@ public class DecorationTableBlockEntity extends BlockEntity { public static final int RED_DYE_SLOT = 0; public static final int GREEN_DYE_SLOT = 1; public static final int BLUE_DYE_SLOT = 2; - - public static final int BLOCK_TOTAL_PARTS = 24; - private static final int MAIN_COLOR_PARTS = 18; - private static final int ACCENT_COLOR_PARTS = 6; - private static final Map DECORATIVE_SLOT_PARTS_NEEDED = Map.of( - TOP_INNER_TRIM_SLOT, 1, - TOP_TRIM_SLOT, 1, - SIDE_TRIM_SLOT, 4, - BOTTOM_TRIM_SLOT, 1, - TOP_CORE_SLOT, 3, - SIDE_CORE_SLOT, 12, - BOTTOM_CORE_SLOT, 3 - ); - - private static final Set STORAGES_WIHOUT_TOP_INNER_TRIM = Set.of(ModBlocks.BARREL_ITEM.get(), ModBlocks.COPPER_BARREL_ITEM.get(), ModBlocks.IRON_BARREL_ITEM.get(), ModBlocks.GOLD_BARREL_ITEM.get(), ModBlocks.DIAMOND_BARREL_ITEM.get(), ModBlocks.NETHERITE_BARREL_ITEM.get(), + public static final Set STORAGES_WIHOUT_TOP_INNER_TRIM = Set.of(ModBlocks.BARREL_ITEM.get(), ModBlocks.COPPER_BARREL_ITEM.get(), ModBlocks.IRON_BARREL_ITEM.get(), ModBlocks.GOLD_BARREL_ITEM.get(), ModBlocks.DIAMOND_BARREL_ITEM.get(), ModBlocks.NETHERITE_BARREL_ITEM.get(), ModBlocks.LIMITED_BARREL_1_ITEM.get(), ModBlocks.LIMITED_COPPER_BARREL_1_ITEM.get(), ModBlocks.LIMITED_IRON_BARREL_1_ITEM.get(), ModBlocks.LIMITED_GOLD_BARREL_1_ITEM.get(), ModBlocks.LIMITED_DIAMOND_BARREL_1_ITEM.get(), ModBlocks.LIMITED_NETHERITE_BARREL_1_ITEM.get()); private final Map remainingParts = new HashMap<>(); @@ -85,6 +72,14 @@ public boolean isItemValid(int slot, ItemStack stack) { } }; + private ItemStack result = ItemStack.EMPTY; + + private final Map slotMaterialInheritance = new HashMap<>(); + private int accentColor = -1; + private int mainColor = -1; + + private final Set missingDyes = new HashSet<>(); + public void updateResultAndSetChanged() { updateResult(); setChanged(); @@ -99,30 +94,41 @@ protected void onContentsChanged(int slot) { @Override public boolean isItemValid(int slot, ItemStack stack) { - return stack.getItem() instanceof StorageBlockItem; + return stack.getItem() instanceof StorageBlockItem || stack.getItem() instanceof PaintbrushItem; } }; - private ItemStack result = ItemStack.EMPTY; - - private final Map slotMaterialInheritance = new HashMap<>(); - private int accentColor = -1; - private int mainColor = -1; - - private final Set missingDyes = new HashSet<>(); - private void updateResult() { missingDyes.clear(); + result = ItemStack.EMPTY; ItemStack storage = storageBlock.getStackInSlot(0); - if (storage.isEmpty() || ( - (InventoryHelper.isEmpty(decorativeBlocks) - || !(storage.getItem() instanceof BarrelBlockItem) - || isTintedStorage(storage) - ) && colorsTransparentOrSameAs(storage))) { - result = ItemStack.EMPTY; + if (storage.isEmpty()) { + return; + } + + if (storage.getItem() instanceof PaintbrushItem) { + updatePaintbrushResult(storage); return; } + + DecorationResult decorationResult = decorateStack(storage); + result = decorationResult.result(); + missingDyes.addAll(decorationResult.missingDyes()); + } + + public boolean hasMaterials() { + return !InventoryHelper.isEmpty(decorativeBlocks); + } + + public record DecorationResult(ItemStack result, Set missingDyes) { + public static final DecorationResult EMPTY = new DecorationResult(ItemStack.EMPTY, Collections.emptySet()); + } + public DecorationResult decorateStack(ItemStack storage) { + ItemStack result; + if ((InventoryHelper.isEmpty(decorativeBlocks) || !(storage.getItem() instanceof BarrelBlockItem) || isTintedStorage(storage)) && (colorsTransparentOrSameAs(storage) || !BarrelBlockItem.getMaterials(storage).isEmpty())) { + return DecorationResult.EMPTY; + } if (!(storage.getItem() instanceof BarrelBlockItem) || InventoryHelper.isEmpty(decorativeBlocks) || isTintedStorage(storage)) { result = storage.copy(); result.setCount(1); @@ -135,13 +141,12 @@ private void updateResult() { tintableBlockItem.setAccentColor(result, accentColor & 0x00FFFFFF); } } - calculateMissingDyes(storage); - return; + Set missingDyes = calculateMissingDyes(storage); + return new DecorationResult(result, missingDyes); } if (InventoryHelper.isEmpty(decorativeBlocks)) { - result = ItemStack.EMPTY; - return; + return DecorationResult.EMPTY; } Map materials = new EnumMap<>(BarrelMaterial.class); @@ -152,8 +157,7 @@ private void updateResult() { BarrelBlockItem.compactMaterials(materials); if (allMaterialsMatch(materials, BarrelBlockItem.getMaterials(storage))) { - result = ItemStack.EMPTY; - return; + return DecorationResult.EMPTY; } result = storage.copy(); @@ -161,6 +165,37 @@ private void updateResult() { BarrelBlockItem.removeCoveredTints(result, materials); BarrelBlockItem.setMaterials(result, materials); + + return new DecorationResult(result, Collections.emptySet()); + } + + private void updatePaintbrushResult(ItemStack paintbrush) { + if (!InventoryHelper.isEmpty(decorativeBlocks)) { + Map materials = new EnumMap<>(BarrelMaterial.class); + setMaterialsFromDecorativeBlocks(materials, true); + BarrelBlockItem.compactMaterials(materials); + + if (allMaterialsMatch(materials, BarrelBlockItem.getMaterials(paintbrush))) { + return; + } + + result = paintbrush.copy(); + result.setCount(1); + PaintbrushItem.setBarrelMaterials(result, materials); + } else { + if ((mainColor == -1 && accentColor == -1) || (mainColor == PaintbrushItem.getMainColor(paintbrush) && accentColor == PaintbrushItem.getAccentColor(paintbrush))) { + return; + } + + result = paintbrush.copy(); + result.setCount(1); + if (mainColor != -1) { + PaintbrushItem.setMainColor(result, mainColor); + } + if (accentColor != -1) { + PaintbrushItem.setAccentColor(result, accentColor); + } + } } private boolean isTintedStorage(ItemStack storage) { @@ -181,13 +216,16 @@ private boolean allMaterialsMatch(Map newMater return true; } - private void calculateMissingDyes(ItemStack storage) { + private Set calculateMissingDyes(ItemStack storage) { + Set missingDyes = new HashSet<>(); if (!dyes.getStackInSlot(RED_DYE_SLOT).isEmpty() && !dyes.getStackInSlot(GREEN_DYE_SLOT).isEmpty() && !dyes.getStackInSlot(BLUE_DYE_SLOT).isEmpty()) { - return; + return missingDyes; } - Map partsNeeded = new HashMap<>(); - addDyePartsNeeded(storage, partsNeeded); + Map partsNeeded = + DecorationHelper.getDyePartsNeeded(mainColor, accentColor, + StorageBlockItem.getMainColorFromStack(storage).orElse(-1), StorageBlockItem.getAccentColorFromStack(storage).orElse(-1)) + .entrySet().stream().map(entry -> Map.entry(entry.getKey().location(), entry.getValue())).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); for (Map.Entry entry : partsNeeded.entrySet()) { if (entry.getKey().equals(Tags.Items.DYES_RED.location()) && dyes.getStackInSlot(RED_DYE_SLOT).isEmpty()) { @@ -198,6 +236,7 @@ private void calculateMissingDyes(ItemStack storage) { missingDyes.add(entry.getKey()); } } + return missingDyes; } public Set getMissingDyes() { @@ -221,7 +260,7 @@ private void setMaterialsFromDecorativeBlocks(Map materials, BarrelMaterial material, boolean addToMaterials) { ItemStack decorativeBlock = decorativeBlocks.getStackInSlot(slotIndex); - ResourceLocation materialLocation = getMaterialLocation(decorativeBlock).orElse(isSlotMaterialInherited(slotIndex) ? defaultMaterialLocation : null); + ResourceLocation materialLocation = DecorationHelper.getMaterialLocation(decorativeBlock).orElse(isSlotMaterialInherited(slotIndex) ? defaultMaterialLocation : null); if (materialLocation != null) { if (addToMaterials) { materials.put(material, materialLocation); @@ -231,13 +270,6 @@ private ResourceLocation setMaterialFromBlock(int slotIndex, @Nullable ResourceL return null; } - private Optional getMaterialLocation(ItemStack stack) { - if (stack.getItem() instanceof BlockItem blockItem) { - return Optional.of(BuiltInRegistries.BLOCK.getKey(blockItem.getBlock())); - } - return Optional.empty(); - } - public DecorationTableBlockEntity(BlockPos pos, BlockState blockState) { super(ModBlocks.DECORATION_TABLE_BLOCK_ENTITY_TYPE.get(), pos, blockState); } @@ -388,159 +420,38 @@ private void saveData(CompoundTag tag) { } public void consumeIngredientsOnCraft() { + if (getStorageBlock().getStackInSlot(0).getItem() instanceof PaintbrushItem) { + return; //paintbrush consumes ingredients by itself on application to storage block + } + ItemStack storageStack = storageBlock.getStackInSlot(0); if (InventoryHelper.isEmpty(decorativeBlocks)) { - consumeDyes(); + DecorationHelper.consumeDyes(mainColor, accentColor, this.remainingParts, List.of(dyes), StorageBlockItem.getMainColorFromStack(storageStack).orElse(-1), StorageBlockItem.getAccentColorFromStack(storageStack).orElse(-1), false); } else { - consumeMaterials(); + Map originalMaterials = BarrelBlockItem.getUncompactedMaterials(storageStack); + DecorationHelper.consumeMaterials(this.remainingParts, List.of(decorativeBlocks), originalMaterials, getMaterialsToApply(storageStack), false); } setChanged(); WorldHelper.notifyBlockUpdate(this); } - private void consumeDyes() { - ItemStack storageStack = storageBlock.getStackInSlot(0); - Map partsNeeded = new HashMap<>(); - Map firstSlotWithMaterial = new HashMap<>(); - firstSlotWithMaterial.put(Tags.Items.DYES_RED.location(), RED_DYE_SLOT); - firstSlotWithMaterial.put(Tags.Items.DYES_GREEN.location(), GREEN_DYE_SLOT); - firstSlotWithMaterial.put(Tags.Items.DYES_BLUE.location(), BLUE_DYE_SLOT); - - addDyePartsNeeded(storageStack, partsNeeded); - - if (partsNeeded.isEmpty()) { - return; - } - - consumePartsNeeded(partsNeeded, firstSlotWithMaterial, dyes); - } - - private void addDyePartsNeeded(ItemStack storageStack, Map partsNeeded) { - if (mainColor != -1 && mainColor != StorageBlockItem.getMainColorFromStack(storageStack).orElse(-1)) { - int[] rgbPartsNeeded = calculateRGBPartsNeeded(mainColor, MAIN_COLOR_PARTS); - addPartsNeededIfAny(rgbPartsNeeded, partsNeeded); - } - if (accentColor != -1 && accentColor != StorageBlockItem.getAccentColorFromStack(storageStack).orElse(-1)) { - int[] rgbPartsNeeded = calculateRGBPartsNeeded(accentColor, ACCENT_COLOR_PARTS); - addPartsNeededIfAny(rgbPartsNeeded, partsNeeded); - } - } - - private static void addPartsNeededIfAny(int[] rgbPartsNeeded, Map partsNeeded) { - addPartsNeededIfAny(rgbPartsNeeded[0], partsNeeded, Tags.Items.DYES_RED.location()); - addPartsNeededIfAny(rgbPartsNeeded[1], partsNeeded, Tags.Items.DYES_GREEN.location()); - addPartsNeededIfAny(rgbPartsNeeded[2], partsNeeded, Tags.Items.DYES_BLUE.location()); - } - - private static void addPartsNeededIfAny(int parts, Map partsNeeded, ResourceLocation dyeName) { - if (parts != 0) { - partsNeeded.compute(dyeName, (location, partsTotal) -> partsTotal == null ? parts : partsTotal + parts); - } - } - - private int[] calculateRGBPartsNeeded(int color, int totalParts) { - float[] ratios = new float[3]; - ratios[0] = FastColor.ARGB32.red(color) / 255f; - ratios[1] = FastColor.ARGB32.green(color) / 255f; - ratios[2] = FastColor.ARGB32.blue(color) / 255f; - - float totalRaios = ratios[0] + ratios[1] + ratios[2]; - ratios[0] /= totalRaios; - ratios[1] /= totalRaios; - ratios[2] /= totalRaios; - - int n = ratios.length; - int[] result = new int[n]; - double[] remainders = new double[n]; - - double[] scaled = new double[n]; - for (int i = 0; i < n; i++) { - scaled[i] = ratios[i] * totalParts; - result[i] = (int) scaled[i]; - remainders[i] = scaled[i] - result[i]; - } - - int remaining = totalParts - Arrays.stream(result).sum(); - - Integer[] indices = new Integer[n]; - for (int i = 0; i < n; i++) indices[i] = i; - - Arrays.sort(indices, Comparator.comparingDouble(i -> -remainders[i])); - - for (int i = 0; i < remaining; i++) { - result[indices[i % n]]++; - } - - return result; - } - - private void consumeMaterials() { - Map partsNeeded = new HashMap<>(); - Map firstSlotWithMaterial = new HashMap<>(); - addMaterialPartsNeeded(partsNeeded, firstSlotWithMaterial); - consumePartsNeeded(partsNeeded, firstSlotWithMaterial, decorativeBlocks); - } - - private void addMaterialPartsNeeded(Map partsNeeded, Map firstSlotWithMaterial) { - ResourceLocation topInnerTrimMaterialLocation = addMaterialCostForSlotAndGetMaterial(TOP_INNER_TRIM_SLOT, null, partsNeeded, firstSlotWithMaterial); - ResourceLocation topTrimMaterialLocation = addMaterialCostForSlotAndGetMaterial(TOP_TRIM_SLOT, topInnerTrimMaterialLocation, partsNeeded, firstSlotWithMaterial); - ResourceLocation sideTrimMaterialLocation = addMaterialCostForSlotAndGetMaterial(SIDE_TRIM_SLOT, topTrimMaterialLocation, partsNeeded, firstSlotWithMaterial); - addMaterialCostForSlotAndGetMaterial(BOTTOM_TRIM_SLOT, sideTrimMaterialLocation, partsNeeded, firstSlotWithMaterial); - ResourceLocation topMaterialLocation = addMaterialCostForSlotAndGetMaterial(TOP_CORE_SLOT, topTrimMaterialLocation, partsNeeded, firstSlotWithMaterial); - ResourceLocation sideMaterialLocation = addMaterialCostForSlotAndGetMaterial(SIDE_CORE_SLOT, topMaterialLocation, partsNeeded, firstSlotWithMaterial); - addMaterialCostForSlotAndGetMaterial(BOTTOM_CORE_SLOT, sideMaterialLocation, partsNeeded, firstSlotWithMaterial); - } - public Map getPartsNeeded() { Map partsNeeded = new HashMap<>(); ItemStack storageStack = storageBlock.getStackInSlot(0); if (InventoryHelper.isEmpty(decorativeBlocks) || !(storageStack.getItem() instanceof BarrelBlockItem)) { - addDyePartsNeeded(storageStack, partsNeeded); + DecorationHelper.getDyePartsNeeded(mainColor, accentColor, StorageBlockItem.getMainColorFromStack(storageStack).orElse(-1), StorageBlockItem.getAccentColorFromStack(storageStack).orElse(-1)) + .forEach((tag, parts) -> partsNeeded.put(tag.location(), parts)); } else { - addMaterialPartsNeeded(partsNeeded, new HashMap<>()); + partsNeeded.putAll(DecorationHelper.getMaterialPartsNeeded(BarrelBlockItem.getUncompactedMaterials(storageStack), getMaterialsToApply(storageStack))); } return partsNeeded; } - private void consumePartsNeeded(Map partsNeeded, Map firstSlotWithMaterial, ItemStackHandler resources) { - partsNeeded.forEach((material, parts) -> { - int remainingParts = this.remainingParts.getOrDefault(material, 0); - if (remainingParts >= parts) { - if (remainingParts == parts) { - this.remainingParts.remove(material); - } else { - this.remainingParts.put(material, remainingParts - parts); - } - } else { - if (firstSlotWithMaterial.get(material) == null) { - return; - } - int slotWithMaterial = firstSlotWithMaterial.get(material); - ItemStack stack = resources.getStackInSlot(slotWithMaterial); - stack.shrink(1); - resources.setStackInSlot(slotWithMaterial, stack); - this.remainingParts.put(material, remainingParts + BLOCK_TOTAL_PARTS - parts); - } - }); - } - - @Nullable - private ResourceLocation addMaterialCostForSlotAndGetMaterial(int slotIndex, @Nullable ResourceLocation defaultMaterialLocation, Map partsNeeded, Map firstSlotWithMaterial) { - boolean hasNoCost = slotIndex == TOP_TRIM_SLOT && defaultMaterialLocation != null; - - ItemStack decorativeBlock = decorativeBlocks.getStackInSlot(slotIndex); - ResourceLocation materialLocation = getMaterialLocation(decorativeBlock).orElse(isSlotMaterialInherited(slotIndex) ? defaultMaterialLocation : null); - if (hasNoCost) { - return materialLocation; - } - - if (materialLocation != null) { - int parts = DECORATIVE_SLOT_PARTS_NEEDED.get(slotIndex); - partsNeeded.compute(materialLocation, (key, value) -> value == null ? parts : value + parts); - firstSlotWithMaterial.putIfAbsent(materialLocation, slotIndex); - } - return materialLocation; + private Map getMaterialsToApply(ItemStack storageStack) { + Map materialsToApply = new EnumMap<>(BarrelMaterial.class); + setMaterialsFromDecorativeBlocks(materialsToApply, !STORAGES_WIHOUT_TOP_INNER_TRIM.contains(storageStack.getItem())); + return materialsToApply; } public int getMainColor() { diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/block/StorageWrapper.java b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/block/StorageWrapper.java index e2bb1781..11e37a11 100644 --- a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/block/StorageWrapper.java +++ b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/block/StorageWrapper.java @@ -317,6 +317,7 @@ public boolean hasMainColor() { public void setMainColor(int mainColor) { this.mainColor = mainColor; + save(); } @Override @@ -330,6 +331,7 @@ public boolean hasAccentColor() { public void setAccentColor(int accentColor) { this.accentColor = accentColor; + save(); } @Override diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/client/ClientEventHandler.java b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/client/ClientEventHandler.java index 03aa96c7..0cef23cd 100644 --- a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/client/ClientEventHandler.java +++ b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/client/ClientEventHandler.java @@ -1,6 +1,7 @@ package net.p3pp3rf1y.sophisticatedstorage.client; import com.mojang.blaze3d.platform.InputConstants; +import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import net.minecraft.client.KeyMapping; import net.minecraft.client.Minecraft; @@ -36,6 +37,8 @@ import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import net.p3pp3rf1y.sophisticatedstorage.SophisticatedStorage; import net.p3pp3rf1y.sophisticatedstorage.block.LimitedBarrelBlock; +import net.p3pp3rf1y.sophisticatedstorage.block.StorageBlockBase; +import net.p3pp3rf1y.sophisticatedstorage.client.gui.PaintbrushOverlay; import net.p3pp3rf1y.sophisticatedstorage.client.gui.StorageScreen; import net.p3pp3rf1y.sophisticatedstorage.client.gui.StorageTranslationHelper; import net.p3pp3rf1y.sophisticatedstorage.client.gui.ToolInfoOverlay; @@ -47,6 +50,7 @@ import net.p3pp3rf1y.sophisticatedstorage.init.ModBlocks; import net.p3pp3rf1y.sophisticatedstorage.init.ModItems; import net.p3pp3rf1y.sophisticatedstorage.item.ChestBlockItem; +import net.p3pp3rf1y.sophisticatedstorage.item.PaintbrushItem; import net.p3pp3rf1y.sophisticatedstorage.item.StorageContentsTooltip; import net.p3pp3rf1y.sophisticatedstorage.network.ScrolledToolMessage; import net.p3pp3rf1y.sophisticatedstorage.network.StoragePacketHandler; @@ -111,11 +115,12 @@ public static void registerHandlers() { private static void onRenderHighlight(RenderHighlightEvent.Block event) { Minecraft minecraft = Minecraft.getInstance(); LocalPlayer player = minecraft.player; - if (player == null) { + if (player == null || minecraft.screen != null) { return; } - if (player.getMainHandItem().getItem() instanceof ChestBlockItem && ChestBlockItem.isDoubleChest(player.getMainHandItem())) { + ItemStack stack = player.getMainHandItem(); + if (stack.getItem() instanceof ChestBlockItem && ChestBlockItem.isDoubleChest(stack)) { BlockHitResult hitresult = event.getTarget(); BlockPos otherPos = hitresult.getBlockPos().relative(player.getDirection().getClockWise()); Level level = player.level(); @@ -127,6 +132,26 @@ private static void onRenderHighlight(RenderHighlightEvent.Block event) { otherPos.getX() - cameraPos.x, otherPos.getY() - cameraPos.y, otherPos.getZ() - cameraPos.z, 0.0F, 0.0F, 0.0F, 0.4F); } } + + if (stack.getItem() instanceof PaintbrushItem) { + BlockHitResult hitresult = event.getTarget(); + Level level = player.level(); + BlockPos pos = hitresult.getBlockPos(); + BlockState blockState = level.getBlockState(pos); + + if (blockState.getBlock() instanceof StorageBlockBase || blockState.getBlock() == ModBlocks.CONTROLLER.get()) { + PaintbrushOverlay.getItemRequirementsFor(stack, player, level, pos).ifPresent(itemRequirements -> { + float red = !itemRequirements.itemsMissing().isEmpty() ? 1 : 0; + float green = itemRequirements.itemsMissing().isEmpty() ? 1 : 0; + VertexConsumer vertexConsumer = event.getMultiBufferSource().getBuffer(RenderType.lines()); + Vec3 cameraPos = event.getCamera().getPosition(); + PoseStack poseStack = event.getPoseStack(); + LevelRenderer.renderShape(poseStack, vertexConsumer, blockState.getShape(level, pos, CollisionContext.of(event.getCamera().getEntity())), + pos.getX() - cameraPos.x, pos.getY() - cameraPos.y, pos.getZ() - cameraPos.z, red, green, 0.0F, 1); + event.setCanceled(true); + }); + } + } } private static void onRegisterAdditionalModels(ModelEvent.RegisterAdditional event) { @@ -246,6 +271,7 @@ private static void registerTooltipComponent(RegisterClientTooltipComponentFacto private static void registerOverlay(RegisterGuiOverlaysEvent event) { event.registerAbove(VanillaGuiOverlay.HOTBAR.id(), "storage_tool_info", ToolInfoOverlay.HUD_TOOL_INFO); + event.registerAbove(VanillaGuiOverlay.HOTBAR.id(), "paintbrush_info", PaintbrushOverlay.HUD_PAINTBRUSH_INFO); } private static void registerEntityRenderers(EntityRenderersEvent.RegisterRenderers event) { diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/client/gui/DecorationTableScreen.java b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/client/gui/DecorationTableScreen.java index 1c165cee..20d9a1d5 100644 --- a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/client/gui/DecorationTableScreen.java +++ b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/client/gui/DecorationTableScreen.java @@ -12,6 +12,7 @@ import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.block.model.ItemTransform; import net.minecraft.client.renderer.entity.ItemRenderer; import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.client.resources.model.BakedModel; @@ -35,6 +36,10 @@ import net.p3pp3rf1y.sophisticatedstorage.SophisticatedStorage; import net.p3pp3rf1y.sophisticatedstorage.block.DecorationTableBlockEntity; import net.p3pp3rf1y.sophisticatedstorage.common.gui.DecorationTableMenu; +import net.p3pp3rf1y.sophisticatedstorage.init.ModBlocks; +import net.p3pp3rf1y.sophisticatedstorage.init.ModItems; +import net.p3pp3rf1y.sophisticatedstorage.item.StorageBlockItem; +import net.p3pp3rf1y.sophisticatedstorage.util.DecorationHelper; import org.joml.Matrix4f; import javax.annotation.Nullable; @@ -78,7 +83,7 @@ public class DecorationTableScreen extends AbstractContainerScreen menu.getSlot(11).getItem()); + blockPreview = new BlockPreview(new Position(leftPos + lastDyeSlot.x + 16 + 1 + 8 + 1, topPos + lastDyeSlot.y), new Dimension(80, resultSlot.y - lastDyeSlot.y + 16 + 4)); + updatePreviewStacks(); addRenderableWidget(blockPreview); addVerticalInheritanceArrow(DecorationTableBlockEntity.TOP_TRIM_SLOT); @@ -117,9 +145,9 @@ protected void init() { addPartHint(DecorationTableBlockEntity.TOP_TRIM_SLOT, TOP_TRIM_HIGHLIGHT, "top_trim"); addPartHint(DecorationTableBlockEntity.SIDE_TRIM_SLOT, SIDE_TRIM_HIGHLIGHT, "side_trim"); addPartHint(DecorationTableBlockEntity.BOTTOM_TRIM_SLOT, BOTTOM_TRIM_HIGHLIGHT, "bottom_trim"); - addPartHint(DecorationTableBlockEntity.TOP_CORE_SLOT, TOP_CORE_HIGHLIGHT, "top_core"); - addPartHint(DecorationTableBlockEntity.SIDE_CORE_SLOT, SIDE_CORE_HIGHLIGHT, "side_core"); - addPartHint(DecorationTableBlockEntity.BOTTOM_CORE_SLOT, BOTTOM_CORE_HIGHLIGHT, "bottom_core"); + addPartHint(DecorationTableBlockEntity.TOP_CORE_SLOT, TOP_CORE_HIGHLIGHT, "top"); + addPartHint(DecorationTableBlockEntity.SIDE_CORE_SLOT, SIDE_CORE_HIGHLIGHT, "side"); + addPartHint(DecorationTableBlockEntity.BOTTOM_CORE_SLOT, BOTTOM_CORE_HIGHLIGHT, "bottom"); Slot slot = menu.getSlot(DecorationTableBlockEntity.TOP_INNER_TRIM_SLOT); addRenderableWidget(new PartStorageInfo(new Position(leftPos + slot.x + 57, topPos + slot.y), menu::getPartsStored)); @@ -153,6 +181,7 @@ private void openColorPicker(int color, IntConsumer colorSetter) { colorSetter.accept(c); colorPicker = null; blockPreview.setVisible(true); + updatePreviewStacks(); }); blockPreview.setVisible(false); } @@ -167,6 +196,7 @@ private void addInheritanceArrow(int slotIndex, int xOffset, int yOffset, Button button -> { resultPartsNeededTooltip.clear(); getMenu().setSlotMaterialInheritance(slotIndex, !getMenu().isSlotMaterialInherited(slotIndex)); + updatePreviewStacks(); }, () -> getMenu().isSlotMaterialInherited(slotIndex)) { @Override public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) { @@ -270,20 +300,20 @@ protected void renderSlot(GuiGraphics guiGraphics, Slot slot) { @Override protected List getTooltipFromContainerItem(ItemStack stack) { List tooltip = super.getTooltipFromContainerItem(stack); - if (hoveredSlot == getMenu().getResultSlot() && !hoveredSlot.getItem().isEmpty()) { - Map partsNeeded = getMenu().getPartsNeeded(); - tooltip.addAll(getResultPartsNeededTooltip(partsNeeded)); + if (hoveredSlot == getMenu().getResultSlot() && !hoveredSlot.getItem().isEmpty() && hoveredSlot.getItem().getItem() != ModItems.PAINTBRUSH.get()) { + tooltip.addAll(getResultPartsNeededTooltip()); } else if (!resultPartsNeededTooltip.isEmpty()) { resultPartsNeededTooltip.clear(); } - return tooltip; } - private List getResultPartsNeededTooltip(Map partsNeeded) { + private List getResultPartsNeededTooltip() { if (!resultPartsNeededTooltip.isEmpty()) { return resultPartsNeededTooltip; } + + Map partsNeeded = getMenu().getPartsNeeded(); addPartCountInfo(partsNeeded, resultPartsNeededTooltip, location -> getMenu().getMissingDyes().contains(location) ? ChatFormatting.RED : ChatFormatting.DARK_GRAY); return resultPartsNeededTooltip; } @@ -304,19 +334,19 @@ private static void addPartCountInfo(Map partCounts, ItemStack itemStack = entry.getKey(); ResourceLocation location = entry.getValue().getA(); int count = entry.getValue().getB(); - MutableComponent partCountText = Component.literal(count + "/" + DecorationTableBlockEntity.BLOCK_TOTAL_PARTS + " (" + String.format("%.0f%%", (float) count / DecorationTableBlockEntity.BLOCK_TOTAL_PARTS * 100) + ") of "); + MutableComponent partCountText = Component.literal(count + "/" + DecorationHelper.BLOCK_TOTAL_PARTS + " (" + String.format("%.0f%%", (float) count / DecorationHelper.BLOCK_TOTAL_PARTS * 100) + ") of "); tooltip.add(partCountText.append(itemStack.getHoverName()).withStyle(getPartFormatting.apply(location))); }); } private static final Map SLOT_PREVIEW_ROTATIONS = Map.of( - 0, new Vec2(90, 0), - 1, new Vec2(90, 0), - 4, new Vec2(90, 0), - 2, new Vec2(0, 0), - 5, new Vec2(0, 0), - 3, new Vec2(-90, 0), - 6, new Vec2(-90, 0) + 0, new Vec2(90, 180), + 1, new Vec2(90, 180), + 4, new Vec2(90, 180), + 2, new Vec2(0, 180), + 5, new Vec2(0, 180), + 3, new Vec2(-90, 180), + 6, new Vec2(-90, 180) ); private void updatePreviewRotation(int mouseX, int mouseY) { @@ -324,7 +354,8 @@ private void updatePreviewRotation(int mouseX, int mouseY) { (slotIndex, rotation) -> updatePreviewRotationForSlot(slotIndex, mouseX, mouseY, (int) rotation.x, (int) rotation.y) ); if (lastRotationSetTime != 0 && System.currentTimeMillis() - lastRotationSetTime > 1000) { - blockPreview.setTargetRotations(30, 45); + blockPreview.resetToDefaultRotation(); + lastRotationSetTime = 0; } } @@ -350,7 +381,7 @@ public boolean mouseDragged(double mouseX, double mouseY, int button, double dra for (GuiEventListener child : children()) { if (child.isMouseOver(mouseX, mouseY) && child.mouseDragged(mouseX, mouseY, button, dragX, dragY)) { - if (child instanceof BlockPreviewWidget) { + if (child instanceof BlockPreview) { lastRotationSetTime = System.currentTimeMillis() + 100_000; } @@ -386,6 +417,13 @@ public boolean mouseClicked(double mouseX, double mouseY, int button) { @Override public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + if (keyCode == 256 && colorPicker != null) { + colorPicker = null; + blockPreview.setVisible(true); + updatePreviewStacks(); + return true; + } + return super.keyPressed(keyCode, scanCode, modifiers); } @@ -468,8 +506,36 @@ private List getPartStorageTooltip() { } } - private static class BlockPreviewWidget extends WidgetBase { - private final Supplier blockStackSupplier; + private static class StackButton extends ButtonBase { + private static final TextureBlitData BUTTON_HOVER = new TextureBlitData(GuiHelper.GUI_CONTROLS, Dimension.SQUARE_256, new UV(63, 42), Dimension.SQUARE_18); + private final Supplier stackSupplier; + + protected StackButton(Position position, IntConsumer onClick, Supplier stackSupplier) { + super(position, Dimension.SQUARE_18, onClick); + this.stackSupplier = stackSupplier; + } + + @Override + protected void renderBg(GuiGraphics guiGraphics, Minecraft minecraft, int mouseX, int mouseY) { + + } + + @Override + protected void renderWidget(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) { + ItemStack stack = stackSupplier.get(); + if (stack.isEmpty()) { + return; + } + + guiGraphics.renderItem(stack, x + 1, y + 1); + if (isMouseOver(mouseX, mouseY)) { + GuiHelper.blit(guiGraphics, x, y, BUTTON_HOVER); + } + } + } + + private static class BlockPreview extends CompositeWidgetBase { + private final List previewStacks = new ArrayList<>(); private float xAxisRotation = 30; private float yAxisRotation = 45; @@ -478,10 +544,54 @@ private static class BlockPreviewWidget extends WidgetBase { private float targetXAxisRotation = xAxisRotation; private float targetYAxisRotation = yAxisRotation; private long lastTargetSetTime = 0; + private int selectedPreview = 0; + private final List previewStackButtons = new ArrayList<>(); - protected BlockPreviewWidget(Position position, Dimension dimension, Supplier blockStackSupplier) { + protected BlockPreview(Position position, Dimension dimension) { super(position, dimension); - this.blockStackSupplier = blockStackSupplier; + } + + public void setPreviewStacks(List previewStacks) { + this.previewStacks.clear(); + this.previewStacks.addAll(previewStacks); + this.selectedPreview = 0; + updatePreviewStackButtons(); + resetToDefaultRotation(); + } + + private void updatePreviewStackButtons() { + previewStackButtons.forEach(this.children::remove); + previewStackButtons.clear(); + + if (previewStacks.size() < 2) { + return; + } + + int x = this.x + (this.getWidth() - previewStacks.size() * (18 + 2)) / 2; + for (int i = 0; i < previewStacks.size(); i++) { + ItemStack stack = previewStacks.get(i); + int finalI = i; + previewStackButtons.add(new StackButton(new Position(x + i * 20, y + getHeight() - 19), button -> { + selectedPreview = finalI; + resetToDefaultRotation(); + }, () -> stack)); + } + previewStackButtons.forEach(this::addChild); + } + + public void resetToDefaultRotation() { + if (previewStacks.isEmpty()) { + return; + } + + ItemStack previewStack = previewStacks.get(selectedPreview); + if (previewStack.isEmpty()) { + return; + } + + + ItemTransform guiTransform = minecraft.getItemRenderer().getModel(previewStack, null, null, 0).getTransforms().getTransform(ItemDisplayContext.GUI); + setTargetRotations((int) guiTransform.rotation.x(), (int) guiTransform.rotation.y()); } public void setTargetRotations(int xAxisRotation, int yAxisRotation) { @@ -503,8 +613,14 @@ protected void renderBg(GuiGraphics guiGraphics, Minecraft minecraft, int mouseX @Override protected void renderWidget(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) { - ItemStack slotStack = blockStackSupplier.get(); - if (slotStack.isEmpty()) { + super.renderWidget(guiGraphics, mouseX, mouseY, partialTicks); + if (previewStacks.isEmpty()) { + return; + } + + ItemStack previewStack = previewStacks.get(selectedPreview); + + if (previewStack.isEmpty()) { return; } @@ -512,24 +628,25 @@ protected void renderWidget(GuiGraphics guiGraphics, int mouseX, int mouseY, flo PoseStack pose = guiGraphics.pose(); pose.pushPose(); - pose.translate(x + getWidth() / 2f, y + getHeight() / 2f, 150); + float yCenter = (getHeight() - (previewStackButtons.isEmpty() ? 0 : 20)) / 2f; + pose.translate(x + getWidth() / 2f, y + yCenter, 150); pose.mulPose(Axis.XN.rotationDegrees(xAxisRotation)); pose.mulPose(Axis.YP.rotationDegrees(yAxisRotation)); int scale = 48; pose.mulPoseMatrix((new Matrix4f()).scaling(scale, -scale, scale)); pose.translate(-0.5, -0.5, -0.5); ItemRenderer itemRenderer = minecraft.getItemRenderer(); - BakedModel bakedModel = itemRenderer.getModel(slotStack, null, null, 0); - int combinedLight = 0xf000f0; + BakedModel bakedModel = itemRenderer.getModel(previewStack, null, null, 0); + int combinedLight = 15728880; if (bakedModel.isCustomRenderer()) { - IClientItemExtensions.of(slotStack).getCustomRenderer().renderByItem(slotStack, ItemDisplayContext.GUI, pose, guiGraphics.bufferSource(), combinedLight, OverlayTexture.NO_OVERLAY); + IClientItemExtensions.of(previewStack).getCustomRenderer().renderByItem(previewStack, ItemDisplayContext.GUI, pose, guiGraphics.bufferSource(), combinedLight, OverlayTexture.NO_OVERLAY); } else { - Iterator renderPasses = bakedModel.getRenderPasses(slotStack, true).iterator(); + Iterator renderPasses = bakedModel.getRenderPasses(previewStack, true).iterator(); renderPasses.forEachRemaining(model -> { - Iterator renderTypes = model.getRenderTypes(slotStack, true).iterator(); + Iterator renderTypes = model.getRenderTypes(previewStack, true).iterator(); renderTypes.forEachRemaining(renderType -> { - VertexConsumer vertexconsumer = ItemRenderer.getFoilBufferDirect(guiGraphics.bufferSource(), renderType, true, slotStack.hasFoil()); - itemRenderer.renderModelLists(model, slotStack, combinedLight, OverlayTexture.NO_OVERLAY, pose, vertexconsumer); + VertexConsumer vertexconsumer = ItemRenderer.getFoilBufferDirect(guiGraphics.bufferSource(), renderType, true, previewStack.hasFoil()); + itemRenderer.renderModelLists(model, previewStack, combinedLight, OverlayTexture.NO_OVERLAY, pose, vertexconsumer); }); }); } @@ -553,6 +670,10 @@ private void updateRotations() { @Override public boolean mouseDragged(double mouseX, double mouseY, int button, double dragX, double dragY) { + if (!previewStackButtons.isEmpty() && mouseY > y + getHeight() - 20) { + return super.mouseDragged(mouseX, mouseY, button, dragX, dragY); + } + yAxisRotation += 2 * dragX; yAxisRotation = yAxisRotation % 360; xAxisRotation += 2 * dragY; diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/client/gui/PaintbrushOverlay.java b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/client/gui/PaintbrushOverlay.java new file mode 100644 index 00000000..96fff690 --- /dev/null +++ b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/client/gui/PaintbrushOverlay.java @@ -0,0 +1,82 @@ +package net.p3pp3rf1y.sophisticatedstorage.client.gui; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.DyeColor; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraftforge.client.gui.overlay.IGuiOverlay; +import net.p3pp3rf1y.sophisticatedcore.util.InventoryHelper; +import net.p3pp3rf1y.sophisticatedstorage.block.StorageBlockBase; +import net.p3pp3rf1y.sophisticatedstorage.init.ModBlocks; +import net.p3pp3rf1y.sophisticatedstorage.init.ModItems; +import net.p3pp3rf1y.sophisticatedstorage.item.PaintbrushItem; + +import javax.annotation.Nullable; +import java.util.Optional; + +public class PaintbrushOverlay { + + private static Optional ITEM_REQUIREMENTS_CACHE = Optional.empty(); + @Nullable + private static BlockPos lastPosCached = null; + @Nullable + private static ItemStack lastPaintbrushCached = null; + + public static Optional getItemRequirementsFor(ItemStack paintbrush, Player player, Level level, BlockPos pos) { + if (!pos.equals(lastPosCached) || paintbrush != lastPaintbrushCached) { + ITEM_REQUIREMENTS_CACHE = PaintbrushItem.getItemRequirements(paintbrush, player, level, pos); + lastPosCached = pos; + lastPaintbrushCached = paintbrush; + } + return ITEM_REQUIREMENTS_CACHE; + } + + public static final IGuiOverlay HUD_PAINTBRUSH_INFO = (gui, guiGraphics, partialTicks, width, height) -> { + Minecraft mc = Minecraft.getInstance(); + if (mc.screen != null) { + if (!mc.screen.isPauseScreen()) { + lastPosCached = null; + lastPaintbrushCached = null; + } + return; + } + + LocalPlayer player = mc.player; + Level level = mc.level; + if (player == null || level == null || !(mc.hitResult instanceof BlockHitResult blockHitResult)) { + return; + } + + BlockPos pos = blockHitResult.getBlockPos(); + if (!(level.getBlockState(pos).getBlock() instanceof StorageBlockBase) && level.getBlockState(pos).getBlock() != ModBlocks.CONTROLLER.get()) { + return; + } + + InventoryHelper.getItemFromEitherHand(player, ModItems.PAINTBRUSH.get()).flatMap(paintbrush -> getItemRequirementsFor(paintbrush, player, level, pos)) + .ifPresent(itemRequirements -> { + if (itemRequirements.itemsMissing().isEmpty()) { + return; + } + + Component missingItems = StorageTranslationHelper.INSTANCE.translItemOverlayMessage(ModItems.PAINTBRUSH.get(), "missing_items"); + Font font = mc.font; + int i = font.width(missingItems); + int x = (gui.screenWidth - i) / 2; + int y = gui.screenHeight - 75 - 10; + guiGraphics.drawString(font, missingItems, x + 1, y, DyeColor.WHITE.getTextColor()); + + x = (gui.screenWidth - itemRequirements.itemsMissing().size() * 18) / 2; + for (ItemStack missingItem : itemRequirements.itemsMissing()) { + guiGraphics.renderItem(missingItem, x, y + 10); + guiGraphics.renderItemDecorations(font, missingItem, x, y + 10); + x += 18; + } + }); + }; +} diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/client/render/BarrelBakedModelBase.java b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/client/render/BarrelBakedModelBase.java index 945727be..0bd32f38 100644 --- a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/client/render/BarrelBakedModelBase.java +++ b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/client/render/BarrelBakedModelBase.java @@ -99,9 +99,19 @@ public Cache load(Direction key) { private static final ItemTransforms ITEM_TRANSFORMS = createItemTransforms(); private static final List PARTICLE_ICON_MATERIAL_PRIORITY = List.of(BarrelMaterial.ALL, BarrelMaterial.ALL_BUT_TRIM, BarrelMaterial.TOP_ALL, BarrelMaterial.TOP); - @SuppressWarnings("java:S4738") //ItemTransforms require Guava ImmutableMap to be passed in so no way to change that to java Map + @SuppressWarnings("java:S4738") + //ItemTransforms require Guava ImmutableMap to be passed in so no way to change that to java Map private static ItemTransforms createItemTransforms() { - return new ItemTransforms(new ItemTransform(new Vector3f(75, 45, 0), new Vector3f(0, 2.5f / 16f, 0), new Vector3f(0.375f, 0.375f, 0.375f), DEFAULT_ROTATION), new ItemTransform(new Vector3f(75, 45, 0), new Vector3f(0, 2.5f / 16f, 0), new Vector3f(0.375f, 0.375f, 0.375f), DEFAULT_ROTATION), new ItemTransform(new Vector3f(0, 225, 0), new Vector3f(0, 0, 0), new Vector3f(0.4f, 0.4f, 0.4f), DEFAULT_ROTATION), new ItemTransform(new Vector3f(0, 45, 0), new Vector3f(0, 0, 0), new Vector3f(0.4f, 0.4f, 0.4f), DEFAULT_ROTATION), new ItemTransform(new Vector3f(0, 0, 0), new Vector3f(0, 14.25f / 16f, 0), new Vector3f(1, 1, 1), DEFAULT_ROTATION), new ItemTransform(new Vector3f(30, 45, 0), new Vector3f(0, 0, 0), new Vector3f(0.625f, 0.625f, 0.625f), DEFAULT_ROTATION), new ItemTransform(new Vector3f(0, 0, 0), new Vector3f(0, 3 / 16f, 0), new Vector3f(0.25f, 0.25f, 0.25f), DEFAULT_ROTATION), new ItemTransform(new Vector3f(0, 0, 0), new Vector3f(0, 0, 0), new Vector3f(0.5f, 0.5f, 0.5f), DEFAULT_ROTATION), ImmutableMap.of()); + return new ItemTransforms( + new ItemTransform(new Vector3f(75, 45, 0), new Vector3f(0, 2.5f / 16f, 0), new Vector3f(0.375f, 0.375f, 0.375f), DEFAULT_ROTATION), + new ItemTransform(new Vector3f(75, 45, 0), new Vector3f(0, 2.5f / 16f, 0), new Vector3f(0.375f, 0.375f, 0.375f), DEFAULT_ROTATION), + new ItemTransform(new Vector3f(0, 225, 0), new Vector3f(0, 0, 0), new Vector3f(0.4f, 0.4f, 0.4f), DEFAULT_ROTATION), + new ItemTransform(new Vector3f(0, 45, 0), new Vector3f(0, 0, 0), new Vector3f(0.4f, 0.4f, 0.4f), DEFAULT_ROTATION), + new ItemTransform(new Vector3f(0, 0, 0), new Vector3f(0, 14.25f / 16f, 0), new Vector3f(1, 1, 1), DEFAULT_ROTATION), + new ItemTransform(new Vector3f(30, 225, 0), new Vector3f(0, 0, 0), new Vector3f(0.625f, 0.625f, 0.625f), DEFAULT_ROTATION), + new ItemTransform(new Vector3f(0, 0, 0), new Vector3f(0, 3 / 16f, 0), new Vector3f(0.25f, 0.25f, 0.25f), DEFAULT_ROTATION), + new ItemTransform(new Vector3f(0, 0, 0), new Vector3f(0, 0, 0), new Vector3f(0.5f, 0.5f, 0.5f), DEFAULT_ROTATION) + , ImmutableMap.of()); } public static void invalidateCache() { @@ -285,7 +295,7 @@ private static Direction getSpriteSide(@Nullable BlockState state, @Nullable Dir } private List bakeAndAddDynamicQuads(@Nullable Direction spriteSide, RandomSource rand, @Nullable String woodName, - Map barrelMaterials, boolean rendersUsingSplitModel, boolean renderCore, boolean renderTrim) { + Map barrelMaterials, boolean rendersUsingSplitModel, boolean renderCore, boolean renderTrim) { Map bakingData = woodDynamicBakingData.get(woodName != null ? woodName : WoodType.ACACIA.name()); @@ -317,7 +327,7 @@ private List bakeAndAddDynamicQuads(@Nullable Direction spriteSide, } private BakedModel getDynamicModel(@Nullable String woodName, Map bakingData, - Map> materials, DynamicBarrelBakingData.DynamicPart dynamicPart) { + Map> materials, DynamicBarrelBakingData.DynamicPart dynamicPart) { int hash = Objects.hash(woodName, materials, dynamicPart.name()); BakedModel bakedModel = dynamicBakedModelCache.getIfPresent(hash); if (bakedModel == null) { @@ -484,7 +494,7 @@ private static boolean shouldRenderForRenderType(ItemStack item, @Nullable Rende } private void addInaccessibleSlotsQuads(BlockState state, RandomSource rand, List ret, ModelData data, BarrelBlock barrelBlock, - @Nullable List displayItems, Minecraft minecraft) { + @Nullable List displayItems, Minecraft minecraft) { List inaccessibleSlots = data.get(INACCESSIBLE_SLOTS); if (displayItems != null && inaccessibleSlots != null) { ItemStack inaccessibleSlotStack = new ItemStack(ModItems.INACCESSIBLE_SLOT.get()); @@ -502,7 +512,7 @@ private void addInaccessibleSlotsQuads(BlockState state, RandomSource rand, List @SuppressWarnings({"deprecation", "java:S107"}) private void addRenderedItemSide(BlockState state, RandomSource rand, List ret, ItemStack displayItem, BakedModel model, int rotation, - @Nullable Direction dir, int displayItemIndex, int displayItemCount) { + @Nullable Direction dir, int displayItemIndex, int displayItemCount) { List quads = model.getQuads(null, dir, rand); quads = MOVE_TO_CORNER.process(quads); quads = QuadTransformers.applying(toTransformation(model.getTransforms().getTransform(ItemDisplayContext.FIXED))).process(quads); @@ -597,7 +607,7 @@ private IQuadTransformer getDisplayRotation(int rotation) { } private void addTintableModelQuads(@Nullable BlockState state, @Nullable Direction side, RandomSource rand, List ret, boolean hasMainColor, - boolean hasAccentColor, Map modelParts, @Nullable RenderType renderType) { + boolean hasAccentColor, Map modelParts, @Nullable RenderType renderType) { if (renderType != null && renderType != RenderType.cutout()) { return; } @@ -618,7 +628,7 @@ private BarrelModelPart getMainPart(@Nullable BlockState state) { protected abstract boolean rendersOpen(); private void addPartQuads(@Nullable BlockState state, @Nullable Direction side, RandomSource rand, List ret, - Map modelParts, BarrelModelPart part, @Nullable RenderType renderType) { + Map modelParts, BarrelModelPart part, @Nullable RenderType renderType) { if (renderType != null && renderType != RenderType.cutout()) { return; } diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/client/render/DecorationTableRenderer.java b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/client/render/DecorationTableRenderer.java index d314cfbc..cfbc2b0d 100644 --- a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/client/render/DecorationTableRenderer.java +++ b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/client/render/DecorationTableRenderer.java @@ -9,6 +9,7 @@ import net.minecraft.world.item.ItemDisplayContext; import net.p3pp3rf1y.sophisticatedstorage.block.DecorationTableBlock; import net.p3pp3rf1y.sophisticatedstorage.block.DecorationTableBlockEntity; +import net.p3pp3rf1y.sophisticatedstorage.init.ModItems; public class DecorationTableRenderer implements BlockEntityRenderer { private final ItemRenderer itemRenderer; @@ -19,7 +20,7 @@ public DecorationTableRenderer(BlockEntityRendererProvider.Context context) { @Override public void render(DecorationTableBlockEntity table, float v, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight, int packedOverlay) { - if (table.getResult().isEmpty()) { + if (table.getResult().isEmpty() || table.getResult().getItem() == ModItems.PAINTBRUSH.get()) { return; } diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/common/gui/DecorationTableMenu.java b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/common/gui/DecorationTableMenu.java index 15280e9d..a7bf0fc9 100644 --- a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/common/gui/DecorationTableMenu.java +++ b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/common/gui/DecorationTableMenu.java @@ -21,6 +21,7 @@ import net.p3pp3rf1y.sophisticatedstorage.block.DecorationTableBlockEntity; import net.p3pp3rf1y.sophisticatedstorage.init.ModBlocks; +import javax.annotation.Nullable; import java.util.Map; import java.util.Set; import java.util.function.Consumer; @@ -40,6 +41,9 @@ public class DecorationTableMenu extends AbstractContainerMenu implements ISynce private SlotRange dyeSlotRange; private SlotRange storageSlotRange; private SlotRange playerSlotRange; + @Nullable + private Runnable slotChangedListener = null; + private SlotItemHandler storageSlot; public DecorationTableMenu(int containerId, Player player, BlockPos pos) { super(ModBlocks.DECORATION_TABLE_CONTAINER_TYPE.get(), containerId); @@ -54,9 +58,29 @@ public DecorationTableMenu(int containerId, Player player, BlockPos pos) { addPlayerSlots(player.getInventory(), y); } + public void setSlotChangedListener(@Nullable Runnable listener) { + slotChangedListener = listener; + } + + public Slot getStorageSlot() { + return storageSlot; + } + + public ItemStack decorateStack(ItemStack stack) { + return blockEntity.decorateStack(stack).result(); + } + private void addStorageSlots() { ItemStackHandler storageBlock = blockEntity.getStorageBlock(); - SlotItemHandler storageSlot = new SlotItemHandler(storageBlock, 0, getSlot(dyeSlotRange.firstSlot()).x, getSlot(DecorationTableBlockEntity.BOTTOM_TRIM_SLOT).y); + storageSlot = new SlotItemHandler(storageBlock, 0, getSlot(dyeSlotRange.firstSlot()).x, getSlot(DecorationTableBlockEntity.BOTTOM_TRIM_SLOT).y) { + @Override + public void setChanged() { + super.setChanged(); + if (slotChangedListener != null) { + slotChangedListener.run(); + } + } + }; addSlot(storageSlot); storageSlotRange = new SlotRange(dyeSlotRange.firstSlot() + dyeSlotRange.numberOfSlots(), 1); @@ -131,7 +155,15 @@ private int addDecorationSlots() { } private int addDecorationSlot(ItemStackHandler itemHandler, int slotIndex, int xOffset, int y, int yPadding) { - addSlot(new SlotItemHandler(itemHandler, slotIndex, xOffset, y).setBackground(InventoryMenu.BLOCK_ATLAS, EMPTY_MATERIAL_SLOT_BACKGROUND)); + addSlot(new SlotItemHandler(itemHandler, slotIndex, xOffset, y) { + @Override + public void setChanged() { + super.setChanged(); + if (slotChangedListener != null) { + slotChangedListener.run(); + } + } + }.setBackground(InventoryMenu.BLOCK_ATLAS, EMPTY_MATERIAL_SLOT_BACKGROUND)); y += 18; y += yPadding; return y; @@ -291,4 +323,8 @@ public void handleMessage(CompoundTag data) { public Map getPartsStored() { return blockEntity.getPartsStored(); } + + public boolean hasMaterials() { + return blockEntity.hasMaterials(); + } } diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/data/StorageRecipeProvider.java b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/data/StorageRecipeProvider.java index 17e058d7..fc45aead 100644 --- a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/data/StorageRecipeProvider.java +++ b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/data/StorageRecipeProvider.java @@ -79,6 +79,15 @@ protected void buildRecipes(Consumer consumer) { .define('B', ModItems.UPGRADE_BASE.get()) .unlockedBy("has_upgrade_base", has(ModItems.UPGRADE_BASE.get())) .save(consumer); + + ShapedRecipeBuilder.shaped(RecipeCategory.MISC, ModItems.PAINTBRUSH.get()) + .pattern(" W ") + .pattern(" SW") + .pattern("S ") + .define('S', Items.STICK) + .define('W', ItemTags.WOOL) + .unlockedBy("has_base_tier_wooden_storage", has(ModBlocks.BASE_TIER_WOODEN_STORAGE_TAG)) + .save(consumer); } private void addBackpackUpgradeConversionRecipes(Consumer consumer) { diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/init/ModItems.java b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/init/ModItems.java index 95ad4e49..b434dea8 100644 --- a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/init/ModItems.java +++ b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/init/ModItems.java @@ -89,6 +89,7 @@ import net.p3pp3rf1y.sophisticatedstorage.client.gui.StorageTranslationHelper; import net.p3pp3rf1y.sophisticatedstorage.crafting.DropPackedDisabledCondition; import net.p3pp3rf1y.sophisticatedstorage.data.CopyStorageDataFunction; +import net.p3pp3rf1y.sophisticatedstorage.item.PaintbrushItem; import net.p3pp3rf1y.sophisticatedstorage.item.StorageTierUpgradeItem; import net.p3pp3rf1y.sophisticatedstorage.item.StorageToolItem; import net.p3pp3rf1y.sophisticatedstorage.item.WoodStorageBlockItem; @@ -218,6 +219,7 @@ public void addCreativeTabItems(Consumer itemConsumer) { }); public static final RegistryObject STORAGE_TOOL = ITEMS.register("storage_tool", StorageToolItem::new); public static final RegistryObject DEBUG_TOOL = ITEMS.register("debug_tool", () -> new ItemBase(new Item.Properties().stacksTo(1))); + public static final RegistryObject PAINTBRUSH = ITEMS.register("paintbrush", PaintbrushItem::new); public static final RegistryObject INACCESSIBLE_SLOT = ITEMS.register("inaccessible_slot", () -> new Item(new Item.Properties().stacksTo(1))); public static final RegistryObject COPY_STORAGE_DATA = LOOT_FUNCTION_TYPES.register("copy_storage_data", () -> new LootItemFunctionType(new CopyStorageDataFunction.Serializer())); diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/item/BarrelBlockItem.java b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/item/BarrelBlockItem.java index 72a04fcb..c767c24d 100644 --- a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/item/BarrelBlockItem.java +++ b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/item/BarrelBlockItem.java @@ -112,6 +112,13 @@ public static void removeCoveredTints(ItemStack barrelStackCopy, Map getUncompactedMaterials(ItemStack storageStack) { + Map materials = new EnumMap<>(BarrelMaterial.class); + materials.putAll(getMaterials(storageStack)); + uncompactMaterials(materials); + return materials; + } + @Override public Component getName(ItemStack stack) { Component name; diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/item/PaintbrushItem.java b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/item/PaintbrushItem.java new file mode 100644 index 00000000..bc2eecf1 --- /dev/null +++ b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/item/PaintbrushItem.java @@ -0,0 +1,418 @@ +package net.p3pp3rf1y.sophisticatedstorage.item; + +import net.minecraft.ChatFormatting; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.nbt.IntTag; +import net.minecraft.nbt.StringTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.Style; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.sounds.SoundSource; +import net.minecraft.tags.TagKey; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.common.Tags; +import net.minecraftforge.items.IItemHandler; +import net.p3pp3rf1y.sophisticatedcore.util.*; +import net.p3pp3rf1y.sophisticatedstorage.block.*; +import net.p3pp3rf1y.sophisticatedstorage.client.gui.StorageTranslationHelper; +import net.p3pp3rf1y.sophisticatedstorage.util.DecorationHelper; +import org.jetbrains.annotations.NotNull; + +import javax.annotation.Nullable; +import java.util.*; + +public class PaintbrushItem extends ItemBase { + private static final String BARREL_MATERIALS_TAG = "barrelMaterials"; + private static final String MAIN_COLOR_TAG = "mainColor"; + private static final String ACCENT_COLOR_TAG = "accentColor"; + private static final String REMAINING_PARTS_TAG = "remainingParts"; + + public PaintbrushItem() { + super(new Properties().stacksTo(1)); + } + + public static void setBarrelMaterials(ItemStack paintbrush, Map materials) { + NBTHelper.putMap(paintbrush.getOrCreateTag(), BARREL_MATERIALS_TAG, materials, BarrelMaterial::getSerializedName, resourceLocation -> StringTag.valueOf(resourceLocation.toString())); + resetMainColor(paintbrush); + resetAccentColor(paintbrush); + } + + public static Optional getItemRequirements(ItemStack paintbrush, Player player, Level level, BlockPos lookingAtPos) { + Map materialsToApply = new HashMap<>(getBarrelMaterials(paintbrush)); + BlockEntity be = level.getBlockEntity(lookingAtPos); + if (be == null) { + return Optional.empty(); + } + + if (!materialsToApply.isEmpty()) { + return getMaterialItemRequirements(paintbrush, player, be, materialsToApply); + } else { + return getDyeItemRequirements(paintbrush, player, be); + } + } + + private static Optional getMaterialItemRequirements(ItemStack paintbrush, Player player, BlockEntity be, Map materialsToApply) { + Map allPartsNeeded = new HashMap<>(); + if (be instanceof StorageBlockEntity storageBe) { + allPartsNeeded = getStorageMaterialPartsNeeded(materialsToApply, storageBe); + } else if (be instanceof ControllerBlockEntity controllerBe) { + for (BlockPos storagePosition : controllerBe.getStoragePositions()) { + addStorageMaterialPartsNeeded(materialsToApply, controllerBe, storagePosition, allPartsNeeded); + } + } + + if (allPartsNeeded.isEmpty()) { + return Optional.empty(); + } + Map remainingParts = getRemainingParts(paintbrush); + DecorationHelper.ConsumptionResult result = DecorationHelper.consumeMaterialPartsNeeded(allPartsNeeded, remainingParts, InventoryHelper.getItemHandlersFromPlayerIncludingContainers(player), true); + + List itemsPresent = new ArrayList<>(); + List itemsMissing = new ArrayList<>(); + + for (Map.Entry entry : allPartsNeeded.entrySet()) { + ResourceLocation part = entry.getKey(); + int count = ceilDiv(entry.getValue() - remainingParts.getOrDefault(part, 0), DecorationHelper.BLOCK_TOTAL_PARTS); + int missing = ceilDiv(result.missingParts().getOrDefault(part, 0), DecorationHelper.BLOCK_TOTAL_PARTS); + int present = count - missing; + BuiltInRegistries.ITEM.getOptional(part).ifPresent(item -> { + if (missing > 0) { + itemsMissing.add(new ItemStack(item, missing)); + } + if (present > 0) { + itemsPresent.add(new ItemStack(item, present)); + } + }); + } + + return Optional.of(new ItemRequirements(itemsPresent, itemsMissing)); + } + + private static int ceilDiv(int x, int y) { + return (int) Math.ceil((double) x / y); + } + + private static void addStorageMaterialPartsNeeded(Map materialsToApply, ControllerBlockEntity controllerBe, BlockPos storagePosition, Map allPartsNeeded) { + WorldHelper.getBlockEntity(controllerBe.getLevel(), storagePosition, StorageBlockEntity.class).ifPresent(storageBe -> { + Map storagePartsNeeded = getStorageMaterialPartsNeeded(materialsToApply, storageBe); + storagePartsNeeded.forEach((part, count) -> allPartsNeeded.merge(part, count, Integer::sum)); + }); + } + + private static Optional getDyeItemRequirements(ItemStack paintbrush, Player player, BlockEntity be) { + int mainColorToSet = getMainColor(paintbrush); + int accentColorToSet = getAccentColor(paintbrush); + + Map, Integer> allPartsNeeded = new HashMap<>(); + if (be instanceof StorageBlockEntity storageBe) { + allPartsNeeded = getStorageDyePartsNeeded(mainColorToSet, accentColorToSet, storageBe); + } else if (be instanceof ControllerBlockEntity controllerBe) { + for (BlockPos storagePosition : controllerBe.getStoragePositions()) { + addStorageDyePartsNeeded(mainColorToSet, accentColorToSet, controllerBe, storagePosition, allPartsNeeded); + } + } + + if (allPartsNeeded.isEmpty()) { + return Optional.empty(); + } + Map remainingParts = getRemainingParts(paintbrush); + DecorationHelper.ConsumptionResult result = DecorationHelper.consumeDyePartsNeeded(allPartsNeeded, InventoryHelper.getItemHandlersFromPlayerIncludingContainers(player), remainingParts, true); + + return compileDyeItemRequirements(allPartsNeeded, remainingParts, result); + } + + private static void addStorageDyePartsNeeded(int mainColorToSet, int accentColorToSet, ControllerBlockEntity controllerBe, BlockPos storagePosition, Map, Integer> allPartsNeeded) { + WorldHelper.getBlockEntity(controllerBe.getLevel(), storagePosition, StorageBlockEntity.class).ifPresent(storageBe -> { + Map, Integer> storagePartsNeeded = getStorageDyePartsNeeded(mainColorToSet, accentColorToSet, storageBe); + storagePartsNeeded.forEach((part, count) -> allPartsNeeded.merge(part, count, Integer::sum)); + }); + } + + private static Map, Integer> getStorageDyePartsNeeded(int mainColorToSet, int accentColorToSet, StorageBlockEntity storageBe) { + StorageWrapper storageWrapper = storageBe.getStorageWrapper(); + return DecorationHelper.getDyePartsNeeded(mainColorToSet, accentColorToSet, storageWrapper.getMainColor(), storageWrapper.getAccentColor()); + } + + + private static @NotNull Optional compileDyeItemRequirements(Map, Integer> allPartsNeeded, Map remainingParts, DecorationHelper.ConsumptionResult result) { + List itemsPresent = new ArrayList<>(); + List itemsMissing = new ArrayList<>(); + + for (Map.Entry, Integer> entry : allPartsNeeded.entrySet()) { + TagKey part = entry.getKey(); + int count = ceilDiv(entry.getValue() - remainingParts.getOrDefault(part.location(), 0), DecorationHelper.BLOCK_TOTAL_PARTS); + int missing = ceilDiv(result.missingParts().getOrDefault(part.location(), 0), DecorationHelper.BLOCK_TOTAL_PARTS); + int present = count - missing; + + Item dyeItem; + if (part == Tags.Items.DYES_RED) { + dyeItem = Items.RED_DYE; + } else if (part == Tags.Items.DYES_GREEN) { + dyeItem = Items.GREEN_DYE; + } else if (part == Tags.Items.DYES_BLUE) { + dyeItem = Items.BLUE_DYE; + } else { + continue; + } + + if (missing > 0) { + itemsMissing.add(new ItemStack(dyeItem, missing)); + } + if (present > 0) { + itemsPresent.add(new ItemStack(dyeItem, present)); + } + } + + return Optional.of(new ItemRequirements(itemsPresent, itemsMissing)); + } + + private static Map getStorageMaterialPartsNeeded(Map materialsToApply, StorageBlockEntity storageBe) { + if (storageBe instanceof BarrelBlockEntity barrelBe) { + Map originalMaterials = new HashMap<>(barrelBe.getMaterials()); + BarrelBlockItem.uncompactMaterials(originalMaterials); + return DecorationHelper.getMaterialPartsNeeded(originalMaterials, materialsToApply); + } + return Collections.emptyMap(); + } + + @Override + public InteractionResult onItemUseFirst(ItemStack paintbrush, UseOnContext context) { + if (!hasMainColor(paintbrush) && !hasAccentColor(paintbrush) && !hasBarrelMaterials(paintbrush)) { + return InteractionResult.PASS; + } + + Level level = context.getLevel(); + BlockEntity be = level.getBlockEntity(context.getClickedPos()); + if (be instanceof StorageBlockEntity storageBe) { + if (!level.isClientSide()) { + paintStorage(context.getPlayer(), paintbrush, storageBe, 1); + } + return InteractionResult.SUCCESS; + } else if (be instanceof ControllerBlockEntity controllerBe) { + if (!level.isClientSide()) { + paintConnectedStorages(context.getPlayer(), level, paintbrush, controllerBe); + } + return InteractionResult.SUCCESS; + } + + return InteractionResult.PASS; + } + + private void paintConnectedStorages(@Nullable Player player, Level level, ItemStack paintbrush, ControllerBlockEntity controllerBe) { + if (player == null) { + return; + } + + for (BlockPos pos : controllerBe.getStoragePositions()) { + WorldHelper.getBlockEntity(level, pos, StorageBlockEntity.class).ifPresent(storageBe -> paintStorage(player, paintbrush, storageBe, 0.6f)); + } + } + + private static void paintStorage(@Nullable Player player, ItemStack paintbrush, StorageBlockEntity storageBe, float soundVolume) { + if (player == null) { + return; + } + List itemHandlers = InventoryHelper.getItemHandlersFromPlayerIncludingContainers(player); + Map remainingParts = new HashMap<>(getRemainingParts(paintbrush)); + if (hasBarrelMaterials(paintbrush)) { + if (!(storageBe instanceof BarrelBlockEntity barrelBe)) { + return; + } + + Map originalMaterials = new HashMap<>(barrelBe.getMaterials()); + Map materialsToApply = new HashMap<>(getBarrelMaterials(paintbrush)); + if (originalMaterials.equals(materialsToApply)) { + return; + } + + BarrelBlockItem.uncompactMaterials(originalMaterials); + + if (!DecorationHelper.consumeMaterials(remainingParts, itemHandlers, originalMaterials, materialsToApply, true)) { + return; + } + + DecorationHelper.consumeMaterials(remainingParts, itemHandlers, originalMaterials, materialsToApply, false); + setRemainingParts(paintbrush, remainingParts); + + barrelBe.getStorageWrapper().setMainColor(-1); + barrelBe.getStorageWrapper().setAccentColor(-1); + BarrelBlockItem.compactMaterials(materialsToApply); + barrelBe.setMaterials(materialsToApply); + + playSoundAndParticles(player.level(), storageBe.getBlockPos(), storageBe, soundVolume); + + WorldHelper.notifyBlockUpdate(storageBe); + } else { + StorageWrapper storageWrapper = storageBe.getStorageWrapper(); + int mainColorToSet = getMainColor(paintbrush) & 0x00FFFFFF; + int accentColorToSet = getAccentColor(paintbrush) & 0x00FFFFFF; + if (storageBe instanceof ChestBlockEntity chestBe) { + storageWrapper = chestBe.getMainStorageWrapper(); + storageBe = chestBe.getMainChestBlockEntity(); + } + + int originalMainColor = storageWrapper.getMainColor(); + int originalAccentColor = storageWrapper.getAccentColor(); + + if (originalMainColor == mainColorToSet && originalAccentColor == accentColorToSet) { + return; + } + + if (!DecorationHelper.consumeDyes(mainColorToSet, accentColorToSet, remainingParts, itemHandlers, originalMainColor, originalAccentColor, true)) { + return; + } + + if (hasMainColor(paintbrush)) { + storageWrapper.setMainColor(mainColorToSet); + } + if (hasAccentColor(paintbrush)) { + storageWrapper.setAccentColor(accentColorToSet); + } + + if (storageBe instanceof BarrelBlockEntity barrelBe) { + barrelBe.setMaterials(Collections.emptyMap()); + } + + DecorationHelper.consumeDyes(mainColorToSet, accentColorToSet, remainingParts, itemHandlers, originalMainColor, originalAccentColor, false); + setRemainingParts(paintbrush, remainingParts); + + playSoundAndParticles(player.level(), storageBe.getBlockPos(), storageBe, soundVolume); + + WorldHelper.notifyBlockUpdate(storageBe); + } + } + + private static void playSoundAndParticles(Level level, BlockPos pos, StorageBlockEntity storageBe, float soundVolume) { + BlockState state = storageBe.getBlockState(); + level.playSound(null, pos, state.getSoundType(level, pos, null).getPlaceSound(), SoundSource.BLOCKS, soundVolume, 1); + + if (state.getBlock() instanceof StorageBlockBase storageBlock && level instanceof ServerLevel serverLevel) { + Direction facing = storageBlock.getFacing(state); + double x = pos.getX() + 0.5D + facing.getStepX() * 0.6D; + double y = pos.getY() + 0.5D + facing.getStepY() * 0.6D; + double z = pos.getZ() + 0.5D + facing.getStepZ() * 0.6D; + double xOffset; + double yOffset; + double zOffset; + if (facing.getAxis().isVertical()) { + xOffset = 0.4D; + yOffset = 0.1D; + zOffset = 0.4D; + } else { + xOffset = 0.1D + facing.getStepZ() * 0.3D; + yOffset = 0.4D; + zOffset = 0.1D + facing.getStepX() * 0.3D; + } + + serverLevel.sendParticles(ParticleTypes.HAPPY_VILLAGER, x, y, z, 4, xOffset, yOffset, zOffset, 1f); + } + } + + private static void resetBarrelMaterials(ItemStack paintbrush) { + NBTHelper.removeTag(paintbrush, BARREL_MATERIALS_TAG); + } + + public static void resetMainColor(ItemStack paintbrush) { + NBTHelper.removeTag(paintbrush, MAIN_COLOR_TAG); + } + + public static void resetAccentColor(ItemStack paintbrush) { + NBTHelper.removeTag(paintbrush, ACCENT_COLOR_TAG); + } + + public static void setMainColor(ItemStack paintbrush, int mainColor) { + NBTHelper.putInt(paintbrush.getOrCreateTag(), MAIN_COLOR_TAG, mainColor); + resetBarrelMaterials(paintbrush); + } + + public static void setAccentColor(ItemStack paintbrush, int secondaryColor) { + NBTHelper.putInt(paintbrush.getOrCreateTag(), ACCENT_COLOR_TAG, secondaryColor); + resetBarrelMaterials(paintbrush); + } + + public static void setRemainingParts(ItemStack paintbrush, Map remainingParts) { + NBTHelper.putMap(paintbrush.getOrCreateTag(), REMAINING_PARTS_TAG, remainingParts, ResourceLocation::toString, IntTag::valueOf); + } + + public static Map getRemainingParts(ItemStack paintbrush) { + return NBTHelper.getMap(paintbrush.getOrCreateTag(), REMAINING_PARTS_TAG, ResourceLocation::new, (rl, tag) -> Optional.of(((IntTag) tag).getAsInt())).orElseGet(HashMap::new); + } + + @Override + public void appendHoverText(ItemStack stack, @Nullable Level level, List tooltip, TooltipFlag tooltipFlag) { + super.appendHoverText(stack, level, tooltip, tooltipFlag); + + tooltip.addAll(StorageTranslationHelper.INSTANCE.getTranslatedLines(StorageTranslationHelper.INSTANCE.translItemTooltip(stack.getItem()), null, ChatFormatting.DARK_GRAY)); + + if (hasBarrelMaterials(stack)) { + tooltip.add(Component.translatable(StorageTranslationHelper.INSTANCE.translItemTooltip("paintbrush") + ".materials").withStyle(ChatFormatting.GRAY)); + Map barrelMaterials = getBarrelMaterials(stack); + barrelMaterials.forEach((barrelMaterial, blockName) -> { + BuiltInRegistries.BLOCK.getOptional(blockName).ifPresent(block -> { + tooltip.add( + Component.translatable(StorageTranslationHelper.INSTANCE.translItemTooltip("paintbrush") + ".material", + Component.translatable(StorageTranslationHelper.INSTANCE.translGui("barrel_part." + barrelMaterial.getSerializedName())), + block.getName().withStyle(ChatFormatting.DARK_AQUA) + ).withStyle(ChatFormatting.GRAY) + ); + }); + }); + } + + if (hasMainColor(stack)) { + int mainColor = getMainColor(stack); + tooltip.add(Component.translatable(StorageTranslationHelper.INSTANCE.translItemTooltip("paintbrush") + ".main_color", + Component.literal(ColorHelper.getHexColor(mainColor)).withStyle(Style.EMPTY.withColor(mainColor)) + ).withStyle(ChatFormatting.GRAY) + ); + } + + if (hasAccentColor(stack)) { + int accentColor = getAccentColor(stack); + tooltip.add(Component.translatable(StorageTranslationHelper.INSTANCE.translItemTooltip("paintbrush") + ".accent_color", + Component.literal(ColorHelper.getHexColor(accentColor)).withStyle(Style.EMPTY.withColor(accentColor)) + ).withStyle(ChatFormatting.GRAY) + ); + } + } + + private static boolean hasMainColor(ItemStack paintbrush) { + return NBTHelper.hasTag(paintbrush, MAIN_COLOR_TAG); + } + + private static boolean hasAccentColor(ItemStack paintbrush) { + return NBTHelper.hasTag(paintbrush, ACCENT_COLOR_TAG); + } + + private static boolean hasBarrelMaterials(ItemStack paintbrush) { + return NBTHelper.hasTag(paintbrush, BARREL_MATERIALS_TAG); + } + + public static int getMainColor(ItemStack paintbrush) { + return NBTHelper.getInt(paintbrush, MAIN_COLOR_TAG).orElse(-1); + } + + public static int getAccentColor(ItemStack paintbrush) { + return NBTHelper.getInt(paintbrush, ACCENT_COLOR_TAG).orElse(-1); + } + + public static Map getBarrelMaterials(ItemStack paintbrush) { + return NBTHelper.getMap(paintbrush.getOrCreateTag(), BARREL_MATERIALS_TAG, BarrelMaterial::fromName, (bm, tag) -> Optional.of(new ResourceLocation(tag.getAsString()))).orElse(Map.of()); + } + + public record ItemRequirements(List itemsPresent, List itemsMissing) { + } +} diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/util/DecorationHelper.java b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/util/DecorationHelper.java new file mode 100644 index 00000000..c7684120 --- /dev/null +++ b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/util/DecorationHelper.java @@ -0,0 +1,219 @@ +package net.p3pp3rf1y.sophisticatedstorage.util; + +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.util.FastColor; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.common.Tags; +import net.minecraftforge.items.IItemHandler; +import net.p3pp3rf1y.sophisticatedstorage.block.BarrelMaterial; +import net.p3pp3rf1y.sophisticatedstorage.item.BarrelBlockItem; + +import javax.annotation.Nullable; +import java.util.*; +import java.util.function.BiPredicate; +import java.util.function.Function; + +public class DecorationHelper { + public static final int BLOCK_TOTAL_PARTS = 24; + private static final int MAIN_COLOR_PARTS = 18; + private static final int ACCENT_COLOR_PARTS = 6; + private static final Map DECORATIVE_SLOT_PARTS_NEEDED = Map.of( + BarrelMaterial.TOP_INNER_TRIM, 1, + BarrelMaterial.TOP_TRIM, 1, + BarrelMaterial.SIDE_TRIM, 4, + BarrelMaterial.BOTTOM_TRIM, 1, + BarrelMaterial.TOP, 3, + BarrelMaterial.SIDE, 12, + BarrelMaterial.BOTTOM, 3 + ); + + private DecorationHelper() { + } + + + public static Optional getMaterialLocation(ItemStack stack) { + if (stack.getItem() instanceof BlockItem blockItem) { + return Optional.of(BuiltInRegistries.BLOCK.getKey(blockItem.getBlock())); + } + return Optional.empty(); + } + + public static boolean consumeDyes(int mainColorBeingSet, int accentColorBeingSet, Map remainingParts, List dyes, Integer storageMainColor, Integer storageAccentColor, boolean simulate) { + Map, Integer> partsNeeded = getDyePartsNeeded(mainColorBeingSet, accentColorBeingSet, storageMainColor, storageAccentColor); + if (partsNeeded.isEmpty()) { + return true; + } + + return consumeDyePartsNeeded(partsNeeded, dyes, remainingParts, simulate).hasEnough(); + } + + public static Map, Integer> getDyePartsNeeded(int mainColorBeingSet, int accentColorBeingSet, int storageMainColor, int storageAccentColor) { + Map, Integer> partsNeeded = new HashMap<>(); + if (mainColorBeingSet != -1 && mainColorBeingSet != storageMainColor) { + int[] rgbPartsNeeded = calculateRGBPartsNeeded(mainColorBeingSet, MAIN_COLOR_PARTS); + addPartsNeededIfAny(rgbPartsNeeded, partsNeeded); + } + if (accentColorBeingSet != -1 && accentColorBeingSet != storageAccentColor) { + int[] rgbPartsNeeded = calculateRGBPartsNeeded(accentColorBeingSet, ACCENT_COLOR_PARTS); + addPartsNeededIfAny(rgbPartsNeeded, partsNeeded); + } + return partsNeeded; + } + + private static void addPartsNeededIfAny(int[] rgbPartsNeeded, Map, Integer> partsNeeded) { + addPartsNeededIfAny(rgbPartsNeeded[0], partsNeeded, Tags.Items.DYES_RED); + addPartsNeededIfAny(rgbPartsNeeded[1], partsNeeded, Tags.Items.DYES_GREEN); + addPartsNeededIfAny(rgbPartsNeeded[2], partsNeeded, Tags.Items.DYES_BLUE); + } + + private static void addPartsNeededIfAny(int parts, Map, Integer> partsNeeded, TagKey dyeName) { + if (parts != 0) { + partsNeeded.compute(dyeName, (location, partsTotal) -> partsTotal == null ? parts : partsTotal + parts); + } + } + + private static int[] calculateRGBPartsNeeded(int color, int totalParts) { + float[] ratios = new float[3]; + ratios[0] = FastColor.ARGB32.red(color) / 255f; + ratios[1] = FastColor.ARGB32.green(color) / 255f; + ratios[2] = FastColor.ARGB32.blue(color) / 255f; + + float totalRaios = ratios[0] + ratios[1] + ratios[2]; + ratios[0] /= totalRaios; + ratios[1] /= totalRaios; + ratios[2] /= totalRaios; + + int n = ratios.length; + int[] result = new int[n]; + double[] remainders = new double[n]; + + double[] scaled = new double[n]; + for (int i = 0; i < n; i++) { + scaled[i] = ratios[i] * totalParts; + result[i] = (int) scaled[i]; + remainders[i] = scaled[i] - result[i]; + } + + int remaining = totalParts - Arrays.stream(result).sum(); + + Integer[] indices = new Integer[n]; + for (int i = 0; i < n; i++) indices[i] = i; + + Arrays.sort(indices, Comparator.comparingDouble(i -> -remainders[i])); + + for (int i = 0; i < remaining; i++) { + result[indices[i % n]]++; + } + + return result; + } + + public static boolean consumeMaterials(Map remainingParts, List decorativeBlocks, Map originalMaterials, Map materials, boolean simulate) { + Map partsNeeded = getMaterialPartsNeeded(originalMaterials, materials); + return consumeMaterialPartsNeeded(partsNeeded, remainingParts, decorativeBlocks, simulate).hasEnough(); + } + + public static ConsumptionResult consumeMaterialPartsNeeded(Map partsNeeded, Map remainingParts, List decorativeBlocks, boolean simulate) { + return consumePartsNeeded(partsNeeded, decorativeBlocks, location -> location, + (materialLocation, stack) -> getMaterialLocation(stack).map(ml -> ml.equals(materialLocation)).orElse(false), remainingParts, simulate); + } + + public static Map getMaterialPartsNeeded(Map originalMaterials, Map materialsToApply) { + Map partsNeeded = new HashMap<>(); + BarrelBlockItem.uncompactMaterials(materialsToApply); + + ResourceLocation topInnerTrimMaterialLocation = addMaterialCostForSlotAndGetMaterial(materialsToApply, BarrelMaterial.TOP_INNER_TRIM, null, partsNeeded, originalMaterials); + ResourceLocation topTrimMaterialLocation = addMaterialCostForSlotAndGetMaterial(materialsToApply, BarrelMaterial.TOP_TRIM, topInnerTrimMaterialLocation, partsNeeded, originalMaterials); + ResourceLocation sideTrimMaterialLocation = addMaterialCostForSlotAndGetMaterial(materialsToApply, BarrelMaterial.SIDE_TRIM, topTrimMaterialLocation, partsNeeded, originalMaterials); + addMaterialCostForSlotAndGetMaterial(materialsToApply, BarrelMaterial.BOTTOM_TRIM, sideTrimMaterialLocation, partsNeeded, originalMaterials); + ResourceLocation topMaterialLocation = addMaterialCostForSlotAndGetMaterial(materialsToApply, BarrelMaterial.TOP, topTrimMaterialLocation, partsNeeded, originalMaterials); + ResourceLocation sideMaterialLocation = addMaterialCostForSlotAndGetMaterial(materialsToApply, BarrelMaterial.SIDE, topMaterialLocation, partsNeeded, originalMaterials); + addMaterialCostForSlotAndGetMaterial(materialsToApply, BarrelMaterial.BOTTOM, sideMaterialLocation, partsNeeded, originalMaterials); + return partsNeeded; + } + + @Nullable + private static ResourceLocation addMaterialCostForSlotAndGetMaterial(Map materials, BarrelMaterial barrelMaterial, @Nullable ResourceLocation defaultMaterialLocation, Map partsNeeded, Map originalMaterials) { + boolean materialIsTheSame = Objects.deepEquals(originalMaterials.get(barrelMaterial), materials.get(barrelMaterial)); + boolean newHasNoMaterial = !materials.containsKey(barrelMaterial); + boolean hasNoCost = (barrelMaterial == BarrelMaterial.TOP_TRIM && defaultMaterialLocation != null) || materialIsTheSame || newHasNoMaterial; + + ResourceLocation materialLocation = materials.getOrDefault(barrelMaterial, defaultMaterialLocation); + if (hasNoCost) { + return materialLocation; + } + + if (materialLocation != null) { + int parts = DECORATIVE_SLOT_PARTS_NEEDED.get(barrelMaterial); + partsNeeded.compute(materialLocation, (key, value) -> value == null ? parts : value + parts); + } + return materialLocation; + } + + public static ConsumptionResult consumeDyePartsNeeded(Map, Integer> partsNeeded, List resourceHandlers, Map remainingParts, boolean simulate) { + return consumePartsNeeded(partsNeeded, resourceHandlers, TagKey::location, (dyeName, stack) -> stack.is(dyeName), remainingParts, simulate); + } + + private static ConsumptionResult consumePartsNeeded(Map partsNeeded, List resourceHandlers, Function locationGetter, BiPredicate stackMatcher, Map remainingParts, boolean simulate) { + Map missingParts = new HashMap<>(); + for (Map.Entry entry : partsNeeded.entrySet()) { + T material = entry.getKey(); + Integer parts = entry.getValue(); + ResourceLocation materialLocation = locationGetter.apply(material); + int remainingPartCount = remainingParts.getOrDefault(materialLocation, 0); + if (remainingPartCount > parts) { + if (!simulate) { + remainingParts.put(materialLocation, remainingPartCount - parts); + } + continue; + } else { + if (!simulate) { + remainingParts.remove(materialLocation); + } + if (remainingPartCount == parts) { + continue; + } + } + + parts -= remainingPartCount; + + SingleItemConsumptionResult singleItemConsumptionResult = consumeFromHandlers(resourceHandlers, stackMatcher, remainingParts, simulate, material, parts, materialLocation); + if (!singleItemConsumptionResult.hasEnough()) { + missingParts.put(materialLocation, singleItemConsumptionResult.countMissing()); + } + } + return new ConsumptionResult(missingParts.isEmpty(), missingParts); + } + + private static SingleItemConsumptionResult consumeFromHandlers(List resourceHandlers, BiPredicate stackMatcher, Map remainingParts, boolean simulate, T material, Integer parts, ResourceLocation materialLocation) { + for (IItemHandler resources : resourceHandlers) { + for (int slot = 0; slot < resources.getSlots(); slot++) { + ItemStack stack = resources.getStackInSlot(slot); + if (!stackMatcher.test(material, stack)) { + continue; + } + + int toRemove = (int) Math.ceil((double)parts / BLOCK_TOTAL_PARTS); + ItemStack removed = resources.extractItem(slot, toRemove, simulate); + int partsRemoved = removed.getCount() * BLOCK_TOTAL_PARTS; + + if (partsRemoved >= parts) { + if (partsRemoved > parts && !simulate) { + remainingParts.put(materialLocation, partsRemoved - parts); + } + return new SingleItemConsumptionResult(true, 0); + } + parts -= partsRemoved; + } + } + return new SingleItemConsumptionResult(false, parts); + } + + private record SingleItemConsumptionResult(boolean hasEnough, int countMissing) {} + + public record ConsumptionResult(boolean hasEnough, Map missingParts) {} +} diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/util/package-info.java b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/util/package-info.java new file mode 100644 index 00000000..47adfc5d --- /dev/null +++ b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/util/package-info.java @@ -0,0 +1,8 @@ +// Auto generated package-info by MCP + +@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault +package net.p3pp3rf1y.sophisticatedstorage.util; + +import net.minecraft.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/src/main/resources/assets/sophisticatedstorage/lang/en_us.json b/src/main/resources/assets/sophisticatedstorage/lang/en_us.json index f35679e2..72248182 100644 --- a/src/main/resources/assets/sophisticatedstorage/lang/en_us.json +++ b/src/main/resources/assets/sophisticatedstorage/lang/en_us.json @@ -26,7 +26,14 @@ "item.sophisticatedstorage.storage_tool.overlay.toggling_tier_display": "Showing/Hiding Tier(s)", "item.sophisticatedstorage.storage_tool.overlay.toggling_upgrades_display": "Showing/Hiding Upgrades", "item.sophisticatedstorage.storage_tool.overlay.toggling_fill_level_display": "Showing/Hiding Fill Levels", + "item.sophisticatedstorage.paintbrush.overlay.missing_items": "Missing items: ", "item.sophisticatedstorage.debug_tool": "Debug Tool", + "item.sophisticatedstorage.paintbrush": "Paintbrush", + "item.sophisticatedstorage.paintbrush.tooltip": "Applies decoration to storage blocks in world\nSet decoration it applies in decoration table", + "item.sophisticatedstorage.paintbrush.tooltip.materials": "Materials:", + "item.sophisticatedstorage.paintbrush.tooltip.material": " - %s: %s", + "item.sophisticatedstorage.paintbrush.tooltip.main_color": "Main Color: %s", + "item.sophisticatedstorage.paintbrush.tooltip.accent_color": "Accent Color: %s", "item.sophisticatedstorage.storage_io": "Storage I/O", "block.sophisticatedstorage.storage_io.tooltip": "When placed in storage multiblock allows to pipe items to and from it", "block.sophisticatedstorage.storage_io": "Storage I/O", @@ -232,9 +239,15 @@ "gui.sophisticatedstorage.barrel_part.top_trim": "Top Accent", "gui.sophisticatedstorage.barrel_part.side_trim": "Side Accent", "gui.sophisticatedstorage.barrel_part.bottom_trim": "Bottom Accent", - "gui.sophisticatedstorage.barrel_part.top_core": "Top Main", - "gui.sophisticatedstorage.barrel_part.side_core": "Side Main", - "gui.sophisticatedstorage.barrel_part.bottom_core": "Bottom Main", + "gui.sophisticatedstorage.barrel_part.top": "Top Main", + "gui.sophisticatedstorage.barrel_part.side": "Side Main", + "gui.sophisticatedstorage.barrel_part.bottom": "Bottom Main", + "gui.sophisticatedstorage.barrel_part.all": "All Parts", + "gui.sophisticatedstorage.barrel_part.all_trim": "All Accents", + "gui.sophisticatedstorage.barrel_part.all_but_trim": "All Main", + "gui.sophisticatedstorage.barrel_part.top_all": "All Top", + "gui.sophisticatedstorage.barrel_part.side_all": "All Side", + "gui.sophisticatedstorage.barrel_part.bottom_all": "All Bottom", "gui.sophisticatedstorage.tint.accent": "Accent Tint", "gui.sophisticatedstorage.tint.main": "Main Tint", "gui.sophisticatedstorage.buttons.upgrade_switch_enabled": "ON", diff --git a/src/main/resources/assets/sophisticatedstorage/lang/zh_cn.json b/src/main/resources/assets/sophisticatedstorage/lang/zh_cn.json index 26760a4c..30bc1f83 100644 --- a/src/main/resources/assets/sophisticatedstorage/lang/zh_cn.json +++ b/src/main/resources/assets/sophisticatedstorage/lang/zh_cn.json @@ -232,9 +232,9 @@ "gui.sophisticatedstorage.barrel_part.top_trim": "顶面强调色", "gui.sophisticatedstorage.barrel_part.side_trim": "侧面强调色", "gui.sophisticatedstorage.barrel_part.bottom_trim": "底面强调色", - "gui.sophisticatedstorage.barrel_part.top_core": "顶面主色", - "gui.sophisticatedstorage.barrel_part.side_core": "侧面主色", - "gui.sophisticatedstorage.barrel_part.bottom_core": "底面主色", + "gui.sophisticatedstorage.barrel_part.top": "顶面主色", + "gui.sophisticatedstorage.barrel_part.side": "侧面主色", + "gui.sophisticatedstorage.barrel_part.bottom": "底面主色", "gui.sophisticatedstorage.tint.accent": "强调色调", "gui.sophisticatedstorage.tint.main": "主色调", "gui.sophisticatedstorage.buttons.upgrade_switch_enabled": "开", diff --git a/src/main/resources/assets/sophisticatedstorage/models/item/paintbrush.json b/src/main/resources/assets/sophisticatedstorage/models/item/paintbrush.json new file mode 100644 index 00000000..391a0de1 --- /dev/null +++ b/src/main/resources/assets/sophisticatedstorage/models/item/paintbrush.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/handheld", + "textures": { + "layer0": "sophisticatedstorage:item/paintbrush" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/sophisticatedstorage/models/item/storage_tool.json b/src/main/resources/assets/sophisticatedstorage/models/item/storage_tool.json index ccab29c3..1f81b218 100644 --- a/src/main/resources/assets/sophisticatedstorage/models/item/storage_tool.json +++ b/src/main/resources/assets/sophisticatedstorage/models/item/storage_tool.json @@ -1,5 +1,5 @@ { - "parent": "item/generated", + "parent": "minecraft:item/handheld", "textures": { "layer0": "sophisticatedstorage:item/storage_tool" } diff --git a/src/main/resources/assets/sophisticatedstorage/textures/item/paintbrush.png b/src/main/resources/assets/sophisticatedstorage/textures/item/paintbrush.png new file mode 100644 index 00000000..1cd82a02 Binary files /dev/null and b/src/main/resources/assets/sophisticatedstorage/textures/item/paintbrush.png differ