From ac9725ef371c53111684968f31205c6826fe156d Mon Sep 17 00:00:00 2001 From: P3pp3rF1y Date: Mon, 9 Dec 2024 16:49:58 +0100 Subject: [PATCH] =?UTF-8?q?feat:=E2=9C=A8=20Added=20new=20Paintbrush=20ite?= =?UTF-8?q?m=20that=20allows=20applying=20decorations=20to=20storage=20blo?= =?UTF-8?q?cks=20in=20world=20-=20put=20into=20Decoration=20Table=20instea?= =?UTF-8?q?d=20of=20storage=20to=20set=20the=20decorations=20that=20the=20?= =?UTF-8?q?paintbrush=20applies=20-=20right=20click=20blocks=20in=20world?= =?UTF-8?q?=20to=20apply=20the=20decoration=20-=20works=20on=20individual?= =?UTF-8?q?=20storage=20blocks=20as=20well=20as=20on=20storage=20controlle?= =?UTF-8?q?r=20to=20apply=20the=20decoration=20to=20all=20storages=20in=20?= =?UTF-8?q?controller=20multiblock=20-=20pulls=20blocks=20/=20dyes=20neede?= =?UTF-8?q?d=20to=20apply=20the=20decoration=20from=20player's=20inventory?= =?UTF-8?q?=20as=20well=20as=20from=20any=20container=20items=20including?= =?UTF-8?q?=20backpacks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle.properties | 4 +- .../recipes/misc/packing_tape.json | 2 +- .../advancement/recipes/misc/paintbrush.json | 32 ++ .../recipe/packing_tape.json | 2 +- .../recipe/paintbrush.json | 21 + .../block/BarrelBlock.java | 3 +- .../block/ChestBlockEntity.java | 8 + .../block/DecorationTableBlockEntity.java | 282 ++++-------- .../block/StorageWrapper.java | 2 + .../client/ClientEventHandler.java | 30 +- .../client/gui/DecorationTableScreen.java | 187 ++++++-- .../client/gui/PaintbrushOverlay.java | 82 ++++ .../client/render/BarrelBakedModelBase.java | 26 +- .../render/DecorationTableRenderer.java | 3 +- .../common/gui/DecorationTableMenu.java | 40 +- .../data/StorageRecipeProvider.java | 13 +- .../init/ModDataComponents.java | 4 + .../sophisticatedstorage/init/ModItems.java | 2 + .../item/BarrelBlockItem.java | 10 +- .../item/PaintbrushItem.java | 424 ++++++++++++++++++ .../util/DecorationHelper.java | 219 +++++++++ .../util/package-info.java | 8 + .../sophisticatedstorage/lang/en_us.json | 19 +- .../sophisticatedstorage/lang/zh_cn.json | 6 +- .../models/item/paintbrush.json | 6 + .../models/item/storage_tool.json | 2 +- .../textures/item/paintbrush.png | Bin 0 -> 299 bytes 27 files changed, 1189 insertions(+), 248 deletions(-) create mode 100644 src/generated/resources/data/sophisticatedstorage/advancement/recipes/misc/paintbrush.json create mode 100644 src/generated/resources/data/sophisticatedstorage/recipe/paintbrush.json create mode 100644 src/main/java/net/p3pp3rf1y/sophisticatedstorage/client/gui/PaintbrushOverlay.java create mode 100644 src/main/java/net/p3pp3rf1y/sophisticatedstorage/item/PaintbrushItem.java create mode 100644 src/main/java/net/p3pp3rf1y/sophisticatedstorage/util/DecorationHelper.java create mode 100644 src/main/java/net/p3pp3rf1y/sophisticatedstorage/util/package-info.java create mode 100644 src/main/resources/assets/sophisticatedstorage/models/item/paintbrush.json create mode 100644 src/main/resources/assets/sophisticatedstorage/textures/item/paintbrush.png diff --git a/gradle.properties b/gradle.properties index 707ca5648..d58d2d011 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,7 +13,7 @@ loader_version_range=[4,) mod_id=sophisticatedstorage mod_name=Sophisticated Storage mod_license=GNU General Public License v3.0 -mod_version=0.11.5 +mod_version=1.0.0 mod_group_id=sophisticatedstorage mod_authors=P3pp3rF1y, Ridanisaurus mod_description=Fancy and functional storage containers. @@ -35,7 +35,7 @@ jade_cf_file_id=5109393 chipped_cf_file_id=5506938 resourcefullib_cf_file_id=5483169 athena_cf_file_id=5431579 -sc_version=[1.21-0.7.14,1.22) +sc_version=[1.21.1-1.0.0,1.22) sb_version=[1.21,1.22) #publish diff --git a/src/generated/resources/data/sophisticatedstorage/advancement/recipes/misc/packing_tape.json b/src/generated/resources/data/sophisticatedstorage/advancement/recipes/misc/packing_tape.json index 89a75ce58..19f46292c 100644 --- a/src/generated/resources/data/sophisticatedstorage/advancement/recipes/misc/packing_tape.json +++ b/src/generated/resources/data/sophisticatedstorage/advancement/recipes/misc/packing_tape.json @@ -10,7 +10,7 @@ "conditions": { "items": [ { - "items": "#c:slimeballs" + "items": "#c:slime_balls" } ] }, diff --git a/src/generated/resources/data/sophisticatedstorage/advancement/recipes/misc/paintbrush.json b/src/generated/resources/data/sophisticatedstorage/advancement/recipes/misc/paintbrush.json new file mode 100644 index 000000000..bb9a4d481 --- /dev/null +++ b/src/generated/resources/data/sophisticatedstorage/advancement/recipes/misc/paintbrush.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_base_tier_wooden_storage": { + "conditions": { + "items": [ + { + "items": "#sophisticatedstorage:base_tier_wooden_storage" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "sophisticatedstorage:paintbrush" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_base_tier_wooden_storage" + ] + ], + "rewards": { + "recipes": [ + "sophisticatedstorage:paintbrush" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/sophisticatedstorage/recipe/packing_tape.json b/src/generated/resources/data/sophisticatedstorage/recipe/packing_tape.json index d6fab6c6f..0cffbaaf2 100644 --- a/src/generated/resources/data/sophisticatedstorage/recipe/packing_tape.json +++ b/src/generated/resources/data/sophisticatedstorage/recipe/packing_tape.json @@ -8,7 +8,7 @@ "category": "misc", "ingredients": [ { - "tag": "c:slimeballs" + "tag": "c:slime_balls" }, { "item": "minecraft:paper" diff --git a/src/generated/resources/data/sophisticatedstorage/recipe/paintbrush.json b/src/generated/resources/data/sophisticatedstorage/recipe/paintbrush.json new file mode 100644 index 000000000..a701f8f06 --- /dev/null +++ b/src/generated/resources/data/sophisticatedstorage/recipe/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": { + "count": 1, + "id": "sophisticatedstorage:paintbrush" + } +} \ 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 9db98ecef..77d0653e5 100644 --- a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/block/BarrelBlock.java +++ b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/block/BarrelBlock.java @@ -45,6 +45,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; @@ -134,7 +135,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 aaf9450a3..f997a3b02 100644 --- a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/block/ChestBlockEntity.java +++ b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/block/ChestBlockEntity.java @@ -362,6 +362,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 2e9df50bb..017972f4d 100644 --- a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/block/DecorationTableBlockEntity.java +++ b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/block/DecorationTableBlockEntity.java @@ -2,13 +2,11 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.HolderLookup; -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; @@ -20,10 +18,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; @@ -36,21 +37,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<>(); @@ -86,6 +73,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(); @@ -100,30 +95,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); @@ -136,13 +142,12 @@ private void updateResult() { tintableBlockItem.setAccentColor(result, accentColor); } } - 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); @@ -153,8 +158,7 @@ private void updateResult() { BarrelBlockItem.compactMaterials(materials); if (allMaterialsMatch(materials, BarrelBlockItem.getMaterials(storage))) { - result = ItemStack.EMPTY; - return; + return DecorationResult.EMPTY; } result = storage.copy(); @@ -162,6 +166,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) { @@ -182,13 +217,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()) { @@ -199,6 +237,7 @@ private void calculateMissingDyes(ItemStack storage) { missingDyes.add(entry.getKey()); } } + return missingDyes; } public Set getMissingDyes() { @@ -222,7 +261,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); @@ -232,13 +271,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); } @@ -389,160 +421,38 @@ private void saveData(CompoundTag tag, HolderLookup.Provider registries) { } 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 fb7fa5c53..f78894c9e 100644 --- a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/block/StorageWrapper.java +++ b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/block/StorageWrapper.java @@ -320,6 +320,7 @@ public boolean hasMainColor() { public void setMainColor(int mainColor) { this.mainColor = mainColor; + save(); } @Override @@ -333,6 +334,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 c8bb43570..aac0b8cb5 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; @@ -40,6 +41,8 @@ import net.p3pp3rf1y.sophisticatedstorage.block.BarrelBlock; import net.p3pp3rf1y.sophisticatedstorage.block.BarrelBlockClientExtensions; 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; @@ -51,6 +54,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.RequestPlayerSettingsPayload; import net.p3pp3rf1y.sophisticatedstorage.network.ScrolledToolPayload; @@ -121,11 +125,12 @@ private static void onPlayerLoggingIn(ClientPlayerNetworkEvent.LoggingIn event) 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(); @@ -137,6 +142,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) { @@ -256,6 +281,7 @@ private static void registerTooltipComponent(RegisterClientTooltipComponentFacto private static void registerOverlay(RegisterGuiLayersEvent event) { event.registerAbove(VanillaGuiLayers.HOTBAR, ResourceLocation.fromNamespaceAndPath(SophisticatedStorage.MOD_ID, "storage_tool_info"), ToolInfoOverlay.HUD_TOOL_INFO); + event.registerAbove(VanillaGuiLayers.HOTBAR, ResourceLocation.fromNamespaceAndPath(SophisticatedStorage.MOD_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 13ca0f5af..2c98b22a0 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 javax.annotation.Nullable; import java.util.*; @@ -77,7 +82,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); @@ -116,9 +144,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)); @@ -152,6 +180,7 @@ private void openColorPicker(int color, IntConsumer colorSetter) { colorSetter.accept(c); colorPicker = null; blockPreview.setVisible(true); + updatePreviewStacks(); }); blockPreview.setVisible(false); } @@ -166,6 +195,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) { @@ -273,20 +303,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; } @@ -307,19 +337,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) { @@ -327,7 +357,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; } } @@ -353,7 +384,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; } @@ -389,6 +420,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); } @@ -471,8 +509,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; @@ -481,10 +547,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) { @@ -506,8 +616,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; } @@ -515,24 +631,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.scale(scale, -scale, scale); pose.translate(-0.5, -0.5, -0.5); ItemRenderer itemRenderer = minecraft.getItemRenderer(); - BakedModel bakedModel = itemRenderer.getModel(slotStack, null, null, 0); + 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); }); }); } @@ -555,6 +672,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 000000000..3a75ae95f --- /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.gui.LayeredDraw; +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.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 LayeredDraw.Layer HUD_PAINTBRUSH_INFO = (guiGraphics, deltaTracker) -> { + 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 = (guiGraphics.guiWidth() - i) / 2; + int y = guiGraphics.guiHeight() - 75 - 10; + guiGraphics.drawStringWithBackdrop(font, missingItems, x + 1, y, DyeColor.WHITE.getTextColor(), 0xFFFFFFFF); + + x = (guiGraphics.guiWidth() - 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 547d5e768..3c3629c5c 100644 --- a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/client/render/BarrelBakedModelBase.java +++ b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/client/render/BarrelBakedModelBase.java @@ -98,9 +98,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() { @@ -284,7 +294,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()); @@ -316,7 +326,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 d314cfbcb..cfbc2b0d6 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 be3de8b57..f7597be76 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 handlePacket(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 d3ff90d32..39a118aae 100644 --- a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/data/StorageRecipeProvider.java +++ b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/data/StorageRecipeProvider.java @@ -65,9 +65,9 @@ protected void buildRecipes(RecipeOutput recipeOutput) { addBackpackUpgradeConversionRecipes(recipeOutput); ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, ModItems.PACKING_TAPE.get()) - .requires(Tags.Items.SLIMEBALLS) + .requires(Tags.Items.SLIME_BALLS) .requires(Items.PAPER) - .unlockedBy("has_slime", has(Tags.Items.SLIMEBALLS)) + .unlockedBy("has_slime", has(Tags.Items.SLIME_BALLS)) .save(recipeOutput.withConditions(new DropPackedDisabledCondition())); ShapedRecipeBuilder.shaped(RecipeCategory.MISC, ModBlocks.DECORATION_TABLE_ITEM.get()) @@ -79,6 +79,15 @@ protected void buildRecipes(RecipeOutput recipeOutput) { .define('B', ModItems.UPGRADE_BASE.get()) .unlockedBy("has_upgrade_base", has(ModItems.UPGRADE_BASE.get())) .save(recipeOutput); + + 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(recipeOutput); } private void addBackpackUpgradeConversionRecipes(RecipeOutput recipeOutput) { diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/init/ModDataComponents.java b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/init/ModDataComponents.java index 62aedf93f..7bb6b0661 100644 --- a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/init/ModDataComponents.java +++ b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/init/ModDataComponents.java @@ -18,6 +18,7 @@ import net.p3pp3rf1y.sophisticatedstorage.SophisticatedStorage; import net.p3pp3rf1y.sophisticatedstorage.block.BarrelMaterial; import net.p3pp3rf1y.sophisticatedstorage.item.BarrelBlockItem; +import net.p3pp3rf1y.sophisticatedstorage.item.PaintbrushItem; import net.p3pp3rf1y.sophisticatedstorage.item.StorageToolItem; import net.p3pp3rf1y.sophisticatedstorage.item.WoodStorageBlockItem; @@ -43,6 +44,9 @@ private ModDataComponents() { public static final Supplier>> BARREL_MATERIALS = DATA_COMPONENT_TYPES.register("barrel_materials", () -> new DataComponentType.Builder>().persistent(BarrelBlockItem.MATERIALS_CODEC).networkSynchronized(BarrelBlockItem.MATERIALS_STREAM_CODEC).build()); + public static final Supplier>> REMAINING_PARTS = DATA_COMPONENT_TYPES.register("remaining_parts", + () -> new DataComponentType.Builder>().persistent(PaintbrushItem.REMAINING_PARTS_CODEC).networkSynchronized(PaintbrushItem.REMAINING_PARTS_STREAM_CODEC).build()); + public static final Supplier> DOUBLE_CHEST = DATA_COMPONENT_TYPES.register("double_chest", () -> new DataComponentType.Builder().persistent(Codec.BOOL).networkSynchronized(ByteBufCodecs.BOOL).build()); diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/init/ModItems.java b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/init/ModItems.java index 19cecfa5f..7a93ae3cc 100644 --- a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/init/ModItems.java +++ b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/init/ModItems.java @@ -71,6 +71,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; @@ -199,6 +200,7 @@ public void addCreativeTabItems(Consumer itemConsumer) { }); public static final Supplier STORAGE_TOOL = ITEMS.register("storage_tool", StorageToolItem::new); public static final Supplier DEBUG_TOOL = ITEMS.register("debug_tool", () -> new ItemBase(new Item.Properties().stacksTo(1))); + public static final Supplier PAINTBRUSH = ITEMS.register("paintbrush", PaintbrushItem::new); public static final Supplier INACCESSIBLE_SLOT = ITEMS.register("inaccessible_slot", () -> new Item(new Item.Properties().stacksTo(1))); public static final Supplier> COPY_STORAGE_DATA = LOOT_FUNCTION_TYPES.register("copy_storage_data", () -> new LootItemFunctionType<>(CopyStorageDataFunction.CODEC)); diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/item/BarrelBlockItem.java b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/item/BarrelBlockItem.java index e11eb3972..709a88cab 100644 --- a/src/main/java/net/p3pp3rf1y/sophisticatedstorage/item/BarrelBlockItem.java +++ b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/item/BarrelBlockItem.java @@ -14,7 +14,6 @@ import net.p3pp3rf1y.sophisticatedstorage.init.ModDataComponents; import java.util.EnumMap; -import java.util.HashMap; import java.util.Map; public class BarrelBlockItem extends WoodStorageBlockItem { @@ -55,7 +54,7 @@ public static void setMaterials(ItemStack barrel, Map getMaterials(ItemStack barrel) { - return new HashMap<>(barrel.getOrDefault(ModDataComponents.BARREL_MATERIALS, Map.of())); + return barrel.getOrDefault(ModDataComponents.BARREL_MATERIALS, Map.of()); } public static void removeMaterials(ItemStack stack) { @@ -120,6 +119,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 000000000..a842989b2 --- /dev/null +++ b/src/main/java/net/p3pp3rf1y/sophisticatedstorage/item/PaintbrushItem.java @@ -0,0 +1,424 @@ +package net.p3pp3rf1y.sophisticatedstorage.item; + +import com.mojang.serialization.Codec; +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.network.FriendlyByteBuf; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.Style; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.sounds.SoundSource; +import net.minecraft.tags.TagKey; +import net.minecraft.util.ExtraCodecs; +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.neoforged.neoforge.common.Tags; +import net.neoforged.neoforge.items.IItemHandler; +import net.p3pp3rf1y.sophisticatedcore.init.ModCoreDataComponents; +import net.p3pp3rf1y.sophisticatedcore.util.ColorHelper; +import net.p3pp3rf1y.sophisticatedcore.util.InventoryHelper; +import net.p3pp3rf1y.sophisticatedcore.util.ItemBase; +import net.p3pp3rf1y.sophisticatedcore.util.WorldHelper; +import net.p3pp3rf1y.sophisticatedstorage.block.*; +import net.p3pp3rf1y.sophisticatedstorage.client.gui.StorageTranslationHelper; +import net.p3pp3rf1y.sophisticatedstorage.init.ModDataComponents; +import net.p3pp3rf1y.sophisticatedstorage.util.DecorationHelper; +import org.jetbrains.annotations.NotNull; + +import javax.annotation.Nullable; +import java.util.*; + +public class PaintbrushItem extends ItemBase { + public static final Codec> REMAINING_PARTS_CODEC = + Codec.unboundedMap(ResourceLocation.CODEC, ExtraCodecs.POSITIVE_INT); + + public static final StreamCodec> REMAINING_PARTS_STREAM_CODEC = + StreamCodec.of((buf, map) -> buf.writeMap(map, ResourceLocation.STREAM_CODEC, ByteBufCodecs.INT), + buf -> buf.readMap(ResourceLocation.STREAM_CODEC, ByteBufCodecs.INT)); + + public PaintbrushItem() { + super(new Properties().stacksTo(1)); + } + + public static void setBarrelMaterials(ItemStack paintbrush, Map materials) { + paintbrush.set(ModDataComponents.BARREL_MATERIALS, Map.copyOf(materials)); + 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 = Math.ceilDiv(entry.getValue() - remainingParts.getOrDefault(part, 0), DecorationHelper.BLOCK_TOTAL_PARTS); + int missing = Math.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 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 = Math.ceilDiv(entry.getValue() - remainingParts.getOrDefault(part.location(), 0), DecorationHelper.BLOCK_TOTAL_PARTS); + int missing = Math.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); + int accentColorToSet = getAccentColor(paintbrush); + 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) { + paintbrush.remove(ModDataComponents.BARREL_MATERIALS); + } + + public static void resetMainColor(ItemStack paintbrush) { + paintbrush.remove(ModCoreDataComponents.MAIN_COLOR); + } + + public static void resetAccentColor(ItemStack paintbrush) { + paintbrush.remove(ModCoreDataComponents.ACCENT_COLOR); + } + + public static void setMainColor(ItemStack paintbrush, int mainColor) { + paintbrush.set(ModCoreDataComponents.MAIN_COLOR, mainColor); + resetBarrelMaterials(paintbrush); + } + + public static void setAccentColor(ItemStack paintbrush, int secondaryColor) { + paintbrush.set(ModCoreDataComponents.ACCENT_COLOR, secondaryColor); + resetBarrelMaterials(paintbrush); + } + + public static void setRemainingParts(ItemStack paintbrush, Map remainingParts) { + paintbrush.set(ModDataComponents.REMAINING_PARTS, remainingParts); + } + + public static Map getRemainingParts(ItemStack paintbrush) { + return paintbrush.getOrDefault(ModDataComponents.REMAINING_PARTS, Collections.emptyMap()); + } + + @Override + public void appendHoverText(ItemStack stack, TooltipContext context, List tooltip, TooltipFlag tooltipFlag) { + super.appendHoverText(stack, context, 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 paintbrush.has(ModCoreDataComponents.MAIN_COLOR); + } + + private static boolean hasAccentColor(ItemStack paintbrush) { + return paintbrush.has(ModCoreDataComponents.ACCENT_COLOR); + } + + private static boolean hasBarrelMaterials(ItemStack paintbrush) { + return paintbrush.has(ModDataComponents.BARREL_MATERIALS); + } + + public static int getMainColor(ItemStack paintbrush) { + return paintbrush.getOrDefault(ModCoreDataComponents.MAIN_COLOR, -1); + } + + public static int getAccentColor(ItemStack paintbrush) { + return paintbrush.getOrDefault(ModCoreDataComponents.ACCENT_COLOR, -1); + } + + public static Map getBarrelMaterials(ItemStack paintbrush) { + return paintbrush.getOrDefault(ModDataComponents.BARREL_MATERIALS, Collections.emptyMap()); + } + + 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 000000000..e2245b577 --- /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.neoforged.neoforge.common.Tags; +import net.neoforged.neoforge.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 = Math.ceilDiv(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 000000000..47adfc5d1 --- /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 f35679e23..722481820 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 26760a4c3..30bc1f83f 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 000000000..391a0de1e --- /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 ccab29c37..1f81b218c 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 0000000000000000000000000000000000000000..1cd82a026f1e64625a95f6128bc10d795fd8ffd2 GIT binary patch literal 299 zcmV+`0o4A9P)c9X=lJF77wzfbNhOSv_ zjqm#yV{#&e0*}3WI|+t}2wH2DQUET`idU{M3!YI*k*<+C5fLX+C@_!K-rI7+x7jfX zh7L}hWDi>D;B);4z<4xplMlafQ+9(pxn=SGL%iBz_mI54(CI$qUg=fAtFK=Vp!LwS xfXhn-ir{0>$ii*aJPCFV&Y7HZa1MNt_y>xbe{tQ+pCAAL002ovPDHLkV1fmXfxQ3# literal 0 HcmV?d00001