diff --git a/src/main/java/dev/zontreck/engineerdecor/ModConfig.java b/src/main/java/dev/zontreck/engineerdecor/ModConfig.java index 6c1f54b..ef33bbf 100644 --- a/src/main/java/dev/zontreck/engineerdecor/ModConfig.java +++ b/src/main/java/dev/zontreck/engineerdecor/ModConfig.java @@ -11,476 +11,477 @@ package dev.zontreck.engineerdecor; import dev.zontreck.engineerdecor.blocks.*; import dev.zontreck.engineerdecor.detail.TreeCutting; -import dev.zontreck.engineerdecor.libmc.Auxiliaries; -import dev.zontreck.engineerdecor.libmc.OptionalRecipeCondition; -import dev.zontreck.engineerdecor.libmc.SlabSliceBlock; -import dev.zontreck.engineerdecor.libmc.VariantSlabBlock; +import dev.zontreck.libzontreck.edlibmc.Auxiliaries; +import dev.zontreck.libzontreck.edlibmc.OptionalRecipeCondition; +import dev.zontreck.libzontreck.edlibmc.SlabSliceBlock; +import dev.zontreck.libzontreck.edlibmc.VariantSlabBlock; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.item.Item; import net.minecraft.world.level.block.Block; import net.minecraftforge.common.ForgeConfigSpec; import org.apache.commons.lang3.tuple.Pair; -import wile.engineersdecor.blocks.*; import javax.annotation.Nullable; -import java.util.*; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; -public class ModConfig -{ - //-------------------------------------------------------------------------------------------------------------------- - private static final String MODID = ModEngineersDecor.MODID; - public static final CommonConfig COMMON; - public static final ServerConfig SERVER; - public static final ForgeConfigSpec COMMON_CONFIG_SPEC; - public static final ForgeConfigSpec SERVER_CONFIG_SPEC; +public class ModConfig { + public static final CommonConfig COMMON; + public static final ServerConfig SERVER; + public static final ForgeConfigSpec COMMON_CONFIG_SPEC; + public static final ForgeConfigSpec SERVER_CONFIG_SPEC; + //-------------------------------------------------------------------------------------------------------------------- + private static final String MODID = ModEngineersDecor.MODID; + private static final CompoundTag server_config_ = new CompoundTag(); - static { - final Pair common_ = (new ForgeConfigSpec.Builder()).configure(CommonConfig::new); - COMMON_CONFIG_SPEC = common_.getRight(); - COMMON = common_.getLeft(); - final Pair server_ = (new ForgeConfigSpec.Builder()).configure(ServerConfig::new); - SERVER_CONFIG_SPEC = server_.getRight(); - SERVER = server_.getLeft(); - } + //-------------------------------------------------------------------------------------------------------------------- + public static boolean immersiveengineering_installed = false; - //-------------------------------------------------------------------------------------------------------------------- + //-------------------------------------------------------------------------------------------------------------------- + public static boolean without_direct_slab_pickup = false; - public static class CommonConfig - { - // Optout - public final ForgeConfigSpec.ConfigValue pattern_excludes; - public final ForgeConfigSpec.ConfigValue pattern_includes; - // MISC - public final ForgeConfigSpec.BooleanValue with_creative_mode_device_drops; - public final ForgeConfigSpec.BooleanValue with_experimental; - public final ForgeConfigSpec.BooleanValue with_config_logging; - public final ForgeConfigSpec.BooleanValue with_debug_logging; + //-------------------------------------------------------------------------------------------------------------------- + // Optout checks + //-------------------------------------------------------------------------------------------------------------------- + public static boolean with_creative_mode_device_drops = false; + private static HashSet optouts_ = new HashSet<>(); + private static boolean with_experimental_features_ = false; + private static boolean with_config_logging_ = false; + private static boolean with_debug_logs_ = false; - CommonConfig(ForgeConfigSpec.Builder builder) - { - builder.comment("Settings affecting the logical server side.") - .push("server"); - // --- OPTOUTS ------------------------------------------------------------ - { - builder.comment("Opt-out settings") - .push("optout"); - pattern_excludes = builder - .translation(MODID + ".config.pattern_excludes") - .comment("Opt-out any block by its registry name ('*' wildcard matching, " - + "comma separated list, whitespaces ignored. You must match the whole name, " - + "means maybe add '*' also at the begin and end. Example: '*wood*,*steel*' " - + "excludes everything that has 'wood' or 'steel' in the registry name. " - + "The matching result is also traced in the log file. ") - .define("pattern_excludes", ""); - pattern_includes = builder - .translation(MODID + ".config.pattern_includes") - .comment("Prevent blocks from being opt'ed by registry name ('*' wildcard matching, " - + "comma separated list, whitespaces ignored. Evaluated before all other opt-out checks. " - + "You must match the whole name, means maybe add '*' also at the begin and end. Example: " - + "'*wood*,*steel*' includes everything that has 'wood' or 'steel' in the registry name." - + "The matching result is also traced in the log file.") - .define("pattern_includes", ""); - builder.pop(); - } - // --- MISC --------------------------------------------------------------- - { - builder.comment("Miscellaneous settings") - .push("miscellaneous"); - with_experimental = builder - .translation(MODID + ".config.with_experimental") - .comment("Enables experimental features. Use at own risk.") - .define("with_experimental", false); - with_creative_mode_device_drops = builder - .translation(MODID + ".config.with_creative_mode_device_drops") - .comment("Enable that devices are dropped as item also in creative mode, allowing " + - " to relocate them with contents and settings.") - .define("with_creative_mode_device_drops", false); - with_config_logging = builder - .translation(MODID + ".config.with_debug_logging") - .comment("Enable detailed logging of the config values and resulting calculations in each mod feature config.") - .define("with_debug_logging", false); - with_debug_logging = builder - .translation(MODID + ".config.with_debug_logging") - .comment("Enable debug log messages for trouble shooting. Don't activate if not really needed, this can spam the log file.") - .define("with_debug_logging", false); - builder.pop(); - } + static { + final Pair common_ = (new ForgeConfigSpec.Builder()).configure(CommonConfig::new); + COMMON_CONFIG_SPEC = common_.getRight(); + COMMON = common_.getLeft(); + final Pair server_ = (new ForgeConfigSpec.Builder()).configure(ServerConfig::new); + SERVER_CONFIG_SPEC = server_.getRight(); + SERVER = server_.getLeft(); } - } - //-------------------------------------------------------------------------------------------------------------------- + //-------------------------------------------------------------------------------------------------------------------- + // Cache + //-------------------------------------------------------------------------------------------------------------------- - public static class ServerConfig - { - // Optout - public final ForgeConfigSpec.BooleanValue without_chair_sitting; - public final ForgeConfigSpec.BooleanValue without_mob_chair_sitting; - public final ForgeConfigSpec.BooleanValue without_ladder_speed_boost; - public final ForgeConfigSpec.BooleanValue without_crafting_table_history; - // Misc - public final ForgeConfigSpec.BooleanValue without_direct_slab_pickup; - // Tweaks - public final ForgeConfigSpec.IntValue furnace_smelting_speed_percent; - public final ForgeConfigSpec.IntValue furnace_fuel_efficiency_percent; - public final ForgeConfigSpec.IntValue furnace_boost_energy_consumption; - public final ForgeConfigSpec.ConfigValue furnace_accepted_heaters; - public final ForgeConfigSpec.IntValue e_furnace_speed_percent; - public final ForgeConfigSpec.IntValue e_furnace_power_consumption; - public final ForgeConfigSpec.IntValue small_solar_panel_peak_production; - public final ForgeConfigSpec.BooleanValue e_furnace_automatic_pulling; - public final ForgeConfigSpec.DoubleValue chair_mob_sitting_probability_percent; - public final ForgeConfigSpec.DoubleValue chair_mob_standup_probability_percent; - public final ForgeConfigSpec.BooleanValue without_crafting_mouse_scrolling; - public final ForgeConfigSpec.IntValue pipevalve_max_flowrate; - public final ForgeConfigSpec.IntValue pipevalve_redstone_gain; - public final ForgeConfigSpec.IntValue block_breaker_power_consumption; - public final ForgeConfigSpec.IntValue block_breaker_reluctance; - public final ForgeConfigSpec.IntValue block_breaker_min_breaking_time; - public final ForgeConfigSpec.BooleanValue block_breaker_requires_power; - public final ForgeConfigSpec.IntValue tree_cutter_energy_consumption; - public final ForgeConfigSpec.IntValue tree_cutter_cutting_time_needed; - public final ForgeConfigSpec.BooleanValue tree_cutter_requires_power; - public final ForgeConfigSpec.ConfigValue> tree_cutter_universal_logs; - public final ForgeConfigSpec.IntValue milking_machine_energy_consumption; - public final ForgeConfigSpec.IntValue milking_machine_milking_delay; - - ServerConfig(ForgeConfigSpec.Builder builder) - { - builder.comment("Settings affecting the logical server side.") - .push("server"); - // --- OPTOUTS ------------------------------------------------------------ - { - builder.comment("Server dev opt-out settings !WARNING THE OPT-OUTs will be moved to common-config.toml in the next MC version!") - .push("optout"); - without_chair_sitting = builder - .translation(MODID + ".config.without_chair_sitting") - .comment("Disable possibility to sit on stools and chairs.") - .define("without_chair_sitting", false); - without_mob_chair_sitting = builder - .translation(MODID + ".config.without_mob_chair_sitting") - .comment("Disable that mobs will sit on chairs and stools.") - .define("without_mob_chair_sitting", false); - without_ladder_speed_boost = builder - .translation(MODID + ".config.without_ladder_speed_boost") - .comment("Disable the speed boost of ladders in this mod.") - .define("without_ladder_speed_boost", false); - without_crafting_table_history = builder - .translation(MODID + ".config.without_crafting_table_history") - .comment("Disable history refabrication feature of the crafting table.") - .define("without_crafting_table_history", false); - builder.pop(); - } - // --- MISC --------------------------------------------------------------- - { - builder.comment("Miscellaneous settings") - .push("miscellaneous"); - without_direct_slab_pickup = builder - .translation(MODID + ".config.without_direct_slab_pickup") - .comment("Disable directly picking up layers from slabs and slab " + - " slices by left clicking while looking up/down.") - .define("without_direct_slab_pickup", false); - builder.pop(); - } - // --- TWEAKS ------------------------------------------------------------- - { - builder.comment("Tweaks") - .push("tweaks"); - furnace_smelting_speed_percent = builder - .translation(MODID + ".config.furnace_smelting_speed_percent") - .comment("Defines, in percent, how fast the lab furnace smelts compared to " + - "a vanilla furnace. 100% means vanilla furnace speed, 150% means the " + - "lab furnace is faster. The value can be changed on-the-fly for tuning.") - .defineInRange("furnace_smelting_speed_percent", 130, 50, 800); - furnace_fuel_efficiency_percent = builder - .translation(MODID + ".config.furnace_fuel_efficiency_percent") - .comment("Defines, in percent, how fuel efficient the lab furnace is, compared " + - "to a vanilla furnace. 100% means vanilla furnace consumiton, 200% means " + - "the lab furnace needs about half the fuel of a vanilla furnace, " + - "The value can be changed on-the-fly for tuning.") - .defineInRange("furnace_fuel_efficiency_percent", 100, 50, 400); - furnace_boost_energy_consumption = builder - .translation(MODID + ".config.furnace_boost_energy_consumption") - .comment("Defines the energy consumption (per tick) for speeding up the smelting process. " + - "If IE is installed, an external heater has to be inserted into an auxiliary slot " + - "of the lab furnace. The power source needs to be able to provide at least 4 times " + - "this consumption (fixed threshold value). The value can be changed on-the-fly for tuning. " + - "The default value corresponds to the IE heater consumption.") - .defineInRange("furnace_boost_energy_consumption", 24, 2, 1024); - furnace_accepted_heaters = builder - .translation(MODID + ".config.furnace_accepted_heaters") - .comment("Defines (as comma separated list of full registry names) which items are allowed as external " + - "heaters in the Aux slot for powered speed boosting.") - .define("furnace_accepted_heaters", "immersiveengineering:furnace_heater"); - chair_mob_sitting_probability_percent = builder - .translation(MODID + ".config.chair_mob_sitting_probability_percent") - .comment("Defines, in percent, how high the probability is that a mob sits on a chair " + - "when colliding with it. Can be changed on-the-fly for tuning.") - .defineInRange("chair_mob_sitting_probability_percent", 10.0, 0.0, 80.0); - chair_mob_standup_probability_percent = builder - .translation(MODID + ".config.chair_mob_standup_probability_percent") - .comment("Defines, in percent, probable it is that a mob leaves a chair when sitting " + - "on it. The 'dice is rolled' about every 20 ticks. There is also a minimum " + - "Sitting time of about 3s. The config value can be changed on-the-fly for tuning.") - .defineInRange("chair_mob_standup_probability_percent", 1.0, 1e-3, 10.0); - without_crafting_mouse_scrolling = builder - .translation(MODID + ".config.without_crafting_mouse_scrolling") - .comment("Disables increasing/decreasing the crafting grid items by scrolling over the crafting result slot.") - .define("without_crafting_mouse_scrolling", false); - pipevalve_max_flowrate = builder - .translation(MODID + ".config.pipevalve_max_flowrate") - .comment("Defines how many millibuckets can be transferred (per tick) through the valves. " + - "That is technically the 'storage size' specified for blocks that want to fill " + - "fluids into the valve (the valve has no container and forward that to the output " + - "block), The value can be changed on-the-fly for tuning. ") - .defineInRange("pipevalve_max_flowrate", 1000, 1, 32000); - pipevalve_redstone_gain = builder - .translation(MODID + ".config.pipevalve_redstone_gain") - .comment("Defines how many millibuckets per redstone signal strength can be transferred per tick " + - "through the analog redstone controlled valves. Note: power 0 is always off, power 15 is always " + - "the max flow rate. Between power 1 and 14 this scaler will result in a flow = 'redstone slope' * 'current redstone power'. " + - "The value can be changed on-the-fly for tuning. ") - .defineInRange("pipevalve_redstone_gain", 20, 1, 32000); - e_furnace_speed_percent = builder - .translation(MODID + ".config.e_furnace_speed_percent") - .comment("Defines, in percent, how fast the electrical furnace smelts compared to " + - "a vanilla furnace. 100% means vanilla furnace speed, 150% means the " + - "electrical furnace is faster. The value can be changed on-the-fly for tuning.") - .defineInRange("e_furnace_speed_percent", EdElectricalFurnace.ElectricalFurnaceTileEntity.DEFAULT_SPEED_PERCENT, 50, 800); - e_furnace_power_consumption = builder - .translation(MODID + ".config.e_furnace_power_consumption") - .comment("Defines how much RF per tick the the electrical furnace consumed (average) for smelting. " + - "The feeders transferring items from/to adjacent have this consumption/8 for each stack transaction. " + - "The default value is only slightly higher than a furnace with an IE external heater (and no burning fuel inside)." + - "The config value can be changed on-the-fly for tuning.") - .defineInRange("e_furnace_power_consumption", EdElectricalFurnace.ElectricalFurnaceTileEntity.DEFAULT_ENERGY_CONSUMPTION, 8, 4096); - e_furnace_automatic_pulling = builder - .translation(MODID + ".config.e_furnace_automatic_pulling") - .comment("Defines if the electrical furnace automatically pulls items from an inventory at the input side." + - "The config value can be changed on-the-fly for tuning.") - .define("e_furnace_automatic_pulling", false); - small_solar_panel_peak_production = builder - .translation(MODID + ".config.small_solar_panel_peak_production") - .comment("Defines the peak power production (at noon) of the Small Solar Panel. " + - "Note that the agerage power is much less, as no power is produced at all during the night, " + - "and the power curve is nonlinear rising/falling during the day. Bad weather conditions also " + - "decrease the production. The config value can be changed on-the-fly for tuning.") - .defineInRange("small_solar_panel_peak_production", EdSolarPanel.DEFAULT_PEAK_POWER, 2, 4096); - block_breaker_power_consumption = builder - .translation(MODID + ".config.block_breaker_power_consumption") - .comment("Defines how much RF power the Small Block Breaker requires to magnificently increase the processing speed. " + - "The config value can be changed on-the-fly for tuning.") - .defineInRange("block_breaker_power_consumption", EdBreaker.DEFAULT_BOOST_ENERGY, 4, 1024); - block_breaker_reluctance = builder - .translation(MODID + ".config.block_breaker_reluctance") - .comment("Defines how much time the Small Block Breaker needs per block hardness, " + - "means: 'reluctance' * hardness + min_time, you change the 'reluctance' here." + - "The unit is ticks/hardness. " + "The config value can be changed on-the-fly for tuning.") - .defineInRange("block_breaker_reluctance", EdBreaker.DEFAULT_BREAKING_RELUCTANCE, 5, 50); - block_breaker_min_breaking_time = builder - .translation(MODID + ".config.block_breaker_min_breaking_time") - .comment("Defines how much time the Small Block Breaker needs at least, better said it's an offset: " + - "'reluctance' * hardness + min_time, you change the 'min_time' here, value " + - "in ticks." + "The config value can be changed on-the-fly for tuning.") - .defineInRange("block_breaker_min_breaking_time", EdBreaker.DEFAULT_MIN_BREAKING_TIME, 10, 100); - block_breaker_requires_power = builder - .translation(MODID + ".config.block_breaker_requires_power") - .comment("Defines if the Small Block Breaker does not work without RF power.") - .define("block_breaker_requires_power", false); - tree_cutter_energy_consumption = builder - .translation(MODID + ".config.tree_cutter_energy_consumption") - .comment("Defines how much RF power the Small Tree Cutter requires to magnificently increase the processing speed. " + - "The config value can be changed on-the-fly for tuning.") - .defineInRange("tree_cutter_energy_consumption", EdTreeCutter.TreeCutterTileEntity.DEFAULT_BOOST_ENERGY, 4, 1024); - tree_cutter_cutting_time_needed = builder - .translation(MODID + ".config.tree_cutter_cutting_time_needed") - .comment("Defines how much time the Small Tree Cutter needs to cut a tree without RF power. " + - "The value is in seconds. With energy it is 6 times faster. " + - "The config value can be changed on-the-fly for tuning.") - .defineInRange("tree_cutter_cutting_time_needed", EdTreeCutter.TreeCutterTileEntity.DEFAULT_CUTTING_TIME_NEEDED, 10, 240); - tree_cutter_requires_power = builder - .translation(MODID + ".config.tree_cutter_requires_power") - .comment("Defines if the Small Tree Cutter does not work without RF power.") - .define("tree_cutter_requires_power", false); - tree_cutter_universal_logs = builder - .translation(MODID + ".config.tree_cutter_universal_logs") - .comment("Defines a list of resource locations which blocks are always to be treated as part of a tree. This is usefull for special log blocks containing resources like rubber.") - .define("tree_cutter_universal_logs", new ArrayList<>()); - milking_machine_energy_consumption = builder - .translation(MODID + ".config.milking_machine_energy_consumption") - .comment("Defines how much time the Small Milking Machine needs work. " + - "Note this is a permanent standby power, not only when the device does something. " + - "Use zero to disable energy dependency and energy handling of the machine. " + - "The config value can be changed on-the-fly for tuning.") - .defineInRange("milking_machine_energy_consumption", EdMilker.DEFAULT_ENERGY_CONSUMPTION, 0, 1024); - milking_machine_milking_delay = builder - .translation(MODID + ".config.milking_machine_milking_delay") - .comment("Defines (for each individual cow) the minimum time between milking." ) - .defineInRange("milking_machine_milking_delay", EdMilker.DEFAULT_MILKING_DELAY_PER_COW, 1000, 24000); - builder.pop(); - } + public static boolean isOptedOut(final @Nullable Block block) { + return (block == null) || isOptedOut(block.asItem()); } - } - //-------------------------------------------------------------------------------------------------------------------- - // Optout checks - //-------------------------------------------------------------------------------------------------------------------- - - public static boolean isOptedOut(final @Nullable Block block) - { return (block==null) || isOptedOut(block.asItem()); } - - public static boolean isOptedOut(final @Nullable Item item) - { return (item!=null) && optouts_.contains(Auxiliaries.getResourceLocation(item).getPath()); } - - public static boolean withExperimental() - { return with_experimental_features_; } - - public static boolean withoutRecipes() - { return false; } - - public static boolean withDebug() - { return with_debug_logs_; } - - public static boolean withDebugLogging() - { return with_experimental_features_ && with_config_logging_; } - - //-------------------------------------------------------------------------------------------------------------------- - // Cache - //-------------------------------------------------------------------------------------------------------------------- - - private static final CompoundTag server_config_ = new CompoundTag(); - private static HashSet optouts_ = new HashSet<>(); - private static boolean with_experimental_features_ = false; - private static boolean with_config_logging_ = false; - private static boolean with_debug_logs_ = false; - public static boolean immersiveengineering_installed = false; - public static boolean without_direct_slab_pickup = false; - public static boolean with_creative_mode_device_drops = false; - - public static CompoundTag getServerConfig() // config that may be synchronized from server to client via net pkg. - { return server_config_; } - - private static void updateOptouts() - { - final ArrayList includes = new ArrayList<>(); - final ArrayList excludes = new ArrayList<>(); - { - String inc = COMMON.pattern_includes.get().toLowerCase().replaceAll(MODID+":", "").replaceAll("[^*_,a-z\\d]", ""); - if(!COMMON.pattern_includes.get().equals(inc)) COMMON.pattern_includes.set(inc); - String[] incl = inc.split(","); - for(int i=0; i< incl.length; ++i) { - incl[i] = incl[i].replaceAll("[*]", ".*?"); - if(!incl[i].isEmpty()) includes.add(incl[i]); - } + public static boolean isOptedOut(final @Nullable Item item) { + return (item != null) && optouts_.contains(Auxiliaries.getResourceLocation(item).getPath()); } - { - String exc = COMMON.pattern_excludes.get().toLowerCase().replaceAll(MODID+":", "").replaceAll("[^*_,a-z\\d]", ""); - String[] excl = exc.split(","); - for(int i=0; i< excl.length; ++i) { - excl[i] = excl[i].replaceAll("[*]", ".*?"); - if(!excl[i].isEmpty()) excludes.add(excl[i]); - } + + public static boolean withExperimental() { + return with_experimental_features_; } - if(!excludes.isEmpty()) log("Config pattern excludes: '" + String.join(",", excludes) + "'"); - if(!includes.isEmpty()) log("Config pattern includes: '" + String.join(",", includes) + "'"); - try { - HashSet optouts = new HashSet<>(); - ModContent.getRegisteredBlocks().stream().filter((Block block) -> { - if(block==null) return true; - if(block==ModContent.getBlock("sign_decor")) return true; - try { - if(!with_experimental_features_) { - if(block instanceof Auxiliaries.IExperimentalFeature) return true; - } - // Force-include/exclude pattern matching - final String rn = Auxiliaries.getResourceLocation(block).getPath(); - try { - for(String e : includes) { - if(rn.matches(e)) { - log("Optout force include: "+rn); - return false; - } - } - for(String e : excludes) { - if(rn.matches(e)) { - log("Optout force exclude: "+rn); - return true; - } - } - } catch(Throwable ex) { - Auxiliaries.logger().error("optout include pattern failed, disabling."); - includes.clear(); - excludes.clear(); - } - } catch(Exception ex) { - Auxiliaries.logger().error("Exception evaluating the optout config: '"+ex.getMessage()+"'"); - } + + public static boolean withoutRecipes() { return false; - }).forEach( - e -> optouts.add(Auxiliaries.getResourceLocation(e).getPath()) - ); - optouts_ = optouts; - OptionalRecipeCondition.on_config(withExperimental(), withoutRecipes(), ModConfig::isOptedOut, ModConfig::isOptedOut); - } catch(Throwable ex) { - Auxiliaries.logger().error("Exception evaluating the optout config: '"+ex.getMessage()+"'"); // Compat issue: config-apply may be called before the registries are all loaded. } - } - public static void apply() - { - with_config_logging_ = COMMON.with_config_logging.get(); - with_experimental_features_ = COMMON.with_experimental.get(); - with_debug_logs_ = COMMON.with_debug_logging.get(); - if(with_experimental_features_) Auxiliaries.logger().info("Config: EXPERIMENTAL FEATURES ENABLED."); - if(with_debug_logs_) Auxiliaries.logger().info("Config: DEBUG LOGGING ENABLED, WARNING, THIS MAY SPAM THE LOG."); - immersiveengineering_installed = Auxiliaries.isModLoaded("immersiveengineering"); - updateOptouts(); - if(!SERVER_CONFIG_SPEC.isLoaded()) return; - without_direct_slab_pickup = SERVER.without_direct_slab_pickup.get(); - // ----------------------------------------------------------------------------------------------------------------- - EdChair.on_config(SERVER.without_chair_sitting.get(), SERVER.without_mob_chair_sitting.get(), SERVER.chair_mob_sitting_probability_percent.get(), SERVER.chair_mob_standup_probability_percent.get()); - EdLadderBlock.on_config(SERVER.without_ladder_speed_boost.get()); - VariantSlabBlock.on_config(!SERVER.without_direct_slab_pickup.get()); - SlabSliceBlock.on_config(!SERVER.without_direct_slab_pickup.get()); - EdFluidBarrel.on_config(12000, 1000); - EdFluidFunnel.on_config(with_experimental_features_); - EdPipeValve.on_config(SERVER.pipevalve_max_flowrate.get(), SERVER.pipevalve_redstone_gain.get()); - EdHopper.on_config(); - EdDropper.on_config(true); - EdPlacer.on_config(); - EdBreaker.on_config(SERVER.block_breaker_power_consumption.get(), SERVER.block_breaker_reluctance.get(), SERVER.block_breaker_min_breaking_time.get(), SERVER.block_breaker_requires_power.get()); - EdTreeCutter.on_config(SERVER.tree_cutter_energy_consumption.get(), SERVER.tree_cutter_cutting_time_needed.get(), SERVER.tree_cutter_requires_power.get()); - EdFurnace.on_config(SERVER.furnace_smelting_speed_percent.get(), SERVER.furnace_fuel_efficiency_percent.get(), SERVER.furnace_boost_energy_consumption.get(), SERVER.furnace_accepted_heaters.get()); - EdElectricalFurnace.on_config(SERVER.e_furnace_speed_percent.get(), SERVER.e_furnace_power_consumption.get(), SERVER.e_furnace_automatic_pulling.get()); - EdSolarPanel.on_config(SERVER.small_solar_panel_peak_production.get(), 64000, 1024); - EdMilker.on_config(SERVER.milking_machine_energy_consumption.get(), SERVER.milking_machine_milking_delay.get()); - EdFreezer.on_config(144, 2); - EdMineralSmelter.on_config(144, 2); - EdWasteIncinerator.on_config(8); - // ----------------------------------------------------------------------------------------------------------------- - { - final List universal_logs = new ArrayList<>(SERVER.tree_cutter_universal_logs.get()); - // Fixed known blocks. @todo, also check AE/menril, etc. - universal_logs.add("myrtrees:filled_rubberwood_log"); - TreeCutting.on_config(universal_logs); + public static boolean withDebug() { + return with_debug_logs_; } - // ----------------------------------------------------------------------------------------------------------------- - { - // Check if the config is already synchronized or has to be synchronised. - server_config_.putBoolean("tree_cutter_requires_power", SERVER.tree_cutter_requires_power.get()); - server_config_.putBoolean("block_breaker_requires_power", SERVER.block_breaker_requires_power.get()); - { - String s = String.join(",", optouts_); - server_config_.putString("optout", s); - if(!s.isEmpty()) log("Opt-outs:" + s); - } - } - } - public static void log(String config_message) - { - if(!with_config_logging_) return; - Auxiliaries.logger().info(config_message); - } + public static boolean withDebugLogging() { + return with_experimental_features_ && with_config_logging_; + } + + public static CompoundTag getServerConfig() // config that may be synchronized from server to client via net pkg. + { + return server_config_; + } + + private static void updateOptouts() { + final ArrayList includes = new ArrayList<>(); + final ArrayList excludes = new ArrayList<>(); + { + String inc = COMMON.pattern_includes.get().toLowerCase().replaceAll(MODID + ":", "").replaceAll("[^*_,a-z\\d]", ""); + if (!COMMON.pattern_includes.get().equals(inc)) COMMON.pattern_includes.set(inc); + String[] incl = inc.split(","); + for (int i = 0; i < incl.length; ++i) { + incl[i] = incl[i].replaceAll("[*]", ".*?"); + if (!incl[i].isEmpty()) includes.add(incl[i]); + } + } + { + String exc = COMMON.pattern_excludes.get().toLowerCase().replaceAll(MODID + ":", "").replaceAll("[^*_,a-z\\d]", ""); + String[] excl = exc.split(","); + for (int i = 0; i < excl.length; ++i) { + excl[i] = excl[i].replaceAll("[*]", ".*?"); + if (!excl[i].isEmpty()) excludes.add(excl[i]); + } + } + if (!excludes.isEmpty()) log("Config pattern excludes: '" + String.join(",", excludes) + "'"); + if (!includes.isEmpty()) log("Config pattern includes: '" + String.join(",", includes) + "'"); + try { + HashSet optouts = new HashSet<>(); + ModContent.getRegisteredBlocks().stream().filter((Block block) -> { + if (block == null) return true; + if (block == ModContent.getBlock("sign_decor")) return true; + try { + if (!with_experimental_features_) { + if (block instanceof Auxiliaries.IExperimentalFeature) return true; + } + // Force-include/exclude pattern matching + final String rn = Auxiliaries.getResourceLocation(block).getPath(); + try { + for (String e : includes) { + if (rn.matches(e)) { + log("Optout force include: " + rn); + return false; + } + } + for (String e : excludes) { + if (rn.matches(e)) { + log("Optout force exclude: " + rn); + return true; + } + } + } catch (Throwable ex) { + Auxiliaries.logger().error("optout include pattern failed, disabling."); + includes.clear(); + excludes.clear(); + } + } catch (Exception ex) { + Auxiliaries.logger().error("Exception evaluating the optout config: '" + ex.getMessage() + "'"); + } + return false; + }).forEach( + e -> optouts.add(Auxiliaries.getResourceLocation(e).getPath()) + ); + optouts_ = optouts; + OptionalRecipeCondition.on_config(withExperimental(), withoutRecipes(), ModConfig::isOptedOut, ModConfig::isOptedOut); + } catch (Throwable ex) { + Auxiliaries.logger().error("Exception evaluating the optout config: '" + ex.getMessage() + "'"); // Compat issue: config-apply may be called before the registries are all loaded. + } + } + + public static void apply() { + with_config_logging_ = COMMON.with_config_logging.get(); + with_experimental_features_ = COMMON.with_experimental.get(); + with_debug_logs_ = COMMON.with_debug_logging.get(); + if (with_experimental_features_) Auxiliaries.logger().info("Config: EXPERIMENTAL FEATURES ENABLED."); + if (with_debug_logs_) + Auxiliaries.logger().info("Config: DEBUG LOGGING ENABLED, WARNING, THIS MAY SPAM THE LOG."); + immersiveengineering_installed = Auxiliaries.isModLoaded("immersiveengineering"); + updateOptouts(); + if (!SERVER_CONFIG_SPEC.isLoaded()) return; + without_direct_slab_pickup = SERVER.without_direct_slab_pickup.get(); + // ----------------------------------------------------------------------------------------------------------------- + EdChair.on_config(SERVER.without_chair_sitting.get(), SERVER.without_mob_chair_sitting.get(), SERVER.chair_mob_sitting_probability_percent.get(), SERVER.chair_mob_standup_probability_percent.get()); + EdLadderBlock.on_config(SERVER.without_ladder_speed_boost.get()); + VariantSlabBlock.on_config(!SERVER.without_direct_slab_pickup.get()); + SlabSliceBlock.on_config(!SERVER.without_direct_slab_pickup.get()); + EdFluidBarrel.on_config(12000, 1000); + EdFluidFunnel.on_config(with_experimental_features_); + EdPipeValve.on_config(SERVER.pipevalve_max_flowrate.get(), SERVER.pipevalve_redstone_gain.get()); + EdHopper.on_config(); + EdDropper.on_config(true); + EdPlacer.on_config(); + EdBreaker.on_config(SERVER.block_breaker_power_consumption.get(), SERVER.block_breaker_reluctance.get(), SERVER.block_breaker_min_breaking_time.get(), SERVER.block_breaker_requires_power.get()); + EdTreeCutter.on_config(SERVER.tree_cutter_energy_consumption.get(), SERVER.tree_cutter_cutting_time_needed.get(), SERVER.tree_cutter_requires_power.get()); + EdFurnace.on_config(SERVER.furnace_smelting_speed_percent.get(), SERVER.furnace_fuel_efficiency_percent.get(), SERVER.furnace_boost_energy_consumption.get(), SERVER.furnace_accepted_heaters.get()); + EdElectricalFurnace.on_config(SERVER.e_furnace_speed_percent.get(), SERVER.e_furnace_power_consumption.get(), SERVER.e_furnace_automatic_pulling.get()); + EdSolarPanel.on_config(SERVER.small_solar_panel_peak_production.get(), 64000, 1024); + EdMilker.on_config(SERVER.milking_machine_energy_consumption.get(), SERVER.milking_machine_milking_delay.get()); + EdFreezer.on_config(144, 2); + EdMineralSmelter.on_config(144, 2); + EdWasteIncinerator.on_config(8); + // ----------------------------------------------------------------------------------------------------------------- + { + final List universal_logs = new ArrayList<>(SERVER.tree_cutter_universal_logs.get()); + // Fixed known blocks. @todo, also check AE/menril, etc. + universal_logs.add("myrtrees:filled_rubberwood_log"); + TreeCutting.on_config(universal_logs); + } + // ----------------------------------------------------------------------------------------------------------------- + { + // Check if the config is already synchronized or has to be synchronised. + server_config_.putBoolean("tree_cutter_requires_power", SERVER.tree_cutter_requires_power.get()); + server_config_.putBoolean("block_breaker_requires_power", SERVER.block_breaker_requires_power.get()); + { + String s = String.join(",", optouts_); + server_config_.putString("optout", s); + if (!s.isEmpty()) log("Opt-outs:" + s); + } + } + } + + public static void log(String config_message) { + if (!with_config_logging_) return; + Auxiliaries.logger().info(config_message); + } + + public static class CommonConfig { + // Optout + public final ForgeConfigSpec.ConfigValue pattern_excludes; + public final ForgeConfigSpec.ConfigValue pattern_includes; + // MISC + public final ForgeConfigSpec.BooleanValue with_creative_mode_device_drops; + public final ForgeConfigSpec.BooleanValue with_experimental; + public final ForgeConfigSpec.BooleanValue with_config_logging; + public final ForgeConfigSpec.BooleanValue with_debug_logging; + + CommonConfig(ForgeConfigSpec.Builder builder) { + builder.comment("Settings affecting the logical server side.") + .push("server"); + // --- OPTOUTS ------------------------------------------------------------ + { + builder.comment("Opt-out settings") + .push("optout"); + pattern_excludes = builder + .translation(MODID + ".config.pattern_excludes") + .comment("Opt-out any block by its registry name ('*' wildcard matching, " + + "comma separated list, whitespaces ignored. You must match the whole name, " + + "means maybe add '*' also at the begin and end. Example: '*wood*,*steel*' " + + "excludes everything that has 'wood' or 'steel' in the registry name. " + + "The matching result is also traced in the log file. ") + .define("pattern_excludes", ""); + pattern_includes = builder + .translation(MODID + ".config.pattern_includes") + .comment("Prevent blocks from being opt'ed by registry name ('*' wildcard matching, " + + "comma separated list, whitespaces ignored. Evaluated before all other opt-out checks. " + + "You must match the whole name, means maybe add '*' also at the begin and end. Example: " + + "'*wood*,*steel*' includes everything that has 'wood' or 'steel' in the registry name." + + "The matching result is also traced in the log file.") + .define("pattern_includes", ""); + builder.pop(); + } + // --- MISC --------------------------------------------------------------- + { + builder.comment("Miscellaneous settings") + .push("miscellaneous"); + with_experimental = builder + .translation(MODID + ".config.with_experimental") + .comment("Enables experimental features. Use at own risk.") + .define("with_experimental", false); + with_creative_mode_device_drops = builder + .translation(MODID + ".config.with_creative_mode_device_drops") + .comment("Enable that devices are dropped as item also in creative mode, allowing " + + " to relocate them with contents and settings.") + .define("with_creative_mode_device_drops", false); + with_config_logging = builder + .translation(MODID + ".config.with_debug_logging") + .comment("Enable detailed logging of the config values and resulting calculations in each mod feature config.") + .define("with_debug_logging", false); + with_debug_logging = builder + .translation(MODID + ".config.with_debug_logging") + .comment("Enable debug log messages for trouble shooting. Don't activate if not really needed, this can spam the log file.") + .define("with_debug_logging", false); + builder.pop(); + } + } + } + + public static class ServerConfig { + // Optout + public final ForgeConfigSpec.BooleanValue without_chair_sitting; + public final ForgeConfigSpec.BooleanValue without_mob_chair_sitting; + public final ForgeConfigSpec.BooleanValue without_ladder_speed_boost; + public final ForgeConfigSpec.BooleanValue without_crafting_table_history; + // Misc + public final ForgeConfigSpec.BooleanValue without_direct_slab_pickup; + // Tweaks + public final ForgeConfigSpec.IntValue furnace_smelting_speed_percent; + public final ForgeConfigSpec.IntValue furnace_fuel_efficiency_percent; + public final ForgeConfigSpec.IntValue furnace_boost_energy_consumption; + public final ForgeConfigSpec.ConfigValue furnace_accepted_heaters; + public final ForgeConfigSpec.IntValue e_furnace_speed_percent; + public final ForgeConfigSpec.IntValue e_furnace_power_consumption; + public final ForgeConfigSpec.IntValue small_solar_panel_peak_production; + public final ForgeConfigSpec.BooleanValue e_furnace_automatic_pulling; + public final ForgeConfigSpec.DoubleValue chair_mob_sitting_probability_percent; + public final ForgeConfigSpec.DoubleValue chair_mob_standup_probability_percent; + public final ForgeConfigSpec.BooleanValue without_crafting_mouse_scrolling; + public final ForgeConfigSpec.IntValue pipevalve_max_flowrate; + public final ForgeConfigSpec.IntValue pipevalve_redstone_gain; + public final ForgeConfigSpec.IntValue block_breaker_power_consumption; + public final ForgeConfigSpec.IntValue block_breaker_reluctance; + public final ForgeConfigSpec.IntValue block_breaker_min_breaking_time; + public final ForgeConfigSpec.BooleanValue block_breaker_requires_power; + public final ForgeConfigSpec.IntValue tree_cutter_energy_consumption; + public final ForgeConfigSpec.IntValue tree_cutter_cutting_time_needed; + public final ForgeConfigSpec.BooleanValue tree_cutter_requires_power; + public final ForgeConfigSpec.ConfigValue> tree_cutter_universal_logs; + public final ForgeConfigSpec.IntValue milking_machine_energy_consumption; + public final ForgeConfigSpec.IntValue milking_machine_milking_delay; + + ServerConfig(ForgeConfigSpec.Builder builder) { + builder.comment("Settings affecting the logical server side.") + .push("server"); + // --- OPTOUTS ------------------------------------------------------------ + { + builder.comment("Server dev opt-out settings !WARNING THE OPT-OUTs will be moved to common-config.toml in the next MC version!") + .push("optout"); + without_chair_sitting = builder + .translation(MODID + ".config.without_chair_sitting") + .comment("Disable possibility to sit on stools and chairs.") + .define("without_chair_sitting", false); + without_mob_chair_sitting = builder + .translation(MODID + ".config.without_mob_chair_sitting") + .comment("Disable that mobs will sit on chairs and stools.") + .define("without_mob_chair_sitting", false); + without_ladder_speed_boost = builder + .translation(MODID + ".config.without_ladder_speed_boost") + .comment("Disable the speed boost of ladders in this mod.") + .define("without_ladder_speed_boost", false); + without_crafting_table_history = builder + .translation(MODID + ".config.without_crafting_table_history") + .comment("Disable history refabrication feature of the crafting table.") + .define("without_crafting_table_history", false); + builder.pop(); + } + // --- MISC --------------------------------------------------------------- + { + builder.comment("Miscellaneous settings") + .push("miscellaneous"); + without_direct_slab_pickup = builder + .translation(MODID + ".config.without_direct_slab_pickup") + .comment("Disable directly picking up layers from slabs and slab " + + " slices by left clicking while looking up/down.") + .define("without_direct_slab_pickup", false); + builder.pop(); + } + // --- TWEAKS ------------------------------------------------------------- + { + builder.comment("Tweaks") + .push("tweaks"); + furnace_smelting_speed_percent = builder + .translation(MODID + ".config.furnace_smelting_speed_percent") + .comment("Defines, in percent, how fast the lab furnace smelts compared to " + + "a vanilla furnace. 100% means vanilla furnace speed, 150% means the " + + "lab furnace is faster. The value can be changed on-the-fly for tuning.") + .defineInRange("furnace_smelting_speed_percent", 130, 50, 800); + furnace_fuel_efficiency_percent = builder + .translation(MODID + ".config.furnace_fuel_efficiency_percent") + .comment("Defines, in percent, how fuel efficient the lab furnace is, compared " + + "to a vanilla furnace. 100% means vanilla furnace consumiton, 200% means " + + "the lab furnace needs about half the fuel of a vanilla furnace, " + + "The value can be changed on-the-fly for tuning.") + .defineInRange("furnace_fuel_efficiency_percent", 100, 50, 400); + furnace_boost_energy_consumption = builder + .translation(MODID + ".config.furnace_boost_energy_consumption") + .comment("Defines the energy consumption (per tick) for speeding up the smelting process. " + + "If IE is installed, an external heater has to be inserted into an auxiliary slot " + + "of the lab furnace. The power source needs to be able to provide at least 4 times " + + "this consumption (fixed threshold value). The value can be changed on-the-fly for tuning. " + + "The default value corresponds to the IE heater consumption.") + .defineInRange("furnace_boost_energy_consumption", 24, 2, 1024); + furnace_accepted_heaters = builder + .translation(MODID + ".config.furnace_accepted_heaters") + .comment("Defines (as comma separated list of full registry names) which items are allowed as external " + + "heaters in the Aux slot for powered speed boosting.") + .define("furnace_accepted_heaters", "immersiveengineering:furnace_heater"); + chair_mob_sitting_probability_percent = builder + .translation(MODID + ".config.chair_mob_sitting_probability_percent") + .comment("Defines, in percent, how high the probability is that a mob sits on a chair " + + "when colliding with it. Can be changed on-the-fly for tuning.") + .defineInRange("chair_mob_sitting_probability_percent", 10.0, 0.0, 80.0); + chair_mob_standup_probability_percent = builder + .translation(MODID + ".config.chair_mob_standup_probability_percent") + .comment("Defines, in percent, probable it is that a mob leaves a chair when sitting " + + "on it. The 'dice is rolled' about every 20 ticks. There is also a minimum " + + "Sitting time of about 3s. The config value can be changed on-the-fly for tuning.") + .defineInRange("chair_mob_standup_probability_percent", 1.0, 1e-3, 10.0); + without_crafting_mouse_scrolling = builder + .translation(MODID + ".config.without_crafting_mouse_scrolling") + .comment("Disables increasing/decreasing the crafting grid items by scrolling over the crafting result slot.") + .define("without_crafting_mouse_scrolling", false); + pipevalve_max_flowrate = builder + .translation(MODID + ".config.pipevalve_max_flowrate") + .comment("Defines how many millibuckets can be transferred (per tick) through the valves. " + + "That is technically the 'storage size' specified for blocks that want to fill " + + "fluids into the valve (the valve has no container and forward that to the output " + + "block), The value can be changed on-the-fly for tuning. ") + .defineInRange("pipevalve_max_flowrate", 1000, 1, 32000); + pipevalve_redstone_gain = builder + .translation(MODID + ".config.pipevalve_redstone_gain") + .comment("Defines how many millibuckets per redstone signal strength can be transferred per tick " + + "through the analog redstone controlled valves. Note: power 0 is always off, power 15 is always " + + "the max flow rate. Between power 1 and 14 this scaler will result in a flow = 'redstone slope' * 'current redstone power'. " + + "The value can be changed on-the-fly for tuning. ") + .defineInRange("pipevalve_redstone_gain", 20, 1, 32000); + e_furnace_speed_percent = builder + .translation(MODID + ".config.e_furnace_speed_percent") + .comment("Defines, in percent, how fast the electrical furnace smelts compared to " + + "a vanilla furnace. 100% means vanilla furnace speed, 150% means the " + + "electrical furnace is faster. The value can be changed on-the-fly for tuning.") + .defineInRange("e_furnace_speed_percent", EdElectricalFurnace.ElectricalFurnaceTileEntity.DEFAULT_SPEED_PERCENT, 50, 800); + e_furnace_power_consumption = builder + .translation(MODID + ".config.e_furnace_power_consumption") + .comment("Defines how much RF per tick the the electrical furnace consumed (average) for smelting. " + + "The feeders transferring items from/to adjacent have this consumption/8 for each stack transaction. " + + "The default value is only slightly higher than a furnace with an IE external heater (and no burning fuel inside)." + + "The config value can be changed on-the-fly for tuning.") + .defineInRange("e_furnace_power_consumption", EdElectricalFurnace.ElectricalFurnaceTileEntity.DEFAULT_ENERGY_CONSUMPTION, 8, 4096); + e_furnace_automatic_pulling = builder + .translation(MODID + ".config.e_furnace_automatic_pulling") + .comment("Defines if the electrical furnace automatically pulls items from an inventory at the input side." + + "The config value can be changed on-the-fly for tuning.") + .define("e_furnace_automatic_pulling", false); + small_solar_panel_peak_production = builder + .translation(MODID + ".config.small_solar_panel_peak_production") + .comment("Defines the peak power production (at noon) of the Small Solar Panel. " + + "Note that the agerage power is much less, as no power is produced at all during the night, " + + "and the power curve is nonlinear rising/falling during the day. Bad weather conditions also " + + "decrease the production. The config value can be changed on-the-fly for tuning.") + .defineInRange("small_solar_panel_peak_production", EdSolarPanel.DEFAULT_PEAK_POWER, 2, 4096); + block_breaker_power_consumption = builder + .translation(MODID + ".config.block_breaker_power_consumption") + .comment("Defines how much RF power the Small Block Breaker requires to magnificently increase the processing speed. " + + "The config value can be changed on-the-fly for tuning.") + .defineInRange("block_breaker_power_consumption", EdBreaker.DEFAULT_BOOST_ENERGY, 4, 1024); + block_breaker_reluctance = builder + .translation(MODID + ".config.block_breaker_reluctance") + .comment("Defines how much time the Small Block Breaker needs per block hardness, " + + "means: 'reluctance' * hardness + min_time, you change the 'reluctance' here." + + "The unit is ticks/hardness. " + "The config value can be changed on-the-fly for tuning.") + .defineInRange("block_breaker_reluctance", EdBreaker.DEFAULT_BREAKING_RELUCTANCE, 5, 50); + block_breaker_min_breaking_time = builder + .translation(MODID + ".config.block_breaker_min_breaking_time") + .comment("Defines how much time the Small Block Breaker needs at least, better said it's an offset: " + + "'reluctance' * hardness + min_time, you change the 'min_time' here, value " + + "in ticks." + "The config value can be changed on-the-fly for tuning.") + .defineInRange("block_breaker_min_breaking_time", EdBreaker.DEFAULT_MIN_BREAKING_TIME, 10, 100); + block_breaker_requires_power = builder + .translation(MODID + ".config.block_breaker_requires_power") + .comment("Defines if the Small Block Breaker does not work without RF power.") + .define("block_breaker_requires_power", false); + tree_cutter_energy_consumption = builder + .translation(MODID + ".config.tree_cutter_energy_consumption") + .comment("Defines how much RF power the Small Tree Cutter requires to magnificently increase the processing speed. " + + "The config value can be changed on-the-fly for tuning.") + .defineInRange("tree_cutter_energy_consumption", EdTreeCutter.TreeCutterTileEntity.DEFAULT_BOOST_ENERGY, 4, 1024); + tree_cutter_cutting_time_needed = builder + .translation(MODID + ".config.tree_cutter_cutting_time_needed") + .comment("Defines how much time the Small Tree Cutter needs to cut a tree without RF power. " + + "The value is in seconds. With energy it is 6 times faster. " + + "The config value can be changed on-the-fly for tuning.") + .defineInRange("tree_cutter_cutting_time_needed", EdTreeCutter.TreeCutterTileEntity.DEFAULT_CUTTING_TIME_NEEDED, 10, 240); + tree_cutter_requires_power = builder + .translation(MODID + ".config.tree_cutter_requires_power") + .comment("Defines if the Small Tree Cutter does not work without RF power.") + .define("tree_cutter_requires_power", false); + tree_cutter_universal_logs = builder + .translation(MODID + ".config.tree_cutter_universal_logs") + .comment("Defines a list of resource locations which blocks are always to be treated as part of a tree. This is usefull for special log blocks containing resources like rubber.") + .define("tree_cutter_universal_logs", new ArrayList<>()); + milking_machine_energy_consumption = builder + .translation(MODID + ".config.milking_machine_energy_consumption") + .comment("Defines how much time the Small Milking Machine needs work. " + + "Note this is a permanent standby power, not only when the device does something. " + + "Use zero to disable energy dependency and energy handling of the machine. " + + "The config value can be changed on-the-fly for tuning.") + .defineInRange("milking_machine_energy_consumption", EdMilker.DEFAULT_ENERGY_CONSUMPTION, 0, 1024); + milking_machine_milking_delay = builder + .translation(MODID + ".config.milking_machine_milking_delay") + .comment("Defines (for each individual cow) the minimum time between milking.") + .defineInRange("milking_machine_milking_delay", EdMilker.DEFAULT_MILKING_DELAY_PER_COW, 1000, 24000); + builder.pop(); + } + } + } } diff --git a/src/main/java/dev/zontreck/engineerdecor/ModContent.java b/src/main/java/dev/zontreck/engineerdecor/ModContent.java index c18cc26..e72391f 100644 --- a/src/main/java/dev/zontreck/engineerdecor/ModContent.java +++ b/src/main/java/dev/zontreck/engineerdecor/ModContent.java @@ -15,7 +15,7 @@ package dev.zontreck.engineerdecor; import dev.zontreck.engineerdecor.blocks.*; import dev.zontreck.engineerdecor.detail.ModRenderers; import dev.zontreck.engineerdecor.items.EdItem; -import dev.zontreck.engineerdecor.libmc.*; +import dev.zontreck.libzontreck.edlibmc.*; import net.minecraft.client.gui.screens.MenuScreens; import net.minecraft.client.renderer.entity.EntityRenderers; import net.minecraft.core.BlockPos; @@ -33,8 +33,6 @@ import net.minecraft.world.level.block.SoundType; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.material.Material; -import net.minecraft.world.level.material.MaterialColor; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.shapes.BooleanOp; import net.minecraft.world.phys.shapes.Shapes; @@ -42,8 +40,6 @@ import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; -import wile.engineersdecor.blocks.*; -import wile.engineersdecor.libmc.*; import javax.annotation.Nonnull; import java.util.ArrayList; @@ -52,920 +48,901 @@ import java.util.List; @SuppressWarnings("unused") -public class ModContent -{ - private static class detail { - public static String MODID = ""; - public static Boolean disallowSpawn(BlockState state, BlockGetter reader, BlockPos pos, EntityType entity) { return false; } - } +public class ModContent { + public static void init(String modid) { + detail.MODID = modid; + initTags(); + initBlocks(); + initItems(); + initEntities(); + } - public static void init(String modid) - { - detail.MODID = modid; - initTags(); - initBlocks(); - initItems(); - initEntities(); - } + public static void initTags() { + Registries.addOptionalBlockTag("accepted_mineral_smelter_input", new ResourceLocation("minecraft", "diorite"), new ResourceLocation("minecraft", "cobblestone")); + } - public static void initTags() - { - Registries.addOptionalBlockTag("accepted_mineral_smelter_input", new ResourceLocation("minecraft", "diorite"), new ResourceLocation("minecraft", "cobblestone")); - } + public static void initBlocks() { + Registries.addBlock("clinker_brick_block", () -> new StandardBlocks.BaseBlock( + StandardBlocks.CFG_DEFAULT, + BlockBehaviour.Properties.of().strength(0.5f, 7f).sound(SoundType.STONE) + )); + Registries.addBlock("clinker_brick_slab", () -> new VariantSlabBlock( + StandardBlocks.CFG_DEFAULT, + BlockBehaviour.Properties.of().strength(0.5f, 7f).sound(SoundType.STONE) + )); + Registries.addBlock("clinker_brick_stairs", () -> new StandardStairsBlock( + StandardBlocks.CFG_DEFAULT, + () -> Registries.getBlock("clinker_brick_block").defaultBlockState(), + BlockBehaviour.Properties.of().strength(0.5f, 7f).sound(SoundType.STONE) + )); + Registries.addBlock("clinker_brick_wall", () -> new EdWallBlock( + StandardBlocks.CFG_DEFAULT, + BlockBehaviour.Properties.of().strength(0.5f, 7f).sound(SoundType.STONE) + )); + Registries.addBlock("clinker_brick_stained_block", () -> new StandardBlocks.BaseBlock( + StandardBlocks.CFG_DEFAULT, + BlockBehaviour.Properties.of().strength(0.5f, 7f).sound(SoundType.STONE) + )); + Registries.addBlock("clinker_brick_stained_slab", () -> new VariantSlabBlock( + StandardBlocks.CFG_DEFAULT, + BlockBehaviour.Properties.of().strength(0.5f, 7f).sound(SoundType.STONE) + )); + Registries.addBlock("clinker_brick_stained_stairs", () -> new StandardStairsBlock( + StandardBlocks.CFG_DEFAULT, + () -> Registries.getBlock("clinker_brick_stained_block").defaultBlockState(), + BlockBehaviour.Properties.of().strength(0.5f, 7f).sound(SoundType.STONE) + )); + Registries.addBlock("clinker_brick_sastor_corner_block", () -> new EdCornerOrnamentedBlock( + StandardBlocks.CFG_DEFAULT, + BlockBehaviour.Properties.of().strength(0.5f, 7f).sound(SoundType.STONE), + new Block[]{ + Registries.getBlock("clinker_brick_block"), + Registries.getBlock("clinker_brick_slab"), + Registries.getBlock("clinker_brick_stairs"), + Registries.getBlock("clinker_brick_stained_block"), + Registries.getBlock("clinker_brick_stained_slab"), + Registries.getBlock("clinker_brick_stained_stairs") + } + )); + Registries.addBlock("clinker_brick_recessed", () -> new StandardBlocks.HorizontalWaterLoggable( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_HORIZIONTAL | StandardBlocks.CFG_LOOK_PLACEMENT, + BlockBehaviour.Properties.of().strength(0.5f, 7f).sound(SoundType.STONE), + new AABB[]{ + Auxiliaries.getPixeledAABB(3, 0, 0, 13, 16, 1), + Auxiliaries.getPixeledAABB(0, 0, 1, 16, 16, 11), + Auxiliaries.getPixeledAABB(4, 0, 11, 12, 16, 13) + } + )); + Registries.addBlock("clinker_brick_vertically_slit", () -> new StandardBlocks.HorizontalWaterLoggable( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_HORIZIONTAL | StandardBlocks.CFG_LOOK_PLACEMENT, + BlockBehaviour.Properties.of().strength(0.5f, 7f).sound(SoundType.STONE), + new AABB[]{ + Auxiliaries.getPixeledAABB(3, 0, 0, 13, 16, 1), + Auxiliaries.getPixeledAABB(3, 0, 15, 13, 16, 16), + Auxiliaries.getPixeledAABB(0, 0, 1, 16, 16, 15) + } + )); + Registries.addBlock("clinker_brick_vertical_slab_structured", () -> new StandardBlocks.HorizontalWaterLoggable( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_HORIZIONTAL | StandardBlocks.CFG_LOOK_PLACEMENT, + BlockBehaviour.Properties.of().strength(0.5f, 7f).sound(SoundType.STONE), + new AABB[]{ + Auxiliaries.getPixeledAABB(0, 0, 0, 16, 16, 8), + } + )); - public static void initBlocks() - { - Registries.addBlock("clinker_brick_block", ()->new StandardBlocks.BaseBlock( - StandardBlocks.CFG_DEFAULT, - BlockBehaviour.Properties.of(Material.STONE, MaterialColor.STONE).strength(0.5f, 7f).sound(SoundType.STONE) - )); - Registries.addBlock("clinker_brick_slab", ()->new VariantSlabBlock( - StandardBlocks.CFG_DEFAULT, - BlockBehaviour.Properties.of(Material.STONE, MaterialColor.STONE).strength(0.5f, 7f).sound(SoundType.STONE) - )); - Registries.addBlock("clinker_brick_stairs", ()->new StandardStairsBlock( - StandardBlocks.CFG_DEFAULT, - ()->Registries.getBlock("clinker_brick_block").defaultBlockState(), - BlockBehaviour.Properties.of(Material.STONE, MaterialColor.STONE).strength(0.5f, 7f).sound(SoundType.STONE) - )); - Registries.addBlock("clinker_brick_wall", ()->new EdWallBlock( - StandardBlocks.CFG_DEFAULT, - BlockBehaviour.Properties.of(Material.STONE, MaterialColor.STONE).strength(0.5f, 7f).sound(SoundType.STONE) - )); - Registries.addBlock("clinker_brick_stained_block", ()->new StandardBlocks.BaseBlock( - StandardBlocks.CFG_DEFAULT, - BlockBehaviour.Properties.of(Material.STONE, MaterialColor.STONE).strength(0.5f, 7f).sound(SoundType.STONE) - )); - Registries.addBlock("clinker_brick_stained_slab", ()->new VariantSlabBlock( - StandardBlocks.CFG_DEFAULT, - BlockBehaviour.Properties.of(Material.STONE, MaterialColor.STONE).strength(0.5f, 7f).sound(SoundType.STONE) - )); - Registries.addBlock("clinker_brick_stained_stairs", ()->new StandardStairsBlock( - StandardBlocks.CFG_DEFAULT, - ()->Registries.getBlock("clinker_brick_stained_block").defaultBlockState(), - BlockBehaviour.Properties.of(Material.STONE, MaterialColor.STONE).strength(0.5f, 7f).sound(SoundType.STONE) - )); - Registries.addBlock("clinker_brick_sastor_corner_block", ()->new EdCornerOrnamentedBlock( - StandardBlocks.CFG_DEFAULT, - BlockBehaviour.Properties.of(Material.STONE, MaterialColor.STONE).strength(0.5f, 7f).sound(SoundType.STONE), - new Block[]{ - Registries.getBlock("clinker_brick_block"), - Registries.getBlock("clinker_brick_slab"), - Registries.getBlock("clinker_brick_stairs"), - Registries.getBlock("clinker_brick_stained_block"), - Registries.getBlock("clinker_brick_stained_slab"), - Registries.getBlock("clinker_brick_stained_stairs") - } - )); - Registries.addBlock("clinker_brick_recessed", ()->new StandardBlocks.HorizontalWaterLoggable( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_HORIZIONTAL|StandardBlocks.CFG_LOOK_PLACEMENT, - BlockBehaviour.Properties.of(Material.STONE, MaterialColor.STONE).strength(0.5f, 7f).sound(SoundType.STONE), - new AABB[] { - Auxiliaries.getPixeledAABB( 3,0, 0, 13,16, 1), - Auxiliaries.getPixeledAABB( 0,0, 1, 16,16,11), - Auxiliaries.getPixeledAABB( 4,0,11, 12,16,13) - } - )); - Registries.addBlock("clinker_brick_vertically_slit", ()->new StandardBlocks.HorizontalWaterLoggable( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_HORIZIONTAL|StandardBlocks.CFG_LOOK_PLACEMENT, - BlockBehaviour.Properties.of(Material.STONE, MaterialColor.STONE).strength(0.5f, 7f).sound(SoundType.STONE), - new AABB[] { - Auxiliaries.getPixeledAABB( 3,0, 0, 13,16, 1), - Auxiliaries.getPixeledAABB( 3,0,15, 13,16,16), - Auxiliaries.getPixeledAABB( 0,0, 1, 16,16,15) - } - )); - Registries.addBlock("clinker_brick_vertical_slab_structured", ()->new StandardBlocks.HorizontalWaterLoggable( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_HORIZIONTAL|StandardBlocks.CFG_LOOK_PLACEMENT, - BlockBehaviour.Properties.of(Material.STONE, MaterialColor.STONE).strength(0.5f, 7f).sound(SoundType.STONE), - new AABB[] { - Auxiliaries.getPixeledAABB( 0,0, 0, 16,16, 8), - } - )); + // ------------------------------------------------------------------------------------------------------------------- - // ------------------------------------------------------------------------------------------------------------------- + Registries.addBlock("slag_brick_block", () -> new StandardBlocks.BaseBlock( + StandardBlocks.CFG_DEFAULT, + BlockBehaviour.Properties.of().strength(0.5f, 7f).sound(SoundType.STONE) + )); + Registries.addBlock("slag_brick_slab", () -> new VariantSlabBlock( + StandardBlocks.CFG_DEFAULT, + BlockBehaviour.Properties.of().strength(0.5f, 7f).sound(SoundType.STONE) + )); + Registries.addBlock("slag_brick_stairs", () -> new StandardStairsBlock( + StandardBlocks.CFG_DEFAULT, + () -> Registries.getBlock("slag_brick_block").defaultBlockState(), + BlockBehaviour.Properties.of().strength(0.5f, 7f).sound(SoundType.STONE) + )); + Registries.addBlock("slag_brick_wall", () -> new EdWallBlock( + StandardBlocks.CFG_DEFAULT, + BlockBehaviour.Properties.of().strength(0.5f, 7f).sound(SoundType.STONE) + )); - Registries.addBlock("slag_brick_block", ()->new StandardBlocks.BaseBlock( - StandardBlocks.CFG_DEFAULT, - BlockBehaviour.Properties.of(Material.STONE, MaterialColor.STONE).strength(0.5f, 7f).sound(SoundType.STONE) - )); - Registries.addBlock("slag_brick_slab", ()->new VariantSlabBlock( - StandardBlocks.CFG_DEFAULT, - BlockBehaviour.Properties.of(Material.STONE, MaterialColor.STONE).strength(0.5f, 7f).sound(SoundType.STONE) - )); - Registries.addBlock("slag_brick_stairs", ()->new StandardStairsBlock( - StandardBlocks.CFG_DEFAULT, - ()->Registries.getBlock("slag_brick_block").defaultBlockState(), - BlockBehaviour.Properties.of(Material.STONE, MaterialColor.STONE).strength(0.5f, 7f).sound(SoundType.STONE) - )); - Registries.addBlock("slag_brick_wall", ()->new EdWallBlock( - StandardBlocks.CFG_DEFAULT, - BlockBehaviour.Properties.of(Material.STONE, MaterialColor.STONE).strength(0.5f, 7f).sound(SoundType.STONE) - )); + // ------------------------------------------------------------------------------------------------------------------- - // ------------------------------------------------------------------------------------------------------------------- + Registries.addBlock("rebar_concrete", () -> new StandardBlocks.BaseBlock( + StandardBlocks.CFG_DEFAULT, + BlockBehaviour.Properties.of().strength(1.0f, 2000f).sound(SoundType.STONE).isValidSpawn(detail::disallowSpawn) + )); + Registries.addBlock("rebar_concrete_slab", () -> new VariantSlabBlock( + StandardBlocks.CFG_DEFAULT, + BlockBehaviour.Properties.of().strength(1.0f, 2000f).sound(SoundType.STONE).isValidSpawn(detail::disallowSpawn) + )); + Registries.addBlock("rebar_concrete_stairs", () -> new StandardStairsBlock( + StandardBlocks.CFG_DEFAULT, + () -> Registries.getBlock("rebar_concrete").defaultBlockState(), + BlockBehaviour.Properties.of().strength(1.0f, 2000f).sound(SoundType.STONE).isValidSpawn(detail::disallowSpawn) + )); + Registries.addBlock("rebar_concrete_wall", () -> new EdWallBlock( + StandardBlocks.CFG_DEFAULT, + BlockBehaviour.Properties.of().strength(1.0f, 2000f).sound(SoundType.STONE).isValidSpawn(detail::disallowSpawn) + )); + Registries.addBlock("halfslab_rebar_concrete", () -> new SlabSliceBlock( + StandardBlocks.CFG_CUTOUT, + BlockBehaviour.Properties.of().strength(1.0f, 2000f).sound(SoundType.STONE).isValidSpawn(detail::disallowSpawn) + )); - Registries.addBlock("rebar_concrete", ()->new StandardBlocks.BaseBlock( - StandardBlocks.CFG_DEFAULT, - BlockBehaviour.Properties.of(Material.STONE, MaterialColor.STONE).strength(1.0f, 2000f).sound(SoundType.STONE).isValidSpawn(detail::disallowSpawn) - )); - Registries.addBlock("rebar_concrete_slab", ()->new VariantSlabBlock( - StandardBlocks.CFG_DEFAULT, - BlockBehaviour.Properties.of(Material.STONE, MaterialColor.STONE).strength(1.0f, 2000f).sound(SoundType.STONE).isValidSpawn(detail::disallowSpawn) - )); - Registries.addBlock("rebar_concrete_stairs", ()->new StandardStairsBlock( - StandardBlocks.CFG_DEFAULT, - ()->Registries.getBlock("rebar_concrete").defaultBlockState(), - BlockBehaviour.Properties.of(Material.STONE, MaterialColor.STONE).strength(1.0f, 2000f).sound(SoundType.STONE).isValidSpawn(detail::disallowSpawn) - )); - Registries.addBlock("rebar_concrete_wall", ()->new EdWallBlock( - StandardBlocks.CFG_DEFAULT, - BlockBehaviour.Properties.of(Material.STONE, MaterialColor.STONE).strength(1.0f, 2000f).sound(SoundType.STONE).isValidSpawn(detail::disallowSpawn) - )); - Registries.addBlock("halfslab_rebar_concrete", ()->new SlabSliceBlock( - StandardBlocks.CFG_CUTOUT, - BlockBehaviour.Properties.of(Material.STONE, MaterialColor.STONE).strength(1.0f, 2000f).sound(SoundType.STONE).isValidSpawn(detail::disallowSpawn) - )); + // ------------------------------------------------------------------------------------------------------------------- - // ------------------------------------------------------------------------------------------------------------------- + Registries.addBlock("rebar_concrete_tile", () -> new StandardBlocks.BaseBlock( + StandardBlocks.CFG_DEFAULT, + BlockBehaviour.Properties.of().strength(1.0f, 2000f).sound(SoundType.STONE).isValidSpawn(detail::disallowSpawn) + )); + Registries.addBlock("rebar_concrete_tile_slab", () -> new VariantSlabBlock( + StandardBlocks.CFG_DEFAULT, + BlockBehaviour.Properties.of().strength(1.0f, 2000f).sound(SoundType.STONE).isValidSpawn(detail::disallowSpawn) + )); + Registries.addBlock("rebar_concrete_tile_stairs", () -> new StandardStairsBlock( + StandardBlocks.CFG_DEFAULT, + () -> Registries.getBlock("rebar_concrete_tile").defaultBlockState(), + BlockBehaviour.Properties.of().strength(1.0f, 2000f).sound(SoundType.STONE).isValidSpawn(detail::disallowSpawn) + )); - Registries.addBlock("rebar_concrete_tile", ()->new StandardBlocks.BaseBlock( - StandardBlocks.CFG_DEFAULT, - BlockBehaviour.Properties.of(Material.STONE, MaterialColor.STONE).strength(1.0f, 2000f).sound(SoundType.STONE).isValidSpawn(detail::disallowSpawn) - )); - Registries.addBlock("rebar_concrete_tile_slab", ()->new VariantSlabBlock( - StandardBlocks.CFG_DEFAULT, - BlockBehaviour.Properties.of(Material.STONE, MaterialColor.STONE).strength(1.0f, 2000f).sound(SoundType.STONE).isValidSpawn(detail::disallowSpawn) - )); - Registries.addBlock("rebar_concrete_tile_stairs", ()->new StandardStairsBlock( - StandardBlocks.CFG_DEFAULT, - ()->Registries.getBlock("rebar_concrete_tile").defaultBlockState(), - BlockBehaviour.Properties.of(Material.STONE, MaterialColor.STONE).strength(1.0f, 2000f).sound(SoundType.STONE).isValidSpawn(detail::disallowSpawn) - )); + // ------------------------------------------------------------------------------------------------------------------- - // ------------------------------------------------------------------------------------------------------------------- + Registries.addBlock("panzerglass_block", () -> new EdGlassBlock( + StandardBlocks.CFG_TRANSLUCENT, + BlockBehaviour.Properties.of().strength(0.5f, 2000f).sound(SoundType.METAL).noOcclusion().isValidSpawn(detail::disallowSpawn) + )); + Registries.addBlock("panzerglass_slab", () -> new VariantSlabBlock( + StandardBlocks.CFG_TRANSLUCENT, + BlockBehaviour.Properties.of().strength(0.5f, 2000f).sound(SoundType.METAL).noOcclusion().isValidSpawn(detail::disallowSpawn) + )); - Registries.addBlock("panzerglass_block", ()->new EdGlassBlock( - StandardBlocks.CFG_TRANSLUCENT, - BlockBehaviour.Properties.of(Material.GLASS, MaterialColor.NONE).strength(0.5f, 2000f).sound(SoundType.METAL).noOcclusion().isValidSpawn(detail::disallowSpawn) - )); - Registries.addBlock("panzerglass_slab", ()->new VariantSlabBlock( - StandardBlocks.CFG_TRANSLUCENT, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.5f, 2000f).sound(SoundType.METAL).noOcclusion().isValidSpawn(detail::disallowSpawn) - )); + // ------------------------------------------------------------------------------------------------------------------- - // ------------------------------------------------------------------------------------------------------------------- + Registries.addBlock("dark_shingle_roof", () -> new EdRoofBlock( + StandardBlocks.CFG_CUTOUT, + BlockBehaviour.Properties.of().strength(0.5f, 6f).sound(SoundType.STONE).noOcclusion().dynamicShape().isValidSpawn(detail::disallowSpawn) + )); + Registries.addBlock("dark_shingle_roof_metallized", () -> new EdRoofBlock( + StandardBlocks.CFG_CUTOUT, + BlockBehaviour.Properties.of().strength(0.5f, 6f).sound(SoundType.STONE).noOcclusion().dynamicShape().isValidSpawn(detail::disallowSpawn) + )); + Registries.addBlock("dark_shingle_roof_skylight", () -> new EdRoofBlock( + StandardBlocks.CFG_TRANSLUCENT, + BlockBehaviour.Properties.of().strength(0.5f, 6f).sound(SoundType.STONE).noOcclusion().dynamicShape().isValidSpawn(detail::disallowSpawn) + )); + Registries.addBlock("dark_shingle_roof_chimneytrunk", () -> new EdChimneyTrunkBlock( + StandardBlocks.CFG_CUTOUT, + BlockBehaviour.Properties.of().strength(0.5f, 6f).sound(SoundType.STONE).noOcclusion().dynamicShape().isValidSpawn(detail::disallowSpawn), + Shapes.create(Auxiliaries.getPixeledAABB(3, 0, 3, 13, 16, 13)), + Shapes.create(Auxiliaries.getPixeledAABB(5, 0, 5, 11, 16, 11)) + )); + Registries.addBlock("dark_shingle_roof_wireconduit", () -> new EdChimneyTrunkBlock( + StandardBlocks.CFG_CUTOUT, + BlockBehaviour.Properties.of().strength(0.5f, 6f).sound(SoundType.STONE).noOcclusion().dynamicShape().isValidSpawn(detail::disallowSpawn), + Shapes.join( + Shapes.create(Auxiliaries.getPixeledAABB(3, 0, 3, 13, 13, 13)), + Shapes.create(Auxiliaries.getPixeledAABB(5, 13, 5, 11, 16, 11)), + BooleanOp.OR + ), + Shapes.join( + Shapes.create(Auxiliaries.getPixeledAABB(5, 0, 5, 11, 15, 11)), + Shapes.create(Auxiliaries.getPixeledAABB(7, 15, 7, 9, 16, 9)), + BooleanOp.OR + ) + )); + Registries.addBlock("dark_shingle_roof_chimney", () -> new EdChimneyBlock( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_AI_PASSABLE, + BlockBehaviour.Properties.of().strength(0.5f, 6f).sound(SoundType.STONE).dynamicShape().isValidSpawn(detail::disallowSpawn), + Auxiliaries.getPixeledAABB(3, 0, 3, 13, 6, 13) + )); + Registries.addBlock("dark_shingle_roof_block", () -> new StandardBlocks.BaseBlock( + StandardBlocks.CFG_DEFAULT, + BlockBehaviour.Properties.of().strength(0.5f, 6f).sound(SoundType.STONE) + )); + Registries.addBlock("dark_shingle_roof_slab", () -> new VariantSlabBlock( + StandardBlocks.CFG_DEFAULT, + BlockBehaviour.Properties.of().strength(0.5f, 6f).sound(SoundType.STONE) + )); - Registries.addBlock("dark_shingle_roof", ()->new EdRoofBlock( - StandardBlocks.CFG_CUTOUT, - BlockBehaviour.Properties.of(Material.STONE, MaterialColor.STONE).strength(0.5f, 6f).sound(SoundType.STONE).noOcclusion().dynamicShape().isValidSpawn(detail::disallowSpawn) - )); - Registries.addBlock("dark_shingle_roof_metallized", ()->new EdRoofBlock( - StandardBlocks.CFG_CUTOUT, - BlockBehaviour.Properties.of(Material.STONE, MaterialColor.STONE).strength(0.5f, 6f).sound(SoundType.STONE).noOcclusion().dynamicShape().isValidSpawn(detail::disallowSpawn) - )); - Registries.addBlock("dark_shingle_roof_skylight", ()->new EdRoofBlock( - StandardBlocks.CFG_TRANSLUCENT, - BlockBehaviour.Properties.of(Material.STONE, MaterialColor.STONE).strength(0.5f, 6f).sound(SoundType.STONE).noOcclusion().dynamicShape().isValidSpawn(detail::disallowSpawn) - )); - Registries.addBlock("dark_shingle_roof_chimneytrunk", ()->new EdChimneyTrunkBlock( - StandardBlocks.CFG_CUTOUT, - BlockBehaviour.Properties.of(Material.STONE, MaterialColor.STONE).strength(0.5f, 6f).sound(SoundType.STONE).noOcclusion().dynamicShape().isValidSpawn(detail::disallowSpawn), - Shapes.create(Auxiliaries.getPixeledAABB(3, 0, 3, 13, 16, 13)), - Shapes.create(Auxiliaries.getPixeledAABB(5, 0, 5, 11, 16, 11)) - )); - Registries.addBlock("dark_shingle_roof_wireconduit", ()->new EdChimneyTrunkBlock( - StandardBlocks.CFG_CUTOUT, - BlockBehaviour.Properties.of(Material.STONE, MaterialColor.STONE).strength(0.5f, 6f).sound(SoundType.STONE).noOcclusion().dynamicShape().isValidSpawn(detail::disallowSpawn), - Shapes.join( - Shapes.create(Auxiliaries.getPixeledAABB(3, 0, 3, 13, 13, 13)), - Shapes.create(Auxiliaries.getPixeledAABB(5, 13, 5, 11, 16, 11)), - BooleanOp.OR - ), - Shapes.join( - Shapes.create(Auxiliaries.getPixeledAABB(5, 0, 5, 11, 15, 11)), - Shapes.create(Auxiliaries.getPixeledAABB(7, 15, 7, 9, 16, 9)), - BooleanOp.OR - ) - )); - Registries.addBlock("dark_shingle_roof_chimney", ()->new EdChimneyBlock( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_AI_PASSABLE, - BlockBehaviour.Properties.of(Material.STONE, MaterialColor.STONE).strength(0.5f, 6f).sound(SoundType.STONE).dynamicShape().isValidSpawn(detail::disallowSpawn), - Auxiliaries.getPixeledAABB(3, 0, 3, 13, 6, 13) - )); - Registries.addBlock("dark_shingle_roof_block", ()->new StandardBlocks.BaseBlock( - StandardBlocks.CFG_DEFAULT, - BlockBehaviour.Properties.of(Material.STONE, MaterialColor.STONE).strength(0.5f, 6f).sound(SoundType.STONE) - )); - Registries.addBlock("dark_shingle_roof_slab", ()->new VariantSlabBlock( - StandardBlocks.CFG_DEFAULT, - BlockBehaviour.Properties.of(Material.STONE, MaterialColor.STONE).strength(0.5f, 6f).sound(SoundType.STONE) - )); + // ------------------------------------------------------------------------------------------------------------------- - // ------------------------------------------------------------------------------------------------------------------- + Registries.addBlock("dense_grit_sand_block", () -> new StandardBlocks.BaseBlock( + StandardBlocks.CFG_DEFAULT, + BlockBehaviour.Properties.of().strength(0.1f, 3f).sound(SoundType.GRAVEL) + )); + Registries.addBlock("dense_grit_dirt_block", () -> new StandardBlocks.BaseBlock( + StandardBlocks.CFG_DEFAULT, + BlockBehaviour.Properties.of().strength(0.1f, 3f).sound(SoundType.GRAVEL) + )); + Registries.addBlock("dark_shingle_roof_slabslice", () -> new SlabSliceBlock( + StandardBlocks.CFG_DEFAULT, + BlockBehaviour.Properties.of().strength(0.5f, 6f).sound(SoundType.STONE) + )); - Registries.addBlock("dense_grit_sand_block", ()->new StandardBlocks.BaseBlock( - StandardBlocks.CFG_DEFAULT, - BlockBehaviour.Properties.of(Material.DIRT, MaterialColor.DIRT).strength(0.1f, 3f).sound(SoundType.GRAVEL) - )); - Registries.addBlock("dense_grit_dirt_block", ()->new StandardBlocks.BaseBlock( - StandardBlocks.CFG_DEFAULT, - BlockBehaviour.Properties.of(Material.DIRT, MaterialColor.DIRT).strength(0.1f, 3f).sound(SoundType.GRAVEL) - )); - Registries.addBlock("dark_shingle_roof_slabslice", ()->new SlabSliceBlock( - StandardBlocks.CFG_DEFAULT, - BlockBehaviour.Properties.of(Material.STONE, MaterialColor.STONE).strength(0.5f, 6f).sound(SoundType.STONE) - )); + // ------------------------------------------------------------------------------------------------------------------- - // ------------------------------------------------------------------------------------------------------------------- + Registries.addBlock("metal_rung_ladder", () -> new EdLadderBlock( + StandardBlocks.CFG_DEFAULT, + BlockBehaviour.Properties.of().strength(0.3f, 8f).sound(SoundType.METAL).noOcclusion() + )); + Registries.addBlock("metal_rung_steps", () -> new EdLadderBlock( + StandardBlocks.CFG_DEFAULT, + BlockBehaviour.Properties.of().strength(0.3f, 8f).sound(SoundType.METAL).noOcclusion() + )); + Registries.addBlock("iron_hatch", () -> new EdHatchBlock( + StandardBlocks.CFG_LOOK_PLACEMENT, + BlockBehaviour.Properties.of().strength(0.3f, 2000f).sound(SoundType.METAL), + new AABB[]{Auxiliaries.getPixeledAABB(0, 0, 0, 16, 3, 16)}, + new AABB[]{ + Auxiliaries.getPixeledAABB(0, 0, 0, 16, 16, 3), + Auxiliaries.getPixeledAABB(0, 0, 3, 1, 1, 16), + Auxiliaries.getPixeledAABB(15, 0, 3, 16, 1, 16), + Auxiliaries.getPixeledAABB(1, 0, 14, 15, 1, 16) + } + )); + Registries.addBlock("metal_sliding_door", () -> new StandardDoorBlock( + StandardBlocks.CFG_TRANSLUCENT | StandardBlocks.CFG_HORIZIONTAL, + BlockBehaviour.Properties.of().strength(0.5f, 8f).sound(SoundType.METAL).noOcclusion() + )); - Registries.addBlock("metal_rung_ladder", ()->new EdLadderBlock( - StandardBlocks.CFG_DEFAULT, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.3f, 8f).sound(SoundType.METAL).noOcclusion() - )); - Registries.addBlock("metal_rung_steps", ()->new EdLadderBlock( - StandardBlocks.CFG_DEFAULT, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.3f, 8f).sound(SoundType.METAL).noOcclusion() - )); - Registries.addBlock("iron_hatch", ()->new EdHatchBlock( - StandardBlocks.CFG_LOOK_PLACEMENT, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.3f, 2000f).sound(SoundType.METAL), - new AABB[] { Auxiliaries.getPixeledAABB(0,0,0, 16,3,16) }, - new AABB[] { - Auxiliaries.getPixeledAABB( 0,0, 0, 16,16, 3), - Auxiliaries.getPixeledAABB( 0,0, 3, 1, 1,16), - Auxiliaries.getPixeledAABB(15,0, 3, 16, 1,16), - Auxiliaries.getPixeledAABB( 1,0,14, 15, 1,16) - } - )); - Registries.addBlock("metal_sliding_door", ()->new StandardDoorBlock( - StandardBlocks.CFG_TRANSLUCENT|StandardBlocks.CFG_HORIZIONTAL, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.5f, 8f).sound(SoundType.METAL).noOcclusion(), - new AABB[]{ - Auxiliaries.getPixeledAABB(15, 0.0,6, 16,16.0,10), - Auxiliaries.getPixeledAABB( 0,15.5,6, 16,16.0,10), - }, - new AABB[]{ - Auxiliaries.getPixeledAABB(15, 0.0,6, 16,16.0,10), - Auxiliaries.getPixeledAABB( 0, 0.0,6, 16, 0.3,10), - }, - new AABB[]{ - Auxiliaries.getPixeledAABB( 0,0,7, 16,16,9) - }, - new AABB[]{ - Auxiliaries.getPixeledAABB( 0,0,7, 16,16,9) - }, - SoundEvents.IRON_DOOR_OPEN, SoundEvents.IRON_DOOR_CLOSE - )); + // ------------------------------------------------------------------------------------------------------------------- - // ------------------------------------------------------------------------------------------------------------------- + Registries.addBlock("old_industrial_wood_planks", () -> new StandardBlocks.BaseBlock( + StandardBlocks.CFG_DEFAULT, + BlockBehaviour.Properties.of().strength(0.5f, 6f).sound(SoundType.WOOD) + )); + Registries.addBlock("old_industrial_wood_slab", () -> new VariantSlabBlock( + StandardBlocks.CFG_DEFAULT, + BlockBehaviour.Properties.of().strength(0.5f, 6f).sound(SoundType.WOOD) + )); + Registries.addBlock("old_industrial_wood_stairs", () -> new StandardStairsBlock( + StandardBlocks.CFG_DEFAULT, + () -> Registries.getBlock("old_industrial_wood_planks").defaultBlockState(), + BlockBehaviour.Properties.of().strength(0.5f, 6f).sound(SoundType.WOOD) + )); + Registries.addBlock("old_industrial_wood_slabslice", () -> new SlabSliceBlock( + StandardBlocks.CFG_CUTOUT, + BlockBehaviour.Properties.of().strength(0.5f, 6f).sound(SoundType.WOOD).noOcclusion() + )); + Registries.addBlock("old_industrial_wood_door", () -> new StandardDoorBlock( + StandardBlocks.CFG_DEFAULT, + BlockBehaviour.Properties.of().strength(0.5f, 6f).sound(SoundType.WOOD).noOcclusion() + )); - Registries.addBlock("old_industrial_wood_planks", ()->new StandardBlocks.BaseBlock( - StandardBlocks.CFG_DEFAULT, - BlockBehaviour.Properties.of(Material.WOOD, MaterialColor.WOOD).strength(0.5f, 6f).sound(SoundType.WOOD) - )); - Registries.addBlock("old_industrial_wood_slab", ()->new VariantSlabBlock( - StandardBlocks.CFG_DEFAULT, - BlockBehaviour.Properties.of(Material.WOOD, MaterialColor.WOOD).strength(0.5f, 6f).sound(SoundType.WOOD) - )); - Registries.addBlock("old_industrial_wood_stairs", ()->new StandardStairsBlock( - StandardBlocks.CFG_DEFAULT, - ()->Registries.getBlock("old_industrial_wood_planks").defaultBlockState(), - BlockBehaviour.Properties.of(Material.WOOD, MaterialColor.WOOD).strength(0.5f, 6f).sound(SoundType.WOOD) - )); - Registries.addBlock("old_industrial_wood_slabslice", ()->new SlabSliceBlock( - StandardBlocks.CFG_CUTOUT, - BlockBehaviour.Properties.of(Material.WOOD, MaterialColor.WOOD).strength(0.5f, 6f).sound(SoundType.WOOD).noOcclusion() - )); - Registries.addBlock("old_industrial_wood_door", ()->new StandardDoorBlock( - StandardBlocks.CFG_DEFAULT, - BlockBehaviour.Properties.of(Material.WOOD, MaterialColor.WOOD).strength(0.5f, 6f).sound(SoundType.WOOD).noOcclusion(), - Auxiliaries.getPixeledAABB(15,0, 0, 16,16,16), - Auxiliaries.getPixeledAABB( 0,0,13, 16,16,16), - SoundEvents.WOODEN_DOOR_OPEN, SoundEvents.WOODEN_DOOR_CLOSE - )); + // ------------------------------------------------------------------------------------------------------------------- - // ------------------------------------------------------------------------------------------------------------------- + Registries.addBlock("treated_wood_stool", () -> new EdChair.ChairBlock( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_HORIZIONTAL | StandardBlocks.CFG_LOOK_PLACEMENT, + BlockBehaviour.Properties.of().strength(0.5f, 5f).sound(SoundType.WOOD).noOcclusion(), + new AABB[]{ + Auxiliaries.getPixeledAABB(4, 7, 4, 12, 8.8, 12), + Auxiliaries.getPixeledAABB(7, 0, 7, 9, 7, 9), + Auxiliaries.getPixeledAABB(4, 0, 7, 12, 1, 9), + Auxiliaries.getPixeledAABB(7, 0, 4, 9, 1, 12), + } + )); - Registries.addBlock("treated_wood_stool", ()->new EdChair.ChairBlock( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_HORIZIONTAL|StandardBlocks.CFG_LOOK_PLACEMENT, - BlockBehaviour.Properties.of(Material.WOOD, MaterialColor.WOOD).strength(0.5f, 5f).sound(SoundType.WOOD).noOcclusion(), - new AABB[]{ - Auxiliaries.getPixeledAABB(4,7,4, 12,8.8,12), - Auxiliaries.getPixeledAABB(7,0,7, 9,7,9), - Auxiliaries.getPixeledAABB(4,0,7, 12,1,9), - Auxiliaries.getPixeledAABB(7,0,4, 9,1,12), - } - )); + // ------------------------------------------------------------------------------------------------------------------- - // ------------------------------------------------------------------------------------------------------------------- + Registries.addBlock("iron_inset_light", () -> new StandardBlocks.DirectedWaterLoggable( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_FACING_PLACEMENT | StandardBlocks.CFG_OPPOSITE_PLACEMENT | StandardBlocks.CFG_AI_PASSABLE, + BlockBehaviour.Properties.of().strength(0.5f, 8f).sound(SoundType.METAL).lightLevel((state) -> 15).noOcclusion(), + new AABB[]{ + Auxiliaries.getPixeledAABB(5, 7, 0, 11, 9, 0.375), + Auxiliaries.getPixeledAABB(6, 6, 0, 10, 10, 0.375), + Auxiliaries.getPixeledAABB(7, 5, 0, 9, 11, 0.375) + } + )); + Registries.addBlock("iron_floor_edge_light", () -> new StandardBlocks.DirectedWaterLoggable( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_LOOK_PLACEMENT | StandardBlocks.CFG_HORIZIONTAL | StandardBlocks.CFG_AI_PASSABLE, + BlockBehaviour.Properties.of().strength(0.5f, 8f).sound(SoundType.METAL).lightLevel((state) -> 15).noOcclusion(), + Auxiliaries.getPixeledAABB(5, 0, 0, 11, 1.8125, 0.375) + )); + Registries.addBlock("iron_ceiling_edge_light", () -> new StandardBlocks.DirectedWaterLoggable( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_LOOK_PLACEMENT | StandardBlocks.CFG_HORIZIONTAL | StandardBlocks.CFG_AI_PASSABLE, + BlockBehaviour.Properties.of().strength(0.5f, 8f).sound(SoundType.METAL).lightLevel((state) -> 15).noOcclusion(), + new AABB[]{ + Auxiliaries.getPixeledAABB(0, 15.5, 0, 16, 16, 2.0), + Auxiliaries.getPixeledAABB(0, 14.0, 0, 16, 16, 0.5), + Auxiliaries.getPixeledAABB(0, 14.0, 0, 1, 16, 2.0), + Auxiliaries.getPixeledAABB(15, 14.0, 0, 16, 16, 2.0), + } + )); + Registries.addBlock("iron_bulb_light", () -> new StandardBlocks.DirectedWaterLoggable( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_FACING_PLACEMENT | StandardBlocks.CFG_OPPOSITE_PLACEMENT | StandardBlocks.CFG_AI_PASSABLE, + BlockBehaviour.Properties.of().strength(0.5f, 8f).sound(SoundType.METAL).lightLevel((state) -> 15).noOcclusion(), + new AABB[]{ + Auxiliaries.getPixeledAABB(6.5, 6.5, 1, 9.5, 9.5, 4), + Auxiliaries.getPixeledAABB(6.0, 6.0, 0, 10.0, 10.0, 1.0) + } + )); - Registries.addBlock("iron_inset_light", ()->new StandardBlocks.DirectedWaterLoggable( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_FACING_PLACEMENT|StandardBlocks.CFG_OPPOSITE_PLACEMENT|StandardBlocks.CFG_AI_PASSABLE, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.5f, 8f).sound(SoundType.METAL).lightLevel((state)->15).noOcclusion(), - new AABB[] { - Auxiliaries.getPixeledAABB( 5,7,0, 11, 9,0.375), - Auxiliaries.getPixeledAABB( 6,6,0, 10,10,0.375), - Auxiliaries.getPixeledAABB( 7,5,0, 9,11,0.375) - } - )); - Registries.addBlock("iron_floor_edge_light", ()->new StandardBlocks.DirectedWaterLoggable( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_LOOK_PLACEMENT|StandardBlocks.CFG_HORIZIONTAL|StandardBlocks.CFG_AI_PASSABLE, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.5f, 8f).sound(SoundType.METAL).lightLevel((state)->15).noOcclusion(), - Auxiliaries.getPixeledAABB(5,0,0, 11,1.8125,0.375) - )); - Registries.addBlock("iron_ceiling_edge_light", ()->new StandardBlocks.DirectedWaterLoggable( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_LOOK_PLACEMENT|StandardBlocks.CFG_HORIZIONTAL|StandardBlocks.CFG_AI_PASSABLE, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.5f, 8f).sound(SoundType.METAL).lightLevel((state)->15).noOcclusion(), - new AABB[]{ - Auxiliaries.getPixeledAABB( 0,15.5,0, 16,16,2.0), - Auxiliaries.getPixeledAABB( 0,14.0,0, 16,16,0.5), - Auxiliaries.getPixeledAABB( 0,14.0,0, 1,16,2.0), - Auxiliaries.getPixeledAABB(15,14.0,0, 16,16,2.0), - } - )); - Registries.addBlock("iron_bulb_light", ()->new StandardBlocks.DirectedWaterLoggable( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_FACING_PLACEMENT|StandardBlocks.CFG_OPPOSITE_PLACEMENT|StandardBlocks.CFG_AI_PASSABLE, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.5f, 8f).sound(SoundType.METAL).lightLevel((state)->15).noOcclusion(), - new AABB[]{ - Auxiliaries.getPixeledAABB(6.5,6.5,1, 9.5,9.5,4), - Auxiliaries.getPixeledAABB(6.0,6.0,0, 10.0,10.0,1.0) - } - )); + // ------------------------------------------------------------------------------------------------------------------- - // ------------------------------------------------------------------------------------------------------------------- + Registries.addBlock("steel_table", () -> new StandardBlocks.WaterLoggable( + StandardBlocks.CFG_CUTOUT, + BlockBehaviour.Properties.of().strength(0.5f, 8f).sound(SoundType.METAL).noOcclusion(), + Auxiliaries.getPixeledAABB(0, 0, 0, 16, 16, 16) + )); + Registries.addBlock("steel_floor_grating", () -> new EdFloorGratingBlock( + StandardBlocks.CFG_CUTOUT, + BlockBehaviour.Properties.of().strength(0.5f, 8f).sound(SoundType.METAL).noOcclusion(), + Auxiliaries.getPixeledAABB(0, 14, 0, 16, 15.5, 16) + )); - Registries.addBlock("steel_table", ()->new StandardBlocks.WaterLoggable( - StandardBlocks.CFG_CUTOUT, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.5f, 8f).sound(SoundType.METAL).noOcclusion(), - Auxiliaries.getPixeledAABB(0,0,0, 16,16,16) - )); - Registries.addBlock("steel_floor_grating", ()->new EdFloorGratingBlock( - StandardBlocks.CFG_CUTOUT, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.5f, 8f).sound(SoundType.METAL).noOcclusion(), - Auxiliaries.getPixeledAABB(0,14,0, 16,15.5,16) - )); + // ------------------------------------------------------------------------------------------------------------------- - // ------------------------------------------------------------------------------------------------------------------- + Registries.addBlock("steel_framed_window", () -> new EdWindowBlock( + StandardBlocks.CFG_LOOK_PLACEMENT, + BlockBehaviour.Properties.of().strength(0.5f, 8f).sound(SoundType.METAL).noOcclusion(), + Auxiliaries.getPixeledAABB(0, 0, 7.5, 16, 16, 8.5) + )); - Registries.addBlock("steel_framed_window", ()->new EdWindowBlock( - StandardBlocks.CFG_LOOK_PLACEMENT, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.5f, 8f).sound(SoundType.METAL).noOcclusion(), - Auxiliaries.getPixeledAABB(0,0,7.5, 16,16,8.5) - )); + // ------------------------------------------------------------------------------------------------------------------- - // ------------------------------------------------------------------------------------------------------------------- + Registries.addBlock("treated_wood_pole", () -> new EdStraightPoleBlock( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_FACING_PLACEMENT | StandardBlocks.CFG_FLIP_PLACEMENT_IF_SAME, + BlockBehaviour.Properties.of().strength(0.5f, 5f).sound(SoundType.WOOD).noOcclusion(), + Auxiliaries.getPixeledAABB(5.8, 5.8, 0, 10.2, 10.2, 16), + null + )); + Registries.addBlock("treated_wood_pole_head", () -> new EdStraightPoleBlock( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_FACING_PLACEMENT | StandardBlocks.CFG_FLIP_PLACEMENT_IF_SAME, + BlockBehaviour.Properties.of().strength(0.5f, 5f).sound(SoundType.WOOD).noOcclusion(), + Auxiliaries.getPixeledAABB(5.8, 5.8, 0, 10.2, 10.2, 16), + (EdStraightPoleBlock) Registries.getBlock("treated_wood_pole") // TREATED_WOOD_POLE + )); + Registries.addBlock("treated_wood_pole_support", () -> new EdStraightPoleBlock( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_FACING_PLACEMENT | StandardBlocks.CFG_FLIP_PLACEMENT_IF_SAME, + BlockBehaviour.Properties.of().strength(0.5f, 5f).sound(SoundType.WOOD).noOcclusion(), + Auxiliaries.getPixeledAABB(5.8, 5.8, 0, 10.2, 10.2, 16), + (EdStraightPoleBlock) Registries.getBlock("treated_wood_pole") // TREATED_WOOD_POLE + )); + Registries.addBlock("thin_steel_pole", () -> new EdStraightPoleBlock( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_FACING_PLACEMENT, + BlockBehaviour.Properties.of().strength(0.5f, 11f).sound(SoundType.METAL).noOcclusion(), + Auxiliaries.getPixeledAABB(6, 6, 0, 10, 10, 16), + null + )); + Registries.addBlock("thin_steel_pole_head", () -> new EdStraightPoleBlock( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_FACING_PLACEMENT | StandardBlocks.CFG_FLIP_PLACEMENT_IF_SAME, + BlockBehaviour.Properties.of().strength(0.5f, 11f).sound(SoundType.METAL).noOcclusion(), + Auxiliaries.getPixeledAABB(6, 6, 0, 10, 10, 16), + (EdStraightPoleBlock) Registries.getBlock("thin_steel_pole") // THIN_STEEL_POLE + )); + Registries.addBlock("thick_steel_pole", () -> new EdStraightPoleBlock( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_FACING_PLACEMENT, + BlockBehaviour.Properties.of().strength(0.5f, 11f).sound(SoundType.METAL).noOcclusion(), + Auxiliaries.getPixeledAABB(5, 5, 0, 11, 11, 16), + null + )); + Registries.addBlock("thick_steel_pole_head", () -> new EdStraightPoleBlock( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_FACING_PLACEMENT | StandardBlocks.CFG_FLIP_PLACEMENT_IF_SAME, + BlockBehaviour.Properties.of().strength(0.5f, 11f).sound(SoundType.METAL).noOcclusion(), + Auxiliaries.getPixeledAABB(5, 5, 0, 11, 11, 16), + (EdStraightPoleBlock) Registries.getBlock("thin_steel_pole") + )); + Registries.addBlock("steel_double_t_support", () -> new EdHorizontalSupportBlock( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_HORIZIONTAL | StandardBlocks.CFG_LOOK_PLACEMENT, + BlockBehaviour.Properties.of().strength(0.5f, 11f).sound(SoundType.METAL).noOcclusion(), + Auxiliaries.getPixeledAABB(5, 11, 0, 11, 16, 16), // main beam + Auxiliaries.getPixeledAABB(10, 11, 5, 16, 16, 11), // east beam (also for west 180deg) + Auxiliaries.getPixeledAABB(6, 0, 6, 10, 16, 10), // down thin + Auxiliaries.getPixeledAABB(5, 0, 5, 11, 16, 11) // down thick + )); - Registries.addBlock("treated_wood_pole", ()->new EdStraightPoleBlock( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_FACING_PLACEMENT|StandardBlocks.CFG_FLIP_PLACEMENT_IF_SAME, - BlockBehaviour.Properties.of(Material.WOOD, MaterialColor.WOOD).strength(0.5f, 5f).sound(SoundType.WOOD).noOcclusion(), - Auxiliaries.getPixeledAABB(5.8,5.8,0, 10.2,10.2,16), - null - )); - Registries.addBlock("treated_wood_pole_head", ()->new EdStraightPoleBlock( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_FACING_PLACEMENT|StandardBlocks.CFG_FLIP_PLACEMENT_IF_SAME, - BlockBehaviour.Properties.of(Material.WOOD, MaterialColor.WOOD).strength(0.5f, 5f).sound(SoundType.WOOD).noOcclusion(), - Auxiliaries.getPixeledAABB(5.8,5.8,0, 10.2,10.2,16), - (EdStraightPoleBlock)Registries.getBlock("treated_wood_pole") // TREATED_WOOD_POLE - )); - Registries.addBlock("treated_wood_pole_support", ()->new EdStraightPoleBlock( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_FACING_PLACEMENT|StandardBlocks.CFG_FLIP_PLACEMENT_IF_SAME, - BlockBehaviour.Properties.of(Material.WOOD, MaterialColor.WOOD).strength(0.5f, 5f).sound(SoundType.WOOD).noOcclusion(), - Auxiliaries.getPixeledAABB(5.8,5.8,0, 10.2,10.2,16), - (EdStraightPoleBlock)Registries.getBlock("treated_wood_pole") // TREATED_WOOD_POLE - )); - Registries.addBlock("thin_steel_pole", ()->new EdStraightPoleBlock( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_FACING_PLACEMENT, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.5f, 11f).sound(SoundType.METAL).noOcclusion(), - Auxiliaries.getPixeledAABB(6,6,0, 10,10,16), - null - )); - Registries.addBlock("thin_steel_pole_head", ()->new EdStraightPoleBlock( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_FACING_PLACEMENT|StandardBlocks.CFG_FLIP_PLACEMENT_IF_SAME, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.5f, 11f).sound(SoundType.METAL).noOcclusion(), - Auxiliaries.getPixeledAABB(6,6,0, 10,10,16), - (EdStraightPoleBlock)Registries.getBlock("thin_steel_pole") // THIN_STEEL_POLE - )); - Registries.addBlock("thick_steel_pole", ()->new EdStraightPoleBlock( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_FACING_PLACEMENT, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.5f, 11f).sound(SoundType.METAL).noOcclusion(), - Auxiliaries.getPixeledAABB(5,5,0, 11,11,16), - null - )); - Registries.addBlock("thick_steel_pole_head", ()->new EdStraightPoleBlock( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_FACING_PLACEMENT|StandardBlocks.CFG_FLIP_PLACEMENT_IF_SAME, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.5f, 11f).sound(SoundType.METAL).noOcclusion(), - Auxiliaries.getPixeledAABB(5,5,0, 11,11,16), - (EdStraightPoleBlock)Registries.getBlock("thin_steel_pole") - )); - Registries.addBlock("steel_double_t_support", ()->new EdHorizontalSupportBlock( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_HORIZIONTAL|StandardBlocks.CFG_LOOK_PLACEMENT, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.5f, 11f).sound(SoundType.METAL).noOcclusion(), - Auxiliaries.getPixeledAABB( 5,11,0, 11,16,16), // main beam - Auxiliaries.getPixeledAABB(10,11,5, 16,16,11), // east beam (also for west 180deg) - Auxiliaries.getPixeledAABB( 6, 0,6, 10,16,10), // down thin - Auxiliaries.getPixeledAABB( 5, 0,5, 11,16,11) // down thick - )); + // ------------------------------------------------------------------------------------------------------------------- - // ------------------------------------------------------------------------------------------------------------------- + Registries.addBlock("steel_mesh_fence", () -> new EdFenceBlock( + StandardBlocks.CFG_CUTOUT, + BlockBehaviour.Properties.of().strength(0.3f, 10f).sound(SoundType.METAL).noOcclusion(), + 1.5, 16, 0.25, 0, 16, 16 + )); + Registries.addBlock("steel_mesh_fence_gate", () -> new EdDoubleGateBlock( + StandardBlocks.CFG_CUTOUT, + BlockBehaviour.Properties.of().strength(0.3f, 10f).sound(SoundType.METAL).noOcclusion(), + Auxiliaries.getPixeledAABB(0, 0, 6.5, 16, 16, 9.5) + )); + Registries.addBlock("steel_railing", () -> new EdgeAlignedRailingBlock( + StandardBlocks.CFG_CUTOUT, + BlockBehaviour.Properties.of().strength(0.3f, 10f).sound(SoundType.METAL).noOcclusion(), + Auxiliaries.getPixeledAABB(0, 0, 0, 0, 0, 0), + Auxiliaries.getPixeledAABB(0, 0, 0, 16, 15.9, 1) + )); + Registries.addBlock("steel_catwalk", () -> new EdCatwalkBlock( + StandardBlocks.CFG_CUTOUT, + BlockBehaviour.Properties.of().strength(0.3f, 10f).sound(SoundType.METAL).noOcclusion(), + Auxiliaries.getPixeledAABB(0, 0, 0, 16, 2, 16), + Auxiliaries.getPixeledAABB(0, 0, 0, 16, 15.9, 1), + Registries.getBlock("steel_railing") + )); + Registries.addBlock("steel_catwalk_ta", () -> new EdCatwalkTopAlignedBlock( + StandardBlocks.CFG_CUTOUT, + BlockBehaviour.Properties.of().strength(0.3f, 10f).sound(SoundType.METAL).noOcclusion(), + new VoxelShape[]{ + Shapes.create(Auxiliaries.getPixeledAABB(0, 14, 0, 16, 16, 16)), // only base + Auxiliaries.getUnionShape( // base with thick pole + Auxiliaries.getPixeledAABB(0, 14, 0, 16, 16, 16), + Auxiliaries.getPixeledAABB(5, 0, 5, 11, 15, 11) + ), + Auxiliaries.getUnionShape( // base with thin pole + Auxiliaries.getPixeledAABB(0, 14, 0, 16, 16, 16), + Auxiliaries.getPixeledAABB(6, 0, 6, 10, 15, 10) + ), + Auxiliaries.getUnionShape( // structure frame-like + Auxiliaries.getPixeledAABB(0, 0, 0, 16, 2, 16), + Auxiliaries.getPixeledAABB(0, 14, 0, 16, 16, 16), + Auxiliaries.getPixeledAABB(0, 0, 0, 1, 16, 1), + Auxiliaries.getPixeledAABB(15, 0, 0, 16, 16, 1), + Auxiliaries.getPixeledAABB(15, 0, 15, 16, 16, 16), + Auxiliaries.getPixeledAABB(0, 0, 15, 1, 16, 16) + ), + Auxiliaries.getUnionShape( // base with inset light + Auxiliaries.getPixeledAABB(0, 14, 0, 16, 16, 16) + ) + }, + Registries.getBlock("iron_inset_light") + )); + Registries.addBlock("steel_catwalk_stairs", () -> new EdCatwalkStairsBlock( + StandardBlocks.CFG_CUTOUT, + BlockBehaviour.Properties.of().strength(0.3f, 10f).sound(SoundType.METAL).noOcclusion(), + new AABB[]{ // base + Auxiliaries.getPixeledAABB(1, 2, 8, 15, 4, 16), + Auxiliaries.getPixeledAABB(1, 10, 0, 15, 12, 8), + }, + new AABB[]{ // railing left + Auxiliaries.getPixeledAABB(0.4, 0, 15, 0.6, 15, 16), + Auxiliaries.getPixeledAABB(0.4, 1, 14, 0.6, 16, 15), + Auxiliaries.getPixeledAABB(0.4, 2, 13, 0.6, 17, 14), + Auxiliaries.getPixeledAABB(0.4, 3, 12, 0.6, 18, 13), + Auxiliaries.getPixeledAABB(0.4, 4, 11, 0.6, 19, 12), + Auxiliaries.getPixeledAABB(0.4, 5, 10, 0.6, 20, 11), + Auxiliaries.getPixeledAABB(0.4, 6, 9, 0.6, 21, 10), + Auxiliaries.getPixeledAABB(0.4, 7, 8, 0.6, 22, 9), + Auxiliaries.getPixeledAABB(0.4, 8, 7, 0.6, 23, 8), + Auxiliaries.getPixeledAABB(0.4, 9, 6, 0.6, 24, 7), + Auxiliaries.getPixeledAABB(0.4, 10, 5, 0.6, 25, 6), + Auxiliaries.getPixeledAABB(0.4, 11, 4, 0.6, 26, 5), + Auxiliaries.getPixeledAABB(0.4, 12, 3, 0.6, 27, 4), + Auxiliaries.getPixeledAABB(0.4, 13, 2, 0.6, 28, 3), + Auxiliaries.getPixeledAABB(0.4, 14, 1, 0.6, 29, 2), + Auxiliaries.getPixeledAABB(0.4, 15, 0, 0.6, 30, 1) + } + )); - Registries.addBlock("steel_mesh_fence", ()->new EdFenceBlock( - StandardBlocks.CFG_CUTOUT, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.3f, 10f).sound(SoundType.METAL).noOcclusion(), - 1.5, 16, 0.25, 0, 16, 16 - )); - Registries.addBlock("steel_mesh_fence_gate", ()->new EdDoubleGateBlock( - StandardBlocks.CFG_CUTOUT, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.3f, 10f).sound(SoundType.METAL).noOcclusion(), - Auxiliaries.getPixeledAABB(0,0,6.5, 16,16,9.5) - )); - Registries.addBlock("steel_railing", ()->new EdgeAlignedRailingBlock( - StandardBlocks.CFG_CUTOUT, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.3f, 10f).sound(SoundType.METAL).noOcclusion(), - Auxiliaries.getPixeledAABB(0,0,0, 0, 0,0), - Auxiliaries.getPixeledAABB(0,0,0, 16,15.9,1) - )); - Registries.addBlock("steel_catwalk", ()->new EdCatwalkBlock( - StandardBlocks.CFG_CUTOUT, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.3f, 10f).sound(SoundType.METAL).noOcclusion(), - Auxiliaries.getPixeledAABB(0,0,0, 16, 2,16), - Auxiliaries.getPixeledAABB(0,0,0, 16,15.9, 1), - Registries.getBlock("steel_railing") - )); - Registries.addBlock("steel_catwalk_ta", ()->new EdCatwalkTopAlignedBlock( - StandardBlocks.CFG_CUTOUT, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.3f, 10f).sound(SoundType.METAL).noOcclusion(), - new VoxelShape[]{ - Shapes.create(Auxiliaries.getPixeledAABB(0,14,0, 16, 16,16)), // only base - Auxiliaries.getUnionShape( // base with thick pole - Auxiliaries.getPixeledAABB(0,14,0, 16, 16,16), - Auxiliaries.getPixeledAABB(5, 0,5, 11,15, 11) - ), - Auxiliaries.getUnionShape( // base with thin pole - Auxiliaries.getPixeledAABB(0,14,0, 16, 16,16), - Auxiliaries.getPixeledAABB(6, 0,6, 10,15, 10) - ), - Auxiliaries.getUnionShape( // structure frame-like - Auxiliaries.getPixeledAABB( 0, 0, 0, 16, 2,16), - Auxiliaries.getPixeledAABB( 0,14, 0, 16, 16,16), - Auxiliaries.getPixeledAABB( 0, 0, 0, 1, 16, 1), - Auxiliaries.getPixeledAABB(15, 0, 0, 16, 16, 1), - Auxiliaries.getPixeledAABB(15, 0,15, 16, 16,16), - Auxiliaries.getPixeledAABB( 0, 0,15, 1, 16,16) - ), - Auxiliaries.getUnionShape( // base with inset light - Auxiliaries.getPixeledAABB( 0,14,0, 16,16,16) - ) - }, - Registries.getBlock("iron_inset_light") - )); - Registries.addBlock("steel_catwalk_stairs", ()->new EdCatwalkStairsBlock( - StandardBlocks.CFG_CUTOUT, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.3f, 10f).sound(SoundType.METAL).noOcclusion(), - new AABB[] { // base - Auxiliaries.getPixeledAABB( 1, 2, 8, 15, 4, 16), - Auxiliaries.getPixeledAABB( 1,10, 0, 15, 12, 8), - }, - new AABB[] { // railing left - Auxiliaries.getPixeledAABB(0.4, 0, 15, 0.6, 15, 16), - Auxiliaries.getPixeledAABB(0.4, 1, 14, 0.6, 16, 15), - Auxiliaries.getPixeledAABB(0.4, 2, 13, 0.6, 17, 14), - Auxiliaries.getPixeledAABB(0.4, 3, 12, 0.6, 18, 13), - Auxiliaries.getPixeledAABB(0.4, 4, 11, 0.6, 19, 12), - Auxiliaries.getPixeledAABB(0.4, 5, 10, 0.6, 20, 11), - Auxiliaries.getPixeledAABB(0.4, 6, 9, 0.6, 21, 10), - Auxiliaries.getPixeledAABB(0.4, 7, 8, 0.6, 22, 9), - Auxiliaries.getPixeledAABB(0.4, 8, 7, 0.6, 23, 8), - Auxiliaries.getPixeledAABB(0.4, 9, 6, 0.6, 24, 7), - Auxiliaries.getPixeledAABB(0.4, 10, 5, 0.6, 25, 6), - Auxiliaries.getPixeledAABB(0.4, 11, 4, 0.6, 26, 5), - Auxiliaries.getPixeledAABB(0.4, 12, 3, 0.6, 27, 4), - Auxiliaries.getPixeledAABB(0.4, 13, 2, 0.6, 28, 3), - Auxiliaries.getPixeledAABB(0.4, 14, 1, 0.6, 29, 2), - Auxiliaries.getPixeledAABB(0.4, 15, 0, 0.6, 30, 1) - } - )); + // ------------------------------------------------------------------------------------------------------------------- - // ------------------------------------------------------------------------------------------------------------------- + Registries.addBlock("sign_decor", () -> new StandardBlocks.DirectedWaterLoggable( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_FACING_PLACEMENT | StandardBlocks.CFG_HORIZIONTAL | StandardBlocks.CFG_AI_PASSABLE, + BlockBehaviour.Properties.of().strength(0.3f, 1000f).sound(SoundType.WOOD).lightLevel((state) -> 1).noOcclusion(), + Auxiliaries.getPixeledAABB(0, 0, 15.6, 16, 16, 16.0) + )); + Registries.addBlock("sign_hotwire", () -> new StandardBlocks.DirectedWaterLoggable( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_FACING_PLACEMENT | StandardBlocks.CFG_HORIZIONTAL | StandardBlocks.CFG_AI_PASSABLE, + BlockBehaviour.Properties.of().strength(0.2f, 1f).sound(SoundType.WOOD).noOcclusion(), + Auxiliaries.getPixeledAABB(2, 2, 15.6, 14, 14, 16) + )); + Registries.addBlock("sign_danger", () -> new StandardBlocks.DirectedWaterLoggable( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_FACING_PLACEMENT | StandardBlocks.CFG_HORIZIONTAL | StandardBlocks.CFG_AI_PASSABLE, + BlockBehaviour.Properties.of().strength(0.2f, 1f).sound(SoundType.WOOD).noOcclusion(), + Auxiliaries.getPixeledAABB(2, 2, 15.6, 14, 14, 16) + )); + Registries.addBlock("sign_radioactive", () -> new StandardBlocks.DirectedWaterLoggable( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_FACING_PLACEMENT | StandardBlocks.CFG_HORIZIONTAL | StandardBlocks.CFG_AI_PASSABLE, + BlockBehaviour.Properties.of().strength(0.2f, 1f).sound(SoundType.WOOD).noOcclusion(), + Auxiliaries.getPixeledAABB(2, 2, 15.6, 14, 14, 16) + )); + Registries.addBlock("sign_laser", () -> new StandardBlocks.DirectedWaterLoggable( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_FACING_PLACEMENT | StandardBlocks.CFG_HORIZIONTAL | StandardBlocks.CFG_AI_PASSABLE, + BlockBehaviour.Properties.of().strength(0.2f, 1f).sound(SoundType.WOOD).noOcclusion(), + Auxiliaries.getPixeledAABB(2, 2, 15.6, 14, 14, 16) + )); + Registries.addBlock("sign_caution", () -> new StandardBlocks.DirectedWaterLoggable( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_FACING_PLACEMENT | StandardBlocks.CFG_HORIZIONTAL | StandardBlocks.CFG_AI_PASSABLE, + BlockBehaviour.Properties.of().strength(0.2f, 1f).sound(SoundType.WOOD).noOcclusion(), + Auxiliaries.getPixeledAABB(2, 2, 15.6, 14, 14, 16) + )); + Registries.addBlock("sign_magichazard", () -> new StandardBlocks.DirectedWaterLoggable( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_FACING_PLACEMENT | StandardBlocks.CFG_HORIZIONTAL | StandardBlocks.CFG_AI_PASSABLE, + BlockBehaviour.Properties.of().strength(0.2f, 1f).sound(SoundType.WOOD).noOcclusion(), + Auxiliaries.getPixeledAABB(2, 2, 15.6, 14, 14, 16) + )); + Registries.addBlock("sign_firehazard", () -> new StandardBlocks.DirectedWaterLoggable( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_FACING_PLACEMENT | StandardBlocks.CFG_HORIZIONTAL | StandardBlocks.CFG_AI_PASSABLE, + BlockBehaviour.Properties.of().strength(0.2f, 1f).sound(SoundType.WOOD).noOcclusion(), + Auxiliaries.getPixeledAABB(2, 2, 15.6, 14, 14, 16) + )); + Registries.addBlock("sign_hotsurface", () -> new StandardBlocks.DirectedWaterLoggable( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_FACING_PLACEMENT | StandardBlocks.CFG_HORIZIONTAL | StandardBlocks.CFG_AI_PASSABLE, + BlockBehaviour.Properties.of().strength(0.2f, 1f).sound(SoundType.WOOD).noOcclusion(), + Auxiliaries.getPixeledAABB(2, 2, 15.6, 14, 14, 16) + )); + Registries.addBlock("sign_magneticfield", () -> new StandardBlocks.DirectedWaterLoggable( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_FACING_PLACEMENT | StandardBlocks.CFG_HORIZIONTAL | StandardBlocks.CFG_AI_PASSABLE, + BlockBehaviour.Properties.of().strength(0.2f, 1f).sound(SoundType.WOOD).noOcclusion(), + Auxiliaries.getPixeledAABB(2, 2, 15.6, 14, 14, 16) + )); + Registries.addBlock("sign_frost", () -> new StandardBlocks.DirectedWaterLoggable( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_FACING_PLACEMENT | StandardBlocks.CFG_HORIZIONTAL | StandardBlocks.CFG_AI_PASSABLE, + BlockBehaviour.Properties.of().strength(0.2f, 1f).sound(SoundType.WOOD).noOcclusion(), + Auxiliaries.getPixeledAABB(2, 2, 15.6, 14, 14, 16) + )); + Registries.addBlock("sign_exit", () -> new StandardBlocks.DirectedWaterLoggable( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_FACING_PLACEMENT | StandardBlocks.CFG_HORIZIONTAL | StandardBlocks.CFG_AI_PASSABLE, + BlockBehaviour.Properties.of().strength(0.2f, 1f).sound(SoundType.WOOD).noOcclusion(), + Auxiliaries.getPixeledAABB(3, 7, 15.6, 13, 13, 16) + )); + Registries.addBlock("sign_defense", () -> new StandardBlocks.DirectedWaterLoggable( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_FACING_PLACEMENT | StandardBlocks.CFG_HORIZIONTAL | StandardBlocks.CFG_AI_PASSABLE, + BlockBehaviour.Properties.of().strength(0.2f, 1f).sound(SoundType.WOOD).noOcclusion(), + Auxiliaries.getPixeledAABB(2, 2, 15.6, 14, 14, 16) + )); - Registries.addBlock("sign_decor", ()->new StandardBlocks.DirectedWaterLoggable( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_FACING_PLACEMENT|StandardBlocks.CFG_HORIZIONTAL|StandardBlocks.CFG_AI_PASSABLE, - BlockBehaviour.Properties.of(Material.WOOD, MaterialColor.WOOD).strength(0.3f, 1000f).sound(SoundType.WOOD).lightLevel((state)->1).noOcclusion(), - Auxiliaries.getPixeledAABB(0,0,15.6, 16,16,16.0) - )); - Registries.addBlock("sign_hotwire", ()->new StandardBlocks.DirectedWaterLoggable( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_FACING_PLACEMENT|StandardBlocks.CFG_HORIZIONTAL|StandardBlocks.CFG_AI_PASSABLE, - BlockBehaviour.Properties.of(Material.WOOD, MaterialColor.WOOD).strength(0.2f, 1f).sound(SoundType.WOOD).noOcclusion(), - Auxiliaries.getPixeledAABB(2,2,15.6, 14,14,16) - )); - Registries.addBlock("sign_danger", ()->new StandardBlocks.DirectedWaterLoggable( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_FACING_PLACEMENT|StandardBlocks.CFG_HORIZIONTAL|StandardBlocks.CFG_AI_PASSABLE, - BlockBehaviour.Properties.of(Material.WOOD, MaterialColor.WOOD).strength(0.2f, 1f).sound(SoundType.WOOD).noOcclusion(), - Auxiliaries.getPixeledAABB(2,2,15.6, 14,14,16) - )); - Registries.addBlock("sign_radioactive", ()->new StandardBlocks.DirectedWaterLoggable( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_FACING_PLACEMENT|StandardBlocks.CFG_HORIZIONTAL|StandardBlocks.CFG_AI_PASSABLE, - BlockBehaviour.Properties.of(Material.WOOD, MaterialColor.WOOD).strength(0.2f, 1f).sound(SoundType.WOOD).noOcclusion(), - Auxiliaries.getPixeledAABB(2,2,15.6, 14,14,16) - )); - Registries.addBlock("sign_laser", ()->new StandardBlocks.DirectedWaterLoggable( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_FACING_PLACEMENT|StandardBlocks.CFG_HORIZIONTAL|StandardBlocks.CFG_AI_PASSABLE, - BlockBehaviour.Properties.of(Material.WOOD, MaterialColor.WOOD).strength(0.2f, 1f).sound(SoundType.WOOD).noOcclusion(), - Auxiliaries.getPixeledAABB(2,2,15.6, 14,14,16) - )); - Registries.addBlock("sign_caution", ()->new StandardBlocks.DirectedWaterLoggable( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_FACING_PLACEMENT|StandardBlocks.CFG_HORIZIONTAL|StandardBlocks.CFG_AI_PASSABLE, - BlockBehaviour.Properties.of(Material.WOOD, MaterialColor.WOOD).strength(0.2f, 1f).sound(SoundType.WOOD).noOcclusion(), - Auxiliaries.getPixeledAABB(2,2,15.6, 14,14,16) - )); - Registries.addBlock("sign_magichazard", ()->new StandardBlocks.DirectedWaterLoggable( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_FACING_PLACEMENT|StandardBlocks.CFG_HORIZIONTAL|StandardBlocks.CFG_AI_PASSABLE, - BlockBehaviour.Properties.of(Material.WOOD, MaterialColor.WOOD).strength(0.2f, 1f).sound(SoundType.WOOD).noOcclusion(), - Auxiliaries.getPixeledAABB(2,2,15.6, 14,14,16) - )); - Registries.addBlock("sign_firehazard", ()->new StandardBlocks.DirectedWaterLoggable( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_FACING_PLACEMENT|StandardBlocks.CFG_HORIZIONTAL|StandardBlocks.CFG_AI_PASSABLE, - BlockBehaviour.Properties.of(Material.WOOD, MaterialColor.WOOD).strength(0.2f, 1f).sound(SoundType.WOOD).noOcclusion(), - Auxiliaries.getPixeledAABB(2,2,15.6, 14,14,16) - )); - Registries.addBlock("sign_hotsurface", ()->new StandardBlocks.DirectedWaterLoggable( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_FACING_PLACEMENT|StandardBlocks.CFG_HORIZIONTAL|StandardBlocks.CFG_AI_PASSABLE, - BlockBehaviour.Properties.of(Material.WOOD, MaterialColor.WOOD).strength(0.2f, 1f).sound(SoundType.WOOD).noOcclusion(), - Auxiliaries.getPixeledAABB(2,2,15.6, 14,14,16) - )); - Registries.addBlock("sign_magneticfield", ()->new StandardBlocks.DirectedWaterLoggable( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_FACING_PLACEMENT|StandardBlocks.CFG_HORIZIONTAL|StandardBlocks.CFG_AI_PASSABLE, - BlockBehaviour.Properties.of(Material.WOOD, MaterialColor.WOOD).strength(0.2f, 1f).sound(SoundType.WOOD).noOcclusion(), - Auxiliaries.getPixeledAABB(2,2,15.6, 14,14,16) - )); - Registries.addBlock("sign_frost", ()->new StandardBlocks.DirectedWaterLoggable( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_FACING_PLACEMENT|StandardBlocks.CFG_HORIZIONTAL|StandardBlocks.CFG_AI_PASSABLE, - BlockBehaviour.Properties.of(Material.WOOD, MaterialColor.WOOD).strength(0.2f, 1f).sound(SoundType.WOOD).noOcclusion(), - Auxiliaries.getPixeledAABB(2,2,15.6, 14,14,16) - )); - Registries.addBlock("sign_exit", ()->new StandardBlocks.DirectedWaterLoggable( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_FACING_PLACEMENT|StandardBlocks.CFG_HORIZIONTAL|StandardBlocks.CFG_AI_PASSABLE, - BlockBehaviour.Properties.of(Material.WOOD, MaterialColor.WOOD).strength(0.2f, 1f).sound(SoundType.WOOD).noOcclusion(), - Auxiliaries.getPixeledAABB(3,7,15.6, 13,13,16) - )); - Registries.addBlock("sign_defense", ()->new StandardBlocks.DirectedWaterLoggable( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_FACING_PLACEMENT|StandardBlocks.CFG_HORIZIONTAL|StandardBlocks.CFG_AI_PASSABLE, - BlockBehaviour.Properties.of(Material.WOOD, MaterialColor.WOOD).strength(0.2f, 1f).sound(SoundType.WOOD).noOcclusion(), - Auxiliaries.getPixeledAABB(2,2,15.6, 14,14,16) - )); + // ------------------------------------------------------------------------------------------------------------------- - // ------------------------------------------------------------------------------------------------------------------- + Registries.addBlock("small_lab_furnace", + () -> new EdFurnace.FurnaceBlock( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_HORIZIONTAL | StandardBlocks.CFG_LOOK_PLACEMENT | StandardBlocks.CFG_OPPOSITE_PLACEMENT, + BlockBehaviour.Properties.of().strength(0.3f, 12f).sound(SoundType.METAL).noOcclusion(), + new AABB[]{ + Auxiliaries.getPixeledAABB(1, 0, 1, 15, 1, 15), + Auxiliaries.getPixeledAABB(0, 1, 1, 16, 16, 16), + } + ), + EdFurnace.FurnaceTileEntity::new + ); - Registries.addBlock("small_lab_furnace", - ()->new EdFurnace.FurnaceBlock( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_HORIZIONTAL|StandardBlocks.CFG_LOOK_PLACEMENT|StandardBlocks.CFG_OPPOSITE_PLACEMENT, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.3f, 12f).sound(SoundType.METAL).noOcclusion(), - new AABB[]{ - Auxiliaries.getPixeledAABB(1,0,1, 15, 1,15), - Auxiliaries.getPixeledAABB(0,1,1, 16,16,16), + Registries.addBlock("small_electrical_furnace", + () -> new EdElectricalFurnace.ElectricalFurnaceBlock( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_HORIZIONTAL | StandardBlocks.CFG_LOOK_PLACEMENT | StandardBlocks.CFG_OPPOSITE_PLACEMENT, + BlockBehaviour.Properties.of().strength(0.3f, 12f).sound(SoundType.METAL).noOcclusion(), + new AABB[]{ + Auxiliaries.getPixeledAABB(0, 0, 0, 16, 11, 16), + Auxiliaries.getPixeledAABB(1, 11, 0, 15, 12, 16), + Auxiliaries.getPixeledAABB(2, 12, 0, 14, 13, 16), + Auxiliaries.getPixeledAABB(3, 13, 0, 13, 14, 16), + Auxiliaries.getPixeledAABB(4, 14, 0, 12, 16, 16), + } + ), + EdElectricalFurnace.ElectricalFurnaceTileEntity::new + ); + Registries.addBlock("factory_dropper", + () -> new EdDropper.DropperBlock( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_LOOK_PLACEMENT | StandardBlocks.CFG_OPPOSITE_PLACEMENT, + BlockBehaviour.Properties.of().strength(0.3f, 12f).sound(SoundType.METAL).noOcclusion(), + Auxiliaries.getPixeledAABB(0, 0, 1, 16, 16, 16) + ), + EdDropper.DropperTileEntity::new + ); + Registries.addBlock("factory_placer", + () -> new EdPlacer.PlacerBlock( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_LOOK_PLACEMENT | StandardBlocks.CFG_FLIP_PLACEMENT_SHIFTCLICK | StandardBlocks.CFG_OPPOSITE_PLACEMENT, + BlockBehaviour.Properties.of().strength(0.3f, 12f).sound(SoundType.METAL).noOcclusion(), + new AABB[]{ + Auxiliaries.getPixeledAABB(0, 0, 2, 16, 16, 16), + Auxiliaries.getPixeledAABB(0, 0, 0, 1, 16, 2), + Auxiliaries.getPixeledAABB(15, 0, 0, 16, 16, 2) + } + ), + EdPlacer.PlacerTileEntity::new + ); + Registries.addBlock("small_block_breaker", + () -> new EdBreaker.BreakerBlock( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_HORIZIONTAL | StandardBlocks.CFG_LOOK_PLACEMENT | StandardBlocks.CFG_OPPOSITE_PLACEMENT | StandardBlocks.CFG_FLIP_PLACEMENT_SHIFTCLICK, + BlockBehaviour.Properties.of().strength(0.3f, 12f).sound(SoundType.METAL).noOcclusion(), + new AABB[]{ + Auxiliaries.getPixeledAABB(1, 0, 0, 15, 4, 7), + Auxiliaries.getPixeledAABB(1, 0, 7, 15, 12, 16), + Auxiliaries.getPixeledAABB(0, 0, 0, 1, 5, 4), + Auxiliaries.getPixeledAABB(0, 0, 4, 1, 12, 16), + Auxiliaries.getPixeledAABB(15, 0, 0, 16, 5, 4), + Auxiliaries.getPixeledAABB(15, 0, 4, 16, 12, 16) + } + ), + EdBreaker.BreakerTileEntity::new + ); + Registries.addBlock("factory_hopper", + () -> new EdHopper.HopperBlock( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_FACING_PLACEMENT | StandardBlocks.CFG_OPPOSITE_PLACEMENT, + BlockBehaviour.Properties.of().strength(0.3f, 12f).sound(SoundType.METAL).noOcclusion(), () -> { + final AABB[] down_aabbs = new AABB[]{ + Auxiliaries.getPixeledAABB(5, 0, 5, 11, 1, 11), + Auxiliaries.getPixeledAABB(4, 1, 4, 12, 7, 12), + Auxiliaries.getPixeledAABB(2, 7, 2, 14, 10, 14), + Auxiliaries.getPixeledAABB(0, 10, 0, 16, 16, 16), + Auxiliaries.getPixeledAABB(0, 4, 5, 2, 10, 11), + Auxiliaries.getPixeledAABB(14, 4, 5, 16, 10, 11), + Auxiliaries.getPixeledAABB(5, 4, 0, 11, 10, 2), + Auxiliaries.getPixeledAABB(5, 4, 14, 11, 10, 16), + }; + final AABB[] up_aabbs = new AABB[]{ + Auxiliaries.getPixeledAABB(5, 15, 5, 11, 16, 11), + Auxiliaries.getPixeledAABB(4, 14, 4, 12, 9, 12), + Auxiliaries.getPixeledAABB(2, 9, 2, 14, 6, 14), + Auxiliaries.getPixeledAABB(0, 6, 0, 16, 0, 16), + Auxiliaries.getPixeledAABB(0, 12, 5, 2, 6, 11), + Auxiliaries.getPixeledAABB(14, 12, 5, 16, 6, 11), + Auxiliaries.getPixeledAABB(5, 12, 0, 11, 6, 2), + Auxiliaries.getPixeledAABB(5, 12, 14, 11, 6, 16), + }; + final AABB[] north_aabbs = new AABB[]{ + Auxiliaries.getPixeledAABB(5, 0, 5, 11, 1, 11), + Auxiliaries.getPixeledAABB(4, 1, 4, 12, 7, 12), + Auxiliaries.getPixeledAABB(2, 7, 2, 14, 10, 14), + Auxiliaries.getPixeledAABB(0, 10, 0, 16, 16, 16), + Auxiliaries.getPixeledAABB(0, 4, 5, 2, 10, 11), + Auxiliaries.getPixeledAABB(14, 4, 5, 16, 10, 11), + Auxiliaries.getPixeledAABB(5, 1, 0, 11, 7, 4), + Auxiliaries.getPixeledAABB(5, 4, 14, 11, 10, 16), + }; + return new ArrayList<>(Arrays.asList( + Auxiliaries.getUnionShape(down_aabbs), + Auxiliaries.getUnionShape(up_aabbs), + Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(north_aabbs, Direction.NORTH, false)), + Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(north_aabbs, Direction.SOUTH, false)), + Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(north_aabbs, Direction.WEST, false)), + Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(north_aabbs, Direction.EAST, false)), + Shapes.block(), + Shapes.block() + )); + } + ), + EdHopper.HopperTileEntity::new + ); + Registries.addBlock("small_waste_incinerator", + () -> new EdWasteIncinerator.WasteIncineratorBlock( + StandardBlocks.CFG_DEFAULT, + BlockBehaviour.Properties.of().strength(0.3f, 12f).sound(SoundType.METAL), + Auxiliaries.getPixeledAABB(0, 0, 0, 16, 16, 16) + ), + EdWasteIncinerator.WasteIncineratorTileEntity::new + ); + Registries.addBlock("small_mineral_smelter", + () -> new EdMineralSmelter.MineralSmelterBlock( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_HORIZIONTAL | StandardBlocks.CFG_LOOK_PLACEMENT, + BlockBehaviour.Properties.of().strength(0.3f, 12f).sound(SoundType.METAL).noOcclusion(), + Auxiliaries.getPixeledAABB(1.1, 0, 1.1, 14.9, 16, 14.9) + ), + EdMineralSmelter.MineralSmelterTileEntity::new + ); + Registries.addBlock("small_freezer", + () -> new EdFreezer.FreezerBlock( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_HORIZIONTAL | StandardBlocks.CFG_LOOK_PLACEMENT, + BlockBehaviour.Properties.of().strength(0.3f, 12f).sound(SoundType.METAL).noOcclusion(), + Auxiliaries.getPixeledAABB(1.1, 0, 1.1, 14.9, 16, 14.9) + ), + EdFreezer.FreezerTileEntity::new + ); + Registries.addBlock("small_solar_panel", + () -> new EdSolarPanel.SolarPanelBlock( + StandardBlocks.CFG_CUTOUT, + BlockBehaviour.Properties.of().strength(0.3f, 10f).sound(SoundType.METAL).noOcclusion(), + new AABB[]{ + Auxiliaries.getPixeledAABB(0, 0, 0, 16, 2, 16), + Auxiliaries.getPixeledAABB(6, 1.5, 3, 10, 10.5, 13), + } + ), + EdSolarPanel.SolarPanelTileEntity::new + ); + Registries.addBlock("small_milking_machine", + () -> new EdMilker.MilkerBlock( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_HORIZIONTAL | StandardBlocks.CFG_LOOK_PLACEMENT, + BlockBehaviour.Properties.of().strength(0.3f, 10f).sound(SoundType.METAL).noOcclusion(), + new AABB[]{ + Auxiliaries.getPixeledAABB(1, 1, 0, 15, 14, 10), + Auxiliaries.getPixeledAABB(0, 14, 0, 16, 16, 13), + Auxiliaries.getPixeledAABB(0, 0, 0, 16, 1, 13), + Auxiliaries.getPixeledAABB(0, 1, 1, 1, 14, 11), + Auxiliaries.getPixeledAABB(15, 1, 1, 16, 14, 11) + } + ), + EdMilker.MilkerTileEntity::new + ); + Registries.addBlock("small_tree_cutter", + () -> new EdTreeCutter.TreeCutterBlock( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_HORIZIONTAL | StandardBlocks.CFG_LOOK_PLACEMENT | StandardBlocks.CFG_FLIP_PLACEMENT_SHIFTCLICK, + BlockBehaviour.Properties.of().strength(0.3f, 10f).sound(SoundType.METAL).noOcclusion(), + new AABB[]{ + Auxiliaries.getPixeledAABB(0, 0, 0, 16, 3, 16), + Auxiliaries.getPixeledAABB(0, 3, 0, 3, 8, 16), + Auxiliaries.getPixeledAABB(3, 7, 0, 5, 8, 16), + Auxiliaries.getPixeledAABB(15, 0, 0, 16, 6, 16), + Auxiliaries.getPixeledAABB(0, 0, 13, 16, 8, 16), + Auxiliaries.getPixeledAABB(5, 6, 12, 16, 8, 13), + } + ), + EdTreeCutter.TreeCutterTileEntity::new + ); + Registries.addBlock("straight_pipe_valve", () -> new EdPipeValve.PipeValveBlock( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_FACING_PLACEMENT | StandardBlocks.CFG_OPPOSITE_PLACEMENT | StandardBlocks.CFG_FLIP_PLACEMENT_SHIFTCLICK, + EdPipeValve.CFG_CHECK_VALVE, + BlockBehaviour.Properties.of().strength(0.3f, 8f).sound(SoundType.METAL).noOcclusion(), + new AABB[]{ + Auxiliaries.getPixeledAABB(2, 2, 0, 14, 14, 2), + Auxiliaries.getPixeledAABB(2, 2, 14, 14, 14, 16), + Auxiliaries.getPixeledAABB(3, 3, 5, 13, 13, 11), + Auxiliaries.getPixeledAABB(4, 4, 2, 12, 12, 14), + } + )); + Registries.addBlock("straight_pipe_valve_redstone", () -> new EdPipeValve.PipeValveBlock( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_FACING_PLACEMENT | StandardBlocks.CFG_OPPOSITE_PLACEMENT, + EdPipeValve.CFG_REDSTONE_CONTROLLED_VALVE, + BlockBehaviour.Properties.of().strength(0.3f, 8f).sound(SoundType.METAL).noOcclusion(), + new AABB[]{ + Auxiliaries.getPixeledAABB(2, 2, 0, 14, 14, 2), + Auxiliaries.getPixeledAABB(2, 2, 14, 14, 14, 16), + Auxiliaries.getPixeledAABB(3, 3, 5, 13, 13, 11), + Auxiliaries.getPixeledAABB(4, 4, 2, 12, 12, 14), + } + )); + Registries.addBlock("straight_pipe_valve_redstone_analog", + () -> new EdPipeValve.PipeValveBlock( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_FACING_PLACEMENT | StandardBlocks.CFG_OPPOSITE_PLACEMENT, + EdPipeValve.CFG_REDSTONE_CONTROLLED_VALVE | EdPipeValve.CFG_ANALOG_VALVE, + BlockBehaviour.Properties.of().strength(0.3f, 8f).sound(SoundType.METAL).noOcclusion(), + new AABB[]{ + Auxiliaries.getPixeledAABB(2, 2, 0, 14, 14, 2), + Auxiliaries.getPixeledAABB(2, 2, 14, 14, 14, 16), + Auxiliaries.getPixeledAABB(3, 3, 5, 13, 13, 11), + Auxiliaries.getPixeledAABB(4, 4, 2, 12, 12, 14), + } + ) + ); + Registries.addBlockEntityType("tet_straight_pipe_valve", + EdPipeValve.PipeValveTileEntity::new, + "straight_pipe_valve", "straight_pipe_valve_redstone", "straight_pipe_valve_redstone_analog" + ); + Registries.addBlock("fluid_barrel", + () -> new EdFluidBarrel.FluidBarrelBlock( + StandardBlocks.CFG_CUTOUT | StandardBlocks.CFG_LOOK_PLACEMENT | StandardBlocks.CFG_OPPOSITE_PLACEMENT, + BlockBehaviour.Properties.of().strength(0.3f, 10f).sound(SoundType.METAL).noOcclusion(), + new AABB[]{ + Auxiliaries.getPixeledAABB(2, 0, 0, 14, 1, 16), + Auxiliaries.getPixeledAABB(1, 1, 0, 15, 2, 16), + Auxiliaries.getPixeledAABB(0, 2, 0, 16, 14, 16), + Auxiliaries.getPixeledAABB(1, 14, 0, 15, 15, 16), + Auxiliaries.getPixeledAABB(2, 15, 0, 14, 16, 16), + } + ), + EdFluidBarrel.FluidBarrelItem::new, + EdFluidBarrel.FluidBarrelTileEntity::new + ); + Registries.addBlock("small_fluid_funnel", + () -> new EdFluidFunnel.FluidFunnelBlock( + StandardBlocks.CFG_CUTOUT, + BlockBehaviour.Properties.of().strength(0.3f, 10f).sound(SoundType.METAL).noOcclusion(), + new AABB[]{ + Auxiliaries.getPixeledAABB(0, 0, 0, 16, 14, 16), + Auxiliaries.getPixeledAABB(1, 14, 1, 15, 15, 15), + Auxiliaries.getPixeledAABB(0, 15, 0, 16, 16, 16) + } + ), + EdFluidFunnel.FluidFunnelTileEntity::new + ); + + // ------------------------------------------------------------------------------------------------------------------- + + Registries.addBlock("test_block", + () -> new EdTestBlock.TestBlock( + StandardBlocks.CFG_LOOK_PLACEMENT, + BlockBehaviour.Properties.of().strength(0f, 32000f).sound(SoundType.METAL), + Auxiliaries.getPixeledAABB(0, 0, 0, 16, 16, 16) + ), + EdTestBlock.TestTileEntity::new + ); + + } + + public static void initItems() { + Registries.addItem("metal_bar", () -> new EdItem((new Item.Properties()))); + } + + public static void initEntities() { + Registries.addEntityType("et_chair", () -> + EntityType.Builder.of(EdChair.EntityChair::new, MobCategory.MISC) + .fireImmune().sized(1e-3f, 1e-3f).noSave() + .setShouldReceiveVelocityUpdates(false).setUpdateInterval(4) + .setCustomClientFactory(EdChair.EntityChair::customClientFactory) + .build(new ResourceLocation(Auxiliaries.modid(), "et_chair").toString()) + ); + } + + public static Block getBlock(String name) { + return Registries.getBlock(name); + } + + //-------------------------------------------------------------------------------------------------------------------- + // Registry wrappers + //-------------------------------------------------------------------------------------------------------------------- + + public static Item getItem(String name) { + return Registries.getItem(name); + } + + public static TagKey getBlockTagKey(String name) { + return Registries.getBlockTagKey(name); + } + + public static TagKey getItemTagKey(String name) { + return Registries.getItemTagKey(name); + } + + public static BlockEntityType getBlockEntityTypeOfBlock(String block_name) { + return Registries.getBlockEntityTypeOfBlock(block_name); + } + + public static BlockEntityType getBlockEntityTypeOfBlock(Block block) { + return Registries.getBlockEntityTypeOfBlock(block); + } + + public static EntityType getEntityType(String name) { + return Registries.getEntityType(name); + } + + public static MenuType getMenuType(String block_name) { + return Registries.getMenuTypeOfBlock(block_name); + } + + @Nonnull + public static List getRegisteredBlocks() { + return Registries.getRegisteredBlocks(); + } + + @Nonnull + public static List getRegisteredItems() { + return Registries.getRegisteredItems(); + } + + @OnlyIn(Dist.CLIENT) + @SuppressWarnings("unchecked") + public static void registerMenuGuis(final FMLClientSetupEvent event) { + MenuScreens.register((MenuType) Registries.getMenuTypeOfBlock("factory_dropper"), EdDropper.DropperGui::new); + MenuScreens.register((MenuType) Registries.getMenuTypeOfBlock("factory_placer"), EdPlacer.PlacerGui::new); + MenuScreens.register((MenuType) Registries.getMenuTypeOfBlock("factory_hopper"), EdHopper.HopperGui::new); + MenuScreens.register((MenuType) Registries.getMenuTypeOfBlock("small_lab_furnace"), EdFurnace.FurnaceGui::new); + MenuScreens.register((MenuType) Registries.getMenuTypeOfBlock("small_electrical_furnace"), EdElectricalFurnace.ElectricalFurnaceGui::new); + MenuScreens.register((MenuType) Registries.getMenuTypeOfBlock("small_waste_incinerator"), EdWasteIncinerator.WasteIncineratorGui::new); + } + + //-------------------------------------------------------------------------------------------------------------------- + // Initialisation events + //-------------------------------------------------------------------------------------------------------------------- + + @OnlyIn(Dist.CLIENT) + @SuppressWarnings("unchecked") + public static void registerBlockEntityRenderers(final FMLClientSetupEvent event) { + } + + @OnlyIn(Dist.CLIENT) + public static void processContentClientSide(final FMLClientSetupEvent event) { + // Block renderer selection + // Disabled in Forge, model based {"render_type": "cutout"/"translucent"} + // for(Block block: Registries.getRegisteredBlocks()) { + // if(block instanceof IStandardBlock) { + // switch(((IStandardBlock)block).getRenderTypeHint()) { + // case CUTOUT: ItemBlockRenderTypes.setRenderLayer(block, RenderType.cutout()); break; + // case CUTOUT_MIPPED: ItemBlockRenderTypes.setRenderLayer(block, RenderType.cutoutMipped()); break; + // case TRANSLUCENT: ItemBlockRenderTypes.setRenderLayer(block, RenderType.translucent()); break; + // case TRANSLUCENT_NO_CRUMBLING: ItemBlockRenderTypes.setRenderLayer(block, RenderType.translucentNoCrumbling()); break; + // case SOLID: break; + // } + // } + // } + // Entity renderers + EntityRenderers.register(Registries.getEntityType("et_chair"), ModRenderers.InvisibleEntityRenderer::new); + } + + private static class detail { + public static String MODID = ""; + + public static Boolean disallowSpawn(BlockState state, BlockGetter reader, BlockPos pos, EntityType entity) { + return false; } - ), - EdFurnace.FurnaceTileEntity::new, - EdFurnace.FurnaceContainer::new - ); - Registries.addBlock("small_electrical_furnace", - ()->new EdElectricalFurnace.ElectricalFurnaceBlock( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_HORIZIONTAL|StandardBlocks.CFG_LOOK_PLACEMENT|StandardBlocks.CFG_OPPOSITE_PLACEMENT, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.3f, 12f).sound(SoundType.METAL).noOcclusion(), - new AABB[]{ - Auxiliaries.getPixeledAABB(0, 0,0, 16,11,16), - Auxiliaries.getPixeledAABB(1,11,0, 15,12,16), - Auxiliaries.getPixeledAABB(2,12,0, 14,13,16), - Auxiliaries.getPixeledAABB(3,13,0, 13,14,16), - Auxiliaries.getPixeledAABB(4,14,0, 12,16,16), - } - ), - EdElectricalFurnace.ElectricalFurnaceTileEntity::new, - EdElectricalFurnace.ElectricalFurnaceContainer::new - ); - Registries.addBlock("factory_dropper", - ()->new EdDropper.DropperBlock( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_LOOK_PLACEMENT|StandardBlocks.CFG_OPPOSITE_PLACEMENT, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.3f, 12f).sound(SoundType.METAL).noOcclusion(), - Auxiliaries.getPixeledAABB(0,0,1, 16,16,16) - ), - EdDropper.DropperTileEntity::new, - EdDropper.DropperUiContainer::new - ); - Registries.addBlock("factory_placer", - ()->new EdPlacer.PlacerBlock( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_LOOK_PLACEMENT|StandardBlocks.CFG_FLIP_PLACEMENT_SHIFTCLICK|StandardBlocks.CFG_OPPOSITE_PLACEMENT, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.3f, 12f).sound(SoundType.METAL).noOcclusion(), - new AABB[]{ - Auxiliaries.getPixeledAABB(0,0,2, 16,16,16), - Auxiliaries.getPixeledAABB( 0,0,0, 1,16, 2), - Auxiliaries.getPixeledAABB(15,0,0,16,16, 2) - } - ), - EdPlacer.PlacerTileEntity::new, - EdPlacer.PlacerContainer::new - ); - Registries.addBlock("small_block_breaker", - ()->new EdBreaker.BreakerBlock( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_HORIZIONTAL|StandardBlocks.CFG_LOOK_PLACEMENT|StandardBlocks.CFG_OPPOSITE_PLACEMENT|StandardBlocks.CFG_FLIP_PLACEMENT_SHIFTCLICK, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.3f, 12f).sound(SoundType.METAL).noOcclusion(), - new AABB[]{ - Auxiliaries.getPixeledAABB(1,0,0, 15, 4, 7), - Auxiliaries.getPixeledAABB(1,0,7, 15,12,16), - Auxiliaries.getPixeledAABB(0,0,0, 1, 5, 4), - Auxiliaries.getPixeledAABB(0,0,4, 1,12,16), - Auxiliaries.getPixeledAABB(15,0,0, 16, 5, 4), - Auxiliaries.getPixeledAABB(15,0,4, 16,12,16) - } - ), - EdBreaker.BreakerTileEntity::new - ); - Registries.addBlock("factory_hopper", - ()->new EdHopper.HopperBlock( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_FACING_PLACEMENT|StandardBlocks.CFG_OPPOSITE_PLACEMENT, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.3f, 12f).sound(SoundType.METAL).noOcclusion(),()->{ - final AABB[] down_aabbs = new AABB[]{ - Auxiliaries.getPixeledAABB( 5, 0, 5, 11, 1,11), - Auxiliaries.getPixeledAABB( 4, 1, 4, 12, 7,12), - Auxiliaries.getPixeledAABB( 2, 7, 2, 14,10,14), - Auxiliaries.getPixeledAABB( 0,10, 0, 16,16,16), - Auxiliaries.getPixeledAABB( 0, 4, 5, 2,10,11), - Auxiliaries.getPixeledAABB(14, 4, 5, 16,10,11), - Auxiliaries.getPixeledAABB( 5, 4, 0, 11,10, 2), - Auxiliaries.getPixeledAABB( 5, 4,14, 11,10,16), - }; - final AABB[] up_aabbs = new AABB[]{ - Auxiliaries.getPixeledAABB( 5,15, 5, 11,16,11), - Auxiliaries.getPixeledAABB( 4,14, 4, 12, 9,12), - Auxiliaries.getPixeledAABB( 2, 9, 2, 14, 6,14), - Auxiliaries.getPixeledAABB( 0, 6, 0, 16, 0,16), - Auxiliaries.getPixeledAABB( 0,12, 5, 2, 6,11), - Auxiliaries.getPixeledAABB(14,12, 5, 16, 6,11), - Auxiliaries.getPixeledAABB( 5,12, 0, 11, 6, 2), - Auxiliaries.getPixeledAABB( 5,12,14, 11, 6,16), - }; - final AABB[] north_aabbs = new AABB[]{ - Auxiliaries.getPixeledAABB( 5, 0, 5, 11, 1,11), - Auxiliaries.getPixeledAABB( 4, 1, 4, 12, 7,12), - Auxiliaries.getPixeledAABB( 2, 7, 2, 14,10,14), - Auxiliaries.getPixeledAABB( 0,10, 0, 16,16,16), - Auxiliaries.getPixeledAABB( 0, 4, 5, 2,10,11), - Auxiliaries.getPixeledAABB(14, 4, 5, 16,10,11), - Auxiliaries.getPixeledAABB( 5, 1, 0, 11, 7, 4), - Auxiliaries.getPixeledAABB( 5, 4,14, 11,10,16), - }; - return new ArrayList<>(Arrays.asList( - Auxiliaries.getUnionShape(down_aabbs), - Auxiliaries.getUnionShape(up_aabbs), - Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(north_aabbs, Direction.NORTH, false)), - Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(north_aabbs, Direction.SOUTH, false)), - Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(north_aabbs, Direction.WEST, false)), - Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(north_aabbs, Direction.EAST, false)), - Shapes.block(), - Shapes.block() - )); - } - ), - EdHopper.HopperTileEntity::new, - EdHopper.HopperContainer::new - ); - Registries.addBlock("small_waste_incinerator", - ()->new EdWasteIncinerator.WasteIncineratorBlock( - StandardBlocks.CFG_DEFAULT, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.3f, 12f).sound(SoundType.METAL), - Auxiliaries.getPixeledAABB(0,0,0, 16,16,16) - ), - EdWasteIncinerator.WasteIncineratorTileEntity::new, - EdWasteIncinerator.WasteIncineratorContainer::new - ); - Registries.addBlock("small_mineral_smelter", - ()->new EdMineralSmelter.MineralSmelterBlock( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_HORIZIONTAL|StandardBlocks.CFG_LOOK_PLACEMENT, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.3f, 12f).sound(SoundType.METAL).noOcclusion(), - Auxiliaries.getPixeledAABB(1.1,0,1.1, 14.9,16,14.9) - ), - EdMineralSmelter.MineralSmelterTileEntity::new - ); - Registries.addBlock("small_freezer", - ()->new EdFreezer.FreezerBlock( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_HORIZIONTAL|StandardBlocks.CFG_LOOK_PLACEMENT, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.3f, 12f).sound(SoundType.METAL).noOcclusion(), - Auxiliaries.getPixeledAABB(1.1,0,1.1, 14.9,16,14.9) - ), - EdFreezer.FreezerTileEntity::new - ); - Registries.addBlock("small_solar_panel", - ()->new EdSolarPanel.SolarPanelBlock( - StandardBlocks.CFG_CUTOUT, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.3f, 10f).sound(SoundType.METAL).noOcclusion(), - new AABB[]{ - Auxiliaries.getPixeledAABB(0,0,0, 16,2,16), - Auxiliaries.getPixeledAABB(6,1.5,3, 10,10.5,13), - } - ), - EdSolarPanel.SolarPanelTileEntity::new - ); - Registries.addBlock("small_milking_machine", - ()->new EdMilker.MilkerBlock( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_HORIZIONTAL|StandardBlocks.CFG_LOOK_PLACEMENT, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.3f, 10f).sound(SoundType.METAL).noOcclusion(), - new AABB[]{ - Auxiliaries.getPixeledAABB( 1, 1,0, 15,14,10), - Auxiliaries.getPixeledAABB( 0,14,0, 16,16,13), - Auxiliaries.getPixeledAABB( 0, 0,0, 16, 1,13), - Auxiliaries.getPixeledAABB( 0, 1,1, 1,14,11), - Auxiliaries.getPixeledAABB(15, 1,1, 16,14,11) - } - ), - EdMilker.MilkerTileEntity::new - ); - Registries.addBlock("small_tree_cutter", - ()->new EdTreeCutter.TreeCutterBlock( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_HORIZIONTAL|StandardBlocks.CFG_LOOK_PLACEMENT|StandardBlocks.CFG_FLIP_PLACEMENT_SHIFTCLICK, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.3f, 10f).sound(SoundType.METAL).noOcclusion(), - new AABB[]{ - Auxiliaries.getPixeledAABB( 0,0, 0, 16,3,16), - Auxiliaries.getPixeledAABB( 0,3, 0, 3,8,16), - Auxiliaries.getPixeledAABB( 3,7, 0, 5,8,16), - Auxiliaries.getPixeledAABB(15,0, 0, 16,6,16), - Auxiliaries.getPixeledAABB( 0,0,13, 16,8,16), - Auxiliaries.getPixeledAABB( 5,6,12, 16,8,13), - } - ), - EdTreeCutter.TreeCutterTileEntity::new - ); - Registries.addBlock("straight_pipe_valve", ()->new EdPipeValve.PipeValveBlock( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_FACING_PLACEMENT|StandardBlocks.CFG_OPPOSITE_PLACEMENT|StandardBlocks.CFG_FLIP_PLACEMENT_SHIFTCLICK, - EdPipeValve.CFG_CHECK_VALVE, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.3f, 8f).sound(SoundType.METAL).noOcclusion(), - new AABB[]{ - Auxiliaries.getPixeledAABB(2,2, 0, 14,14, 2), - Auxiliaries.getPixeledAABB(2,2,14, 14,14,16), - Auxiliaries.getPixeledAABB(3,3, 5, 13,13,11), - Auxiliaries.getPixeledAABB(4,4, 2, 12,12,14), - } - )); - Registries.addBlock("straight_pipe_valve_redstone", ()->new EdPipeValve.PipeValveBlock( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_FACING_PLACEMENT|StandardBlocks.CFG_OPPOSITE_PLACEMENT, - EdPipeValve.CFG_REDSTONE_CONTROLLED_VALVE, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.3f, 8f).sound(SoundType.METAL).noOcclusion(), - new AABB[]{ - Auxiliaries.getPixeledAABB(2,2, 0, 14,14, 2), - Auxiliaries.getPixeledAABB(2,2,14, 14,14,16), - Auxiliaries.getPixeledAABB(3,3, 5, 13,13,11), - Auxiliaries.getPixeledAABB(4,4, 2, 12,12,14), - } - )); - Registries.addBlock("straight_pipe_valve_redstone_analog", - ()->new EdPipeValve.PipeValveBlock( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_FACING_PLACEMENT|StandardBlocks.CFG_OPPOSITE_PLACEMENT, - EdPipeValve.CFG_REDSTONE_CONTROLLED_VALVE|EdPipeValve.CFG_ANALOG_VALVE, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.3f, 8f).sound(SoundType.METAL).noOcclusion(), - new AABB[]{ - Auxiliaries.getPixeledAABB(2,2, 0, 14,14, 2), - Auxiliaries.getPixeledAABB(2,2,14, 14,14,16), - Auxiliaries.getPixeledAABB(3,3, 5, 13,13,11), - Auxiliaries.getPixeledAABB(4,4, 2, 12,12,14), - } - ) - ); - Registries.addBlockEntityType("tet_straight_pipe_valve", - EdPipeValve.PipeValveTileEntity::new, - "straight_pipe_valve", "straight_pipe_valve_redstone", "straight_pipe_valve_redstone_analog" - ); - Registries.addBlock("fluid_barrel", - ()->new EdFluidBarrel.FluidBarrelBlock( - StandardBlocks.CFG_CUTOUT|StandardBlocks.CFG_LOOK_PLACEMENT|StandardBlocks.CFG_OPPOSITE_PLACEMENT, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.3f, 10f).sound(SoundType.METAL).noOcclusion(), - new AABB[]{ - Auxiliaries.getPixeledAABB(2, 0,0, 14, 1,16), - Auxiliaries.getPixeledAABB(1, 1,0, 15, 2,16), - Auxiliaries.getPixeledAABB(0, 2,0, 16,14,16), - Auxiliaries.getPixeledAABB(1,14,0, 15,15,16), - Auxiliaries.getPixeledAABB(2,15,0, 14,16,16), - } - ), - EdFluidBarrel.FluidBarrelItem::new, - EdFluidBarrel.FluidBarrelTileEntity::new - ); - Registries.addBlock("small_fluid_funnel", - ()->new EdFluidFunnel.FluidFunnelBlock( - StandardBlocks.CFG_CUTOUT, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0.3f, 10f).sound(SoundType.METAL).noOcclusion(), - new AABB[]{ - Auxiliaries.getPixeledAABB(0, 0,0, 16,14,16), - Auxiliaries.getPixeledAABB(1,14,1, 15,15,15), - Auxiliaries.getPixeledAABB(0,15,0, 16,16,16) - } - ), - EdFluidFunnel.FluidFunnelTileEntity::new - ); - - // ------------------------------------------------------------------------------------------------------------------- - - Registries.addBlock("test_block", - ()->new EdTestBlock.TestBlock( - StandardBlocks.CFG_LOOK_PLACEMENT, - BlockBehaviour.Properties.of(Material.METAL, MaterialColor.METAL).strength(0f, 32000f).sound(SoundType.METAL), - Auxiliaries.getPixeledAABB(0,0,0, 16,16,16) - ), - EdTestBlock.TestTileEntity::new - ); - - } - - public static void initItems() - { - Registries.addItem("metal_bar", ()->new EdItem((new Item.Properties()).tab(Registries.getCreativeModeTab()))); - } - - public static void initEntities() - { - Registries.addEntityType("et_chair", ()-> - EntityType.Builder.of(EdChair.EntityChair::new, MobCategory.MISC) - .fireImmune().sized(1e-3f, 1e-3f).noSave() - .setShouldReceiveVelocityUpdates(false).setUpdateInterval(4) - .setCustomClientFactory(EdChair.EntityChair::customClientFactory) - .build(new ResourceLocation(Auxiliaries.modid(), "et_chair").toString()) - ); - } - - //-------------------------------------------------------------------------------------------------------------------- - // Registry wrappers - //-------------------------------------------------------------------------------------------------------------------- - - public static Block getBlock(String name) - { return Registries.getBlock(name); } - - public static Item getItem(String name) - { return Registries.getItem(name); } - - public static TagKey getBlockTagKey(String name) - { return Registries.getBlockTagKey(name); } - - public static TagKey getItemTagKey(String name) - { return Registries.getItemTagKey(name); } - - public static BlockEntityType getBlockEntityTypeOfBlock(String block_name) - { return Registries.getBlockEntityTypeOfBlock(block_name); } - - public static BlockEntityType getBlockEntityTypeOfBlock(Block block) - { return Registries.getBlockEntityTypeOfBlock(block); } - - public static EntityType getEntityType(String name) - { return Registries.getEntityType(name); } - - public static MenuType getMenuType(String block_name) - { return Registries.getMenuTypeOfBlock(block_name); } - - @Nonnull - public static List getRegisteredBlocks() - { return Registries.getRegisteredBlocks(); } - - @Nonnull - public static List getRegisteredItems() - { return Registries.getRegisteredItems(); } - - //-------------------------------------------------------------------------------------------------------------------- - // Initialisation events - //-------------------------------------------------------------------------------------------------------------------- - - @OnlyIn(Dist.CLIENT) - @SuppressWarnings("unchecked") - public static void registerMenuGuis(final FMLClientSetupEvent event) - { - MenuScreens.register((MenuType)Registries.getMenuTypeOfBlock("factory_dropper"), EdDropper.DropperGui::new); - MenuScreens.register((MenuType)Registries.getMenuTypeOfBlock("factory_placer"), EdPlacer.PlacerGui::new); - MenuScreens.register((MenuType)Registries.getMenuTypeOfBlock("factory_hopper"), EdHopper.HopperGui::new); - MenuScreens.register((MenuType)Registries.getMenuTypeOfBlock("small_lab_furnace"), EdFurnace.FurnaceGui::new); - MenuScreens.register((MenuType)Registries.getMenuTypeOfBlock("small_electrical_furnace"), EdElectricalFurnace.ElectricalFurnaceGui::new); - MenuScreens.register((MenuType)Registries.getMenuTypeOfBlock("small_waste_incinerator"), EdWasteIncinerator.WasteIncineratorGui::new); - } - - @OnlyIn(Dist.CLIENT) - @SuppressWarnings("unchecked") - public static void registerBlockEntityRenderers(final FMLClientSetupEvent event) - { - } - - @OnlyIn(Dist.CLIENT) - public static void processContentClientSide(final FMLClientSetupEvent event) - { - // Block renderer selection - // Disabled in Forge, model based {"render_type": "cutout"/"translucent"} - // for(Block block: Registries.getRegisteredBlocks()) { - // if(block instanceof IStandardBlock) { - // switch(((IStandardBlock)block).getRenderTypeHint()) { - // case CUTOUT: ItemBlockRenderTypes.setRenderLayer(block, RenderType.cutout()); break; - // case CUTOUT_MIPPED: ItemBlockRenderTypes.setRenderLayer(block, RenderType.cutoutMipped()); break; - // case TRANSLUCENT: ItemBlockRenderTypes.setRenderLayer(block, RenderType.translucent()); break; - // case TRANSLUCENT_NO_CRUMBLING: ItemBlockRenderTypes.setRenderLayer(block, RenderType.translucentNoCrumbling()); break; - // case SOLID: break; - // } - // } - // } - // Entity renderers - EntityRenderers.register(Registries.getEntityType("et_chair"), ModRenderers.InvisibleEntityRenderer::new); - } + } } diff --git a/src/main/java/dev/zontreck/engineerdecor/ModEngineersDecor.java b/src/main/java/dev/zontreck/engineerdecor/ModEngineersDecor.java index 7d2cd71..17ded2a 100644 --- a/src/main/java/dev/zontreck/engineerdecor/ModEngineersDecor.java +++ b/src/main/java/dev/zontreck/engineerdecor/ModEngineersDecor.java @@ -1,7 +1,7 @@ package dev.zontreck.engineerdecor; import dev.zontreck.engineerdecor.blocks.EdLadderBlock; -import dev.zontreck.engineerdecor.libmc.*; +import dev.zontreck.libzontreck.edlibmc.*; import net.minecraft.world.entity.player.Player; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; @@ -16,91 +16,80 @@ import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import org.slf4j.Logger; -import dev.zontreck.engineerdecor.libmc.Auxiliaries; -import dev.zontreck.engineerdecor.libmc.OptionalRecipeCondition; -import dev.zontreck.engineerdecor.libmc.Overlay; -import dev.zontreck.engineerdecor.libmc.Registries; @Mod("engineersdecor") -public class ModEngineersDecor -{ - public static final String MODID = "engineersdecor"; - public static final String MODNAME = "Engineer's Decor"; - public static final int VERSION_DATAFIXER = 0; - private static final Logger LOGGER = com.mojang.logging.LogUtils.getLogger(); +public class ModEngineersDecor { + public static final String MODID = "engineersdecor"; + public static final String MODNAME = "Engineer's Decor"; + public static final int VERSION_DATAFIXER = 0; + private static final Logger LOGGER = com.mojang.logging.LogUtils.getLogger(); - public ModEngineersDecor() - { - Auxiliaries.init(MODID, LOGGER, ModConfig::getServerConfig); - Auxiliaries.logGitVersion(MODNAME); - Registries.init(MODID, "sign_decor", (reg)->reg.register(FMLJavaModLoadingContext.get().getModEventBus())); - ModContent.init(MODID); - OptionalRecipeCondition.init(MODID, LOGGER); - ModLoadingContext.get().registerConfig(net.minecraftforge.fml.config.ModConfig.Type.SERVER, ModConfig.SERVER_CONFIG_SPEC); - ModLoadingContext.get().registerConfig(net.minecraftforge.fml.config.ModConfig.Type.COMMON, ModConfig.COMMON_CONFIG_SPEC); - FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onSetup); - FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onClientSetup); - MinecraftForge.EVENT_BUS.register(this); - } - - private void onSetup(final FMLCommonSetupEvent event) - { - CraftingHelper.register(OptionalRecipeCondition.Serializer.INSTANCE); - Networking.init(MODID); - } - - private void onClientSetup(final FMLClientSetupEvent event) - { - Overlay.TextOverlayGui.on_config(0.75, 0x00ffaa00, 0x55333333, 0x55333333, 0x55444444); - Networking.OverlayTextMessage.setHandler(Overlay.TextOverlayGui::show); - ModContent.registerMenuGuis(event); - ModContent.registerBlockEntityRenderers(event); - ModContent.processContentClientSide(event); - } - - @Mod.EventBusSubscriber(bus=Mod.EventBusSubscriber.Bus.MOD) - public static class ForgeEvents - { - @SubscribeEvent - public static void onConfigLoad(final ModConfigEvent.Loading event) - { ModConfig.apply(); } - - @SubscribeEvent - public static void onConfigReload(final ModConfigEvent.Reloading event) - { - try { - Auxiliaries.logger().info("Config file changed {}", event.getConfig().getFileName()); - ModConfig.apply(); - } catch(Throwable e) { - Auxiliaries.logger().error("Failed to load changed config: " + e.getMessage()); - } + public ModEngineersDecor() { + Auxiliaries.init(MODID, LOGGER, ModConfig::getServerConfig); + Auxiliaries.logGitVersion(MODNAME); + Registries.init(MODID, "sign_decor", FMLJavaModLoadingContext.get().getModEventBus()); + ModContent.init(MODID); + OptionalRecipeCondition.init(MODID, LOGGER); + ModLoadingContext.get().registerConfig(net.minecraftforge.fml.config.ModConfig.Type.SERVER, ModConfig.SERVER_CONFIG_SPEC); + ModLoadingContext.get().registerConfig(net.minecraftforge.fml.config.ModConfig.Type.COMMON, ModConfig.COMMON_CONFIG_SPEC); + FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onSetup); + FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onClientSetup); + MinecraftForge.EVENT_BUS.register(this); } - } - @SubscribeEvent - public void onPlayerEvent(final LivingEvent.LivingTickEvent event) - { - if(!(event.getEntity() instanceof final Player player)) return; - if(player.onClimbable()) EdLadderBlock.onPlayerUpdateEvent(player); - } + private void onSetup(final FMLCommonSetupEvent event) { + CraftingHelper.register(OptionalRecipeCondition.Serializer.INSTANCE); + Networking.init(MODID); + } - @OnlyIn(Dist.CLIENT) - @Mod.EventBusSubscriber(Dist.CLIENT) - public static class ForgeClientEvents - { - @SubscribeEvent - public static void onRenderGui(net.minecraftforge.client.event.RenderGuiOverlayEvent.Post event) - { Overlay.TextOverlayGui.INSTANCE.onRenderGui(event.getPoseStack()); } + private void onClientSetup(final FMLClientSetupEvent event) { + Overlay.TextOverlayGui.on_config(0.75, 0x00ffaa00, 0x55333333, 0x55333333, 0x55444444); + Networking.OverlayTextMessage.setHandler(Overlay.TextOverlayGui::show); + ModContent.registerMenuGuis(event); + ModContent.registerBlockEntityRenderers(event); + ModContent.processContentClientSide(event); + } @SubscribeEvent + public void onPlayerEvent(final LivingEvent.LivingTickEvent event) { + if (!(event.getEntity() instanceof final Player player)) return; + if (player.onClimbable()) EdLadderBlock.onPlayerUpdateEvent(player); + } + + @Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) + public static class ForgeEvents { + @SubscribeEvent + public static void onConfigLoad(final ModConfigEvent.Loading event) { + ModConfig.apply(); + } + + @SubscribeEvent + public static void onConfigReload(final ModConfigEvent.Reloading event) { + try { + Auxiliaries.logger().info("Config file changed {}", event.getConfig().getFileName()); + ModConfig.apply(); + } catch (Throwable e) { + Auxiliaries.logger().error("Failed to load changed config: " + e.getMessage()); + } + } + } + @OnlyIn(Dist.CLIENT) - public static void onRenderWorldOverlay(net.minecraftforge.client.event.RenderLevelStageEvent event) - { - if(event.getStage() == net.minecraftforge.client.event.RenderLevelStageEvent.Stage.AFTER_WEATHER) { - Overlay.TextOverlayGui.INSTANCE.onRenderWorldOverlay(event.getPoseStack(), event.getPartialTick()); - } + @Mod.EventBusSubscriber(Dist.CLIENT) + public static class ForgeClientEvents { + @SubscribeEvent + public static void onRenderGui(net.minecraftforge.client.event.RenderGuiOverlayEvent.Post event) { + Overlay.TextOverlayGui.INSTANCE.onRenderGui(event.getGuiGraphics()); + } + + @SubscribeEvent + @OnlyIn(Dist.CLIENT) + public static void onRenderWorldOverlay(net.minecraftforge.client.event.RenderLevelStageEvent event) { + if (event.getStage() == net.minecraftforge.client.event.RenderLevelStageEvent.Stage.AFTER_WEATHER) { + Overlay.TextOverlayGui.INSTANCE.onRenderWorldOverlay(event.getPoseStack(), event.getPartialTick()); + } + } } - } } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdBreaker.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdBreaker.java index d09dab8..a4f87ad 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdBreaker.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdBreaker.java @@ -8,6 +8,9 @@ */ package dev.zontreck.engineerdecor.blocks; +import dev.zontreck.engineerdecor.ModConfig; +import dev.zontreck.engineerdecor.ModContent; +import dev.zontreck.libzontreck.edlibmc.*; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.particles.ParticleTypes; @@ -28,6 +31,7 @@ import net.minecraft.world.level.GameRules; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.LiquidBlock; import net.minecraft.world.level.block.SoundType; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockBehaviour; @@ -41,14 +45,6 @@ import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.common.capabilities.ForgeCapabilities; import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.energy.IEnergyStorage; -import dev.zontreck.engineerdecor.ModConfig; -import dev.zontreck.engineerdecor.ModContent; -import dev.zontreck.engineerdecor.libmc.StandardBlocks; -import dev.zontreck.engineerdecor.libmc.StandardEntityBlocks; -import dev.zontreck.engineerdecor.libmc.Auxiliaries; -import dev.zontreck.engineerdecor.libmc.Inventories; -import dev.zontreck.engineerdecor.libmc.Overlay; -import dev.zontreck.engineerdecor.libmc.RfEnergy; import javax.annotation.Nullable; import java.util.HashSet; @@ -56,292 +52,299 @@ import java.util.List; import java.util.Random; -public class EdBreaker -{ - public static final int BOOST_FACTOR = 8; - public static final int DEFAULT_BOOST_ENERGY = 64; - public static final int DEFAULT_BREAKING_RELUCTANCE = 17; - public static final int DEFAULT_MIN_BREAKING_TIME = 15; - public static final int MAX_BREAKING_TIME = 800; +public class EdBreaker { + public static final int BOOST_FACTOR = 8; + public static final int DEFAULT_BOOST_ENERGY = 64; + public static final int DEFAULT_BREAKING_RELUCTANCE = 17; + public static final int DEFAULT_MIN_BREAKING_TIME = 15; + public static final int MAX_BREAKING_TIME = 800; - private static int boost_energy_consumption = DEFAULT_BOOST_ENERGY; - private static int energy_max = 32000; - private static int breaking_reluctance = DEFAULT_BREAKING_RELUCTANCE; - private static int min_breaking_time = DEFAULT_MIN_BREAKING_TIME; - private static boolean requires_power = false; + private static int boost_energy_consumption = DEFAULT_BOOST_ENERGY; + private static int energy_max = 32000; + private static int breaking_reluctance = DEFAULT_BREAKING_RELUCTANCE; + private static int min_breaking_time = DEFAULT_MIN_BREAKING_TIME; + private static boolean requires_power = false; - public static void on_config(int boost_energy_per_tick, int breaking_time_per_hardness, int min_breaking_time_ticks, boolean power_required) - { - final int interval = BreakerTileEntity.TICK_INTERVAL; - boost_energy_consumption = interval * Mth.clamp(boost_energy_per_tick, 4, 4096); - energy_max = Math.max(boost_energy_consumption * 10, 100000); - breaking_reluctance = Mth.clamp(breaking_time_per_hardness, 5, 50); - min_breaking_time = Mth.clamp(min_breaking_time_ticks, 10, 100); - requires_power = power_required; - ModConfig.log("Config block breaker: Boost energy consumption:" + (boost_energy_consumption/interval) + "rf/t, reluctance=" + breaking_reluctance + "t/hrdn, break time offset=" + min_breaking_time + "t."); - } + public static void on_config(int boost_energy_per_tick, int breaking_time_per_hardness, int min_breaking_time_ticks, boolean power_required) { + final int interval = BreakerTileEntity.TICK_INTERVAL; + boost_energy_consumption = interval * Mth.clamp(boost_energy_per_tick, 4, 4096); + energy_max = Math.max(boost_energy_consumption * 10, 100000); + breaking_reluctance = Mth.clamp(breaking_time_per_hardness, 5, 50); + min_breaking_time = Mth.clamp(min_breaking_time_ticks, 10, 100); + requires_power = power_required; + ModConfig.log("Config block breaker: Boost energy consumption:" + (boost_energy_consumption / interval) + "rf/t, reluctance=" + breaking_reluctance + "t/hrdn, break time offset=" + min_breaking_time + "t."); + } - //-------------------------------------------------------------------------------------------------------------------- - // Block - //-------------------------------------------------------------------------------------------------------------------- + //-------------------------------------------------------------------------------------------------------------------- + // Block + //-------------------------------------------------------------------------------------------------------------------- - public static class BreakerBlock extends StandardBlocks.HorizontalWaterLoggable implements StandardEntityBlocks.IStandardEntityBlock - { - public static final BooleanProperty ACTIVE = BooleanProperty.create("active"); + public static class BreakerBlock extends StandardBlocks.HorizontalWaterLoggable implements StandardEntityBlocks.IStandardEntityBlock { + public static final BooleanProperty ACTIVE = BooleanProperty.create("active"); - public BreakerBlock(long config, BlockBehaviour.Properties builder, final AABB[] unrotatedAABBs) - { super(config, builder, unrotatedAABBs); } - - @Override - public boolean isBlockEntityTicking(Level world, BlockState state) - { return true; } - - @Override - protected void createBlockStateDefinition(StateDefinition.Builder builder) - { super.createBlockStateDefinition(builder); builder.add(ACTIVE); } - - @Override - @Nullable - public BlockState getStateForPlacement(BlockPlaceContext context) - { return super.getStateForPlacement(context).setValue(ACTIVE, false); } - - @OnlyIn(Dist.CLIENT) - @SuppressWarnings("deprecation") - public void animateTick(BlockState state, Level world, BlockPos pos, Random rnd) - { - if((state.getBlock()!=this) || (!state.getValue(ACTIVE))) return; - // Sound - { - SoundEvent sound = SoundEvents.WOOD_HIT; - BlockState target_state = world.getBlockState(pos.relative(state.getValue(BreakerBlock.HORIZONTAL_FACING))); - SoundType stype = target_state.getBlock().getSoundType(target_state); - if((stype == SoundType.WOOL) || (stype == SoundType.GRASS) || (stype == SoundType.SNOW)) { - sound = SoundEvents.WOOL_HIT; - } else if((stype == SoundType.GRAVEL) || (stype == SoundType.SAND)) { - sound = SoundEvents.GRAVEL_HIT; + public BreakerBlock(long config, BlockBehaviour.Properties builder, final AABB[] unrotatedAABBs) { + super(config, builder, unrotatedAABBs); } - world.playLocalSound(pos.getX(), pos.getY(), pos.getZ(), sound, SoundSource.BLOCKS, 0.1f, 1.2f, false); - } - // Particles - { - final double rv = rnd.nextDouble(); - if(rv < 0.8) { - final double x=0.5+pos.getX(), y=0.5+pos.getY(), z=0.5+pos.getZ(); - final double xc=0.52, xr=rnd.nextDouble()*0.4-0.2, yr=(y-0.3+rnd.nextDouble()*0.2); - switch (state.getValue(HORIZONTAL_FACING)) { - case WEST -> world.addParticle(ParticleTypes.SMOKE, x - xc, yr, z + xr, 0.0, 0.0, 0.0); - case EAST -> world.addParticle(ParticleTypes.SMOKE, x + xc, yr, z + xr, 0.0, 0.0, 0.0); - case NORTH -> world.addParticle(ParticleTypes.SMOKE, x + xr, yr, z - xc, 0.0, 0.0, 0.0); - default -> world.addParticle(ParticleTypes.SMOKE, x + xr, yr, z + xc, 0.0, 0.0, 0.0); - } + + @Override + public boolean isBlockEntityTicking(Level world, BlockState state) { + return true; } - } - } - @Override - @SuppressWarnings("deprecation") - public void neighborChanged(BlockState state, Level world, BlockPos pos, Block block, BlockPos fromPos, boolean unused) - { - if(!(world instanceof Level) || (world.isClientSide)) return; - BlockEntity te = world.getBlockEntity(pos); - if(!(te instanceof BreakerTileEntity)) return; - ((BreakerTileEntity)te).block_updated(); - } - - @Override - @SuppressWarnings("deprecation") - public boolean isSignalSource(BlockState state) - { return true; } - - @Override - @SuppressWarnings("deprecation") - public int getSignal(BlockState blockState, BlockGetter blockAccess, BlockPos pos, Direction side) - { return 0; } - - @Override - @SuppressWarnings("deprecation") - public int getDirectSignal(BlockState blockState, BlockGetter blockAccess, BlockPos pos, Direction side) - { return 0; } - - @Override - @SuppressWarnings("deprecation") - public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) - { - if(world.isClientSide()) return InteractionResult.SUCCESS; - BlockEntity te = world.getBlockEntity(pos); - if(te instanceof BreakerTileEntity) ((BreakerTileEntity)te).state_message(player); - return InteractionResult.CONSUME; - } - } - - //-------------------------------------------------------------------------------------------------------------------- - // Tile entity - //-------------------------------------------------------------------------------------------------------------------- - - public static class BreakerTileEntity extends StandardEntityBlocks.StandardBlockEntity - { - public static final int IDLE_TICK_INTERVAL = 40; - public static final int TICK_INTERVAL = 5; - private int tick_timer_; - private int active_timer_; - private int proc_time_elapsed_; - private int time_needed_; - private final RfEnergy.Battery battery_ = new RfEnergy.Battery(energy_max, boost_energy_consumption, 0); - private final LazyOptional energy_handler_ = battery_.createEnergyHandler(); - - public BreakerTileEntity(BlockPos pos, BlockState state) - { super(ModContent.getBlockEntityTypeOfBlock(state.getBlock()), pos, state); } - - public void block_updated() - { if(tick_timer_ > 2) tick_timer_ = 2; } - - public void readnbt(CompoundTag nbt) - { battery_.load(nbt); } - - private void writenbt(CompoundTag nbt) - { battery_.save(nbt); } - - public void state_message(Player player) - { - String progress = "0"; - if((proc_time_elapsed_ > 0) && (time_needed_ > 0)) { - progress = Integer.toString((int)Mth.clamp((((double)proc_time_elapsed_) / ((double)time_needed_) * 100), 0, 100)); - } - Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.small_block_breaker.status", battery_.getSOC(), energy_max, progress)); - } - - // BlockEntity ------------------------------------------------------------------------------ - - @Override - public void load(CompoundTag nbt) - { super.load(nbt); readnbt(nbt); } - - @Override - protected void saveAdditional(CompoundTag nbt) - { super.saveAdditional(nbt); writenbt(nbt); } - - @Override - public void setRemoved() - { - super.setRemoved(); - energy_handler_.invalidate(); - } - - // Capability export ---------------------------------------------------------------------------- - - @Override - public LazyOptional getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable Direction facing) - { - if(capability == ForgeCapabilities.ENERGY) return energy_handler_.cast(); - return super.getCapability(capability, facing); - } - - // ITickable ------------------------------------------------------------------------------------ - - private static final HashSet blacklist = new HashSet<>(); - static { - blacklist.add(Blocks.AIR); - blacklist.add(Blocks.BEDROCK); - blacklist.add(Blocks.FIRE); - blacklist.add(Blocks.END_PORTAL); - blacklist.add(Blocks.END_GATEWAY); - blacklist.add(Blocks.END_PORTAL_FRAME); - blacklist.add(Blocks.NETHER_PORTAL); - blacklist.add(Blocks.BARRIER); - } - - private static boolean isBreakable(BlockState state, BlockPos pos, Level world) - { - final Block block = state.getBlock(); - if(blacklist.contains(block)) return false; - if(state.isAir()) return false; - if(state.getMaterial().isLiquid()) return false; - float bh = state.getDestroySpeed(world, pos); - return !((bh<0) || (bh>55)); - } - - private static void spawnBlockAsEntity(Level world, BlockPos pos, ItemStack stack) { - if(world.isClientSide || stack.isEmpty() || (!world.getGameRules().getBoolean(GameRules.RULE_DOBLOCKDROPS)) || world.restoringBlockSnapshots) return; - ItemEntity e = new ItemEntity(world, - ((world.random.nextFloat()*0.1)+0.5) + pos.getX(), - ((world.random.nextFloat()*0.1)+0.5) + pos.getY(), - ((world.random.nextFloat()*0.1)+0.5) + pos.getZ(), - stack - ); - e.setDefaultPickUpDelay(); - e.setDeltaMovement((world.random.nextFloat()*0.1-0.05), (world.random.nextFloat()*0.1-0.03), (world.random.nextFloat()*0.1-0.05)); - world.addFreshEntity(e); - } - - private static boolean canInsertInto(Level world, BlockPos pos) - { - // Maybe make a tag for that. The question is if it is actually worth it, or if that would be only - // tag spamming the game. So for now only FH and VH. - final BlockState state = world.getBlockState(pos); - return (state.getBlock() == ModContent.getBlock("factory_hopper")) || (state.getBlock() == Blocks.HOPPER); - } - - private boolean breakBlock(BlockState state, BlockPos pos, Level world) - { - if(world.isClientSide || (!(world instanceof ServerLevel)) || world.restoringBlockSnapshots) return false; // retry next cycle - List drops; - final Block block = state.getBlock(); - final boolean insert = canInsertInto(world, getBlockPos().below()); - drops = Block.getDrops(state, (ServerLevel)world, pos, world.getBlockEntity(pos)); - world.removeBlock(pos, false); - for(ItemStack drop:drops) { - if(!insert) { - spawnBlockAsEntity(world, pos, drop); - } else { - final ItemStack remaining = Inventories.insert(world, getBlockPos().below(), Direction.UP, drop, false); - if(!remaining.isEmpty()) spawnBlockAsEntity(world, pos, remaining); + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + super.createBlockStateDefinition(builder); + builder.add(ACTIVE); + } + + @Override + @Nullable + public BlockState getStateForPlacement(BlockPlaceContext context) { + return super.getStateForPlacement(context).setValue(ACTIVE, false); + } + + @OnlyIn(Dist.CLIENT) + @SuppressWarnings("deprecation") + public void animateTick(BlockState state, Level world, BlockPos pos, Random rnd) { + if ((state.getBlock() != this) || (!state.getValue(ACTIVE))) return; + // Sound + { + SoundEvent sound = SoundEvents.WOOD_HIT; + BlockState target_state = world.getBlockState(pos.relative(state.getValue(BreakerBlock.HORIZONTAL_FACING))); + SoundType stype = target_state.getBlock().getSoundType(target_state); + if ((stype == SoundType.WOOL) || (stype == SoundType.GRASS) || (stype == SoundType.SNOW)) { + sound = SoundEvents.WOOL_HIT; + } else if ((stype == SoundType.GRAVEL) || (stype == SoundType.SAND)) { + sound = SoundEvents.GRAVEL_HIT; + } + world.playLocalSound(pos.getX(), pos.getY(), pos.getZ(), sound, SoundSource.BLOCKS, 0.1f, 1.2f, false); + } + // Particles + { + final double rv = rnd.nextDouble(); + if (rv < 0.8) { + final double x = 0.5 + pos.getX(), y = 0.5 + pos.getY(), z = 0.5 + pos.getZ(); + final double xc = 0.52, xr = rnd.nextDouble() * 0.4 - 0.2, yr = (y - 0.3 + rnd.nextDouble() * 0.2); + switch (state.getValue(HORIZONTAL_FACING)) { + case WEST -> world.addParticle(ParticleTypes.SMOKE, x - xc, yr, z + xr, 0.0, 0.0, 0.0); + case EAST -> world.addParticle(ParticleTypes.SMOKE, x + xc, yr, z + xr, 0.0, 0.0, 0.0); + case NORTH -> world.addParticle(ParticleTypes.SMOKE, x + xr, yr, z - xc, 0.0, 0.0, 0.0); + default -> world.addParticle(ParticleTypes.SMOKE, x + xr, yr, z + xc, 0.0, 0.0, 0.0); + } + } + } + } + + @Override + @SuppressWarnings("deprecation") + public void neighborChanged(BlockState state, Level world, BlockPos pos, Block block, BlockPos fromPos, boolean unused) { + if (!(world instanceof Level) || (world.isClientSide)) return; + BlockEntity te = world.getBlockEntity(pos); + if (!(te instanceof BreakerTileEntity)) return; + ((BreakerTileEntity) te).block_updated(); + } + + @Override + @SuppressWarnings("deprecation") + public boolean isSignalSource(BlockState state) { + return true; + } + + @Override + @SuppressWarnings("deprecation") + public int getSignal(BlockState blockState, BlockGetter blockAccess, BlockPos pos, Direction side) { + return 0; + } + + @Override + @SuppressWarnings("deprecation") + public int getDirectSignal(BlockState blockState, BlockGetter blockAccess, BlockPos pos, Direction side) { + return 0; + } + + @Override + @SuppressWarnings("deprecation") + public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { + if (world.isClientSide()) return InteractionResult.SUCCESS; + BlockEntity te = world.getBlockEntity(pos); + if (te instanceof BreakerTileEntity) ((BreakerTileEntity) te).state_message(player); + return InteractionResult.CONSUME; } - } - SoundType stype = state.getBlock().getSoundType(state, world, pos, null); - if(stype != null) world.playSound(null, pos, stype.getPlaceSound(), SoundSource.BLOCKS, stype.getVolume()*0.6f, stype.getPitch()); - return true; } - @Override - @SuppressWarnings("deprecation") - public void tick() - { - if(--tick_timer_ > 0) return; - tick_timer_ = TICK_INTERVAL; - final BlockState device_state = level.getBlockState(worldPosition); - if(!(device_state.getBlock() instanceof BreakerBlock)) return; - final BlockPos target_pos = worldPosition.relative(device_state.getValue(BreakerBlock.HORIZONTAL_FACING)); - final BlockState target_state = level.getBlockState(target_pos); - if((level.hasNeighborSignal(worldPosition)) || (!isBreakable(target_state, target_pos, level))) { - if(device_state.getValue(BreakerBlock.ACTIVE)) level.setBlock(worldPosition, device_state.setValue(BreakerBlock.ACTIVE, false), 1|2); - proc_time_elapsed_ = 0; - tick_timer_ = IDLE_TICK_INTERVAL; - return; - } - time_needed_ = Mth.clamp((int)(target_state.getDestroySpeed(level, worldPosition) * breaking_reluctance) + min_breaking_time, min_breaking_time, MAX_BREAKING_TIME); - if(battery_.draw(boost_energy_consumption)) { - proc_time_elapsed_ += TICK_INTERVAL * (1+BOOST_FACTOR); - time_needed_ += min_breaking_time * (3*BOOST_FACTOR/5); - active_timer_ = 2; - } else if(!requires_power) { - proc_time_elapsed_ += TICK_INTERVAL; - active_timer_ = 1024; - } else if(active_timer_ > 0) { - --active_timer_; - } - boolean active = (active_timer_ > 0); - if(requires_power && !active) { - proc_time_elapsed_ = Math.max(0, proc_time_elapsed_ - 2*TICK_INTERVAL); - } - if(proc_time_elapsed_ >= time_needed_) { - proc_time_elapsed_ = 0; - breakBlock(target_state, target_pos, level); - active = false; - } - if(device_state.getValue(BreakerBlock.ACTIVE) != active) { - level.setBlock(worldPosition, device_state.setValue(BreakerBlock.ACTIVE, active), 1|2); - } - } + //-------------------------------------------------------------------------------------------------------------------- + // Tile entity + //-------------------------------------------------------------------------------------------------------------------- - } + public static class BreakerTileEntity extends StandardEntityBlocks.StandardBlockEntity { + public static final int IDLE_TICK_INTERVAL = 40; + public static final int TICK_INTERVAL = 5; + private static final HashSet blacklist = new HashSet<>(); + + static { + blacklist.add(Blocks.AIR); + blacklist.add(Blocks.BEDROCK); + blacklist.add(Blocks.FIRE); + blacklist.add(Blocks.END_PORTAL); + blacklist.add(Blocks.END_GATEWAY); + blacklist.add(Blocks.END_PORTAL_FRAME); + blacklist.add(Blocks.NETHER_PORTAL); + blacklist.add(Blocks.BARRIER); + } + + private final RfEnergy.Battery battery_ = new RfEnergy.Battery(energy_max, boost_energy_consumption, 0); + private final LazyOptional energy_handler_ = battery_.createEnergyHandler(); + private int tick_timer_; + private int active_timer_; + private int proc_time_elapsed_; + private int time_needed_; + + public BreakerTileEntity(BlockPos pos, BlockState state) { + super(ModContent.getBlockEntityTypeOfBlock(state.getBlock()), pos, state); + } + + private static boolean isBreakable(BlockState state, BlockPos pos, Level world) { + final Block block = state.getBlock(); + if (blacklist.contains(block)) return false; + if (state.isAir()) return false; + if (state.getBlock() instanceof LiquidBlock) return false; + float bh = state.getDestroySpeed(world, pos); + return !((bh < 0) || (bh > 55)); + } + + private static void spawnBlockAsEntity(Level world, BlockPos pos, ItemStack stack) { + if (world.isClientSide || stack.isEmpty() || (!world.getGameRules().getBoolean(GameRules.RULE_DOBLOCKDROPS)) || world.restoringBlockSnapshots) + return; + ItemEntity e = new ItemEntity(world, + ((world.random.nextFloat() * 0.1) + 0.5) + pos.getX(), + ((world.random.nextFloat() * 0.1) + 0.5) + pos.getY(), + ((world.random.nextFloat() * 0.1) + 0.5) + pos.getZ(), + stack + ); + e.setDefaultPickUpDelay(); + e.setDeltaMovement((world.random.nextFloat() * 0.1 - 0.05), (world.random.nextFloat() * 0.1 - 0.03), (world.random.nextFloat() * 0.1 - 0.05)); + world.addFreshEntity(e); + } + + // BlockEntity ------------------------------------------------------------------------------ + + private static boolean canInsertInto(Level world, BlockPos pos) { + // Maybe make a tag for that. The question is if it is actually worth it, or if that would be only + // tag spamming the game. So for now only FH and VH. + final BlockState state = world.getBlockState(pos); + return (state.getBlock() == ModContent.getBlock("factory_hopper")) || (state.getBlock() == Blocks.HOPPER); + } + + public void block_updated() { + if (tick_timer_ > 2) tick_timer_ = 2; + } + + public void readnbt(CompoundTag nbt) { + battery_.load(nbt); + } + + // Capability export ---------------------------------------------------------------------------- + + private void writenbt(CompoundTag nbt) { + battery_.save(nbt); + } + + // ITickable ------------------------------------------------------------------------------------ + + public void state_message(Player player) { + String progress = "0"; + if ((proc_time_elapsed_ > 0) && (time_needed_ > 0)) { + progress = Integer.toString((int) Mth.clamp((((double) proc_time_elapsed_) / ((double) time_needed_) * 100), 0, 100)); + } + Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.small_block_breaker.status", battery_.getSOC(), energy_max, progress)); + } + + @Override + public void load(CompoundTag nbt) { + super.load(nbt); + readnbt(nbt); + } + + @Override + protected void saveAdditional(CompoundTag nbt) { + super.saveAdditional(nbt); + writenbt(nbt); + } + + @Override + public void setRemoved() { + super.setRemoved(); + energy_handler_.invalidate(); + } + + @Override + public LazyOptional getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable Direction facing) { + if (capability == ForgeCapabilities.ENERGY) return energy_handler_.cast(); + return super.getCapability(capability, facing); + } + + private boolean breakBlock(BlockState state, BlockPos pos, Level world) { + if (world.isClientSide || (!(world instanceof ServerLevel)) || world.restoringBlockSnapshots) + return false; // retry next cycle + List drops; + final Block block = state.getBlock(); + final boolean insert = canInsertInto(world, getBlockPos().below()); + drops = Block.getDrops(state, (ServerLevel) world, pos, world.getBlockEntity(pos)); + world.removeBlock(pos, false); + for (ItemStack drop : drops) { + if (!insert) { + spawnBlockAsEntity(world, pos, drop); + } else { + final ItemStack remaining = Inventories.insert(world, getBlockPos().below(), Direction.UP, drop, false); + if (!remaining.isEmpty()) spawnBlockAsEntity(world, pos, remaining); + } + } + SoundType stype = state.getBlock().getSoundType(state, world, pos, null); + if (stype != null) + world.playSound(null, pos, stype.getPlaceSound(), SoundSource.BLOCKS, stype.getVolume() * 0.6f, stype.getPitch()); + return true; + } + + @Override + @SuppressWarnings("deprecation") + public void tick() { + if (--tick_timer_ > 0) return; + tick_timer_ = TICK_INTERVAL; + final BlockState device_state = level.getBlockState(worldPosition); + if (!(device_state.getBlock() instanceof BreakerBlock)) return; + final BlockPos target_pos = worldPosition.relative(device_state.getValue(BreakerBlock.HORIZONTAL_FACING)); + final BlockState target_state = level.getBlockState(target_pos); + if ((level.hasNeighborSignal(worldPosition)) || (!isBreakable(target_state, target_pos, level))) { + if (device_state.getValue(BreakerBlock.ACTIVE)) + level.setBlock(worldPosition, device_state.setValue(BreakerBlock.ACTIVE, false), 1 | 2); + proc_time_elapsed_ = 0; + tick_timer_ = IDLE_TICK_INTERVAL; + return; + } + time_needed_ = Mth.clamp((int) (target_state.getDestroySpeed(level, worldPosition) * breaking_reluctance) + min_breaking_time, min_breaking_time, MAX_BREAKING_TIME); + if (battery_.draw(boost_energy_consumption)) { + proc_time_elapsed_ += TICK_INTERVAL * (1 + BOOST_FACTOR); + time_needed_ += min_breaking_time * (3 * BOOST_FACTOR / 5); + active_timer_ = 2; + } else if (!requires_power) { + proc_time_elapsed_ += TICK_INTERVAL; + active_timer_ = 1024; + } else if (active_timer_ > 0) { + --active_timer_; + } + boolean active = (active_timer_ > 0); + if (requires_power && !active) { + proc_time_elapsed_ = Math.max(0, proc_time_elapsed_ - 2 * TICK_INTERVAL); + } + if (proc_time_elapsed_ >= time_needed_) { + proc_time_elapsed_ = 0; + breakBlock(target_state, target_pos, level); + active = false; + } + if (device_state.getValue(BreakerBlock.ACTIVE) != active) { + level.setBlock(worldPosition, device_state.setValue(BreakerBlock.ACTIVE, active), 1 | 2); + } + } + + } } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdCatwalkBlock.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdCatwalkBlock.java index 5f18528..7fc4509 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdCatwalkBlock.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdCatwalkBlock.java @@ -8,6 +8,8 @@ */ package dev.zontreck.engineerdecor.blocks; +import dev.zontreck.libzontreck.edlibmc.Inventories; +import dev.zontreck.libzontreck.edlibmc.StandardBlocks; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.sounds.SoundEvents; @@ -31,8 +33,6 @@ import net.minecraft.world.level.material.Fluids; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.Vec3; -import dev.zontreck.engineerdecor.libmc.StandardBlocks; -import dev.zontreck.engineerdecor.libmc.Inventories; import javax.annotation.Nullable; import java.util.ArrayList; @@ -41,96 +41,100 @@ import java.util.Collections; import java.util.List; -public class EdCatwalkBlock extends StandardBlocks.HorizontalFourWayWaterLoggable -{ - final Block railing_block; - final AABB base_aabb; +public class EdCatwalkBlock extends StandardBlocks.HorizontalFourWayWaterLoggable { + final Block railing_block; + final AABB base_aabb; - public EdCatwalkBlock(long config, BlockBehaviour.Properties properties, final AABB base_aabb, final AABB railing_aabb, final Block railing_block) - { super(config, properties, base_aabb, railing_aabb, 0); this.railing_block = railing_block; this.base_aabb=base_aabb; } - - @Override - public boolean propagatesSkylightDown(BlockState state, BlockGetter reader, BlockPos pos) - { return true; } - - @Override - @Nullable - public BlockState getStateForPlacement(BlockPlaceContext context) - { return super.getStateForPlacement(context).setValue(NORTH, false).setValue(EAST, false).setValue(SOUTH, false).setValue(WEST, false); } - - public static boolean place_consume(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, int shrink) - { - if(!world.setBlock(pos, state, Block.UPDATE_ALL)) return false; - world.playSound(player, pos, SoundEvents.METAL_PLACE, SoundSource.BLOCKS, 1f, 1f); - if((!player.isCreative()) && (!world.isClientSide())) { - ItemStack stack = player.getItemInHand(hand); - if(shrink >= 0) { - stack.shrink(shrink); - } else if(stack.getCount() < stack.getMaxStackSize()) { - stack.grow(Math.abs(shrink)); - } else { - Inventories.give(player, new ItemStack(stack.getItem(), Math.abs(shrink))); - } - Inventories.setItemInPlayerHand(player, hand, stack); + public EdCatwalkBlock(long config, BlockBehaviour.Properties properties, final AABB base_aabb, final AABB railing_aabb, final Block railing_block) { + super(config, properties, base_aabb, railing_aabb, 0); + this.railing_block = railing_block; + this.base_aabb = base_aabb; } - return true; - } - @Override - @SuppressWarnings("deprecation") - public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) - { - final Item item = player.getItemInHand(hand).getItem(); - if((!(item instanceof BlockItem))) return InteractionResult.PASS; - final Block block = ((BlockItem)item).getBlock(); - if(block == this) { - if(hit.getDirection().getAxis().isHorizontal()) return InteractionResult.PASS; // place new block on the clicked side. - BlockPos adjacent_pos = pos.relative(player.getDirection()); - BlockState adjacent_state = world.getBlockState(adjacent_pos); - if(adjacent_state.canBeReplaced(new DirectionalPlaceContext(world, adjacent_pos, hit.getDirection().getOpposite(), player.getItemInHand(hand), hit.getDirection()))) { - BlockState place_state = defaultBlockState(); - place_state = place_state.setValue(WATERLOGGED,adjacent_state.getFluidState().getType()==Fluids.WATER); - place_consume(place_state, world, adjacent_pos, player, hand, 1); - } - return InteractionResult.sidedSuccess(world.isClientSide()); + public static boolean place_consume(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, int shrink) { + if (!world.setBlock(pos, state, Block.UPDATE_ALL)) return false; + world.playSound(player, pos, SoundEvents.METAL_PLACE, SoundSource.BLOCKS, 1f, 1f); + if ((!player.isCreative()) && (!world.isClientSide())) { + ItemStack stack = player.getItemInHand(hand); + if (shrink >= 0) { + stack.shrink(shrink); + } else if (stack.getCount() < stack.getMaxStackSize()) { + stack.grow(Math.abs(shrink)); + } else { + Inventories.give(player, new ItemStack(stack.getItem(), Math.abs(shrink))); + } + Inventories.setItemInPlayerHand(player, hand, stack); + } + return true; } - if(block == railing_block) { - Direction face = hit.getDirection(); - final Vec3 rhv = hit.getLocation().subtract(Vec3.atCenterOf(hit.getBlockPos())); - if(face.getAxis().isHorizontal()) { - // Side or railing clicked - if(rhv.multiply(Vec3.atLowerCornerOf(face.getNormal())).scale(2).lengthSqr() < 0.99) face = face.getOpposite(); // click on railing, not the outer side. - } else if(player.distanceToSqr(Vec3.atCenterOf(pos)) < 3) { - // near accurate placement - face = Direction.getNearest(rhv.x, 0, rhv.z); - } else { - // far automatic placement - face = Direction.getNearest(player.getLookAngle().x, 0, player.getLookAngle().z); - List free_sides = Arrays.stream(Direction.values()).filter(d -> d.getAxis().isHorizontal() && (world.getBlockState(pos.relative(d)).getBlock() != this)).toList(); - if(free_sides.isEmpty()) return InteractionResult.sidedSuccess(world.isClientSide()); - if(!free_sides.contains(face)) face = free_sides.get(0); - } - BooleanProperty railing = getDirectionProperty(face); - boolean add = (!state.getValue(railing)); - place_consume(state.setValue(railing, add), world, pos, player, hand, add ? 1 : -1); - return InteractionResult.sidedSuccess(world.isClientSide()); + + @Override + public boolean propagatesSkylightDown(BlockState state, BlockGetter reader, BlockPos pos) { + return true; } - return InteractionResult.PASS; - } - @Override - public boolean hasDynamicDropList() - { return true; } + @Override + @Nullable + public BlockState getStateForPlacement(BlockPlaceContext context) { + return super.getStateForPlacement(context).setValue(NORTH, false).setValue(EAST, false).setValue(SOUTH, false).setValue(WEST, false); + } - @Override - public List dropList(BlockState state, Level world, @Nullable BlockEntity te, boolean explosion) - { - if(world.isClientSide()) return Collections.singletonList(ItemStack.EMPTY); - List drops = new ArrayList<>(); - drops.add(new ItemStack(state.getBlock().asItem())); - int n = (state.getValue(NORTH)?1:0)+(state.getValue(EAST)?1:0)+(state.getValue(SOUTH)?1:0)+(state.getValue(WEST)?1:0); - if(n > 0) drops.add(new ItemStack(railing_block, n)); - return drops; - } + @Override + @SuppressWarnings("deprecation") + public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { + final Item item = player.getItemInHand(hand).getItem(); + if ((!(item instanceof BlockItem))) return InteractionResult.PASS; + final Block block = ((BlockItem) item).getBlock(); + if (block == this) { + if (hit.getDirection().getAxis().isHorizontal()) + return InteractionResult.PASS; // place new block on the clicked side. + BlockPos adjacent_pos = pos.relative(player.getDirection()); + BlockState adjacent_state = world.getBlockState(adjacent_pos); + if (adjacent_state.canBeReplaced(new DirectionalPlaceContext(world, adjacent_pos, hit.getDirection().getOpposite(), player.getItemInHand(hand), hit.getDirection()))) { + BlockState place_state = defaultBlockState(); + place_state = place_state.setValue(WATERLOGGED, adjacent_state.getFluidState().getType() == Fluids.WATER); + place_consume(place_state, world, adjacent_pos, player, hand, 1); + } + return InteractionResult.sidedSuccess(world.isClientSide()); + } + if (block == railing_block) { + Direction face = hit.getDirection(); + final Vec3 rhv = hit.getLocation().subtract(Vec3.atCenterOf(hit.getBlockPos())); + if (face.getAxis().isHorizontal()) { + // Side or railing clicked + if (rhv.multiply(Vec3.atLowerCornerOf(face.getNormal())).scale(2).lengthSqr() < 0.99) + face = face.getOpposite(); // click on railing, not the outer side. + } else if (player.distanceToSqr(Vec3.atCenterOf(pos)) < 3) { + // near accurate placement + face = Direction.getNearest(rhv.x, 0, rhv.z); + } else { + // far automatic placement + face = Direction.getNearest(player.getLookAngle().x, 0, player.getLookAngle().z); + List free_sides = Arrays.stream(Direction.values()).filter(d -> d.getAxis().isHorizontal() && (world.getBlockState(pos.relative(d)).getBlock() != this)).toList(); + if (free_sides.isEmpty()) return InteractionResult.sidedSuccess(world.isClientSide()); + if (!free_sides.contains(face)) face = free_sides.get(0); + } + BooleanProperty railing = getDirectionProperty(face); + boolean add = (!state.getValue(railing)); + place_consume(state.setValue(railing, add), world, pos, player, hand, add ? 1 : -1); + return InteractionResult.sidedSuccess(world.isClientSide()); + } + return InteractionResult.PASS; + } + + @Override + public boolean hasDynamicDropList() { + return true; + } + + @Override + public List dropList(BlockState state, Level world, @Nullable BlockEntity te, boolean explosion) { + if (world.isClientSide()) return Collections.singletonList(ItemStack.EMPTY); + List drops = new ArrayList<>(); + drops.add(new ItemStack(state.getBlock().asItem())); + int n = (state.getValue(NORTH) ? 1 : 0) + (state.getValue(EAST) ? 1 : 0) + (state.getValue(SOUTH) ? 1 : 0) + (state.getValue(WEST) ? 1 : 0); + if (n > 0) drops.add(new ItemStack(railing_block, n)); + return drops; + } } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdCatwalkStairsBlock.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdCatwalkStairsBlock.java index a49ff35..dca83c8 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdCatwalkStairsBlock.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdCatwalkStairsBlock.java @@ -8,6 +8,9 @@ */ package dev.zontreck.engineerdecor.blocks; +import dev.zontreck.engineerdecor.ModContent; +import dev.zontreck.libzontreck.edlibmc.Auxiliaries; +import dev.zontreck.libzontreck.edlibmc.StandardBlocks; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.InteractionHand; @@ -34,153 +37,156 @@ import net.minecraft.world.phys.shapes.BooleanOp; import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; -import dev.zontreck.engineerdecor.ModContent; -import dev.zontreck.engineerdecor.libmc.StandardBlocks; -import dev.zontreck.engineerdecor.libmc.Auxiliaries; import javax.annotation.Nullable; import java.util.*; -public class EdCatwalkStairsBlock extends StandardBlocks.HorizontalWaterLoggable -{ - public static final BooleanProperty RIGHT_RAILING = BooleanProperty.create("right_railing"); - public static final BooleanProperty LEFT_RAILING = BooleanProperty.create("left_railing"); - protected final Map shapes; - protected final Map collision_shapes; - protected final Map y_rotations; +public class EdCatwalkStairsBlock extends StandardBlocks.HorizontalWaterLoggable { + public static final BooleanProperty RIGHT_RAILING = BooleanProperty.create("right_railing"); + public static final BooleanProperty LEFT_RAILING = BooleanProperty.create("left_railing"); + protected final Map shapes; + protected final Map collision_shapes; + protected final Map y_rotations; - public EdCatwalkStairsBlock(long config, BlockBehaviour.Properties properties, final AABB[] base_aabb, final AABB[] railing_aabbs) - { - super(config, properties, base_aabb); - Map sh = new HashMap<>(); - Map csh = new HashMap<>(); - getStateDefinition().getPossibleStates().forEach(state->{ - Direction facing = state.getValue(HORIZONTAL_FACING); - VoxelShape base_shape = Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(base_aabb, facing, true)); - if(state.getValue(RIGHT_RAILING)) { - VoxelShape right_shape = Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(Auxiliaries.getMirroredAABB(railing_aabbs, Direction.Axis.X), facing, true)); - base_shape = Shapes.joinUnoptimized(base_shape, right_shape, BooleanOp.OR); - } - if(state.getValue(LEFT_RAILING)) { - VoxelShape left_shape = Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(railing_aabbs, facing, true)); - base_shape = Shapes.joinUnoptimized(base_shape, left_shape, BooleanOp.OR); - } - sh.put(state, base_shape); - csh.put(state, base_shape); - }); - shapes = sh; - collision_shapes = csh; - y_rotations = new HashMap<>(); - y_rotations.put(Direction.NORTH, 0); - y_rotations.put(Direction.EAST, 1); - y_rotations.put(Direction.SOUTH, 2); - y_rotations.put(Direction.WEST, 3); - y_rotations.put(Direction.UP, 0); - y_rotations.put(Direction.DOWN, 0); - registerDefaultState(super.defaultBlockState().setValue(LEFT_RAILING, false).setValue(RIGHT_RAILING, false)); - } - - @Override - public VoxelShape getShape(BlockState state, BlockGetter worldIn, BlockPos pos, CollisionContext context) - { return shapes.getOrDefault(state, Shapes.block()); } - - @Override - public VoxelShape getCollisionShape(BlockState state, BlockGetter worldIn, BlockPos pos, CollisionContext context) - { return collision_shapes.getOrDefault(state, Shapes.block()); } - - @Override - protected void createBlockStateDefinition(StateDefinition.Builder builder) - { super.createBlockStateDefinition(builder); builder.add(RIGHT_RAILING, LEFT_RAILING); } - - @Override - public boolean propagatesSkylightDown(BlockState state, BlockGetter reader, BlockPos pos) - { return true; } - - @Override - @Nullable - public BlockState getStateForPlacement(BlockPlaceContext context) - { return super.getStateForPlacement(context); } - - @Override - @SuppressWarnings("deprecation") - public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) - { - final Item item = player.getItemInHand(hand).getItem(); - if((!(item instanceof BlockItem))) return InteractionResult.PASS; - final Block block = ((BlockItem)item).getBlock(); - final Direction facing = state.getValue(HORIZONTAL_FACING); - if(block == this) { - final Direction hlv = Arrays.stream(Direction.orderedByNearest(player)).filter(d->d.getAxis().isHorizontal()).findFirst().orElse(Direction.NORTH); - BlockPos adjacent_pos; - if(hlv == facing) { - adjacent_pos = pos.above().relative(hlv); - } else if(hlv == facing.getOpposite()) { - adjacent_pos = pos.below().relative(hlv); - } else { - return world.isClientSide() ? InteractionResult.SUCCESS : InteractionResult.CONSUME; - } - final BlockState adjacent_state = world.getBlockState(adjacent_pos); - if(adjacent_state == null) return world.isClientSide() ? InteractionResult.SUCCESS : InteractionResult.CONSUME; - if(!adjacent_state.canBeReplaced(new DirectionalPlaceContext(world, adjacent_pos, hit.getDirection().getOpposite(), player.getItemInHand(hand), hit.getDirection()))) return InteractionResult.CONSUME; - BlockState place_state = defaultBlockState().setValue(HORIZONTAL_FACING, facing); - place_state = place_state.setValue(WATERLOGGED,adjacent_state.getFluidState().getType()==Fluids.WATER); - EdCatwalkBlock.place_consume(place_state, world, adjacent_pos, player, hand, 1); - return world.isClientSide() ? InteractionResult.SUCCESS : InteractionResult.CONSUME; - } else if((block == ModContent.getBlock("steel_catwalk")) || (block == ModContent.getBlock("steel_catwalk_ta"))) { - BlockPos adjacent_pos; - adjacent_pos = pos.relative(facing); - final BlockState adjacent_state = world.getBlockState(adjacent_pos); - if(adjacent_state == null) return InteractionResult.CONSUME; - if(!adjacent_state.canBeReplaced(new DirectionalPlaceContext(world, adjacent_pos, hit.getDirection().getOpposite(), player.getItemInHand(hand), hit.getDirection()))) return InteractionResult.CONSUME; - BlockState place_state = ModContent.getBlock("steel_catwalk_ta").defaultBlockState(); // ModContent.STEEL_CATWALK_TOP_ALIGNED - place_state = place_state.setValue(WATERLOGGED,adjacent_state.getFluidState().getType()==Fluids.WATER); - EdCatwalkBlock.place_consume(place_state, world, adjacent_pos, player, hand, 1); - return world.isClientSide() ? InteractionResult.SUCCESS : InteractionResult.CONSUME; - } else if(block == ModContent.getBlock("steel_railing")) { - Direction face = hit.getDirection(); - int shrink = 0; - BlockState place_state = state; - if(face == Direction.UP) { - Vec3 rhv = hit.getLocation().subtract(Vec3.atCenterOf(hit.getBlockPos())).multiply(new Vec3(1,0,1)).cross(Vec3.atLowerCornerOf(facing.getNormal())); - face = (rhv.y > 0) ? (facing.getClockWise()) : (facing.getCounterClockWise()); - } - if(face == facing.getClockWise()) { - if(state.getValue(RIGHT_RAILING)) { - place_state = state.setValue(RIGHT_RAILING, false); - shrink = -1; - } else { - place_state = state.setValue(RIGHT_RAILING, true); - shrink = 1; - } - } else if(face == facing.getCounterClockWise()) { - if(state.getValue(LEFT_RAILING)) { - place_state = state.setValue(LEFT_RAILING, false); - shrink = -1; - } else { - place_state = state.setValue(LEFT_RAILING, true); - shrink = 1; - } - } - if(shrink != 0) EdCatwalkBlock.place_consume(place_state, world, pos, player, hand, shrink); - return world.isClientSide() ? InteractionResult.SUCCESS : InteractionResult.CONSUME; + public EdCatwalkStairsBlock(long config, BlockBehaviour.Properties properties, final AABB[] base_aabb, final AABB[] railing_aabbs) { + super(config, properties, base_aabb); + Map sh = new HashMap<>(); + Map csh = new HashMap<>(); + getStateDefinition().getPossibleStates().forEach(state -> { + Direction facing = state.getValue(HORIZONTAL_FACING); + VoxelShape base_shape = Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(base_aabb, facing, true)); + if (state.getValue(RIGHT_RAILING)) { + VoxelShape right_shape = Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(Auxiliaries.getMirroredAABB(railing_aabbs, Direction.Axis.X), facing, true)); + base_shape = Shapes.joinUnoptimized(base_shape, right_shape, BooleanOp.OR); + } + if (state.getValue(LEFT_RAILING)) { + VoxelShape left_shape = Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(railing_aabbs, facing, true)); + base_shape = Shapes.joinUnoptimized(base_shape, left_shape, BooleanOp.OR); + } + sh.put(state, base_shape); + csh.put(state, base_shape); + }); + shapes = sh; + collision_shapes = csh; + y_rotations = new HashMap<>(); + y_rotations.put(Direction.NORTH, 0); + y_rotations.put(Direction.EAST, 1); + y_rotations.put(Direction.SOUTH, 2); + y_rotations.put(Direction.WEST, 3); + y_rotations.put(Direction.UP, 0); + y_rotations.put(Direction.DOWN, 0); + registerDefaultState(super.defaultBlockState().setValue(LEFT_RAILING, false).setValue(RIGHT_RAILING, false)); } - return InteractionResult.PASS; - } - @Override - public boolean hasDynamicDropList() - { return true; } + @Override + public VoxelShape getShape(BlockState state, BlockGetter worldIn, BlockPos pos, CollisionContext context) { + return shapes.getOrDefault(state, Shapes.block()); + } - @Override - public List dropList(BlockState state, Level world, @Nullable BlockEntity te, boolean explosion) - { - if(world.isClientSide()) return Collections.singletonList(ItemStack.EMPTY); - List drops = new ArrayList<>(); - drops.add(new ItemStack(state.getBlock().asItem())); - int n = (state.getValue(LEFT_RAILING)?1:0)+(state.getValue(RIGHT_RAILING)?1:0); - if(n > 0) drops.add(new ItemStack(ModContent.getBlock("steel_railing"), n)); - return drops; - } + @Override + public VoxelShape getCollisionShape(BlockState state, BlockGetter worldIn, BlockPos pos, CollisionContext context) { + return collision_shapes.getOrDefault(state, Shapes.block()); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + super.createBlockStateDefinition(builder); + builder.add(RIGHT_RAILING, LEFT_RAILING); + } + + @Override + public boolean propagatesSkylightDown(BlockState state, BlockGetter reader, BlockPos pos) { + return true; + } + + @Override + @Nullable + public BlockState getStateForPlacement(BlockPlaceContext context) { + return super.getStateForPlacement(context); + } + + @Override + @SuppressWarnings("deprecation") + public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { + final Item item = player.getItemInHand(hand).getItem(); + if ((!(item instanceof BlockItem))) return InteractionResult.PASS; + final Block block = ((BlockItem) item).getBlock(); + final Direction facing = state.getValue(HORIZONTAL_FACING); + if (block == this) { + final Direction hlv = Arrays.stream(Direction.orderedByNearest(player)).filter(d -> d.getAxis().isHorizontal()).findFirst().orElse(Direction.NORTH); + BlockPos adjacent_pos; + if (hlv == facing) { + adjacent_pos = pos.above().relative(hlv); + } else if (hlv == facing.getOpposite()) { + adjacent_pos = pos.below().relative(hlv); + } else { + return world.isClientSide() ? InteractionResult.SUCCESS : InteractionResult.CONSUME; + } + final BlockState adjacent_state = world.getBlockState(adjacent_pos); + if (adjacent_state == null) + return world.isClientSide() ? InteractionResult.SUCCESS : InteractionResult.CONSUME; + if (!adjacent_state.canBeReplaced(new DirectionalPlaceContext(world, adjacent_pos, hit.getDirection().getOpposite(), player.getItemInHand(hand), hit.getDirection()))) + return InteractionResult.CONSUME; + BlockState place_state = defaultBlockState().setValue(HORIZONTAL_FACING, facing); + place_state = place_state.setValue(WATERLOGGED, adjacent_state.getFluidState().getType() == Fluids.WATER); + EdCatwalkBlock.place_consume(place_state, world, adjacent_pos, player, hand, 1); + return world.isClientSide() ? InteractionResult.SUCCESS : InteractionResult.CONSUME; + } else if ((block == ModContent.getBlock("steel_catwalk")) || (block == ModContent.getBlock("steel_catwalk_ta"))) { + BlockPos adjacent_pos; + adjacent_pos = pos.relative(facing); + final BlockState adjacent_state = world.getBlockState(adjacent_pos); + if (adjacent_state == null) return InteractionResult.CONSUME; + if (!adjacent_state.canBeReplaced(new DirectionalPlaceContext(world, adjacent_pos, hit.getDirection().getOpposite(), player.getItemInHand(hand), hit.getDirection()))) + return InteractionResult.CONSUME; + BlockState place_state = ModContent.getBlock("steel_catwalk_ta").defaultBlockState(); // ModContent.STEEL_CATWALK_TOP_ALIGNED + place_state = place_state.setValue(WATERLOGGED, adjacent_state.getFluidState().getType() == Fluids.WATER); + EdCatwalkBlock.place_consume(place_state, world, adjacent_pos, player, hand, 1); + return world.isClientSide() ? InteractionResult.SUCCESS : InteractionResult.CONSUME; + } else if (block == ModContent.getBlock("steel_railing")) { + Direction face = hit.getDirection(); + int shrink = 0; + BlockState place_state = state; + if (face == Direction.UP) { + Vec3 rhv = hit.getLocation().subtract(Vec3.atCenterOf(hit.getBlockPos())).multiply(new Vec3(1, 0, 1)).cross(Vec3.atLowerCornerOf(facing.getNormal())); + face = (rhv.y > 0) ? (facing.getClockWise()) : (facing.getCounterClockWise()); + } + if (face == facing.getClockWise()) { + if (state.getValue(RIGHT_RAILING)) { + place_state = state.setValue(RIGHT_RAILING, false); + shrink = -1; + } else { + place_state = state.setValue(RIGHT_RAILING, true); + shrink = 1; + } + } else if (face == facing.getCounterClockWise()) { + if (state.getValue(LEFT_RAILING)) { + place_state = state.setValue(LEFT_RAILING, false); + shrink = -1; + } else { + place_state = state.setValue(LEFT_RAILING, true); + shrink = 1; + } + } + if (shrink != 0) EdCatwalkBlock.place_consume(place_state, world, pos, player, hand, shrink); + return world.isClientSide() ? InteractionResult.SUCCESS : InteractionResult.CONSUME; + } + return InteractionResult.PASS; + } + + @Override + public boolean hasDynamicDropList() { + return true; + } + + @Override + public List dropList(BlockState state, Level world, @Nullable BlockEntity te, boolean explosion) { + if (world.isClientSide()) return Collections.singletonList(ItemStack.EMPTY); + List drops = new ArrayList<>(); + drops.add(new ItemStack(state.getBlock().asItem())); + int n = (state.getValue(LEFT_RAILING) ? 1 : 0) + (state.getValue(RIGHT_RAILING) ? 1 : 0); + if (n > 0) drops.add(new ItemStack(ModContent.getBlock("steel_railing"), n)); + return drops; + } } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdCatwalkTopAlignedBlock.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdCatwalkTopAlignedBlock.java index 2fd81bb..49387fa 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdCatwalkTopAlignedBlock.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdCatwalkTopAlignedBlock.java @@ -8,6 +8,8 @@ */ package dev.zontreck.engineerdecor.blocks; +import dev.zontreck.engineerdecor.ModContent; +import dev.zontreck.libzontreck.edlibmc.StandardBlocks; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.InteractionHand; @@ -32,8 +34,6 @@ import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; -import dev.zontreck.engineerdecor.ModContent; -import dev.zontreck.engineerdecor.libmc.StandardBlocks; import javax.annotation.Nullable; import java.util.ArrayList; @@ -42,109 +42,113 @@ import java.util.List; import java.util.stream.Collectors; -public class EdCatwalkTopAlignedBlock extends StandardBlocks.WaterLoggable -{ - public static final IntegerProperty VARIANT = IntegerProperty.create("variant", 0, 4); - protected final List variant_shapes; - protected final Block inset_light_block; +public class EdCatwalkTopAlignedBlock extends StandardBlocks.WaterLoggable { + public static final IntegerProperty VARIANT = IntegerProperty.create("variant", 0, 4); + protected final List variant_shapes; + protected final Block inset_light_block; - public EdCatwalkTopAlignedBlock(long config, BlockBehaviour.Properties properties, final VoxelShape[] variant_shapes, final Block inset_light_block) - { - super(config, properties, variant_shapes[0]); - registerDefaultState(super.defaultBlockState().setValue(VARIANT, 0)); - this.variant_shapes = VARIANT.getPossibleValues().stream().map(i->(i builder) - { super.createBlockStateDefinition(builder); builder.add(VARIANT); } - - @Override - @Nullable - public BlockState getStateForPlacement(BlockPlaceContext context) - { - BlockState state = adapted_state(super.getStateForPlacement(context), context.getLevel(), context.getClickedPos()); - if(context.getClickedFace() != Direction.UP) return state; - BlockState below = context.getLevel().getBlockState(context.getClickedPos().below()); - if((state.getValue(VARIANT)==0) && (below.isFaceSturdy(context.getLevel(), context.getClickedPos().below(), Direction.UP))) return state.setValue(VARIANT, 3); - return state; - } - - @Override - @SuppressWarnings("deprecation") - public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) - { - final Item item = player.getItemInHand(hand).getItem(); - if((!(item instanceof BlockItem))) return InteractionResult.PASS; - final Block block = ((BlockItem)item).getBlock(); - if(block == this) { - if (hit.getDirection().getAxis().isHorizontal()) return InteractionResult.PASS; - BlockPos adjacent_pos = pos.relative(player.getDirection()); - BlockState adjacent_state = world.getBlockState(adjacent_pos); - if (adjacent_state.canBeReplaced(new DirectionalPlaceContext(world, adjacent_pos, hit.getDirection().getOpposite(), player.getItemInHand(hand), hit.getDirection()))) { - BlockState place_state = defaultBlockState(); - place_state = place_state.setValue(WATERLOGGED, adjacent_state.getFluidState().getType() == Fluids.WATER); - EdCatwalkBlock.place_consume(adapted_state(place_state, world, adjacent_pos), world, adjacent_pos, player, hand, 1); - } - return InteractionResult.sidedSuccess(world.isClientSide()); + public EdCatwalkTopAlignedBlock(long config, BlockBehaviour.Properties properties, final VoxelShape[] variant_shapes, final Block inset_light_block) { + super(config, properties, variant_shapes[0]); + registerDefaultState(super.defaultBlockState().setValue(VARIANT, 0)); + this.variant_shapes = VARIANT.getPossibleValues().stream().map(i -> (i < variant_shapes.length) ? (variant_shapes[i]) : (Shapes.block())).collect(Collectors.toList()); + this.inset_light_block = inset_light_block; } - if(block == inset_light_block && hit.getDirection() == Direction.DOWN) { - int currentVariant = state.getValue(VARIANT); - if (!(currentVariant == 0 || currentVariant == 4)) return InteractionResult.PASS; - boolean add = currentVariant == 0; - EdCatwalkBlock.place_consume(adapted_state(state.setValue(VARIANT, add ? 4 : 0), world, pos), world, pos, player, hand, add ? 1 : -1); - return InteractionResult.sidedSuccess(world.isClientSide()); + + @Override + public boolean propagatesSkylightDown(BlockState state, BlockGetter reader, BlockPos pos) { + return true; } - return InteractionResult.PASS; - } - @Override - public BlockState updateShape(BlockState state, Direction facing, BlockState facingState, LevelAccessor world, BlockPos pos, BlockPos facingPos) - { return adapted_state(super.updateShape(state, facing, facingState, world, pos, facingPos), world, pos); } + @Override + public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) { + return variant_shapes.get(state.getValue(VARIANT)); + } - // --- + @Override + public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) { + return getShape(state, world, pos, selectionContext); + } - private BlockState adapted_state(BlockState state, LevelAccessor world, BlockPos pos) - { - BlockState below = world.getBlockState(pos.below()); - if (state == null || state.getValue(VARIANT) == 4) return state; - if((below.getBlock() == ModContent.getBlock("thick_steel_pole")) || (below.getBlock() == ModContent.getBlock("thick_steel_pole_head"))) return state.setValue(VARIANT, 1); - if((below.getBlock() == ModContent.getBlock("thin_steel_pole")) || (below.getBlock() == ModContent.getBlock("thin_steel_pole_head"))) return state.setValue(VARIANT, 2); - return state; - } + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + super.createBlockStateDefinition(builder); + builder.add(VARIANT); + } - @Override - public int getLightEmission(BlockState state, BlockGetter level, BlockPos pos) { - return state.getValue(VARIANT) == 4 - ? inset_light_block.getLightEmission(inset_light_block.defaultBlockState().setValue(StandardBlocks.Directed.FACING, Direction.UP), level, pos) - : super.getLightEmission(state, level, pos); - } + @Override + @Nullable + public BlockState getStateForPlacement(BlockPlaceContext context) { + BlockState state = adapted_state(super.getStateForPlacement(context), context.getLevel(), context.getClickedPos()); + if (context.getClickedFace() != Direction.UP) return state; + BlockState below = context.getLevel().getBlockState(context.getClickedPos().below()); + if ((state.getValue(VARIANT) == 0) && (below.isFaceSturdy(context.getLevel(), context.getClickedPos().below(), Direction.UP))) + return state.setValue(VARIANT, 3); + return state; + } - @Override - public boolean hasDynamicDropList() - { return true; } + @Override + @SuppressWarnings("deprecation") + public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { + final Item item = player.getItemInHand(hand).getItem(); + if ((!(item instanceof BlockItem))) return InteractionResult.PASS; + final Block block = ((BlockItem) item).getBlock(); + if (block == this) { + if (hit.getDirection().getAxis().isHorizontal()) return InteractionResult.PASS; + BlockPos adjacent_pos = pos.relative(player.getDirection()); + BlockState adjacent_state = world.getBlockState(adjacent_pos); + if (adjacent_state.canBeReplaced(new DirectionalPlaceContext(world, adjacent_pos, hit.getDirection().getOpposite(), player.getItemInHand(hand), hit.getDirection()))) { + BlockState place_state = defaultBlockState(); + place_state = place_state.setValue(WATERLOGGED, adjacent_state.getFluidState().getType() == Fluids.WATER); + EdCatwalkBlock.place_consume(adapted_state(place_state, world, adjacent_pos), world, adjacent_pos, player, hand, 1); + } + return InteractionResult.sidedSuccess(world.isClientSide()); + } + if (block == inset_light_block && hit.getDirection() == Direction.DOWN) { + int currentVariant = state.getValue(VARIANT); + if (!(currentVariant == 0 || currentVariant == 4)) return InteractionResult.PASS; + boolean add = currentVariant == 0; + EdCatwalkBlock.place_consume(adapted_state(state.setValue(VARIANT, add ? 4 : 0), world, pos), world, pos, player, hand, add ? 1 : -1); + return InteractionResult.sidedSuccess(world.isClientSide()); + } + return InteractionResult.PASS; + } - @Override - public List dropList(BlockState state, Level world, @Nullable BlockEntity te, boolean explosion) - { - if(world.isClientSide()) return Collections.singletonList(ItemStack.EMPTY); - List drops = new ArrayList<>(); - drops.add(new ItemStack(state.getBlock().asItem())); - if (state.getValue(VARIANT) == 4) drops.add(new ItemStack(inset_light_block, 1)); - return drops; - } + @Override + public BlockState updateShape(BlockState state, Direction facing, BlockState facingState, LevelAccessor world, BlockPos pos, BlockPos facingPos) { + return adapted_state(super.updateShape(state, facing, facingState, world, pos, facingPos), world, pos); + } + + // --- + + private BlockState adapted_state(BlockState state, LevelAccessor world, BlockPos pos) { + BlockState below = world.getBlockState(pos.below()); + if (state == null || state.getValue(VARIANT) == 4) return state; + if ((below.getBlock() == ModContent.getBlock("thick_steel_pole")) || (below.getBlock() == ModContent.getBlock("thick_steel_pole_head"))) + return state.setValue(VARIANT, 1); + if ((below.getBlock() == ModContent.getBlock("thin_steel_pole")) || (below.getBlock() == ModContent.getBlock("thin_steel_pole_head"))) + return state.setValue(VARIANT, 2); + return state; + } + + @Override + public int getLightEmission(BlockState state, BlockGetter level, BlockPos pos) { + return state.getValue(VARIANT) == 4 + ? inset_light_block.getLightEmission(inset_light_block.defaultBlockState().setValue(StandardBlocks.Directed.FACING, Direction.UP), level, pos) + : super.getLightEmission(state, level, pos); + } + + @Override + public boolean hasDynamicDropList() { + return true; + } + + @Override + public List dropList(BlockState state, Level world, @Nullable BlockEntity te, boolean explosion) { + if (world.isClientSide()) return Collections.singletonList(ItemStack.EMPTY); + List drops = new ArrayList<>(); + drops.add(new ItemStack(state.getBlock().asItem())); + if (state.getValue(VARIANT) == 4) drops.add(new ItemStack(inset_light_block, 1)); + return drops; + } } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdChair.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdChair.java index 6307cc8..5f2750e 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdChair.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdChair.java @@ -8,9 +8,13 @@ */ package dev.zontreck.engineerdecor.blocks; +import dev.zontreck.engineerdecor.ModConfig; +import dev.zontreck.engineerdecor.ModContent; +import dev.zontreck.libzontreck.edlibmc.StandardBlocks; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ClientGamePacketListener; import net.minecraft.server.level.ServerLevel; import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; @@ -29,175 +33,172 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.Vec3; -import net.minecraftforge.network.PlayMessages; import net.minecraftforge.network.NetworkHooks; -import dev.zontreck.engineerdecor.ModConfig; -import dev.zontreck.engineerdecor.ModContent; -import dev.zontreck.engineerdecor.libmc.StandardBlocks; +import net.minecraftforge.network.PlayMessages; import java.util.List; -public class EdChair -{ - private static boolean sitting_enabled = true; - private static double sitting_probability = 0.1; - private static double standup_probability = 0.01; +public class EdChair { + private static boolean sitting_enabled = true; + private static double sitting_probability = 0.1; + private static double standup_probability = 0.01; - public static void on_config(boolean without_sitting, boolean without_mob_sitting, double sitting_probability_percent, double standup_probability_percent) - { - sitting_enabled = (!without_sitting); - sitting_probability = (without_sitting||without_mob_sitting) ? 0.0 : Mth.clamp(sitting_probability_percent/100, 0, 0.9); - standup_probability = (without_sitting||without_mob_sitting) ? 1.0 : Mth.clamp(standup_probability_percent/100, 1e-6, 1e-2); - ModConfig.log("Config chairs: sit:" + sitting_enabled + ", mob-sit: " + (sitting_probability*100) + "%, standup: " + (standup_probability) + "%."); - } - - //-------------------------------------------------------------------------------------------------------------------- - // Block - //-------------------------------------------------------------------------------------------------------------------- - - public static class ChairBlock extends StandardBlocks.HorizontalWaterLoggable - { - public ChairBlock(long config, BlockBehaviour.Properties builder, final AABB[] unrotatedAABBs) - { super(config, builder.randomTicks(), unrotatedAABBs); } - - @Override - @SuppressWarnings("deprecation") - public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult) - { - if(!sitting_enabled) return InteractionResult.PASS; - if(world.isClientSide()) return InteractionResult.SUCCESS; - EntityChair.sit(world, player, pos); - return InteractionResult.CONSUME; + public static void on_config(boolean without_sitting, boolean without_mob_sitting, double sitting_probability_percent, double standup_probability_percent) { + sitting_enabled = (!without_sitting); + sitting_probability = (without_sitting || without_mob_sitting) ? 0.0 : Mth.clamp(sitting_probability_percent / 100, 0, 0.9); + standup_probability = (without_sitting || without_mob_sitting) ? 1.0 : Mth.clamp(standup_probability_percent / 100, 1e-6, 1e-2); + ModConfig.log("Config chairs: sit:" + sitting_enabled + ", mob-sit: " + (sitting_probability * 100) + "%, standup: " + (standup_probability) + "%."); } - @Override - @SuppressWarnings("deprecation") - public void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) - { - if(sitting_enabled && (Math.random() < sitting_probability) && (entity instanceof Mob)) EntityChair.sit(world, (LivingEntity)entity, pos); - } + //-------------------------------------------------------------------------------------------------------------------- + // Block + //-------------------------------------------------------------------------------------------------------------------- - @Override - @SuppressWarnings("deprecation") - public void tick(BlockState state, ServerLevel world, BlockPos pos, RandomSource rnd) - { - if((!sitting_enabled) || (sitting_probability < 1e-6)) return; - final List entities = world.getEntitiesOfClass(Mob.class, new AABB(pos).inflate(2,1,2).expandTowards(0,1,0), e->true); - if(entities.isEmpty()) return; - int index = rnd.nextInt(entities.size()); - if((index < 0) || (index >= entities.size())) return; - EntityChair.sit(world, entities.get(index), pos); - } - } - - //-------------------------------------------------------------------------------------------------------------------- - // Entity - //-------------------------------------------------------------------------------------------------------------------- - - public static class EntityChair extends Entity - { - public static final double x_offset = 0.5d; - public static final double y_offset = 0.4d; - public static final double z_offset = 0.5d; - private int t_sit = 0; - public BlockPos chair_pos = new BlockPos(0,0,0); - - public EntityChair(EntityType entityType, Level world) - { - super(entityType, world); - blocksBuilding=true; - setDeltaMovement(Vec3.ZERO); - canUpdate(true); - noPhysics=true; - } - - public EntityChair(Level world) - { this(ModContent.getEntityType("et_chair"), world); } - - public static EntityChair customClientFactory(PlayMessages.SpawnEntity spkt, Level world) - { return new EntityChair(world); } - - public Packet getAddEntityPacket() - { return NetworkHooks.getEntitySpawningPacket(this); } - - public static boolean accepts_mob(LivingEntity entity) - { - if(!(entity instanceof Monster)) return false; - if((entity.getType().getDimensions().height > 2.5) || (entity.getType().getDimensions().height > 2.0)) return false; - if(entity instanceof Zombie) return true; - if(entity instanceof ZombieVillager) return true; - if(entity instanceof ZombifiedPiglin) return true; - if(entity instanceof Piglin) return true; - if(entity instanceof Husk) return true; - if(entity instanceof Stray) return true; - if(entity instanceof Skeleton) return true; - if(entity instanceof WitherSkeleton) return true; - return false; - } - - public static void sit(Level world, LivingEntity sitter, BlockPos pos) - { - if(!sitting_enabled) return; - if((world==null) || (world.isClientSide) || (sitter==null) || (pos==null)) return; - if((!(sitter instanceof Player)) && (!accepts_mob(sitter))) return; - if(!world.getEntitiesOfClass(EntityChair.class, new AABB(pos)).isEmpty()) return; - if(sitter.isVehicle() || (!sitter.isAlive()) || (sitter.isPassenger()) ) return; - if((!world.isEmptyBlock(pos.above())) || (!world.isEmptyBlock(pos.above(2)))) return; - boolean on_top_of_block_position = true; - boolean use_next_negative_y_position = false; - EntityChair chair = new EntityChair(world); - BlockPos chair_pos = chair.blockPosition(); - chair.chair_pos = pos; - chair.t_sit = 5; - chair.xo = chair_pos.getX(); - chair.yo = chair_pos.getY(); - chair.zo = chair_pos.getZ(); - chair.setPos(pos.getX()+x_offset,pos.getY()+y_offset,pos.getZ()+z_offset); - world.addFreshEntity(chair); - sitter.startRiding(chair, true); - } - - @Override - protected void defineSynchedData() {} - - @Override - protected void readAdditionalSaveData(CompoundTag compound) {} - - @Override - protected void addAdditionalSaveData(CompoundTag compound) {} - - @Override - public boolean isPushable() - { return false; } - - @Override - public double getPassengersRidingOffset() - { return 0.0; } - - @Override - public void tick() - { - if(level.isClientSide) return; - super.tick(); - if(--t_sit > 0) return; - Entity sitter = getPassengers().isEmpty() ? null : getPassengers().get(0); - if((sitter==null) || (!sitter.isAlive())) { - this.remove(RemovalReason.DISCARDED); - return; - } - boolean abort = (!sitting_enabled); - final BlockState state = level.getBlockState(chair_pos); - if((state==null) || (!(state.getBlock() instanceof ChairBlock))) abort = true; - if(!level.isEmptyBlock(chair_pos.above())) abort = true; - if((!(sitter instanceof Player)) && (Math.random() < standup_probability)) abort = true; - if(abort) { - for(Entity e:getPassengers()) { - if(e.isAlive()) e.stopRiding(); + public static class ChairBlock extends StandardBlocks.HorizontalWaterLoggable { + public ChairBlock(long config, BlockBehaviour.Properties builder, final AABB[] unrotatedAABBs) { + super(config, builder.randomTicks(), unrotatedAABBs); + } + + @Override + @SuppressWarnings("deprecation") + public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult) { + if (!sitting_enabled) return InteractionResult.PASS; + if (world.isClientSide()) return InteractionResult.SUCCESS; + EntityChair.sit(world, player, pos); + return InteractionResult.CONSUME; + } + + @Override + @SuppressWarnings("deprecation") + public void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { + if (sitting_enabled && (Math.random() < sitting_probability) && (entity instanceof Mob)) + EntityChair.sit(world, (LivingEntity) entity, pos); + } + + @Override + @SuppressWarnings("deprecation") + public void tick(BlockState state, ServerLevel world, BlockPos pos, RandomSource rnd) { + if ((!sitting_enabled) || (sitting_probability < 1e-6)) return; + final List entities = world.getEntitiesOfClass(Mob.class, new AABB(pos).inflate(2, 1, 2).expandTowards(0, 1, 0), e -> true); + if (entities.isEmpty()) return; + int index = rnd.nextInt(entities.size()); + if ((index < 0) || (index >= entities.size())) return; + EntityChair.sit(world, entities.get(index), pos); + } + } + + //-------------------------------------------------------------------------------------------------------------------- + // Entity + //-------------------------------------------------------------------------------------------------------------------- + + public static class EntityChair extends Entity { + public static final double x_offset = 0.5d; + public static final double y_offset = 0.4d; + public static final double z_offset = 0.5d; + public BlockPos chair_pos = new BlockPos(0, 0, 0); + private int t_sit = 0; + + public EntityChair(EntityType entityType, Level world) { + super(entityType, world); + blocksBuilding = true; + setDeltaMovement(Vec3.ZERO); + canUpdate(true); + noPhysics = true; + } + + public EntityChair(Level world) { + this(ModContent.getEntityType("et_chair"), world); + } + + public static EntityChair customClientFactory(PlayMessages.SpawnEntity spkt, Level world) { + return new EntityChair(world); + } + + public static boolean accepts_mob(LivingEntity entity) { + if (!(entity instanceof Monster)) return false; + if ((entity.getType().getDimensions().height > 2.5) || (entity.getType().getDimensions().height > 2.0)) + return false; + if (entity instanceof Zombie) return true; + if (entity instanceof ZombieVillager) return true; + if (entity instanceof ZombifiedPiglin) return true; + if (entity instanceof Piglin) return true; + if (entity instanceof Husk) return true; + if (entity instanceof Stray) return true; + if (entity instanceof Skeleton) return true; + if (entity instanceof WitherSkeleton) return true; + return false; + } + + public static void sit(Level world, LivingEntity sitter, BlockPos pos) { + if (!sitting_enabled) return; + if ((world == null) || (world.isClientSide) || (sitter == null) || (pos == null)) return; + if ((!(sitter instanceof Player)) && (!accepts_mob(sitter))) return; + if (!world.getEntitiesOfClass(EntityChair.class, new AABB(pos)).isEmpty()) return; + if (sitter.isVehicle() || (!sitter.isAlive()) || (sitter.isPassenger())) return; + if ((!world.isEmptyBlock(pos.above())) || (!world.isEmptyBlock(pos.above(2)))) return; + boolean on_top_of_block_position = true; + boolean use_next_negative_y_position = false; + EntityChair chair = new EntityChair(world); + BlockPos chair_pos = chair.blockPosition(); + chair.chair_pos = pos; + chair.t_sit = 5; + chair.xo = chair_pos.getX(); + chair.yo = chair_pos.getY(); + chair.zo = chair_pos.getZ(); + chair.setPos(pos.getX() + x_offset, pos.getY() + y_offset, pos.getZ() + z_offset); + world.addFreshEntity(chair); + sitter.startRiding(chair, true); + } + + public Packet getAddEntityPacket() { + return NetworkHooks.getEntitySpawningPacket(this); + } + + @Override + protected void defineSynchedData() { + } + + @Override + protected void readAdditionalSaveData(CompoundTag compound) { + } + + @Override + protected void addAdditionalSaveData(CompoundTag compound) { + } + + @Override + public boolean isPushable() { + return false; + } + + @Override + public double getPassengersRidingOffset() { + return 0.0; + } + + @Override + public void tick() { + if (level().isClientSide) return; + super.tick(); + if (--t_sit > 0) return; + Entity sitter = getPassengers().isEmpty() ? null : getPassengers().get(0); + if ((sitter == null) || (!sitter.isAlive())) { + this.remove(RemovalReason.DISCARDED); + return; + } + boolean abort = (!sitting_enabled); + final BlockState state = level().getBlockState(chair_pos); + if ((state == null) || (!(state.getBlock() instanceof ChairBlock))) abort = true; + if (!level().isEmptyBlock(chair_pos.above())) abort = true; + if ((!(sitter instanceof Player)) && (Math.random() < standup_probability)) abort = true; + if (abort) { + for (Entity e : getPassengers()) { + if (e.isAlive()) e.stopRiding(); + } + this.remove(RemovalReason.DISCARDED); + } } - this.remove(RemovalReason.DISCARDED); - } } - } } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdChimneyBlock.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdChimneyBlock.java index 5160fae..3c43b47 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdChimneyBlock.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdChimneyBlock.java @@ -8,6 +8,7 @@ */ package dev.zontreck.engineerdecor.blocks; +import dev.zontreck.libzontreck.edlibmc.StandardBlocks; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.particles.ParticleTypes; @@ -17,7 +18,7 @@ import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.SignalGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; @@ -28,74 +29,74 @@ import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.BlockHitResult; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -import dev.zontreck.engineerdecor.libmc.StandardBlocks; import javax.annotation.Nullable; -public class EdChimneyBlock extends StandardBlocks.Cutout -{ - public static final IntegerProperty POWER = BlockStateProperties.POWER; +public class EdChimneyBlock extends StandardBlocks.Cutout { + public static final IntegerProperty POWER = BlockStateProperties.POWER; - public EdChimneyBlock(long config, BlockBehaviour.Properties properties, AABB aabb) - { super(config, properties, aabb); } - - public EdChimneyBlock(long config, BlockBehaviour.Properties builder) - { - this(config, builder, new AABB(0,0,0,1,1,1)); - registerDefaultState(super.defaultBlockState().setValue(POWER, 0)); // no smoke in JEI - } - - @Override - protected void createBlockStateDefinition(StateDefinition.Builder builder) - { super.createBlockStateDefinition(builder); builder.add(POWER); } - - @Override - @Nullable - public BlockState getStateForPlacement(BlockPlaceContext context) - { - BlockState state = super.getStateForPlacement(context); - if(state==null) return state; - int p = context.getLevel().getBestNeighborSignal(context.getClickedPos()); - return state.setValue(POWER, p==0 ? 5 : p); - } - - @Override - @SuppressWarnings("deprecation") - public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult) - { world.setBlock(pos, state.setValue(POWER, (state.getValue(POWER)+1) & 0xf), 1|2); return InteractionResult.sidedSuccess(world.isClientSide()); } - - @Override - @SuppressWarnings("deprecation") - public void neighborChanged(BlockState state, Level world, BlockPos pos, Block block, BlockPos fromPos, boolean unused) - { - int p = world.getBestNeighborSignal(pos); - if(p != state.getValue(POWER)) world.setBlock(pos, state.setValue(POWER, p), 2); - } - - @Override - public boolean shouldCheckWeakPower(BlockState state, LevelReader world, BlockPos pos, Direction side) - { return false; } - - @Override - @OnlyIn(Dist.CLIENT) - public void animateTick(BlockState state, Level world, BlockPos pos, RandomSource rnd) - { - if(state.getBlock() != this) return; - final int p = state.getValue(POWER); - if(p==0) return; - int end = 1+rnd.nextInt(10) * p / 15; - for(int i=0; i 0.7 ? ParticleTypes.LARGE_SMOKE : (rv>0.4 ? ParticleTypes.SMOKE : ParticleTypes.CAMPFIRE_COSY_SMOKE)), - 0.5+pos.getX()+(rnd.nextDouble()*0.2), - 0.9+pos.getY()+(rnd.nextDouble()*0.1), - 0.5+pos.getZ()+(rnd.nextDouble()*0.2), - -0.02 + rnd.nextDouble()*0.04, - +0.05 + rnd.nextDouble()*0.1, - -0.02 + rnd.nextDouble()*0.04 - ); + public EdChimneyBlock(long config, BlockBehaviour.Properties properties, AABB aabb) { + super(config, properties, aabb); + } + + public EdChimneyBlock(long config, BlockBehaviour.Properties builder) { + this(config, builder, new AABB(0, 0, 0, 1, 1, 1)); + registerDefaultState(super.defaultBlockState().setValue(POWER, 0)); // no smoke in JEI + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + super.createBlockStateDefinition(builder); + builder.add(POWER); + } + + @Override + @Nullable + public BlockState getStateForPlacement(BlockPlaceContext context) { + BlockState state = super.getStateForPlacement(context); + if (state == null) return state; + int p = context.getLevel().getBestNeighborSignal(context.getClickedPos()); + return state.setValue(POWER, p == 0 ? 5 : p); + } + + @Override + @SuppressWarnings("deprecation") + public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult) { + world.setBlock(pos, state.setValue(POWER, (state.getValue(POWER) + 1) & 0xf), 1 | 2); + return InteractionResult.sidedSuccess(world.isClientSide()); + } + + @Override + @SuppressWarnings("deprecation") + public void neighborChanged(BlockState state, Level world, BlockPos pos, Block block, BlockPos fromPos, boolean unused) { + int p = world.getBestNeighborSignal(pos); + if (p != state.getValue(POWER)) world.setBlock(pos, state.setValue(POWER, p), 2); + } + + @Override + public boolean shouldCheckWeakPower(BlockState state, SignalGetter level, BlockPos pos, Direction side) { + return false; + } + + @Override + @OnlyIn(Dist.CLIENT) + public void animateTick(BlockState state, Level world, BlockPos pos, RandomSource rnd) { + if (state.getBlock() != this) return; + final int p = state.getValue(POWER); + if (p == 0) return; + int end = 1 + rnd.nextInt(10) * p / 15; + for (int i = 0; i < end; ++i) { + double rv = rnd.nextDouble() * p / 5; + world.addParticle( + (rv > 0.7 ? ParticleTypes.LARGE_SMOKE : (rv > 0.4 ? ParticleTypes.SMOKE : ParticleTypes.CAMPFIRE_COSY_SMOKE)), + 0.5 + pos.getX() + (rnd.nextDouble() * 0.2), + 0.9 + pos.getY() + (rnd.nextDouble() * 0.1), + 0.5 + pos.getZ() + (rnd.nextDouble() * 0.2), + -0.02 + rnd.nextDouble() * 0.04, + +0.05 + rnd.nextDouble() * 0.1, + -0.02 + rnd.nextDouble() * 0.04 + ); + } } - } } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdChimneyTrunkBlock.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdChimneyTrunkBlock.java index 60d6152..de27cc3 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdChimneyTrunkBlock.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdChimneyTrunkBlock.java @@ -19,19 +19,19 @@ import net.minecraft.world.phys.shapes.VoxelShape; import javax.annotation.Nullable; -public class EdChimneyTrunkBlock extends EdRoofBlock -{ - public EdChimneyTrunkBlock(long config, BlockBehaviour.Properties properties) - { super(config, properties.dynamicShape(), Shapes.empty(), Shapes.empty()); } +public class EdChimneyTrunkBlock extends EdRoofBlock { + public EdChimneyTrunkBlock(long config, BlockBehaviour.Properties properties) { + super(config, properties.dynamicShape(), Shapes.empty(), Shapes.empty()); + } - public EdChimneyTrunkBlock(long config, BlockBehaviour.Properties properties, VoxelShape add, VoxelShape cut) - { super(config, properties, add, cut); } + public EdChimneyTrunkBlock(long config, BlockBehaviour.Properties properties, VoxelShape add, VoxelShape cut) { + super(config, properties, add, cut); + } - @Override - @Nullable - public BlockState getStateForPlacement(BlockPlaceContext context) - { - BlockState state = super.getStateForPlacement(context); - return (state==null) ? (state) : (state.setValue(EdRoofBlock.SHAPE, StairsShape.STRAIGHT).setValue(EdRoofBlock.HALF, Half.BOTTOM)); - } + @Override + @Nullable + public BlockState getStateForPlacement(BlockPlaceContext context) { + BlockState state = super.getStateForPlacement(context); + return (state == null) ? (state) : (state.setValue(EdRoofBlock.SHAPE, StairsShape.STRAIGHT).setValue(EdRoofBlock.HALF, Half.BOTTOM)); + } } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdCornerOrnamentedBlock.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdCornerOrnamentedBlock.java index 547400b..9ff0042 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdCornerOrnamentedBlock.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdCornerOrnamentedBlock.java @@ -8,6 +8,8 @@ */ package dev.zontreck.engineerdecor.blocks; +import dev.zontreck.libzontreck.edlibmc.Auxiliaries; +import dev.zontreck.libzontreck.edlibmc.StandardBlocks; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.util.Mth; @@ -17,56 +19,51 @@ import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec2; -import dev.zontreck.engineerdecor.libmc.StandardBlocks; -import dev.zontreck.engineerdecor.libmc.Auxiliaries; import javax.annotation.Nullable; import java.util.Arrays; import java.util.HashSet; -public class EdCornerOrnamentedBlock extends StandardBlocks.Directed -{ - protected final HashSet compatible_blocks; +public class EdCornerOrnamentedBlock extends StandardBlocks.Directed { + protected final HashSet compatible_blocks; - public EdCornerOrnamentedBlock(long config, BlockBehaviour.Properties properties, Block[] assigned_wall_blocks) - { - super(config, properties, Auxiliaries.getPixeledAABB(0,0,0,16,16,16)); - compatible_blocks = new HashSet<>(Arrays.asList(assigned_wall_blocks)); - } + public EdCornerOrnamentedBlock(long config, BlockBehaviour.Properties properties, Block[] assigned_wall_blocks) { + super(config, properties, Auxiliaries.getPixeledAABB(0, 0, 0, 16, 16, 16)); + compatible_blocks = new HashSet<>(Arrays.asList(assigned_wall_blocks)); + } - @Override - @Nullable - public BlockState getStateForPlacement(BlockPlaceContext context) - { - final Level world = context.getLevel(); - final BlockPos pos = context.getClickedPos(); - // 1. Placement as below/above for corners, or placement adjacent horizontally if up/down facing. - for(Direction adj: Direction.values()) { - BlockState state = world.getBlockState(pos.relative(adj)); - if(state.getBlock() != this) continue; - Direction facing = state.getValue(FACING); - if(facing.getAxis().isHorizontal() == (adj.getAxis().isVertical())) { - return super.getStateForPlacement(context).setValue(FACING, state.getValue(FACING)); - } + @Override + @Nullable + public BlockState getStateForPlacement(BlockPlaceContext context) { + final Level world = context.getLevel(); + final BlockPos pos = context.getClickedPos(); + // 1. Placement as below/above for corners, or placement adjacent horizontally if up/down facing. + for (Direction adj : Direction.values()) { + BlockState state = world.getBlockState(pos.relative(adj)); + if (state.getBlock() != this) continue; + Direction facing = state.getValue(FACING); + if (facing.getAxis().isHorizontal() == (adj.getAxis().isVertical())) { + return super.getStateForPlacement(context).setValue(FACING, state.getValue(FACING)); + } + } + // 2. By Player look angles with minimum horizontal diagonal deviation. + { + Direction facing = Direction.WEST; + final Vec2 look = context.getPlayer().getRotationVector(); + final Direction hit_face = context.getClickedFace(); + if ((context.getClickedFace() == Direction.DOWN) && (look.x <= -60)) { + facing = Direction.DOWN; + } else if ((context.getClickedFace() == Direction.UP) && (look.x >= 60)) { + facing = Direction.UP; + } else if (Mth.degreesDifferenceAbs(look.y, 45) <= 45) { + facing = Direction.NORTH; + } else if (Mth.degreesDifferenceAbs(look.y, 45 + 90) <= 45) { + facing = Direction.EAST; + } else if (Mth.degreesDifferenceAbs(look.y, 45 + 180) <= 45) { + facing = Direction.SOUTH; + } + return super.getStateForPlacement(context).setValue(FACING, facing); + } } - // 2. By Player look angles with minimum horizontal diagonal deviation. - { - Direction facing = Direction.WEST; - final Vec2 look = context.getPlayer().getRotationVector(); - final Direction hit_face = context.getClickedFace(); - if((context.getClickedFace()==Direction.DOWN) && (look.x <= -60)) { - facing = Direction.DOWN; - } else if((context.getClickedFace()==Direction.UP) && (look.x >= 60)) { - facing = Direction.UP; - } else if(Mth.degreesDifferenceAbs(look.y, 45) <= 45) { - facing = Direction.NORTH; - } else if(Mth.degreesDifferenceAbs(look.y, 45+90) <= 45) { - facing = Direction.EAST; - } else if(Mth.degreesDifferenceAbs(look.y, 45+180) <= 45) { - facing = Direction.SOUTH; - } - return super.getStateForPlacement(context).setValue(FACING, facing); - } - } } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdDoubleGateBlock.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdDoubleGateBlock.java index a75f1f5..f48aa9a 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdDoubleGateBlock.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdDoubleGateBlock.java @@ -8,6 +8,8 @@ */ package dev.zontreck.engineerdecor.blocks; +import dev.zontreck.libzontreck.edlibmc.Auxiliaries; +import dev.zontreck.libzontreck.edlibmc.StandardBlocks; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.sounds.SoundEvents; @@ -32,134 +34,137 @@ import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; -import dev.zontreck.engineerdecor.libmc.StandardBlocks; -import dev.zontreck.engineerdecor.libmc.Auxiliaries; import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Arrays; -public class EdDoubleGateBlock extends StandardBlocks.HorizontalWaterLoggable -{ - public static final IntegerProperty SEGMENT = IntegerProperty.create("segment", 0, 1); - public static final BooleanProperty OPEN = FenceGateBlock.OPEN; - public static final int SEGMENT_LOWER = 0; - public static final int SEGMENT_UPPER = 1; - protected final ArrayList collision_shapes_; +public class EdDoubleGateBlock extends StandardBlocks.HorizontalWaterLoggable { + public static final IntegerProperty SEGMENT = IntegerProperty.create("segment", 0, 1); + public static final BooleanProperty OPEN = FenceGateBlock.OPEN; + public static final int SEGMENT_LOWER = 0; + public static final int SEGMENT_UPPER = 1; + protected final ArrayList collision_shapes_; - public EdDoubleGateBlock(long config, BlockBehaviour.Properties properties, AABB aabb) - { this(config, properties, new AABB[]{aabb}); } - - public EdDoubleGateBlock(long config, BlockBehaviour.Properties properties, AABB[] aabbs) - { - super(config, properties, aabbs); - AABB[] caabbs = new AABB[aabbs.length]; - for(int i=0; i(Arrays.asList( - Shapes.block(), - Shapes.block(), - Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(caabbs, Direction.NORTH, true)), - Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(caabbs, Direction.SOUTH, true)), - Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(caabbs, Direction.WEST, true)), - Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(caabbs, Direction.EAST, true)), - Shapes.block(), - Shapes.block() - )); - } - - @Override - public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) - { return state.getValue(OPEN) ? Shapes.empty() : collision_shapes_.get(state.getValue(HORIZONTAL_FACING).get3DDataValue() & 0x7); } - - @Override - protected void createBlockStateDefinition(StateDefinition.Builder builder) - { super.createBlockStateDefinition(builder); builder.add(SEGMENT).add(OPEN); } - - @Override - @Nullable - public BlockState getStateForPlacement(BlockPlaceContext context) - { return getInitialState(super.getStateForPlacement(context), context.getLevel(), context.getClickedPos()); } - - @Override - @SuppressWarnings("deprecation") - public BlockState updateShape(BlockState state, Direction facing, BlockState facingState, LevelAccessor world, BlockPos pos, BlockPos facingPos) - { return getInitialState(super.updateShape(state, facing, facingState, world, pos, facingPos), world, pos); } - - @Override - @SuppressWarnings("deprecation") - public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult) - { - if((rayTraceResult.getDirection()==Direction.UP) || (rayTraceResult.getDirection()==Direction.DOWN) && (player.getItemInHand(hand).getItem()==this.asItem())) return InteractionResult.PASS; - if(world.isClientSide()) return InteractionResult.SUCCESS; - final boolean open = !state.getValue(OPEN); - world.setBlock(pos, state.setValue(OPEN, open),2|8|16); - if(state.getValue(SEGMENT) == SEGMENT_UPPER) { - final BlockState adjacent = world.getBlockState(pos.below()); - if(adjacent.getBlock()==this) world.setBlock(pos.below(), adjacent.setValue(OPEN, open), 2|8|16); - } else { - final BlockState adjacent = world.getBlockState(pos.above()); - if(adjacent.getBlock()==this) world.setBlock(pos.above(), adjacent.setValue(OPEN, open), 2|8|16); + public EdDoubleGateBlock(long config, BlockBehaviour.Properties properties, AABB aabb) { + this(config, properties, new AABB[]{aabb}); } - world.playSound(null, pos, open?SoundEvents.IRON_DOOR_OPEN:SoundEvents.IRON_DOOR_CLOSE, SoundSource.BLOCKS, 0.7f, 1.4f); - return InteractionResult.CONSUME; - } - @Override - @SuppressWarnings("deprecation") - public boolean isPathfindable(BlockState state, BlockGetter world, BlockPos pos, PathComputationType type) - { return state.getValue(OPEN); } + public EdDoubleGateBlock(long config, BlockBehaviour.Properties properties, AABB[] aabbs) { + super(config, properties, aabbs); + AABB[] caabbs = new AABB[aabbs.length]; + for (int i = 0; i < caabbs.length; ++i) caabbs[i] = aabbs[i].expandTowards(0, 0.5, 0); + collision_shapes_ = new ArrayList<>(Arrays.asList( + Shapes.block(), + Shapes.block(), + Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(caabbs, Direction.NORTH, true)), + Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(caabbs, Direction.SOUTH, true)), + Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(caabbs, Direction.WEST, true)), + Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(caabbs, Direction.EAST, true)), + Shapes.block(), + Shapes.block() + )); + } - @Override - @SuppressWarnings("deprecation") - public void neighborChanged(BlockState state, Level world, BlockPos pos, Block block, BlockPos fromPos, boolean isMoving) - { - if(world.isClientSide) return; - boolean powered = false; - BlockState adjacent; - BlockPos adjacent_pos; - if(state.getValue(SEGMENT) == SEGMENT_UPPER) { - adjacent_pos = pos.below(); - adjacent = world.getBlockState(adjacent_pos); - if(adjacent.getBlock()!=this) adjacent = null; - if(world.getSignal(pos.above(), Direction.UP) > 0) { - powered = true; - } else if((adjacent!=null) && (world.hasNeighborSignal(pos.below(2)))) { - powered = true; - } - } else { - adjacent_pos = pos.above(); - adjacent = world.getBlockState(adjacent_pos); - if(adjacent.getBlock()!=this) adjacent = null; - if(world.hasNeighborSignal(pos)) { - powered = true; - } else if((adjacent!=null) && (world.getSignal(pos.above(2), Direction.UP) > 0)) { - powered = true; - } + @Override + public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) { + return state.getValue(OPEN) ? Shapes.empty() : collision_shapes_.get(state.getValue(HORIZONTAL_FACING).get3DDataValue() & 0x7); } - boolean sound = false; - if(powered != state.getValue(OPEN)) { - world.setBlock(pos, state.setValue(OPEN, powered), 2|8|16); - sound = true; - } - if((adjacent != null) && (powered != adjacent.getValue(OPEN))) { - world.setBlock(adjacent_pos, adjacent.setValue(OPEN, powered), 2|8|16); - sound = true; - } - if(sound) { - world.playSound(null, pos, powered?SoundEvents.IRON_DOOR_OPEN:SoundEvents.IRON_DOOR_CLOSE, SoundSource.BLOCKS, 0.7f, 1.4f); - } - } - // ------------------------------------------------------------------------------------------------------------------- + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + super.createBlockStateDefinition(builder); + builder.add(SEGMENT).add(OPEN); + } - private BlockState getInitialState(BlockState state, LevelAccessor world, BlockPos pos) - { - final BlockState down = world.getBlockState(pos.below()); - if(down.getBlock() == this) return state.setValue(SEGMENT, SEGMENT_UPPER).setValue(OPEN, down.getValue(OPEN)).setValue(HORIZONTAL_FACING, down.getValue(HORIZONTAL_FACING)); - final BlockState up = world.getBlockState(pos.above()); - if(up.getBlock() == this) return state.setValue(SEGMENT, SEGMENT_LOWER).setValue(OPEN, up.getValue(OPEN)).setValue(HORIZONTAL_FACING, up.getValue(HORIZONTAL_FACING)); - return state.setValue(SEGMENT, SEGMENT_LOWER).setValue(OPEN, false); - } + @Override + @Nullable + public BlockState getStateForPlacement(BlockPlaceContext context) { + return getInitialState(super.getStateForPlacement(context), context.getLevel(), context.getClickedPos()); + } + + @Override + @SuppressWarnings("deprecation") + public BlockState updateShape(BlockState state, Direction facing, BlockState facingState, LevelAccessor world, BlockPos pos, BlockPos facingPos) { + return getInitialState(super.updateShape(state, facing, facingState, world, pos, facingPos), world, pos); + } + + @Override + @SuppressWarnings("deprecation") + public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult) { + if ((rayTraceResult.getDirection() == Direction.UP) || (rayTraceResult.getDirection() == Direction.DOWN) && (player.getItemInHand(hand).getItem() == this.asItem())) + return InteractionResult.PASS; + if (world.isClientSide()) return InteractionResult.SUCCESS; + final boolean open = !state.getValue(OPEN); + world.setBlock(pos, state.setValue(OPEN, open), 2 | 8 | 16); + if (state.getValue(SEGMENT) == SEGMENT_UPPER) { + final BlockState adjacent = world.getBlockState(pos.below()); + if (adjacent.getBlock() == this) world.setBlock(pos.below(), adjacent.setValue(OPEN, open), 2 | 8 | 16); + } else { + final BlockState adjacent = world.getBlockState(pos.above()); + if (adjacent.getBlock() == this) world.setBlock(pos.above(), adjacent.setValue(OPEN, open), 2 | 8 | 16); + } + world.playSound(null, pos, open ? SoundEvents.IRON_DOOR_OPEN : SoundEvents.IRON_DOOR_CLOSE, SoundSource.BLOCKS, 0.7f, 1.4f); + return InteractionResult.CONSUME; + } + + @Override + @SuppressWarnings("deprecation") + public boolean isPathfindable(BlockState state, BlockGetter world, BlockPos pos, PathComputationType type) { + return state.getValue(OPEN); + } + + @Override + @SuppressWarnings("deprecation") + public void neighborChanged(BlockState state, Level world, BlockPos pos, Block block, BlockPos fromPos, boolean isMoving) { + if (world.isClientSide) return; + boolean powered = false; + BlockState adjacent; + BlockPos adjacent_pos; + if (state.getValue(SEGMENT) == SEGMENT_UPPER) { + adjacent_pos = pos.below(); + adjacent = world.getBlockState(adjacent_pos); + if (adjacent.getBlock() != this) adjacent = null; + if (world.getSignal(pos.above(), Direction.UP) > 0) { + powered = true; + } else if ((adjacent != null) && (world.hasNeighborSignal(pos.below(2)))) { + powered = true; + } + } else { + adjacent_pos = pos.above(); + adjacent = world.getBlockState(adjacent_pos); + if (adjacent.getBlock() != this) adjacent = null; + if (world.hasNeighborSignal(pos)) { + powered = true; + } else if ((adjacent != null) && (world.getSignal(pos.above(2), Direction.UP) > 0)) { + powered = true; + } + } + boolean sound = false; + if (powered != state.getValue(OPEN)) { + world.setBlock(pos, state.setValue(OPEN, powered), 2 | 8 | 16); + sound = true; + } + if ((adjacent != null) && (powered != adjacent.getValue(OPEN))) { + world.setBlock(adjacent_pos, adjacent.setValue(OPEN, powered), 2 | 8 | 16); + sound = true; + } + if (sound) { + world.playSound(null, pos, powered ? SoundEvents.IRON_DOOR_OPEN : SoundEvents.IRON_DOOR_CLOSE, SoundSource.BLOCKS, 0.7f, 1.4f); + } + } + + // ------------------------------------------------------------------------------------------------------------------- + + private BlockState getInitialState(BlockState state, LevelAccessor world, BlockPos pos) { + final BlockState down = world.getBlockState(pos.below()); + if (down.getBlock() == this) + return state.setValue(SEGMENT, SEGMENT_UPPER).setValue(OPEN, down.getValue(OPEN)).setValue(HORIZONTAL_FACING, down.getValue(HORIZONTAL_FACING)); + final BlockState up = world.getBlockState(pos.above()); + if (up.getBlock() == this) + return state.setValue(SEGMENT, SEGMENT_LOWER).setValue(OPEN, up.getValue(OPEN)).setValue(HORIZONTAL_FACING, up.getValue(HORIZONTAL_FACING)); + return state.setValue(SEGMENT, SEGMENT_LOWER).setValue(OPEN, false); + } } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdDropper.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdDropper.java index 3472852..6c01ff8 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdDropper.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdDropper.java @@ -8,9 +8,10 @@ */ package dev.zontreck.engineerdecor.blocks; -import com.mojang.blaze3d.vertex.PoseStack; -import dev.zontreck.engineerdecor.libmc.Networking; -import dev.zontreck.engineerdecor.libmc.TooltipDisplay; +import dev.zontreck.engineerdecor.ModConfig; +import dev.zontreck.engineerdecor.ModContent; +import dev.zontreck.libzontreck.edlibmc.*; +import net.minecraft.client.gui.GuiGraphics; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; @@ -29,7 +30,7 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.SignalGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockBehaviour; @@ -48,16 +49,6 @@ import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.common.capabilities.ForgeCapabilities; import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.items.IItemHandler; -import dev.zontreck.engineerdecor.ModConfig; -import dev.zontreck.engineerdecor.ModContent; -import dev.zontreck.engineerdecor.libmc.StandardBlocks; -import dev.zontreck.engineerdecor.libmc.StandardEntityBlocks; -import dev.zontreck.engineerdecor.libmc.Auxiliaries; -import dev.zontreck.engineerdecor.libmc.Inventories; -import dev.zontreck.engineerdecor.libmc.Inventories.InventoryRange; -import dev.zontreck.engineerdecor.libmc.Inventories.StorageInventory; -import dev.zontreck.engineerdecor.libmc.RsSignals; -import dev.zontreck.engineerdecor.libmc.Guis; import javax.annotation.Nullable; import java.util.ArrayList; @@ -66,880 +57,907 @@ import java.util.Collections; import java.util.List; -public class EdDropper -{ - private static boolean with_adjacent_item_insertion = false; +public class EdDropper { + private static boolean with_adjacent_item_insertion = false; - public static void on_config(boolean with_item_insertion) - { - with_adjacent_item_insertion = with_item_insertion; - ModConfig.log("Config dropper: item-insertion:" + with_adjacent_item_insertion + "."); - } - - //-------------------------------------------------------------------------------------------------------------------- - // Block - //-------------------------------------------------------------------------------------------------------------------- - - public static class DropperBlock extends StandardBlocks.Directed implements StandardEntityBlocks.IStandardEntityBlock - { - public static final BooleanProperty OPEN = BlockStateProperties.OPEN; - - public DropperBlock(long config, BlockBehaviour.Properties builder, final AABB unrotatedAABB) - { super(config, builder, unrotatedAABB); } - - @Override - public boolean isBlockEntityTicking(Level world, BlockState state) - { return true; } - - @Override - public RenderTypeHint getRenderTypeHint() - { return RenderTypeHint.SOLID; } - - @Override - public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) - { return Shapes.block(); } - - @Override - protected void createBlockStateDefinition(StateDefinition.Builder builder) - { super.createBlockStateDefinition(builder); builder.add(OPEN); } - - @Override - @Nullable - public BlockState getStateForPlacement(BlockPlaceContext context) - { return super.getStateForPlacement(context).setValue(OPEN, false); } - - @Override - @SuppressWarnings("deprecation") - public boolean hasAnalogOutputSignal(BlockState state) - { return true; } - - @Override - @SuppressWarnings("deprecation") - public int getAnalogOutputSignal(BlockState blockState, Level world, BlockPos pos) - { return (world.getBlockEntity(pos) instanceof DropperTileEntity te) ? RsSignals.fromContainer(te.storage_slot_range_) : 0; } - - @Override - public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) - { - if(world.isClientSide) return; - if((!stack.hasTag()) || (!stack.getTag().contains("tedata"))) return; - CompoundTag te_nbt = stack.getTag().getCompound("tedata"); - if(te_nbt.isEmpty()) return; - final BlockEntity te = world.getBlockEntity(pos); - if(!(te instanceof EdDropper.DropperTileEntity)) return; - ((EdDropper.DropperTileEntity)te).readnbt(te_nbt, false); - ((EdDropper.DropperTileEntity)te).reset_rtstate(); - te.setChanged(); + public static void on_config(boolean with_item_insertion) { + with_adjacent_item_insertion = with_item_insertion; + ModConfig.log("Config dropper: item-insertion:" + with_adjacent_item_insertion + "."); } - @Override - public boolean hasDynamicDropList() - { return true; } + //-------------------------------------------------------------------------------------------------------------------- + // Block + //-------------------------------------------------------------------------------------------------------------------- - @Override - public List dropList(BlockState state, Level world, final BlockEntity te, boolean explosion) - { - final List stacks = new ArrayList<>(); - if(world.isClientSide) return stacks; - if(!(te instanceof DropperTileEntity)) return stacks; - if(!explosion) { - ItemStack stack = new ItemStack(this, 1); - CompoundTag te_nbt = ((DropperTileEntity) te).clear_getnbt(); - if(!te_nbt.isEmpty()) { - CompoundTag nbt = new CompoundTag(); - nbt.put("tedata", te_nbt); - stack.setTag(nbt); + public static class DropperBlock extends StandardBlocks.Directed implements StandardEntityBlocks.IStandardEntityBlock { + public static final BooleanProperty OPEN = BlockStateProperties.OPEN; + + public DropperBlock(long config, BlockBehaviour.Properties builder, final AABB unrotatedAABB) { + super(config, builder, unrotatedAABB); } - stacks.add(stack); - } else { - for(ItemStack stack: ((DropperTileEntity)te).main_inventory_) { - if(!stack.isEmpty()) stacks.add(stack); + + @Override + public boolean isBlockEntityTicking(Level world, BlockState state) { + return true; } - ((DropperTileEntity)te).reset_rtstate(); - } - return stacks; - } - @Override - @SuppressWarnings("deprecation") - public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult) - { return useOpenGui(state, world, pos, player); } - - @Override - @SuppressWarnings("deprecation") - public void neighborChanged(BlockState state, Level world, BlockPos pos, Block block, BlockPos fromPos, boolean unused) - { - if(!(world instanceof Level) || (world.isClientSide)) return; - BlockEntity te = world.getBlockEntity(pos); - if(!(te instanceof DropperTileEntity)) return; - ((DropperTileEntity)te).block_updated(); - } - - @Override - public boolean shouldCheckWeakPower(BlockState state, LevelReader world, BlockPos pos, Direction side) - { return false; } - } - - //-------------------------------------------------------------------------------------------------------------------- - // Tile entity - //-------------------------------------------------------------------------------------------------------------------- - - public static class DropperTileEntity extends StandardEntityBlocks.StandardBlockEntity implements MenuProvider, Nameable - { - public static final int NUM_OF_FIELDS = 16; - public static final int TICK_INTERVAL = 32; - public static final int NUM_OF_SLOTS = 15; - public static final int INPUT_SLOTS_FIRST = 0; - public static final int INPUT_SLOTS_SIZE = 12; - public static final int CTRL_SLOTS_FIRST = INPUT_SLOTS_SIZE; - public static final int CTRL_SLOTS_SIZE = 3; - public static final int SHUTTER_CLOSE_DELAY = 40; - public static final int MAX_DROP_COUNT = 32; - public static final int DROP_PERIOD_OFFSET = 10; - /// - public static final int DROPLOGIC_FILTER_ANDGATE = 0x01; - public static final int DROPLOGIC_EXTERN_ANDGATE = 0x02; - public static final int DROPLOGIC_SILENT_DROP = 0x04; - public static final int DROPLOGIC_SILENT_OPEN = 0x08; - public static final int DROPLOGIC_CONTINUOUS = 0x10; - public static final int DROPLOGIC_IGNORE_EXT = 0x20; - /// - private final int[] filter_matches_ = new int[CTRL_SLOTS_SIZE]; - private int open_timer_ = 0; - private int drop_timer_ = 0; - private boolean triggered_ = false; - private boolean block_power_signal_ = false; - private boolean block_power_updated_ = false; - private int drop_speed_ = 10; - private int drop_noise_ = 0; - private int drop_xdev_ = 0; - private int drop_ydev_ = 0; - private int drop_count_ = 1; - private int drop_logic_ = DROPLOGIC_EXTERN_ANDGATE; - private int drop_period_ = 0; - private int drop_slot_index_ = 0; - private int tick_timer_ = 0; - protected final Inventories.StorageInventory main_inventory_ = new StorageInventory(this, NUM_OF_SLOTS, 1); - protected final InventoryRange storage_slot_range_ = new InventoryRange(main_inventory_, INPUT_SLOTS_FIRST, INPUT_SLOTS_SIZE); - protected final InventoryRange filter_slot_range_ = new InventoryRange(main_inventory_, CTRL_SLOTS_FIRST, CTRL_SLOTS_SIZE); - protected LazyOptional item_handler_ = Inventories.MappedItemHandler.createGenericHandler(storage_slot_range_); - - public DropperTileEntity(BlockPos pos, BlockState state) - { super(ModContent.getBlockEntityTypeOfBlock(state.getBlock()), pos, state); reset_rtstate(); } - - public CompoundTag clear_getnbt() - { - CompoundTag nbt = new CompoundTag(); - writenbt(nbt, false); - main_inventory_.clearContent(); - reset_rtstate(); - triggered_ = false; - block_power_updated_ = false; - return nbt; - } - - public void reset_rtstate() - { - block_power_signal_ = false; - block_power_updated_ = false; - Arrays.fill(filter_matches_, 0); - } - - public void readnbt(CompoundTag nbt, boolean update_packet) - { - main_inventory_.load(nbt); - block_power_signal_ = nbt.getBoolean("powered"); - open_timer_ = nbt.getInt("open_timer"); - drop_speed_ = nbt.getInt("drop_speed"); - drop_noise_ = nbt.getInt("drop_noise"); - drop_xdev_ = nbt.getInt("drop_xdev"); - drop_ydev_ = nbt.getInt("drop_ydev"); - drop_slot_index_ = nbt.getInt("drop_slot_index"); - drop_count_ = Mth.clamp(nbt.getInt("drop_count"), 1, MAX_DROP_COUNT); - drop_logic_ = nbt.getInt("drop_logic"); - drop_period_ = nbt.getInt("drop_period"); - } - - protected void writenbt(CompoundTag nbt, boolean update_packet) - { - main_inventory_.save(nbt); - nbt.putBoolean("powered", block_power_signal_); - nbt.putInt("open_timer", open_timer_); - nbt.putInt("drop_speed", drop_speed_); - nbt.putInt("drop_noise", drop_noise_); - nbt.putInt("drop_xdev", drop_xdev_); - nbt.putInt("drop_ydev", drop_ydev_); - nbt.putInt("drop_slot_index", drop_slot_index_); - nbt.putInt("drop_count", drop_count_); - nbt.putInt("drop_logic", drop_logic_); - nbt.putInt("drop_period", drop_period_); - } - - public void block_updated() - { - // RS power check, both edges - boolean powered = level.hasNeighborSignal(worldPosition); - if(block_power_signal_ != powered) block_power_updated_ = true; - block_power_signal_ = powered; - tick_timer_ = 1; - } - - // BlockEntity ------------------------------------------------------------------------------ - - @Override - public void load(CompoundTag nbt) - { super.load(nbt); readnbt(nbt, false); } - - @Override - protected void saveAdditional(CompoundTag nbt) - { super.saveAdditional(nbt); writenbt(nbt, false); } - - @Override - public void setRemoved() - { - super.setRemoved(); - item_handler_.invalidate(); - } - - // Namable ----------------------------------------------------------------------------------------------- - - @Override - public Component getName() - { return Auxiliaries.localizable(getBlockState().getBlock().getDescriptionId()); } - - @Override - public boolean hasCustomName() - { return false; } - - @Override - public Component getCustomName() - { return getName(); } - - // INamedContainerProvider ------------------------------------------------------------------------------ - - @Override - public Component getDisplayName() - { return Nameable.super.getDisplayName(); } - - @Override - public AbstractContainerMenu createMenu(int id, Inventory inventory, Player player ) - { return new DropperUiContainer(id, inventory, main_inventory_, ContainerLevelAccess.create(level, worldPosition), fields); } - - // Fields ----------------------------------------------------------------------------------------------- - - protected final ContainerData fields = new ContainerData() - { - @Override - public int getCount() - { return DropperTileEntity.NUM_OF_FIELDS; } - - @Override - public int get(int id) - { - return switch (id) { - case 0 -> drop_speed_; - case 1 -> drop_xdev_; - case 2 -> drop_ydev_; - case 3 -> drop_noise_; - case 4 -> drop_count_; - case 5 -> drop_logic_; - case 6 -> drop_period_; - case 9 -> drop_timer_; - case 10 -> open_timer_; - case 11 -> block_power_signal_ ? 1 : 0; - case 12 -> filter_matches_[0]; - case 13 -> filter_matches_[1]; - case 14 -> filter_matches_[2]; - case 15 -> drop_slot_index_; - default -> 0; - }; - } - @Override - public void set(int id, int value) - { - switch (id) { - case 0 -> drop_speed_ = Mth.clamp(value, 0, 100); - case 1 -> drop_xdev_ = Mth.clamp(value, -100, 100); - case 2 -> drop_ydev_ = Mth.clamp(value, -100, 100); - case 3 -> drop_noise_ = Mth.clamp(value, 0, 100); - case 4 -> drop_count_ = Mth.clamp(value, 1, MAX_DROP_COUNT); - case 5 -> drop_logic_ = value; - case 6 -> drop_period_ = Mth.clamp(value, 0, 100); - case 9 -> drop_timer_ = Mth.clamp(value, 0, 400); - case 10 -> open_timer_ = Mth.clamp(value, 0, 400); - case 11 -> block_power_signal_ = (value != 0); - case 12 -> filter_matches_[0] = (value & 0x3); - case 13 -> filter_matches_[1] = (value & 0x3); - case 14 -> filter_matches_[2] = (value & 0x3); - case 15 -> drop_slot_index_ = Mth.clamp(value, INPUT_SLOTS_FIRST, INPUT_SLOTS_FIRST + INPUT_SLOTS_SIZE - 1); + @Override + public RenderTypeHint getRenderTypeHint() { + return RenderTypeHint.SOLID; } - } - }; - // Capability export ------------------------------------------------------------------------------------ - - @Override - public LazyOptional getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable Direction facing) - { - if(capability == ForgeCapabilities.ITEM_HANDLER) return item_handler_.cast(); - return super.getCapability(capability, facing); - } - - // ITickable and aux methods ---------------------------------------------------------------------------- - - private static void drop(Level world, BlockPos pos, Direction facing, ItemStack stack, int speed_percent, int xdeviation, int ydeviation, int noise_percent) - { - final double ofs = facing==Direction.DOWN ? 0.8 : 0.7; - Vec3 v0 = new Vec3(facing.getStepX(), facing.getStepY(), facing.getStepZ()); - final ItemEntity ei = new ItemEntity(world, (pos.getX()+0.5)+(ofs*v0.x), (pos.getY()+0.5)+(ofs*v0.y), (pos.getZ()+0.5)+(ofs*v0.z), stack); - if((xdeviation != 0) || (ydeviation != 0)) { - double vdx = 1e-2 * Mth.clamp(xdeviation, -100, 100); - double vdy = 1e-2 * Mth.clamp(ydeviation, -100, 100); - v0 = switch (facing) { // switch-case faster than coorsys fwd transform - case DOWN -> v0.add(vdx, 0, -vdy); - case NORTH -> v0.add(vdx, vdy, 0); - case SOUTH -> v0.add(-vdx, vdy, 0); - case EAST -> v0.add(0, vdy, vdx); - case WEST -> v0.add(0, vdy, -vdx); - case UP -> v0.add(vdx, 0, vdy); - }; - } - if(noise_percent > 0) { - v0 = v0.add( - ((world.random.nextDouble()-0.5) * 1e-3 * noise_percent), - ((world.random.nextDouble()-0.5) * 1e-3 * noise_percent), - ((world.random.nextDouble()-0.5) * 1e-3 * noise_percent) - ); - } - if(speed_percent < 5) speed_percent = 5; - double speed = 1e-2 * speed_percent; - if(noise_percent > 0) speed += (world.random.nextDouble()-0.5) * 1e-4 * noise_percent; - v0 = v0.normalize().scale(speed); - ei.setDeltaMovement(v0.x, v0.y, v0.z); - ei.hurtMarked = true; - world.addFreshEntity(ei); - } - - private static Tuple> try_eject(Level world, BlockPos pos, Direction facing, ItemStack[] stacks, int speed_percent, int xdeviation, int ydeviation, int noise_percent) - { - if(Arrays.stream(stacks).allMatch(ItemStack::isEmpty)) return new Tuple<>(false, Arrays.asList(stacks)); - if(with_adjacent_item_insertion) { - final BlockEntity te = world.getBlockEntity(pos.relative(facing)); - if(te != null) { - final IItemHandler ih = te.getCapability(ForgeCapabilities.ITEM_HANDLER, (facing==null)?(null):(facing.getOpposite())).orElse(null); - if(ih != null) { - boolean inserted = false; - List remaining = new ArrayList<>(); - for(final ItemStack stack : stacks) { - final ItemStack rs = Inventories.insert(ih, stack.copy(), false); - if (rs.getCount() < stack.getCount()) inserted = true; - if (!rs.isEmpty()) remaining.add(rs); - } - return new Tuple<>(inserted, remaining); - } + @Override + public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) { + return Shapes.block(); } - } - for(ItemStack stack: stacks) { - if(stack.isEmpty()) continue; - drop(world, pos, facing, stack, speed_percent, xdeviation, ydeviation, noise_percent); - } - return new Tuple<>(true, Collections.emptyList()); - } - @Nullable - BlockState update_blockstate() - { - BlockState state = level.getBlockState(worldPosition); - if(!(state.getBlock() instanceof DropperBlock)) return null; - boolean open = (open_timer_ > 0); - if(state.getValue(DropperBlock.OPEN) != open) { - state = state.setValue(DropperBlock.OPEN, open); - level.setBlock(worldPosition, state, 2|16); - if((drop_logic_ & DROPLOGIC_SILENT_OPEN) == 0) { - if(open) { - level.playSound(null, worldPosition, SoundEvents.WOODEN_TRAPDOOR_OPEN, SoundSource.BLOCKS, 0.08f, 3f); - } else { - level.playSound(null, worldPosition, SoundEvents.WOODEN_TRAPDOOR_CLOSE, SoundSource.BLOCKS, 0.08f, 3f); - } + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + super.createBlockStateDefinition(builder); + builder.add(OPEN); } - } - return state; - } - private static int next_slot(int i) - { return (i 0) && ((--drop_timer_) == 0)) setChanged(); - if(--tick_timer_ > 0) return; - tick_timer_ = TICK_INTERVAL; - if(!(level.getBlockState(worldPosition).getBlock() instanceof DropperBlock)) return; - if(storage_slot_range_.isEmpty()) return; - final boolean continuous_mode = (drop_logic_ & DROPLOGIC_CONTINUOUS)!=0; - boolean dirty = block_power_updated_; - boolean redstone_trigger = (block_power_signal_ && ((block_power_updated_) || (continuous_mode))) || ((drop_logic_ & DROPLOGIC_IGNORE_EXT)!=0); - boolean filter_trigger; - boolean filter_defined; - boolean trigger; - // Trigger logic - { - // From filters / inventory checks - { - int filter_nset = 0; - int[] last_filter_matches_ = filter_matches_.clone(); - for(int ci=0; ci 0) ++filter_nset; - if(filter_matches_[i] > 1) ++nmatched; - if(filter_matches_[i] != last_filter_matches_[i]) dirty = true; - } - filter_defined = (filter_nset > 0); - filter_trigger = ((filter_nset > 0) && (nmatched > 0)); - if(((drop_logic_ & DROPLOGIC_FILTER_ANDGATE) != 0) && (nmatched != filter_nset)) filter_trigger = false; + @Override + @SuppressWarnings("deprecation") + public boolean hasAnalogOutputSignal(BlockState state) { + return true; } - // gates - { - if(filter_defined) { - trigger = ((drop_logic_ & DROPLOGIC_EXTERN_ANDGATE) != 0) ? (filter_trigger && redstone_trigger) : (filter_trigger || redstone_trigger); - } else { - trigger = redstone_trigger; - } - if(triggered_) { triggered_ = false; trigger = true; } - if(storage_slot_range_.stream().noneMatch(is->is.getCount() >= drop_count_)) { - if(open_timer_> 10) open_timer_ = 10; // override if dropping is not possible at all. - } else if(trigger || filter_trigger || redstone_trigger) { - open_timer_ = SHUTTER_CLOSE_DELAY; - } + + @Override + @SuppressWarnings("deprecation") + public int getAnalogOutputSignal(BlockState blockState, Level world, BlockPos pos) { + return (world.getBlockEntity(pos) instanceof DropperTileEntity te) ? RsSignals.fromContainer(te.storage_slot_range_) : 0; } - // edge detection for next cycle - { - boolean tr = level.hasNeighborSignal(worldPosition); - block_power_updated_ = (block_power_signal_ != tr); - block_power_signal_ = tr; - if(block_power_updated_) dirty = true; + + @Override + public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) { + if (world.isClientSide) return; + if ((!stack.hasTag()) || (!stack.getTag().contains("tedata"))) return; + CompoundTag te_nbt = stack.getTag().getCompound("tedata"); + if (te_nbt.isEmpty()) return; + final BlockEntity te = world.getBlockEntity(pos); + if (!(te instanceof EdDropper.DropperTileEntity)) return; + ((EdDropper.DropperTileEntity) te).readnbt(te_nbt, false); + ((EdDropper.DropperTileEntity) te).reset_rtstate(); + te.setChanged(); } - } - // block state update - final BlockState state = update_blockstate(); - if(state == null) { block_power_signal_= false; return; } - // dispense action - if(trigger && (drop_timer_ <= 0)) { - // drop stack for non-filter triggers - ItemStack[] drop_stacks = {ItemStack.EMPTY,ItemStack.EMPTY,ItemStack.EMPTY}; - if(!filter_trigger) { - for(int i=0; i= INPUT_SLOTS_SIZE) drop_slot_index_ = 0; - final int ic = drop_slot_index_; - drop_slot_index_ = next_slot(drop_slot_index_); - ItemStack ds = main_inventory_.getItem(ic); - if((!ds.isEmpty()) && ((ds.getCount() >= drop_count_) || (!ds.isStackable()))) { - boolean skip_stack = false; - for(int ci = 0; (ci 1) { - drop_stacks[fi] = main_inventory_.getItem(CTRL_SLOTS_FIRST+fi).copy(); - int ntoremove = drop_stacks[fi].getCount(); - for(int i=INPUT_SLOTS_SIZE-1; (i>=0) && (ntoremove>0); --i) { - ItemStack stack = main_inventory_.getItem(i); - if(Inventories.areItemStacksDifferent(stack, drop_stacks[fi])) continue; - if(stack.getCount() <= ntoremove) { - ntoremove -= stack.getCount(); - main_inventory_.setItem(i, ItemStack.EMPTY); - } else { - stack.shrink(ntoremove); - ntoremove = 0; - main_inventory_.setItem(i, stack); + + @Override + public boolean hasDynamicDropList() { + return true; + } + + @Override + public List dropList(BlockState state, Level world, final BlockEntity te, boolean explosion) { + final List stacks = new ArrayList<>(); + if (world.isClientSide) return stacks; + if (!(te instanceof DropperTileEntity)) return stacks; + if (!explosion) { + ItemStack stack = new ItemStack(this, 1); + CompoundTag te_nbt = ((DropperTileEntity) te).clear_getnbt(); + if (!te_nbt.isEmpty()) { + CompoundTag nbt = new CompoundTag(); + nbt.put("tedata", te_nbt); + stack.setTag(nbt); } - } - if(ntoremove > 0) drop_stacks[fi].shrink(ntoremove); + stacks.add(stack); + } else { + for (ItemStack stack : ((DropperTileEntity) te).main_inventory_) { + if (!stack.isEmpty()) stacks.add(stack); + } + ((DropperTileEntity) te).reset_rtstate(); } - } + return stacks; } - // drop action - if(Arrays.stream(drop_stacks).allMatch(ItemStack::isEmpty)) { - // @todo: check if a re-stacking action is appropriate, or if players intentionally use the stack-in-place feature. - } else { - Tuple> res = try_eject(level, worldPosition, state.getValue(DropperBlock.FACING), drop_stacks, drop_speed_, drop_xdev_, drop_ydev_, drop_noise_); - final boolean dropped = res.getA(); - final List remaining = res.getB(); - for(ItemStack st:remaining) { - if(!storage_slot_range_.insert(st).isEmpty()) Auxiliaries.logger().debug("NOT ALL NON-DROPPED ITEMS PUT BACK:" + st); - } - if(dropped || (!remaining.isEmpty())) dirty = true; - // cooldown - if(dropped) drop_timer_ = DROP_PERIOD_OFFSET + drop_period_ * 2; // 0.1s time base -> 100%===10s - // drop sound - if(dropped && ((drop_logic_ & DROPLOGIC_SILENT_DROP) == 0)) { - level.playSound(null, worldPosition, SoundEvents.WOOD_HIT, SoundSource.BLOCKS, 0.1f, 4f); - } + + @Override + @SuppressWarnings("deprecation") + public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult) { + return useOpenGui(state, world, pos, player); } - // advance to next nonempty slot. - { - boolean found = false; - for(int i = 0; i < storage_slot_range_.size(); ++i) { - if(!main_inventory_.getItem(drop_slot_index_).isEmpty()) { found=true; break; } - drop_slot_index_ = next_slot(drop_slot_index_); - } - if(!found) drop_slot_index_ = 0; + + @Override + @SuppressWarnings("deprecation") + public void neighborChanged(BlockState state, Level world, BlockPos pos, Block block, BlockPos fromPos, boolean unused) { + if (!(world instanceof Level) || (world.isClientSide)) return; + BlockEntity te = world.getBlockEntity(pos); + if (!(te instanceof DropperTileEntity)) return; + ((DropperTileEntity) te).block_updated(); } - } - if(dirty) setChanged(); - if(trigger && (tick_timer_ > 10)) tick_timer_ = 10; - } - } - //-------------------------------------------------------------------------------------------------------------------- - // Container - //-------------------------------------------------------------------------------------------------------------------- - - public static class DropperUiContainer extends AbstractContainerMenu implements Networking.INetworkSynchronisableContainer - { - protected static final String QUICK_MOVE_ALL = "quick-move-all"; - private static final int PLAYER_INV_START_SLOTNO = DropperTileEntity.NUM_OF_SLOTS; - private final Player player_; - private final Container inventory_; - private final ContainerLevelAccess wpc_; - private final ContainerData fields_; - private final InventoryRange player_inventory_range_; - private final InventoryRange block_storage_range_; - - public final int field(int index) { return fields_.get(index); } - - public DropperUiContainer(int cid, Inventory player_inventory) - { this(cid, player_inventory, new SimpleContainer(DropperTileEntity.NUM_OF_SLOTS), ContainerLevelAccess.NULL, new SimpleContainerData(DropperTileEntity.NUM_OF_FIELDS)); } - - private DropperUiContainer(int cid, Inventory player_inventory, Container block_inventory, ContainerLevelAccess wpc, ContainerData fields) - { - super(ModContent.getMenuType("factory_dropper"), cid); // @todo: class mapping - fields_ = fields; - wpc_ = wpc; - player_ = player_inventory.player; - inventory_ = block_inventory; - block_storage_range_ = new InventoryRange(inventory_, 0, DropperTileEntity.NUM_OF_SLOTS); - player_inventory_range_ = InventoryRange.fromPlayerInventory(player_); - int i=-1; - // input slots (stacks 0 to 11) - for(int y=0; y<2; ++y) { - for(int x=0; x<6; ++x) { - int xpos = 10+x*18, ypos = 6+y*17; - addSlot(new Slot(inventory_, ++i, xpos, ypos)); + @Override + public boolean shouldCheckWeakPower(BlockState state, SignalGetter level, BlockPos pos, Direction side) { + return false; } - } - // filter slots (stacks 12 to 14) - addSlot(new Slot(inventory_, ++i, 19, 48)); - addSlot(new Slot(inventory_, ++i, 55, 48)); - addSlot(new Slot(inventory_, ++i, 91, 48)); - // player slots - for(int x=0; x<9; ++x) { - addSlot(new Slot(player_inventory, x, 8+x*18, 144)); // player slots: 0..8 - } - for(int y=0; y<3; ++y) { - for(int x=0; x<9; ++x) { - addSlot(new Slot(player_inventory, x+y*9+9, 8+x*18, 86+y*18)); // player slots: 9..35 - } - } - this.addDataSlots(fields_); // === Add reference holders } - @Override - public boolean stillValid(Player player) - { return inventory_.stillValid(player); } + //-------------------------------------------------------------------------------------------------------------------- + // Tile entity + //-------------------------------------------------------------------------------------------------------------------- - @Override - public ItemStack quickMoveStack(Player player, int index) - { - Slot slot = getSlot(index); - if((slot==null) || (!slot.hasItem())) return ItemStack.EMPTY; - ItemStack slot_stack = slot.getItem(); - ItemStack transferred = slot_stack.copy(); - if((index>=0) && (index= PLAYER_INV_START_SLOTNO) && (index <= PLAYER_INV_START_SLOTNO+36)) { - // Player slot - if(!moveItemStackTo(slot_stack, 0, DropperTileEntity.INPUT_SLOTS_SIZE, false)) return ItemStack.EMPTY; - } else { - // invalid slot - return ItemStack.EMPTY; - } - if(slot_stack.isEmpty()) { - slot.set(ItemStack.EMPTY); - } else { - slot.setChanged(); - } - if(slot_stack.getCount() == transferred.getCount()) return ItemStack.EMPTY; - slot.onTake(player, slot_stack); - return transferred; - } - - // INetworkSynchronisableContainer --------------------------------------------------------- - - @OnlyIn(Dist.CLIENT) - public void onGuiAction(CompoundTag nbt) - { Networking.PacketContainerSyncClientToServer.sendToServer(containerId, nbt); } - - @OnlyIn(Dist.CLIENT) - public void onGuiAction(String key, int value) - { - CompoundTag nbt = new CompoundTag(); - nbt.putInt(key, value); - Networking.PacketContainerSyncClientToServer.sendToServer(containerId, nbt); - } - - @OnlyIn(Dist.CLIENT) - public void onGuiAction(String message, CompoundTag nbt) - { - nbt.putString("action", message); - Networking.PacketContainerSyncClientToServer.sendToServer(containerId, nbt); - } - - @Override - public void onServerPacketReceived(int windowId, CompoundTag nbt) - {} - - @Override - public void onClientPacketReceived(int windowId, Player player, CompoundTag nbt) - { - if(!(inventory_ instanceof Inventories.StorageInventory)) return; - if(!(((Inventories.StorageInventory)inventory_).getBlockEntity() instanceof final DropperTileEntity te)) return; - if(nbt.contains("action")) { - boolean changed = false; - final int slotId = nbt.contains("slot") ? nbt.getInt("slot") : -1; - switch(nbt.getString("action")) { - case QUICK_MOVE_ALL -> { - if ((slotId >= 0) && (slotId < PLAYER_INV_START_SLOTNO) && (getSlot(slotId).hasItem())) { - changed = block_storage_range_.move(getSlot(slotId).getSlotIndex(), player_inventory_range_, true, false, true, true); - } else if ((slotId >= PLAYER_INV_START_SLOTNO) && (slotId < PLAYER_INV_START_SLOTNO + 36) && (getSlot(slotId).hasItem())) { - changed = player_inventory_range_.move(getSlot(slotId).getSlotIndex(), block_storage_range_, true, false, false, true); + public static class DropperTileEntity extends StandardEntityBlocks.StandardBlockEntity implements MenuProvider, Nameable { + public static final int NUM_OF_FIELDS = 16; + public static final int TICK_INTERVAL = 32; + public static final int NUM_OF_SLOTS = 15; + public static final int INPUT_SLOTS_FIRST = 0; + public static final int INPUT_SLOTS_SIZE = 12; + public static final int CTRL_SLOTS_FIRST = INPUT_SLOTS_SIZE; + public static final int CTRL_SLOTS_SIZE = 3; + public static final int SHUTTER_CLOSE_DELAY = 40; + public static final int MAX_DROP_COUNT = 32; + public static final int DROP_PERIOD_OFFSET = 10; + /// + public static final int DROPLOGIC_FILTER_ANDGATE = 0x01; + public static final int DROPLOGIC_EXTERN_ANDGATE = 0x02; + public static final int DROPLOGIC_SILENT_DROP = 0x04; + public static final int DROPLOGIC_SILENT_OPEN = 0x08; + public static final int DROPLOGIC_CONTINUOUS = 0x10; + public static final int DROPLOGIC_IGNORE_EXT = 0x20; + protected final Inventories.StorageInventory main_inventory_ = new Inventories.StorageInventory(this, NUM_OF_SLOTS, 1); + protected final Inventories.InventoryRange storage_slot_range_ = new Inventories.InventoryRange(main_inventory_, INPUT_SLOTS_FIRST, INPUT_SLOTS_SIZE); + protected LazyOptional item_handler_ = Inventories.MappedItemHandler.createGenericHandler(storage_slot_range_); + protected final Inventories.InventoryRange filter_slot_range_ = new Inventories.InventoryRange(main_inventory_, CTRL_SLOTS_FIRST, CTRL_SLOTS_SIZE); + /// + private final int[] filter_matches_ = new int[CTRL_SLOTS_SIZE]; + private int open_timer_ = 0; + private int drop_timer_ = 0; + private boolean triggered_ = false; + private boolean block_power_signal_ = false; + private boolean block_power_updated_ = false; + private int drop_speed_ = 10; + private int drop_noise_ = 0; + private int drop_xdev_ = 0; + private int drop_ydev_ = 0; + private int drop_count_ = 1; + private int drop_logic_ = DROPLOGIC_EXTERN_ANDGATE; + private int drop_period_ = 0; + private int drop_slot_index_ = 0; + protected final ContainerData fields = new ContainerData() { + @Override + public int getCount() { + return DropperTileEntity.NUM_OF_FIELDS; } - } - } - if(changed) { - inventory_.setChanged(); - player.getInventory().setChanged(); - broadcastChanges(); - } - } else { - if(nbt.contains("drop_speed")) te.drop_speed_ = Mth.clamp(nbt.getInt("drop_speed"), 0, 100); - if(nbt.contains("drop_xdev")) te.drop_xdev_ = Mth.clamp(nbt.getInt("drop_xdev"), -100, 100); - if(nbt.contains("drop_ydev")) te.drop_ydev_ = Mth.clamp(nbt.getInt("drop_ydev"), -100, 100); - if(nbt.contains("drop_count")) te.drop_count_ = Mth.clamp(nbt.getInt("drop_count"), 1, DropperTileEntity.MAX_DROP_COUNT); - if(nbt.contains("drop_period")) te.drop_period_ = Mth.clamp(nbt.getInt("drop_period"), 0, 100); - if(nbt.contains("drop_logic")) te.drop_logic_ = nbt.getInt("drop_logic"); - if(nbt.contains("manual_rstrigger") && (nbt.getInt("manual_rstrigger")!=0)) { te.block_power_signal_=true; te.block_power_updated_=true; te.tick_timer_=1; } - if(nbt.contains("manual_trigger") && (nbt.getInt("manual_trigger")!=0)) { te.tick_timer_ = 1; te.triggered_ = true; } - te.setChanged(); - } - } - } + @Override + public int get(int id) { + return switch (id) { + case 0 -> drop_speed_; + case 1 -> drop_xdev_; + case 2 -> drop_ydev_; + case 3 -> drop_noise_; + case 4 -> drop_count_; + case 5 -> drop_logic_; + case 6 -> drop_period_; + case 9 -> drop_timer_; + case 10 -> open_timer_; + case 11 -> block_power_signal_ ? 1 : 0; + case 12 -> filter_matches_[0]; + case 13 -> filter_matches_[1]; + case 14 -> filter_matches_[2]; + case 15 -> drop_slot_index_; + default -> 0; + }; + } - //-------------------------------------------------------------------------------------------------------------------- - // GUI - //-------------------------------------------------------------------------------------------------------------------- - - @OnlyIn(Dist.CLIENT) - public static class DropperGui extends Guis.ContainerGui - { - public DropperGui(DropperUiContainer container, Inventory player_inventory, Component title) - { super(container, player_inventory, title, "textures/gui/factory_dropper_gui.png"); } - - @Override - public void init() - { - super.init(); - { - final Block block = ModContent.getBlock(Auxiliaries.getResourceLocation(getMenu().getType()).getPath().replaceAll("^ct_","")); - final String prefix = block.getDescriptionId() + ".tooltips."; - final int x0 = getGuiLeft(), y0 = getGuiTop(); - tooltip_.init( - new TooltipDisplay.TipRange(x0+130, y0+10, 12, 25, Component.translatable(prefix + "velocity")), - new TooltipDisplay.TipRange(x0+145, y0+10, 25, 25, Component.translatable(prefix + "direction")), - new TooltipDisplay.TipRange(x0+129, y0+40, 44, 10, Component.translatable(prefix + "dropcount")), - new TooltipDisplay.TipRange(x0+129, y0+50, 44, 10, Component.translatable(prefix + "period")), - new TooltipDisplay.TipRange(x0+114, y0+51, 9, 9, Component.translatable(prefix + "rssignal")), - new TooltipDisplay.TipRange(x0+162, y0+66, 7, 9, Component.translatable(prefix + "triggermode")), - new TooltipDisplay.TipRange(x0+132, y0+66, 9, 9, Component.translatable(prefix + "filtergate")), - new TooltipDisplay.TipRange(x0+148, y0+66, 9, 9, Component.translatable(prefix + "externgate")) - ); - } - } - - @Override - protected void renderBgWidgets(PoseStack mx, float partialTicks, int mouseX, int mouseY) - { - final int x0=getGuiLeft(), y0=getGuiTop(), w=getXSize(), h=getYSize(); - DropperUiContainer container = getMenu(); - // active drop slot - { - int drop_slot_index = container.field(15); - if((drop_slot_index < 0) || (drop_slot_index >= 16)) drop_slot_index = 0; - int x = (x0+9+((drop_slot_index % 6) * 18)); - int y = (y0+5+((drop_slot_index / 6) * 17)); - blit(mx, x, y, 180, 45, 18, 18); - } - // filter LEDs - { - for(int i=0; i<3; ++i) { - int xt = 180 + (6 * container.field(12+i)), yt = 38; - int x = x0 + 31 + (i * 36), y = y0 + 65; - blit(mx, x, y, xt, yt, 6, 6); - } - } - // force adjustment - { - int hy = 2 + (((100-container.field(0)) * 21) / 100); - int x = x0+135, y = y0+12, xt = 181; - int yt = 4 + (23-hy); - blit(mx, x, y, xt, yt, 3, hy); - } - // angle adjustment - { - int x = x0 + 157 - 3 + ((container.field(1) * 12) / 100); - int y = y0 + 22 - 3 - ((container.field(2) * 12) / 100); - blit(mx, x, y, 180, 30, 7, 7); - } - // drop count - { - int x = x0 + 134 - 2 + (container.field(4)); - int y = y0 + 45; - blit(mx, x, y, 190, 31, 5, 5); - } - // drop period - { - int px = (int)Math.round(((33.0 * container.field(6)) / 100) + 1); - int x = x0 + 134 - 2 + Mth.clamp(px, 0, 33); - int y = y0 + 56; - blit(mx, x, y, 190, 31, 5, 5); - } - // redstone input - { - if(container.field(11) != 0) { - blit(mx, x0+114, y0+51, 189, 18, 9, 9); - } - } - // trigger logic - { - final int logic = container.field(5); - int filter_gate_offset = ((logic & DropperTileEntity.DROPLOGIC_FILTER_ANDGATE) != 0) ? 11 : 0; - int pulse_mode_offset = ((logic & DropperTileEntity.DROPLOGIC_CONTINUOUS ) != 0) ? 10 : 0; - int extern_gate_offset_x = ((logic & DropperTileEntity.DROPLOGIC_EXTERN_ANDGATE) != 0) ? 11 : 0; - int extern_gate_offset_y = ((logic & DropperTileEntity.DROPLOGIC_IGNORE_EXT) != 0) ? 10 : 0; - blit(mx, x0+132, y0+66, 179+filter_gate_offset, 66, 9, 9); - blit(mx, x0+148, y0+66, 179+extern_gate_offset_x, 66+extern_gate_offset_y, 9, 9); - blit(mx, x0+162, y0+66, 200+pulse_mode_offset, 66, 9, 9); - } - // drop timer running indicator - { - if((container.field(9) > DropperTileEntity.DROP_PERIOD_OFFSET) && ((System.currentTimeMillis() % 1000) < 500)) { - blit(mx, x0+149, y0+51, 201, 39, 3, 3); - } - } - } - - @Override - public boolean mouseClicked(double mouseX, double mouseY, int mouseButton) - { - tooltip_.resetTimer(); - DropperUiContainer container = getMenu(); - int mx = (int)(mouseX - getGuiLeft() + .5), my = (int)(mouseY - getGuiTop() + .5); - if((!isHovering(114, 1, 61, 79, mouseX, mouseY))) { - return super.mouseClicked(mouseX, mouseY, mouseButton); - } else if(isHovering(130, 10, 12, 25, mouseX, mouseY)) { - int force_percent = 100 - Mth.clamp(((my-10)*100)/25, 0, 100); - container.onGuiAction("drop_speed", force_percent); - } else if(isHovering(145, 10, 25, 25, mouseX, mouseY)) { - int xdev = Mth.clamp( (int)Math.round(((double)((mx-157) * 100)) / 12), -100, 100); - int ydev = Mth.clamp(-(int)Math.round(((double)((my- 22) * 100)) / 12), -100, 100); - if(Math.abs(xdev) < 9) xdev = 0; - if(Math.abs(ydev) < 9) ydev = 0; - CompoundTag nbt = new CompoundTag(); - nbt.putInt("drop_xdev", xdev); - nbt.putInt("drop_ydev", ydev); - container.onGuiAction(nbt); - } else if(isHovering(129, 40, 44, 10, mouseX, mouseY)) { - int ndrop = (mx-135); - if(ndrop < -1) { - ndrop = container.field(4) - 1; // - - } else if(ndrop >= 34) { - ndrop = container.field(4) + 1; // + - } else { - ndrop = Mth.clamp(1+ndrop, 1, DropperTileEntity.MAX_DROP_COUNT); // slider - } - container.onGuiAction("drop_count", ndrop); - } else if(isHovering(129, 50, 44, 10, mouseX, mouseY)) { - int period = (mx-135); - if(period < -1) { - period = container.field(6) - 3; // - - } else if(period >= 34) { - period = container.field(6) + 3; // + - } else { - period = (int)(0.5 + ((100.0 * period)/34)); - } - period = Mth.clamp(period, 0, 100); - container.onGuiAction("drop_period", period); - } else if(isHovering(114, 51, 9, 9, mouseX, mouseY)) { - container.onGuiAction("manual_rstrigger", 1); - } else if(isHovering(162, 66, 7, 9, mouseX, mouseY)) { - container.onGuiAction("drop_logic", container.field(5) ^ DropperTileEntity.DROPLOGIC_CONTINUOUS); - } else if(isHovering(132, 66, 9, 9, mouseX, mouseY)) { - container.onGuiAction("drop_logic", container.field(5) ^ DropperTileEntity.DROPLOGIC_FILTER_ANDGATE); - } else if(isHovering(148, 66, 9, 9, mouseX, mouseY)) { - final int mask = (DropperTileEntity.DROPLOGIC_EXTERN_ANDGATE|DropperTileEntity.DROPLOGIC_IGNORE_EXT); - final int logic = switch((container.field(5) & mask)) { - case DropperTileEntity.DROPLOGIC_EXTERN_ANDGATE -> 0; - case 0 -> DropperTileEntity.DROPLOGIC_IGNORE_EXT; - case DropperTileEntity.DROPLOGIC_IGNORE_EXT -> DropperTileEntity.DROPLOGIC_EXTERN_ANDGATE; - default -> DropperTileEntity.DROPLOGIC_EXTERN_ANDGATE; + @Override + public void set(int id, int value) { + switch (id) { + case 0 -> drop_speed_ = Mth.clamp(value, 0, 100); + case 1 -> drop_xdev_ = Mth.clamp(value, -100, 100); + case 2 -> drop_ydev_ = Mth.clamp(value, -100, 100); + case 3 -> drop_noise_ = Mth.clamp(value, 0, 100); + case 4 -> drop_count_ = Mth.clamp(value, 1, MAX_DROP_COUNT); + case 5 -> drop_logic_ = value; + case 6 -> drop_period_ = Mth.clamp(value, 0, 100); + case 9 -> drop_timer_ = Mth.clamp(value, 0, 400); + case 10 -> open_timer_ = Mth.clamp(value, 0, 400); + case 11 -> block_power_signal_ = (value != 0); + case 12 -> filter_matches_[0] = (value & 0x3); + case 13 -> filter_matches_[1] = (value & 0x3); + case 14 -> filter_matches_[2] = (value & 0x3); + case 15 -> + drop_slot_index_ = Mth.clamp(value, INPUT_SLOTS_FIRST, INPUT_SLOTS_FIRST + INPUT_SLOTS_SIZE - 1); + } + } }; - container.onGuiAction("drop_logic", (container.field(5) & (~mask)) | logic); - } - return true; + private int tick_timer_ = 0; + + public DropperTileEntity(BlockPos pos, BlockState state) { + super(ModContent.getBlockEntityTypeOfBlock(state.getBlock()), pos, state); + reset_rtstate(); + } + + private static void drop(Level world, BlockPos pos, Direction facing, ItemStack stack, int speed_percent, int xdeviation, int ydeviation, int noise_percent) { + final double ofs = facing == Direction.DOWN ? 0.8 : 0.7; + Vec3 v0 = new Vec3(facing.getStepX(), facing.getStepY(), facing.getStepZ()); + final ItemEntity ei = new ItemEntity(world, (pos.getX() + 0.5) + (ofs * v0.x), (pos.getY() + 0.5) + (ofs * v0.y), (pos.getZ() + 0.5) + (ofs * v0.z), stack); + if ((xdeviation != 0) || (ydeviation != 0)) { + double vdx = 1e-2 * Mth.clamp(xdeviation, -100, 100); + double vdy = 1e-2 * Mth.clamp(ydeviation, -100, 100); + v0 = switch (facing) { // switch-case faster than coorsys fwd transform + case DOWN -> v0.add(vdx, 0, -vdy); + case NORTH -> v0.add(vdx, vdy, 0); + case SOUTH -> v0.add(-vdx, vdy, 0); + case EAST -> v0.add(0, vdy, vdx); + case WEST -> v0.add(0, vdy, -vdx); + case UP -> v0.add(vdx, 0, vdy); + }; + } + if (noise_percent > 0) { + v0 = v0.add( + ((world.random.nextDouble() - 0.5) * 1e-3 * noise_percent), + ((world.random.nextDouble() - 0.5) * 1e-3 * noise_percent), + ((world.random.nextDouble() - 0.5) * 1e-3 * noise_percent) + ); + } + if (speed_percent < 5) speed_percent = 5; + double speed = 1e-2 * speed_percent; + if (noise_percent > 0) speed += (world.random.nextDouble() - 0.5) * 1e-4 * noise_percent; + v0 = v0.normalize().scale(speed); + ei.setDeltaMovement(v0.x, v0.y, v0.z); + ei.hurtMarked = true; + world.addFreshEntity(ei); + } + + private static Tuple> try_eject(Level world, BlockPos pos, Direction facing, ItemStack[] stacks, int speed_percent, int xdeviation, int ydeviation, int noise_percent) { + if (Arrays.stream(stacks).allMatch(ItemStack::isEmpty)) return new Tuple<>(false, Arrays.asList(stacks)); + if (with_adjacent_item_insertion) { + final BlockEntity te = world.getBlockEntity(pos.relative(facing)); + if (te != null) { + final IItemHandler ih = te.getCapability(ForgeCapabilities.ITEM_HANDLER, (facing == null) ? (null) : (facing.getOpposite())).orElse(null); + if (ih != null) { + boolean inserted = false; + List remaining = new ArrayList<>(); + for (final ItemStack stack : stacks) { + final ItemStack rs = Inventories.insert(ih, stack.copy(), false); + if (rs.getCount() < stack.getCount()) inserted = true; + if (!rs.isEmpty()) remaining.add(rs); + } + return new Tuple<>(inserted, remaining); + } + } + } + for (ItemStack stack : stacks) { + if (stack.isEmpty()) continue; + drop(world, pos, facing, stack, speed_percent, xdeviation, ydeviation, noise_percent); + } + return new Tuple<>(true, Collections.emptyList()); + } + + private static int next_slot(int i) { + return (i < INPUT_SLOTS_SIZE - 1) ? (i + 1) : INPUT_SLOTS_FIRST; + } + + public CompoundTag clear_getnbt() { + CompoundTag nbt = new CompoundTag(); + writenbt(nbt, false); + main_inventory_.clearContent(); + reset_rtstate(); + triggered_ = false; + block_power_updated_ = false; + return nbt; + } + + // BlockEntity ------------------------------------------------------------------------------ + + public void reset_rtstate() { + block_power_signal_ = false; + block_power_updated_ = false; + Arrays.fill(filter_matches_, 0); + } + + public void readnbt(CompoundTag nbt, boolean update_packet) { + main_inventory_.load(nbt); + block_power_signal_ = nbt.getBoolean("powered"); + open_timer_ = nbt.getInt("open_timer"); + drop_speed_ = nbt.getInt("drop_speed"); + drop_noise_ = nbt.getInt("drop_noise"); + drop_xdev_ = nbt.getInt("drop_xdev"); + drop_ydev_ = nbt.getInt("drop_ydev"); + drop_slot_index_ = nbt.getInt("drop_slot_index"); + drop_count_ = Mth.clamp(nbt.getInt("drop_count"), 1, MAX_DROP_COUNT); + drop_logic_ = nbt.getInt("drop_logic"); + drop_period_ = nbt.getInt("drop_period"); + } + + protected void writenbt(CompoundTag nbt, boolean update_packet) { + main_inventory_.save(nbt); + nbt.putBoolean("powered", block_power_signal_); + nbt.putInt("open_timer", open_timer_); + nbt.putInt("drop_speed", drop_speed_); + nbt.putInt("drop_noise", drop_noise_); + nbt.putInt("drop_xdev", drop_xdev_); + nbt.putInt("drop_ydev", drop_ydev_); + nbt.putInt("drop_slot_index", drop_slot_index_); + nbt.putInt("drop_count", drop_count_); + nbt.putInt("drop_logic", drop_logic_); + nbt.putInt("drop_period", drop_period_); + } + + // Namable ----------------------------------------------------------------------------------------------- + + public void block_updated() { + // RS power check, both edges + boolean powered = level.hasNeighborSignal(worldPosition); + if (block_power_signal_ != powered) block_power_updated_ = true; + block_power_signal_ = powered; + tick_timer_ = 1; + } + + @Override + public void load(CompoundTag nbt) { + super.load(nbt); + readnbt(nbt, false); + } + + @Override + protected void saveAdditional(CompoundTag nbt) { + super.saveAdditional(nbt); + writenbt(nbt, false); + } + + // INamedContainerProvider ------------------------------------------------------------------------------ + + @Override + public void setRemoved() { + super.setRemoved(); + item_handler_.invalidate(); + } + + @Override + public Component getName() { + return Auxiliaries.localizable(getBlockState().getBlock().getDescriptionId()); + } + + // Fields ----------------------------------------------------------------------------------------------- + + @Override + public boolean hasCustomName() { + return false; + } + + // Capability export ------------------------------------------------------------------------------------ + + @Override + public Component getCustomName() { + return getName(); + } + + // ITickable and aux methods ---------------------------------------------------------------------------- + + @Override + public Component getDisplayName() { + return Nameable.super.getDisplayName(); + } + + @Override + public AbstractContainerMenu createMenu(int id, Inventory inventory, Player player) { + return new DropperUiContainer(id, inventory, main_inventory_, ContainerLevelAccess.create(level, worldPosition), fields); + } + + @Override + public LazyOptional getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable Direction facing) { + if (capability == ForgeCapabilities.ITEM_HANDLER) return item_handler_.cast(); + return super.getCapability(capability, facing); + } + + @Nullable + BlockState update_blockstate() { + BlockState state = level.getBlockState(worldPosition); + if (!(state.getBlock() instanceof DropperBlock)) return null; + boolean open = (open_timer_ > 0); + if (state.getValue(DropperBlock.OPEN) != open) { + state = state.setValue(DropperBlock.OPEN, open); + level.setBlock(worldPosition, state, 2 | 16); + if ((drop_logic_ & DROPLOGIC_SILENT_OPEN) == 0) { + if (open) { + level.playSound(null, worldPosition, SoundEvents.WOODEN_TRAPDOOR_OPEN, SoundSource.BLOCKS, 0.08f, 3f); + } else { + level.playSound(null, worldPosition, SoundEvents.WOODEN_TRAPDOOR_CLOSE, SoundSource.BLOCKS, 0.08f, 3f); + } + } + } + return state; + } + + @Override + public void tick() { + if (level.isClientSide) return; + if (--open_timer_ < 0) open_timer_ = 0; + if ((drop_timer_ > 0) && ((--drop_timer_) == 0)) setChanged(); + if (--tick_timer_ > 0) return; + tick_timer_ = TICK_INTERVAL; + if (!(level.getBlockState(worldPosition).getBlock() instanceof DropperBlock)) return; + if (storage_slot_range_.isEmpty()) return; + final boolean continuous_mode = (drop_logic_ & DROPLOGIC_CONTINUOUS) != 0; + boolean dirty = block_power_updated_; + boolean redstone_trigger = (block_power_signal_ && ((block_power_updated_) || (continuous_mode))) || ((drop_logic_ & DROPLOGIC_IGNORE_EXT) != 0); + boolean filter_trigger; + boolean filter_defined; + boolean trigger; + // Trigger logic + { + // From filters / inventory checks + { + int filter_nset = 0; + int[] last_filter_matches_ = filter_matches_.clone(); + for (int ci = 0; ci < CTRL_SLOTS_SIZE; ++ci) { + filter_matches_[ci] = 0; + final ItemStack cmp_stack = main_inventory_.getItem(CTRL_SLOTS_FIRST + ci); + if (cmp_stack.isEmpty()) continue; + filter_matches_[ci] = 1; + final int cmp_stack_count = cmp_stack.getCount(); + int inventory_item_count = 0; + int slot = drop_slot_index_; + for (final ItemStack inp_stack : storage_slot_range_) { + if (Inventories.areItemStacksDifferent(inp_stack, cmp_stack)) { + slot = next_slot(slot); + continue; + } + inventory_item_count += inp_stack.getCount(); + if (inventory_item_count < cmp_stack_count) { + slot = next_slot(slot); + continue; + } + filter_matches_[ci] = 2; + break; + } + } + int nmatched = 0; + for (int i = 0; i < filter_matches_.length; ++i) { + if (filter_matches_[i] > 0) ++filter_nset; + if (filter_matches_[i] > 1) ++nmatched; + if (filter_matches_[i] != last_filter_matches_[i]) dirty = true; + } + filter_defined = (filter_nset > 0); + filter_trigger = ((filter_nset > 0) && (nmatched > 0)); + if (((drop_logic_ & DROPLOGIC_FILTER_ANDGATE) != 0) && (nmatched != filter_nset)) + filter_trigger = false; + } + // gates + { + if (filter_defined) { + trigger = ((drop_logic_ & DROPLOGIC_EXTERN_ANDGATE) != 0) ? (filter_trigger && redstone_trigger) : (filter_trigger || redstone_trigger); + } else { + trigger = redstone_trigger; + } + if (triggered_) { + triggered_ = false; + trigger = true; + } + if (storage_slot_range_.stream().noneMatch(is -> is.getCount() >= drop_count_)) { + if (open_timer_ > 10) open_timer_ = 10; // override if dropping is not possible at all. + } else if (trigger || filter_trigger || redstone_trigger) { + open_timer_ = SHUTTER_CLOSE_DELAY; + } + } + // edge detection for next cycle + { + boolean tr = level.hasNeighborSignal(worldPosition); + block_power_updated_ = (block_power_signal_ != tr); + block_power_signal_ = tr; + if (block_power_updated_) dirty = true; + } + } + // block state update + final BlockState state = update_blockstate(); + if (state == null) { + block_power_signal_ = false; + return; + } + // dispense action + if (trigger && (drop_timer_ <= 0)) { + // drop stack for non-filter triggers + ItemStack[] drop_stacks = {ItemStack.EMPTY, ItemStack.EMPTY, ItemStack.EMPTY}; + if (!filter_trigger) { + for (int i = 0; i < INPUT_SLOTS_SIZE; ++i) { + if (drop_slot_index_ >= INPUT_SLOTS_SIZE) drop_slot_index_ = 0; + final int ic = drop_slot_index_; + drop_slot_index_ = next_slot(drop_slot_index_); + ItemStack ds = main_inventory_.getItem(ic); + if ((!ds.isEmpty()) && ((ds.getCount() >= drop_count_) || (!ds.isStackable()))) { + boolean skip_stack = false; + for (int ci = 0; (ci < CTRL_SLOTS_SIZE) && (!skip_stack); ++ci) { + final ItemStack cmp_stack = main_inventory_.getItem(CTRL_SLOTS_FIRST + ci); + if (Inventories.areItemStacksIdentical(ds, cmp_stack)) skip_stack = true; + } + if (skip_stack) continue; + drop_stacks[0] = ds.split(drop_count_); + main_inventory_.setItem(ic, ds); + break; + } + } + } else { + for (int fi = 0; fi < filter_matches_.length; ++fi) { + if (filter_matches_[fi] > 1) { + drop_stacks[fi] = main_inventory_.getItem(CTRL_SLOTS_FIRST + fi).copy(); + int ntoremove = drop_stacks[fi].getCount(); + for (int i = INPUT_SLOTS_SIZE - 1; (i >= 0) && (ntoremove > 0); --i) { + ItemStack stack = main_inventory_.getItem(i); + if (Inventories.areItemStacksDifferent(stack, drop_stacks[fi])) continue; + if (stack.getCount() <= ntoremove) { + ntoremove -= stack.getCount(); + main_inventory_.setItem(i, ItemStack.EMPTY); + } else { + stack.shrink(ntoremove); + ntoremove = 0; + main_inventory_.setItem(i, stack); + } + } + if (ntoremove > 0) drop_stacks[fi].shrink(ntoremove); + } + } + } + // drop action + if (Arrays.stream(drop_stacks).allMatch(ItemStack::isEmpty)) { + // @todo: check if a re-stacking action is appropriate, or if players intentionally use the stack-in-place feature. + } else { + Tuple> res = try_eject(level, worldPosition, state.getValue(DropperBlock.FACING), drop_stacks, drop_speed_, drop_xdev_, drop_ydev_, drop_noise_); + final boolean dropped = res.getA(); + final List remaining = res.getB(); + for (ItemStack st : remaining) { + if (!storage_slot_range_.insert(st).isEmpty()) + Auxiliaries.logger().debug("NOT ALL NON-DROPPED ITEMS PUT BACK:" + st); + } + if (dropped || (!remaining.isEmpty())) dirty = true; + // cooldown + if (dropped) drop_timer_ = DROP_PERIOD_OFFSET + drop_period_ * 2; // 0.1s time base -> 100%===10s + // drop sound + if (dropped && ((drop_logic_ & DROPLOGIC_SILENT_DROP) == 0)) { + level.playSound(null, worldPosition, SoundEvents.WOOD_HIT, SoundSource.BLOCKS, 0.1f, 4f); + } + } + // advance to next nonempty slot. + { + boolean found = false; + for (int i = 0; i < storage_slot_range_.size(); ++i) { + if (!main_inventory_.getItem(drop_slot_index_).isEmpty()) { + found = true; + break; + } + drop_slot_index_ = next_slot(drop_slot_index_); + } + if (!found) drop_slot_index_ = 0; + } + } + if (dirty) setChanged(); + if (trigger && (tick_timer_ > 10)) tick_timer_ = 10; + } } - @Override - protected void slotClicked(Slot slot, int slotId, int button, ClickType type) - { - tooltip_.resetTimer(); - if((type == ClickType.QUICK_MOVE) && (slot!=null) && slot.hasItem() && Auxiliaries.isShiftDown() && Auxiliaries.isCtrlDown()) { - CompoundTag nbt = new CompoundTag(); - nbt.putInt("slot", slotId); - menu.onGuiAction(DropperUiContainer.QUICK_MOVE_ALL, nbt); - } else { - super.slotClicked(slot, slotId, button, type); - } + //-------------------------------------------------------------------------------------------------------------------- + // Container + //-------------------------------------------------------------------------------------------------------------------- + + public static class DropperUiContainer extends AbstractContainerMenu implements Networking.INetworkSynchronisableContainer { + protected static final String QUICK_MOVE_ALL = "quick-move-all"; + private static final int PLAYER_INV_START_SLOTNO = DropperTileEntity.NUM_OF_SLOTS; + private final Player player_; + private final Container inventory_; + private final ContainerLevelAccess wpc_; + private final ContainerData fields_; + private final Inventories.InventoryRange player_inventory_range_; + private final Inventories.InventoryRange block_storage_range_; + + public DropperUiContainer(int cid, Inventory player_inventory) { + this(cid, player_inventory, new SimpleContainer(DropperTileEntity.NUM_OF_SLOTS), ContainerLevelAccess.NULL, new SimpleContainerData(DropperTileEntity.NUM_OF_FIELDS)); + } + + private DropperUiContainer(int cid, Inventory player_inventory, Container block_inventory, ContainerLevelAccess wpc, ContainerData fields) { + super(ModContent.getMenuType("factory_dropper"), cid); // @todo: class mapping + fields_ = fields; + wpc_ = wpc; + player_ = player_inventory.player; + inventory_ = block_inventory; + block_storage_range_ = new Inventories.InventoryRange(inventory_, 0, DropperTileEntity.NUM_OF_SLOTS); + player_inventory_range_ = Inventories.InventoryRange.fromPlayerInventory(player_); + int i = -1; + // input slots (stacks 0 to 11) + for (int y = 0; y < 2; ++y) { + for (int x = 0; x < 6; ++x) { + int xpos = 10 + x * 18, ypos = 6 + y * 17; + addSlot(new Slot(inventory_, ++i, xpos, ypos)); + } + } + // filter slots (stacks 12 to 14) + addSlot(new Slot(inventory_, ++i, 19, 48)); + addSlot(new Slot(inventory_, ++i, 55, 48)); + addSlot(new Slot(inventory_, ++i, 91, 48)); + // player slots + for (int x = 0; x < 9; ++x) { + addSlot(new Slot(player_inventory, x, 8 + x * 18, 144)); // player slots: 0..8 + } + for (int y = 0; y < 3; ++y) { + for (int x = 0; x < 9; ++x) { + addSlot(new Slot(player_inventory, x + y * 9 + 9, 8 + x * 18, 86 + y * 18)); // player slots: 9..35 + } + } + this.addDataSlots(fields_); // === Add reference holders + } + + public final int field(int index) { + return fields_.get(index); + } + + @Override + public boolean stillValid(Player player) { + return inventory_.stillValid(player); + } + + @Override + public ItemStack quickMoveStack(Player player, int index) { + Slot slot = getSlot(index); + if ((slot == null) || (!slot.hasItem())) return ItemStack.EMPTY; + ItemStack slot_stack = slot.getItem(); + ItemStack transferred = slot_stack.copy(); + if ((index >= 0) && (index < PLAYER_INV_START_SLOTNO)) { + // Device slots + if (!moveItemStackTo(slot_stack, PLAYER_INV_START_SLOTNO, PLAYER_INV_START_SLOTNO + 36, false)) + return ItemStack.EMPTY; + } else if ((index >= PLAYER_INV_START_SLOTNO) && (index <= PLAYER_INV_START_SLOTNO + 36)) { + // Player slot + if (!moveItemStackTo(slot_stack, 0, DropperTileEntity.INPUT_SLOTS_SIZE, false)) return ItemStack.EMPTY; + } else { + // invalid slot + return ItemStack.EMPTY; + } + if (slot_stack.isEmpty()) { + slot.set(ItemStack.EMPTY); + } else { + slot.setChanged(); + } + if (slot_stack.getCount() == transferred.getCount()) return ItemStack.EMPTY; + slot.onTake(player, slot_stack); + return transferred; + } + + // INetworkSynchronisableContainer --------------------------------------------------------- + + @OnlyIn(Dist.CLIENT) + public void onGuiAction(CompoundTag nbt) { + Networking.PacketContainerSyncClientToServer.sendToServer(containerId, nbt); + } + + @OnlyIn(Dist.CLIENT) + public void onGuiAction(String key, int value) { + CompoundTag nbt = new CompoundTag(); + nbt.putInt(key, value); + Networking.PacketContainerSyncClientToServer.sendToServer(containerId, nbt); + } + + @OnlyIn(Dist.CLIENT) + public void onGuiAction(String message, CompoundTag nbt) { + nbt.putString("action", message); + Networking.PacketContainerSyncClientToServer.sendToServer(containerId, nbt); + } + + @Override + public void onServerPacketReceived(int windowId, CompoundTag nbt) { + } + + @Override + public void onClientPacketReceived(int windowId, Player player, CompoundTag nbt) { + if (!(inventory_ instanceof Inventories.StorageInventory)) return; + if (!(((Inventories.StorageInventory) inventory_).getBlockEntity() instanceof final DropperTileEntity te)) + return; + if (nbt.contains("action")) { + boolean changed = false; + final int slotId = nbt.contains("slot") ? nbt.getInt("slot") : -1; + switch (nbt.getString("action")) { + case QUICK_MOVE_ALL -> { + if ((slotId >= 0) && (slotId < PLAYER_INV_START_SLOTNO) && (getSlot(slotId).hasItem())) { + changed = block_storage_range_.move(getSlot(slotId).getSlotIndex(), player_inventory_range_, true, false, true, true); + } else if ((slotId >= PLAYER_INV_START_SLOTNO) && (slotId < PLAYER_INV_START_SLOTNO + 36) && (getSlot(slotId).hasItem())) { + changed = player_inventory_range_.move(getSlot(slotId).getSlotIndex(), block_storage_range_, true, false, false, true); + } + } + } + if (changed) { + inventory_.setChanged(); + player.getInventory().setChanged(); + broadcastChanges(); + } + } else { + if (nbt.contains("drop_speed")) te.drop_speed_ = Mth.clamp(nbt.getInt("drop_speed"), 0, 100); + if (nbt.contains("drop_xdev")) te.drop_xdev_ = Mth.clamp(nbt.getInt("drop_xdev"), -100, 100); + if (nbt.contains("drop_ydev")) te.drop_ydev_ = Mth.clamp(nbt.getInt("drop_ydev"), -100, 100); + if (nbt.contains("drop_count")) + te.drop_count_ = Mth.clamp(nbt.getInt("drop_count"), 1, DropperTileEntity.MAX_DROP_COUNT); + if (nbt.contains("drop_period")) te.drop_period_ = Mth.clamp(nbt.getInt("drop_period"), 0, 100); + if (nbt.contains("drop_logic")) te.drop_logic_ = nbt.getInt("drop_logic"); + if (nbt.contains("manual_rstrigger") && (nbt.getInt("manual_rstrigger") != 0)) { + te.block_power_signal_ = true; + te.block_power_updated_ = true; + te.tick_timer_ = 1; + } + if (nbt.contains("manual_trigger") && (nbt.getInt("manual_trigger") != 0)) { + te.tick_timer_ = 1; + te.triggered_ = true; + } + te.setChanged(); + } + } + } - } + //-------------------------------------------------------------------------------------------------------------------- + // GUI + //-------------------------------------------------------------------------------------------------------------------- + + @OnlyIn(Dist.CLIENT) + public static class DropperGui extends Guis.ContainerGui { + public DropperGui(DropperUiContainer container, Inventory player_inventory, Component title) { + super(container, player_inventory, title, "textures/gui/factory_dropper_gui.png"); + } + + @Override + public void init() { + super.init(); + { + final Block block = ModContent.getBlock(Auxiliaries.getResourceLocation(getMenu().getType()).getPath().replaceAll("^ct_", "")); + final String prefix = block.getDescriptionId() + ".tooltips."; + final int x0 = getGuiLeft(), y0 = getGuiTop(); + tooltip_.init( + new TooltipDisplay.TipRange(x0 + 130, y0 + 10, 12, 25, Component.translatable(prefix + "velocity")), + new TooltipDisplay.TipRange(x0 + 145, y0 + 10, 25, 25, Component.translatable(prefix + "direction")), + new TooltipDisplay.TipRange(x0 + 129, y0 + 40, 44, 10, Component.translatable(prefix + "dropcount")), + new TooltipDisplay.TipRange(x0 + 129, y0 + 50, 44, 10, Component.translatable(prefix + "period")), + new TooltipDisplay.TipRange(x0 + 114, y0 + 51, 9, 9, Component.translatable(prefix + "rssignal")), + new TooltipDisplay.TipRange(x0 + 162, y0 + 66, 7, 9, Component.translatable(prefix + "triggermode")), + new TooltipDisplay.TipRange(x0 + 132, y0 + 66, 9, 9, Component.translatable(prefix + "filtergate")), + new TooltipDisplay.TipRange(x0 + 148, y0 + 66, 9, 9, Component.translatable(prefix + "externgate")) + ); + } + } + + @Override + protected void renderBgWidgets(GuiGraphics mx, float partialTicks, int mouseX, int mouseY) { + final int x0 = getGuiLeft(), y0 = getGuiTop(), w = getXSize(), h = getYSize(); + DropperUiContainer container = getMenu(); + // active drop slot + { + int drop_slot_index = container.field(15); + if ((drop_slot_index < 0) || (drop_slot_index >= 16)) drop_slot_index = 0; + int x = (x0 + 9 + ((drop_slot_index % 6) * 18)); + int y = (y0 + 5 + ((drop_slot_index / 6) * 17)); + mx.blit(getBackgroundImage(), x, y, 180, 45, 18, 18); + } + // filter LEDs + { + for (int i = 0; i < 3; ++i) { + int xt = 180 + (6 * container.field(12 + i)), yt = 38; + int x = x0 + 31 + (i * 36), y = y0 + 65; + mx.blit(getBackgroundImage(), x, y, xt, yt, 6, 6); + } + } + // force adjustment + { + int hy = 2 + (((100 - container.field(0)) * 21) / 100); + int x = x0 + 135, y = y0 + 12, xt = 181; + int yt = 4 + (23 - hy); + mx.blit(getBackgroundImage(), x, y, xt, yt, 3, hy); + } + // angle adjustment + { + int x = x0 + 157 - 3 + ((container.field(1) * 12) / 100); + int y = y0 + 22 - 3 - ((container.field(2) * 12) / 100); + mx.blit(getBackgroundImage(), x, y, 180, 30, 7, 7); + } + // drop count + { + int x = x0 + 134 - 2 + (container.field(4)); + int y = y0 + 45; + mx.blit(getBackgroundImage(), x, y, 190, 31, 5, 5); + } + // drop period + { + int px = (int) Math.round(((33.0 * container.field(6)) / 100) + 1); + int x = x0 + 134 - 2 + Mth.clamp(px, 0, 33); + int y = y0 + 56; + mx.blit(getBackgroundImage(), x, y, 190, 31, 5, 5); + } + // redstone input + { + if (container.field(11) != 0) { + mx.blit(getBackgroundImage(), x0 + 114, y0 + 51, 189, 18, 9, 9); + } + } + // trigger logic + { + final int logic = container.field(5); + int filter_gate_offset = ((logic & DropperTileEntity.DROPLOGIC_FILTER_ANDGATE) != 0) ? 11 : 0; + int pulse_mode_offset = ((logic & DropperTileEntity.DROPLOGIC_CONTINUOUS) != 0) ? 10 : 0; + int extern_gate_offset_x = ((logic & DropperTileEntity.DROPLOGIC_EXTERN_ANDGATE) != 0) ? 11 : 0; + int extern_gate_offset_y = ((logic & DropperTileEntity.DROPLOGIC_IGNORE_EXT) != 0) ? 10 : 0; + mx.blit(getBackgroundImage(), x0 + 132, y0 + 66, 179 + filter_gate_offset, 66, 9, 9); + mx.blit(getBackgroundImage(), x0 + 148, y0 + 66, 179 + extern_gate_offset_x, 66 + extern_gate_offset_y, 9, 9); + mx.blit(getBackgroundImage(), x0 + 162, y0 + 66, 200 + pulse_mode_offset, 66, 9, 9); + } + // drop timer running indicator + { + if ((container.field(9) > DropperTileEntity.DROP_PERIOD_OFFSET) && ((System.currentTimeMillis() % 1000) < 500)) { + mx.blit(getBackgroundImage(), x0 + 149, y0 + 51, 201, 39, 3, 3); + } + } + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int mouseButton) { + tooltip_.resetTimer(); + DropperUiContainer container = getMenu(); + int mx = (int) (mouseX - getGuiLeft() + .5), my = (int) (mouseY - getGuiTop() + .5); + if ((!isHovering(114, 1, 61, 79, mouseX, mouseY))) { + return super.mouseClicked(mouseX, mouseY, mouseButton); + } else if (isHovering(130, 10, 12, 25, mouseX, mouseY)) { + int force_percent = 100 - Mth.clamp(((my - 10) * 100) / 25, 0, 100); + container.onGuiAction("drop_speed", force_percent); + } else if (isHovering(145, 10, 25, 25, mouseX, mouseY)) { + int xdev = Mth.clamp((int) Math.round(((double) ((mx - 157) * 100)) / 12), -100, 100); + int ydev = Mth.clamp(-(int) Math.round(((double) ((my - 22) * 100)) / 12), -100, 100); + if (Math.abs(xdev) < 9) xdev = 0; + if (Math.abs(ydev) < 9) ydev = 0; + CompoundTag nbt = new CompoundTag(); + nbt.putInt("drop_xdev", xdev); + nbt.putInt("drop_ydev", ydev); + container.onGuiAction(nbt); + } else if (isHovering(129, 40, 44, 10, mouseX, mouseY)) { + int ndrop = (mx - 135); + if (ndrop < -1) { + ndrop = container.field(4) - 1; // - + } else if (ndrop >= 34) { + ndrop = container.field(4) + 1; // + + } else { + ndrop = Mth.clamp(1 + ndrop, 1, DropperTileEntity.MAX_DROP_COUNT); // slider + } + container.onGuiAction("drop_count", ndrop); + } else if (isHovering(129, 50, 44, 10, mouseX, mouseY)) { + int period = (mx - 135); + if (period < -1) { + period = container.field(6) - 3; // - + } else if (period >= 34) { + period = container.field(6) + 3; // + + } else { + period = (int) (0.5 + ((100.0 * period) / 34)); + } + period = Mth.clamp(period, 0, 100); + container.onGuiAction("drop_period", period); + } else if (isHovering(114, 51, 9, 9, mouseX, mouseY)) { + container.onGuiAction("manual_rstrigger", 1); + } else if (isHovering(162, 66, 7, 9, mouseX, mouseY)) { + container.onGuiAction("drop_logic", container.field(5) ^ DropperTileEntity.DROPLOGIC_CONTINUOUS); + } else if (isHovering(132, 66, 9, 9, mouseX, mouseY)) { + container.onGuiAction("drop_logic", container.field(5) ^ DropperTileEntity.DROPLOGIC_FILTER_ANDGATE); + } else if (isHovering(148, 66, 9, 9, mouseX, mouseY)) { + final int mask = (DropperTileEntity.DROPLOGIC_EXTERN_ANDGATE | DropperTileEntity.DROPLOGIC_IGNORE_EXT); + final int logic = switch ((container.field(5) & mask)) { + case DropperTileEntity.DROPLOGIC_EXTERN_ANDGATE -> 0; + case 0 -> DropperTileEntity.DROPLOGIC_IGNORE_EXT; + case DropperTileEntity.DROPLOGIC_IGNORE_EXT -> DropperTileEntity.DROPLOGIC_EXTERN_ANDGATE; + default -> DropperTileEntity.DROPLOGIC_EXTERN_ANDGATE; + }; + container.onGuiAction("drop_logic", (container.field(5) & (~mask)) | logic); + } + return true; + } + + @Override + protected void slotClicked(Slot slot, int slotId, int button, ClickType type) { + tooltip_.resetTimer(); + if ((type == ClickType.QUICK_MOVE) && (slot != null) && slot.hasItem() && Auxiliaries.isShiftDown() && Auxiliaries.isCtrlDown()) { + CompoundTag nbt = new CompoundTag(); + nbt.putInt("slot", slotId); + menu.onGuiAction(DropperUiContainer.QUICK_MOVE_ALL, nbt); + } else { + super.slotClicked(slot, slotId, button, type); + } + } + + } } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdElectricalFurnace.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdElectricalFurnace.java index e06a6b3..2d5d215 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdElectricalFurnace.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdElectricalFurnace.java @@ -8,8 +8,10 @@ */ package dev.zontreck.engineerdecor.blocks; -import com.mojang.blaze3d.vertex.PoseStack; -import dev.zontreck.engineerdecor.libmc.*; +import dev.zontreck.engineerdecor.ModConfig; +import dev.zontreck.engineerdecor.ModContent; +import dev.zontreck.libzontreck.edlibmc.*; +import net.minecraft.client.gui.GuiGraphics; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; @@ -30,7 +32,7 @@ import net.minecraft.world.item.crafting.RecipeType; import net.minecraft.world.item.crafting.SmeltingRecipe; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.SignalGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; @@ -53,11 +55,6 @@ import net.minecraftforge.energy.IEnergyStorage; import net.minecraftforge.event.ForgeEventFactory; import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.ItemHandlerHelper; -import dev.zontreck.engineerdecor.ModConfig; -import dev.zontreck.engineerdecor.ModContent; -import wile.engineersdecor.libmc.*; -import dev.zontreck.engineerdecor.libmc.Inventories.MappedItemHandler; -import dev.zontreck.engineerdecor.libmc.Inventories.StorageInventory; import javax.annotation.Nullable; import java.util.ArrayList; @@ -65,868 +62,903 @@ import java.util.Arrays; import java.util.List; -public class EdElectricalFurnace -{ - public static void on_config(int speed_percent, int standard_energy_per_tick, boolean with_automatic_inventory_pulling) - { ElectricalFurnaceTileEntity.on_config(speed_percent, standard_energy_per_tick, with_automatic_inventory_pulling); } - - //-------------------------------------------------------------------------------------------------------------------- - // Block - //-------------------------------------------------------------------------------------------------------------------- - - public static class ElectricalFurnaceBlock extends StandardBlocks.Horizontal implements StandardEntityBlocks.IStandardEntityBlock - { - public static final BooleanProperty LIT = BlockStateProperties.LIT; - - public ElectricalFurnaceBlock(long config, BlockBehaviour.Properties builder, final AABB[] unrotatedAABBs) - { super(config, builder, unrotatedAABBs); } - - @Override - public boolean isBlockEntityTicking(Level world, BlockState state) - { return true; } - - @Override - public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) - { return Shapes.block(); } - - @Override - protected void createBlockStateDefinition(StateDefinition.Builder builder) - { super.createBlockStateDefinition(builder); builder.add(LIT); } - - @Override - @SuppressWarnings("deprecation") - public int getLightEmission(BlockState state, BlockGetter world, BlockPos pos) - { return state.getValue(LIT) ? super.getLightEmission(state, world, pos) : 0; } - - @Override - @Nullable - public BlockState getStateForPlacement(BlockPlaceContext context) - { return super.getStateForPlacement(context).setValue(LIT, false); } - - @Override - @SuppressWarnings("deprecation") - public boolean hasAnalogOutputSignal(BlockState state) - { return true; } - - @Override - @SuppressWarnings("deprecation") - public int getAnalogOutputSignal(BlockState blockState, Level world, BlockPos pos) - { - BlockEntity te = world.getBlockEntity(pos); - return (te instanceof EdElectricalFurnace.ElectricalFurnaceTileEntity) ? ((EdElectricalFurnace.ElectricalFurnaceTileEntity)te).getComparatorOutput() : 0; +public class EdElectricalFurnace { + public static void on_config(int speed_percent, int standard_energy_per_tick, boolean with_automatic_inventory_pulling) { + ElectricalFurnaceTileEntity.on_config(speed_percent, standard_energy_per_tick, with_automatic_inventory_pulling); } - @Override - public boolean shouldCheckWeakPower(BlockState state, LevelReader world, BlockPos pos, Direction side) - { return false; } + //-------------------------------------------------------------------------------------------------------------------- + // Block + //-------------------------------------------------------------------------------------------------------------------- - @Override - @SuppressWarnings("deprecation") - public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult) - { return useOpenGui(state, world, pos, player); } + public static class ElectricalFurnaceBlock extends StandardBlocks.Horizontal implements StandardEntityBlocks.IStandardEntityBlock { + public static final BooleanProperty LIT = BlockStateProperties.LIT; - @Override - public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) - { - world.setBlockAndUpdate(pos, state.setValue(LIT, false)); - if(world.isClientSide) return; - if((!stack.hasTag()) || (!stack.getTag().contains("inventory"))) return; - CompoundTag inventory_nbt = stack.getTag().getCompound("inventory"); - if(inventory_nbt.isEmpty()) return; - final BlockEntity te = world.getBlockEntity(pos); - if(!(te instanceof ElectricalFurnaceTileEntity bte)) return; - bte.readnbt(inventory_nbt); - bte.setChanged(); - world.setBlockAndUpdate(pos, state.setValue(LIT, bte.burning())); - } - - @Override - public boolean hasDynamicDropList() - { return true; } - - @Override - public List dropList(BlockState state, Level world, final BlockEntity te, boolean explosion) { - final List stacks = new ArrayList<>(); - if(world.isClientSide) return stacks; - if(!(te instanceof EdElectricalFurnace.ElectricalFurnaceTileEntity)) return stacks; - if(!explosion) { - ItemStack stack = new ItemStack(this, 1); - CompoundTag inventory_nbt = ((EdElectricalFurnace.ElectricalFurnaceTileEntity)te).reset_getnbt(); - if(!inventory_nbt.isEmpty()) { - CompoundTag nbt = new CompoundTag(); - nbt.put("inventory", inventory_nbt); - stack.setTag(nbt); + public ElectricalFurnaceBlock(long config, BlockBehaviour.Properties builder, final AABB[] unrotatedAABBs) { + super(config, builder, unrotatedAABBs); } - stacks.add(stack); - } else { - for(ItemStack stack: ((EdElectricalFurnace.ElectricalFurnaceTileEntity)te).inventory_) stacks.add(stack); - ((EdElectricalFurnace.ElectricalFurnaceTileEntity)te).reset(); - } - return stacks; - } - } - //-------------------------------------------------------------------------------------------------------------------- - // Tile entity - //-------------------------------------------------------------------------------------------------------------------- - - public static class ElectricalFurnaceTileEntity extends StandardEntityBlocks.StandardBlockEntity implements MenuProvider, Nameable - { - private static final RecipeType RECIPE_TYPE = RecipeType.SMELTING; - private static final int DEFAULT_SMELTING_TIME = 200; - private static final int NUM_OF_FIELDS = 8; - private static final int TICK_INTERVAL = 4; - private static final int FIFO_INTERVAL = 20; - private static final int HEAT_CAPACITY = 200; - private static final int HEAT_INCREMENT = 20; - private static final int MAX_BURNTIME = 0x7fff; - private static final int MAX_XP_STORED = 65535; - private static final int MAX_ENERGY_TRANSFER = 1024; - private static final int MAX_ENERGY_BUFFER = 32000; - private static final int MAX_SPEED_SETTING = 3; - private static final int NUM_OF_SLOTS = 7; - private static final int SMELTING_INPUT_SLOT_NO = 0; - private static final int SMELTING_AUX_SLOT_NO = 1; - private static final int SMELTING_OUTPUT_SLOT_NO = 2; - private static final int FIFO_INPUT_0_SLOT_NO = 3; - private static final int FIFO_INPUT_1_SLOT_NO = 4; - private static final int FIFO_OUTPUT_0_SLOT_NO = 5; - private static final int FIFO_OUTPUT_1_SLOT_NO = 6; - - public static final int DEFAULT_SPEED_PERCENT = 290; - public static final int DEFAULT_ENERGY_CONSUMPTION = 16; - public static final int DEFAULT_SCALED_ENERGY_CONSUMPTION = DEFAULT_ENERGY_CONSUMPTION * TICK_INTERVAL; - - // Config ---------------------------------------------------------------------------------- - - private static boolean with_automatic_inventory_pulling_ = false; - private static int energy_consumption_ = DEFAULT_SCALED_ENERGY_CONSUMPTION; - private static int transfer_energy_consumption_ = DEFAULT_SCALED_ENERGY_CONSUMPTION / 8; - private static int proc_speed_percent_ = DEFAULT_SPEED_PERCENT; - private static final double[] speed_setting_factor_ = {0.0, 1.0, 1.5, 2.0}; - - public static void on_config(int speed_percent, int standard_energy_per_tick, boolean with_automatic_inventory_pulling) - { - proc_speed_percent_ = Mth.clamp(speed_percent, 10, 800); - energy_consumption_ = Mth.clamp(standard_energy_per_tick, 4, 4096) * TICK_INTERVAL; - transfer_energy_consumption_ = Mth.clamp(energy_consumption_ / 8, 8, HEAT_INCREMENT); - with_automatic_inventory_pulling_ = with_automatic_inventory_pulling; - ModConfig.log("Config electrical furnace speed:" + proc_speed_percent_ + "%, heat-loss: 1K/t, heating consumption:" + (energy_consumption_/TICK_INTERVAL)+"rf/t."); - } - - // ElectricalFurnaceTileEntity ----------------------------------------------------------------------------- - private int tick_timer_; - private int fifo_timer_; - private double proc_time_elapsed_; - private int proc_time_needed_; - private int burntime_left_; - private int field_is_burning_; - private float xp_stored_; - private @Nullable Recipe current_recipe_ = null; - private int fuel_burntime_; - private int field_proc_time_elapsed_; - private final StorageInventory inventory_; - private final LazyOptional item_handler_; - private final RfEnergy.Battery battery_; - private final LazyOptional energy_handler_; - private int speed_ = 1; - private boolean enabled_ = false; - private int field_power_consumption_; - private int power_consumption_accumulator_; - private int power_consumption_timer_; - - public ElectricalFurnaceTileEntity(BlockPos pos, BlockState state) - { - super(ModContent.getBlockEntityTypeOfBlock(state.getBlock()), pos, state); - inventory_ = new StorageInventory(this, NUM_OF_SLOTS) { @Override - public void setItem(int index, ItemStack stack) - { - ItemStack slot_stack = stacks_.get(index); - boolean already_in_slot = (!stack.isEmpty()) && (Inventories.areItemStacksIdentical(stack, slot_stack)); - stacks_.set(index, stack); - if(stack.getCount() > getMaxStackSize()) stack.setCount(getMaxStackSize()); - if((index == SMELTING_INPUT_SLOT_NO) && (!already_in_slot)) { - proc_time_needed_ = Crafting.getSmeltingTimeNeeded(RECIPE_TYPE, level, stack); - proc_time_elapsed_ = 0; - setChanged(); - } + public boolean isBlockEntityTicking(Level world, BlockState state) { + return true; } - }; - inventory_.setValidator((index, stack)->switch (index) { - case SMELTING_INPUT_SLOT_NO, FIFO_INPUT_0_SLOT_NO, FIFO_INPUT_1_SLOT_NO -> true; - default -> false; - }); - item_handler_ = MappedItemHandler.createGenericHandler(inventory_, - (slot,stack)->((slot==FIFO_OUTPUT_0_SLOT_NO) || (slot==FIFO_OUTPUT_1_SLOT_NO)), - (slot,stack)->((slot==FIFO_INPUT_0_SLOT_NO) || (slot==FIFO_INPUT_1_SLOT_NO)), - Arrays.asList(FIFO_OUTPUT_0_SLOT_NO,FIFO_OUTPUT_1_SLOT_NO,FIFO_INPUT_0_SLOT_NO,FIFO_INPUT_1_SLOT_NO) - ); - battery_ = new RfEnergy.Battery(MAX_ENERGY_BUFFER, MAX_ENERGY_TRANSFER, 0); - energy_handler_ = battery_.createEnergyHandler(); - } - public void reset() - { - inventory_.clearContent(); - burntime_left_ = 0; - proc_time_elapsed_ = 0; - proc_time_needed_ = 0; - fifo_timer_ = 0; - tick_timer_ = 0; - battery_.clear(); - speed_ = 1; - field_is_burning_ = 0; - } - - public CompoundTag reset_getnbt() - { - CompoundTag nbt = new CompoundTag(); - writenbt(nbt); - reset(); - return nbt; - } - - public void readnbt(CompoundTag nbt) - { - burntime_left_ = nbt.getInt("BurnTime"); - proc_time_elapsed_ = nbt.getInt("CookTime"); - proc_time_needed_ = nbt.getInt("CookTimeTotal"); - xp_stored_ = nbt.getFloat("XpStored"); - speed_ = nbt.getInt("SpeedSetting"); - speed_ = (speed_ < 0) ? (1) : (Math.min(speed_, MAX_SPEED_SETTING)); - battery_.load(nbt, "Energy"); - inventory_.load(nbt); - } - - protected void writenbt(CompoundTag nbt) - { - nbt.putInt("BurnTime", Mth.clamp(burntime_left_, 0, HEAT_CAPACITY)); - nbt.putInt("CookTime", Mth.clamp((int)proc_time_elapsed_, 0, MAX_BURNTIME)); - nbt.putInt("CookTimeTotal", Mth.clamp(proc_time_needed_, 0, MAX_BURNTIME)); - nbt.putInt("SpeedSetting", Mth.clamp(speed_, -1, MAX_SPEED_SETTING)); - nbt.putFloat("XpStored", Mth.clamp(xp_stored_, 0, MAX_XP_STORED)); - battery_.save(nbt, "Energy"); - inventory_.save(nbt); - } - - public int getComparatorOutput() - { - return (battery_.isEmpty()) ? (0) : ( - (inventory_.getItem(FIFO_INPUT_1_SLOT_NO).isEmpty() ? 0 : 5) + - (inventory_.getItem(FIFO_INPUT_0_SLOT_NO).isEmpty() ? 0 : 5) + - (inventory_.getItem(SMELTING_INPUT_SLOT_NO).isEmpty() ? 0 : 5) - ); - } - - // BlockEntity ------------------------------------------------------------------------------ - - @Override - public void load(CompoundTag nbt) - { super.load(nbt); readnbt(nbt); } - - @Override - protected void saveAdditional(CompoundTag nbt) - { super.saveAdditional(nbt); writenbt(nbt); } - - @Override - public void setRemoved() - { - super.setRemoved(); - item_handler_.invalidate(); - energy_handler_.invalidate(); - } - - @Override - public Component getName() - { return Auxiliaries.localizable(getBlockState().getBlock().getDescriptionId()); } - - @Override - public Component getDisplayName() - { return Nameable.super.getDisplayName(); } - - @Override - public AbstractContainerMenu createMenu(int id, Inventory inventory, Player player ) - { return new EdElectricalFurnace.ElectricalFurnaceContainer(id, inventory, inventory_, ContainerLevelAccess.create(level, worldPosition), fields); } - - // Fields ----------------------------------------------------------------------------------------------- - - protected final ContainerData fields = new ContainerData() - { - @Override - public int getCount() - { return ElectricalFurnaceTileEntity.NUM_OF_FIELDS; } - - @Override - public int get(int id) - { - return switch(id) { - case 0 -> ElectricalFurnaceTileEntity.this.burntime_left_; - case 1 -> ElectricalFurnaceTileEntity.this.battery_.getEnergyStored(); - case 2 -> (int) ElectricalFurnaceTileEntity.this.proc_time_elapsed_; - case 3 -> ElectricalFurnaceTileEntity.this.proc_time_needed_; - case 4 -> ElectricalFurnaceTileEntity.this.speed_; - case 5 -> ElectricalFurnaceTileEntity.this.battery_.getMaxEnergyStored(); - case 6 -> ElectricalFurnaceTileEntity.this.field_is_burning_; - case 7 -> ElectricalFurnaceTileEntity.this.field_power_consumption_; - default -> 0; - }; - } - @Override - public void set(int id, int value) - { - switch(id) { - case 0 -> ElectricalFurnaceTileEntity.this.burntime_left_ = value; - case 1 -> ElectricalFurnaceTileEntity.this.battery_.setEnergyStored(value); - case 2 -> ElectricalFurnaceTileEntity.this.proc_time_elapsed_ = value; - case 3 -> ElectricalFurnaceTileEntity.this.proc_time_needed_ = value; - case 4 -> ElectricalFurnaceTileEntity.this.speed_ = value; - case 5 -> ElectricalFurnaceTileEntity.this.battery_.setMaxEnergyStored(value); - case 6 -> ElectricalFurnaceTileEntity.this.field_is_burning_ = value; - case 7 -> ElectricalFurnaceTileEntity.this.field_power_consumption_ = value; + @Override + public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) { + return Shapes.block(); } - } - }; - // Capability export ---------------------------------------------------------------------------- - - @Override - public LazyOptional getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable Direction facing) - { - if(capability == ForgeCapabilities.ITEM_HANDLER) return item_handler_.cast(); - if(capability == ForgeCapabilities.ENERGY) return energy_handler_.cast(); - return super.getCapability(capability, facing); - } - - // ----------------------------------------------------------------------------------------------------------------- - - @Override - public void tick() - { - if(--tick_timer_ > 0) return; - tick_timer_ = TICK_INTERVAL; - if(!(level.getBlockState(worldPosition).getBlock() instanceof ElectricalFurnaceBlock)) return; - final boolean was_burning = burning(); - if(was_burning) burntime_left_ -= TICK_INTERVAL; - if(burntime_left_ < 0) burntime_left_ = 0; - if(level.isClientSide()) return; - int battery_energy = battery_.getEnergyStored(); - boolean update_blockstate = (was_burning != burning()); - boolean dirty = update_blockstate; - boolean shift_in = false; - boolean shift_out = false; - if(--fifo_timer_ <= 0) { - fifo_timer_ = FIFO_INTERVAL/TICK_INTERVAL; - if(transferItems(FIFO_OUTPUT_0_SLOT_NO, FIFO_OUTPUT_1_SLOT_NO, 64)) { dirty = true; } else { shift_out = true; } - if(transferItems(SMELTING_OUTPUT_SLOT_NO, FIFO_OUTPUT_0_SLOT_NO, 64)) dirty = true; - if(transferItems(FIFO_INPUT_0_SLOT_NO, SMELTING_INPUT_SLOT_NO, 64)) dirty = true; - if(transferItems(FIFO_INPUT_1_SLOT_NO, FIFO_INPUT_0_SLOT_NO, 64)) { dirty = true; } else { shift_in = true; } - } - if(battery_.getEnergyStored() < energy_consumption()) { - enabled_ = false; - } else if(battery_.getEnergyStored() >= (MAX_ENERGY_BUFFER/2)) { - enabled_ = true; - } - if((!(inventory_.getItem(SMELTING_INPUT_SLOT_NO)).isEmpty()) && (enabled_) && (speed_>0)) { - Recipe last_recipe = currentRecipe(); - updateCurrentRecipe(); - if(currentRecipe() != last_recipe) { - proc_time_elapsed_ = 0; - proc_time_needed_ = getSmeltingTimeNeeded(level, inventory_.getItem(SMELTING_INPUT_SLOT_NO)); + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + super.createBlockStateDefinition(builder); + builder.add(LIT); } - final boolean can_smelt = canSmeltCurrentItem(); - if((!can_smelt) && (getSmeltingResult(inventory_.getItem(SMELTING_INPUT_SLOT_NO)).isEmpty())) { - // bypass - if(transferItems(SMELTING_INPUT_SLOT_NO, SMELTING_OUTPUT_SLOT_NO, 1)) dirty = true; - } else { - // smelt, automatically choke speed on low power storage - final int speed = Mth.clamp((battery_.getSOC() >= 25) ? (speed_) : (1), 0, MAX_SPEED_SETTING); - if(!burning() && can_smelt) { - if(heat_up(speed)) { dirty = true; update_blockstate = true; } - } - if(burning() && can_smelt) { - if(heat_up(speed)) dirty = true; - proc_time_elapsed_ += (int)(TICK_INTERVAL * proc_speed_percent_ * speed_setting_factor_[speed] / 100); - if(proc_time_elapsed_ >= proc_time_needed_) { - proc_time_elapsed_ = 0; - proc_time_needed_ = getSmeltingTimeNeeded(level, inventory_.getItem(SMELTING_INPUT_SLOT_NO)); - smeltCurrentItem(); - dirty = true; - shift_out = true; + + @Override + @SuppressWarnings("deprecation") + public int getLightEmission(BlockState state, BlockGetter world, BlockPos pos) { + return state.getValue(LIT) ? super.getLightEmission(state, world, pos) : 0; + } + + @Override + @Nullable + public BlockState getStateForPlacement(BlockPlaceContext context) { + return super.getStateForPlacement(context).setValue(LIT, false); + } + + @Override + @SuppressWarnings("deprecation") + public boolean hasAnalogOutputSignal(BlockState state) { + return true; + } + + @Override + @SuppressWarnings("deprecation") + public int getAnalogOutputSignal(BlockState blockState, Level world, BlockPos pos) { + BlockEntity te = world.getBlockEntity(pos); + return (te instanceof EdElectricalFurnace.ElectricalFurnaceTileEntity) ? ((EdElectricalFurnace.ElectricalFurnaceTileEntity) te).getComparatorOutput() : 0; + } + + @Override + public boolean shouldCheckWeakPower(BlockState state, SignalGetter level, BlockPos pos, Direction side) { + return false; + } + + @Override + @SuppressWarnings("deprecation") + public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult) { + return useOpenGui(state, world, pos, player); + } + + @Override + public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) { + world.setBlockAndUpdate(pos, state.setValue(LIT, false)); + if (world.isClientSide) return; + if ((!stack.hasTag()) || (!stack.getTag().contains("inventory"))) return; + CompoundTag inventory_nbt = stack.getTag().getCompound("inventory"); + if (inventory_nbt.isEmpty()) return; + final BlockEntity te = world.getBlockEntity(pos); + if (!(te instanceof ElectricalFurnaceTileEntity bte)) return; + bte.readnbt(inventory_nbt); + bte.setChanged(); + world.setBlockAndUpdate(pos, state.setValue(LIT, bte.burning())); + } + + @Override + public boolean hasDynamicDropList() { + return true; + } + + @Override + public List dropList(BlockState state, Level world, final BlockEntity te, boolean explosion) { + final List stacks = new ArrayList<>(); + if (world.isClientSide) return stacks; + if (!(te instanceof EdElectricalFurnace.ElectricalFurnaceTileEntity)) return stacks; + if (!explosion) { + ItemStack stack = new ItemStack(this, 1); + CompoundTag inventory_nbt = ((EdElectricalFurnace.ElectricalFurnaceTileEntity) te).reset_getnbt(); + if (!inventory_nbt.isEmpty()) { + CompoundTag nbt = new CompoundTag(); + nbt.put("inventory", inventory_nbt); + stack.setTag(nbt); + } + stacks.add(stack); + } else { + for (ItemStack stack : ((EdElectricalFurnace.ElectricalFurnaceTileEntity) te).inventory_) + stacks.add(stack); + ((EdElectricalFurnace.ElectricalFurnaceTileEntity) te).reset(); } - } else { + return stacks; + } + } + + //-------------------------------------------------------------------------------------------------------------------- + // Tile entity + //-------------------------------------------------------------------------------------------------------------------- + + public static class ElectricalFurnaceTileEntity extends StandardEntityBlocks.StandardBlockEntity implements MenuProvider, Nameable { + public static final int DEFAULT_SPEED_PERCENT = 290; + public static final int DEFAULT_ENERGY_CONSUMPTION = 16; + private static final RecipeType RECIPE_TYPE = RecipeType.SMELTING; + private static final int DEFAULT_SMELTING_TIME = 200; + private static final int NUM_OF_FIELDS = 8; + private static final int TICK_INTERVAL = 4; + public static final int DEFAULT_SCALED_ENERGY_CONSUMPTION = DEFAULT_ENERGY_CONSUMPTION * TICK_INTERVAL; + private static int energy_consumption_ = DEFAULT_SCALED_ENERGY_CONSUMPTION; + private static int transfer_energy_consumption_ = DEFAULT_SCALED_ENERGY_CONSUMPTION / 8; + private static final int FIFO_INTERVAL = 20; + private static final int HEAT_CAPACITY = 200; + private static final int HEAT_INCREMENT = 20; + private static final int MAX_BURNTIME = 0x7fff; + private static final int MAX_XP_STORED = 65535; + private static final int MAX_ENERGY_TRANSFER = 1024; + private static final int MAX_ENERGY_BUFFER = 32000; + private static final int MAX_SPEED_SETTING = 3; + private static final int NUM_OF_SLOTS = 7; + private static final int SMELTING_INPUT_SLOT_NO = 0; + private static final int SMELTING_AUX_SLOT_NO = 1; + private static final int SMELTING_OUTPUT_SLOT_NO = 2; + private static final int FIFO_INPUT_0_SLOT_NO = 3; + private static final int FIFO_INPUT_1_SLOT_NO = 4; + + // Config ---------------------------------------------------------------------------------- + private static final int FIFO_OUTPUT_0_SLOT_NO = 5; + private static final int FIFO_OUTPUT_1_SLOT_NO = 6; + private static final double[] speed_setting_factor_ = {0.0, 1.0, 1.5, 2.0}; + private static boolean with_automatic_inventory_pulling_ = false; + private static int proc_speed_percent_ = DEFAULT_SPEED_PERCENT; + private final Inventories.StorageInventory inventory_; + private final LazyOptional item_handler_; + private final RfEnergy.Battery battery_; + private final LazyOptional energy_handler_; + // ElectricalFurnaceTileEntity ----------------------------------------------------------------------------- + private int tick_timer_; + private int fifo_timer_; + private double proc_time_elapsed_; + private int proc_time_needed_; + private int burntime_left_; + private int field_is_burning_; + private float xp_stored_; + private @Nullable Recipe current_recipe_ = null; + private int fuel_burntime_; + private int field_proc_time_elapsed_; + private int speed_ = 1; + private boolean enabled_ = false; + private int field_power_consumption_; + protected final ContainerData fields = new ContainerData() { + @Override + public int getCount() { + return ElectricalFurnaceTileEntity.NUM_OF_FIELDS; + } + + @Override + public int get(int id) { + return switch (id) { + case 0 -> ElectricalFurnaceTileEntity.this.burntime_left_; + case 1 -> ElectricalFurnaceTileEntity.this.battery_.getEnergyStored(); + case 2 -> (int) ElectricalFurnaceTileEntity.this.proc_time_elapsed_; + case 3 -> ElectricalFurnaceTileEntity.this.proc_time_needed_; + case 4 -> ElectricalFurnaceTileEntity.this.speed_; + case 5 -> ElectricalFurnaceTileEntity.this.battery_.getMaxEnergyStored(); + case 6 -> ElectricalFurnaceTileEntity.this.field_is_burning_; + case 7 -> ElectricalFurnaceTileEntity.this.field_power_consumption_; + default -> 0; + }; + } + + @Override + public void set(int id, int value) { + switch (id) { + case 0 -> ElectricalFurnaceTileEntity.this.burntime_left_ = value; + case 1 -> ElectricalFurnaceTileEntity.this.battery_.setEnergyStored(value); + case 2 -> ElectricalFurnaceTileEntity.this.proc_time_elapsed_ = value; + case 3 -> ElectricalFurnaceTileEntity.this.proc_time_needed_ = value; + case 4 -> ElectricalFurnaceTileEntity.this.speed_ = value; + case 5 -> ElectricalFurnaceTileEntity.this.battery_.setMaxEnergyStored(value); + case 6 -> ElectricalFurnaceTileEntity.this.field_is_burning_ = value; + case 7 -> ElectricalFurnaceTileEntity.this.field_power_consumption_ = value; + } + } + }; + private int power_consumption_accumulator_; + private int power_consumption_timer_; + + public ElectricalFurnaceTileEntity(BlockPos pos, BlockState state) { + super(ModContent.getBlockEntityTypeOfBlock(state.getBlock()), pos, state); + inventory_ = new Inventories.StorageInventory(this, NUM_OF_SLOTS) { + @Override + public void setItem(int index, ItemStack stack) { + ItemStack slot_stack = stacks_.get(index); + boolean already_in_slot = (!stack.isEmpty()) && (Inventories.areItemStacksIdentical(stack, slot_stack)); + stacks_.set(index, stack); + if (stack.getCount() > getMaxStackSize()) stack.setCount(getMaxStackSize()); + if ((index == SMELTING_INPUT_SLOT_NO) && (!already_in_slot)) { + proc_time_needed_ = Crafting.getSmeltingTimeNeeded(RECIPE_TYPE, level, stack); + proc_time_elapsed_ = 0; + setChanged(); + } + } + }; + inventory_.setValidator((index, stack) -> switch (index) { + case SMELTING_INPUT_SLOT_NO, FIFO_INPUT_0_SLOT_NO, FIFO_INPUT_1_SLOT_NO -> true; + default -> false; + }); + item_handler_ = Inventories.MappedItemHandler.createGenericHandler(inventory_, + (slot, stack) -> ((slot == FIFO_OUTPUT_0_SLOT_NO) || (slot == FIFO_OUTPUT_1_SLOT_NO)), + (slot, stack) -> ((slot == FIFO_INPUT_0_SLOT_NO) || (slot == FIFO_INPUT_1_SLOT_NO)), + Arrays.asList(FIFO_OUTPUT_0_SLOT_NO, FIFO_OUTPUT_1_SLOT_NO, FIFO_INPUT_0_SLOT_NO, FIFO_INPUT_1_SLOT_NO) + ); + battery_ = new RfEnergy.Battery(MAX_ENERGY_BUFFER, MAX_ENERGY_TRANSFER, 0); + energy_handler_ = battery_.createEnergyHandler(); + } + + public static void on_config(int speed_percent, int standard_energy_per_tick, boolean with_automatic_inventory_pulling) { + proc_speed_percent_ = Mth.clamp(speed_percent, 10, 800); + energy_consumption_ = Mth.clamp(standard_energy_per_tick, 4, 4096) * TICK_INTERVAL; + transfer_energy_consumption_ = Mth.clamp(energy_consumption_ / 8, 8, HEAT_INCREMENT); + with_automatic_inventory_pulling_ = with_automatic_inventory_pulling; + ModConfig.log("Config electrical furnace speed:" + proc_speed_percent_ + "%, heat-loss: 1K/t, heating consumption:" + (energy_consumption_ / TICK_INTERVAL) + "rf/t."); + } + + @Nullable + public static T getSmeltingResult(RecipeType recipe_type, Level world, ItemStack stack) { + if (stack.isEmpty()) return null; + Container inventory = new SimpleContainer(3); + inventory.setItem(0, stack); + return world.getRecipeManager().getRecipeFor(recipe_type, inventory, world).orElse(null); + } + + public static int getFuelBurntime(Level world, ItemStack stack) { + if (stack.isEmpty()) return 0; + int t = ForgeHooks.getBurnTime(stack, null); + return Math.max(t, 0); + } + + public static boolean isFuel(Level world, ItemStack stack) { + return (getFuelBurntime(world, stack) > 0) || (stack.getItem() == Items.LAVA_BUCKET); + } + + public static boolean canSmelt(Level world, final ItemStack stack) { + return getSmeltingResult(RECIPE_TYPE, world, stack) != null; + } + + // BlockEntity ------------------------------------------------------------------------------ + + public void reset() { + inventory_.clearContent(); + burntime_left_ = 0; proc_time_elapsed_ = 0; - } + proc_time_needed_ = 0; + fifo_timer_ = 0; + tick_timer_ = 0; + battery_.clear(); + speed_ = 1; + field_is_burning_ = 0; } - } else if(proc_time_elapsed_ > 0) { - proc_time_elapsed_ -= ((inventory_.getItem(SMELTING_INPUT_SLOT_NO)).isEmpty() ? 20 : 1); - if(proc_time_elapsed_ < 0) { proc_time_elapsed_ = 0; shift_out = true; update_blockstate = true; } - } - if(update_blockstate) { - dirty = true; - sync_blockstate(); - } - if(adjacent_inventory_shift(shift_in, shift_out)) dirty = true; - field_is_burning_ = burning() ? 1 : 0; - // power consumption - power_consumption_timer_ += TICK_INTERVAL; - power_consumption_accumulator_ += (battery_.getEnergyStored() - battery_energy); - if(power_consumption_timer_ >= 20) { - field_power_consumption_ = power_consumption_accumulator_/power_consumption_timer_; - power_consumption_accumulator_ = 0; - power_consumption_timer_ = 0; - } - if(dirty) setChanged(); - } - // Furnace -------------------------------------------------------------------------------------- - - private boolean is_accepted_hopper(final ItemStack stack) - { return (stack.getItem() == Blocks.HOPPER.asItem()) || (stack.getItem() == ModContent.getBlock("factory_hopper").asItem()); } - - private boolean transferItems(final int index_from, final int index_to, int count) - { - ItemStack from = inventory_.getItem(index_from); - if(from.isEmpty()) return false; - ItemStack to = inventory_.getItem(index_to); - if(from.getCount() < count) count = from.getCount(); - if(count <= 0) return false; - boolean changed = true; - if(to.isEmpty()) { - inventory_.setItem(index_to, from.split(count)); - } else if(to.getCount() >= to.getMaxStackSize()) { - changed = false; - } else if(Inventories.areItemStacksDifferent(from, to)) { - changed = false; - } else { - if((to.getCount()+count) >= to.getMaxStackSize()) { - from.shrink(to.getMaxStackSize()-to.getCount()); - to.setCount(to.getMaxStackSize()); - } else { - from.shrink(count); - to.grow(count); + public CompoundTag reset_getnbt() { + CompoundTag nbt = new CompoundTag(); + writenbt(nbt); + reset(); + return nbt; } - } - if(from.isEmpty() && from!=ItemStack.EMPTY) { - inventory_.setItem(index_from, ItemStack.EMPTY); - changed = true; - } - return changed; - } - private boolean adjacent_inventory_shift(boolean inp, boolean out) - { - boolean dirty = false; - if(battery_.getEnergyStored() < transfer_energy_consumption_) return false; - final BlockState state = level.getBlockState(worldPosition); - if(!(state.getBlock() instanceof ElectricalFurnaceBlock)) return false; - final Direction out_facing = state.getValue(ElectricalFurnaceBlock.HORIZONTAL_FACING); - if(out && (!inventory_.getItem(FIFO_OUTPUT_1_SLOT_NO).isEmpty())) { - BlockEntity te = level.getBlockEntity(worldPosition.relative(out_facing)); - if(te!=null) { - IItemHandler hnd = te.getCapability(ForgeCapabilities.ITEM_HANDLER, out_facing).orElse(null); - if(hnd != null) { - ItemStack remaining = ItemHandlerHelper.insertItemStacked(hnd, inventory_.getItem(FIFO_OUTPUT_1_SLOT_NO).copy(), false); - inventory_.setItem(FIFO_OUTPUT_1_SLOT_NO, remaining); - battery_.draw(transfer_energy_consumption_); - dirty = true; - } + public void readnbt(CompoundTag nbt) { + burntime_left_ = nbt.getInt("BurnTime"); + proc_time_elapsed_ = nbt.getInt("CookTime"); + proc_time_needed_ = nbt.getInt("CookTimeTotal"); + xp_stored_ = nbt.getFloat("XpStored"); + speed_ = nbt.getInt("SpeedSetting"); + speed_ = (speed_ < 0) ? (1) : (Math.min(speed_, MAX_SPEED_SETTING)); + battery_.load(nbt, "Energy"); + inventory_.load(nbt); } - } - if(with_automatic_inventory_pulling_ || is_accepted_hopper(inventory_.getItem(SMELTING_AUX_SLOT_NO))) { - final Direction inp_facing = state.getValue(ElectricalFurnaceBlock.HORIZONTAL_FACING).getOpposite(); - if(inp && (inventory_.getItem(FIFO_INPUT_1_SLOT_NO).isEmpty()) && (battery_.getEnergyStored() >= transfer_energy_consumption_)) { - final int max_count = Mth.clamp((transfer_energy_consumption_ <= 0) ? (64) : (battery_.getEnergyStored()/transfer_energy_consumption_), 1, 64); - final ItemStack retrieved = Inventories.extract(Inventories.itemhandler(level, worldPosition.relative(inp_facing), inp_facing), null, max_count, false); - if(!retrieved.isEmpty()) { - inventory_.setItem(FIFO_INPUT_1_SLOT_NO, retrieved); - battery_.draw(max_count * transfer_energy_consumption_); - dirty = true; - } + + protected void writenbt(CompoundTag nbt) { + nbt.putInt("BurnTime", Mth.clamp(burntime_left_, 0, HEAT_CAPACITY)); + nbt.putInt("CookTime", Mth.clamp((int) proc_time_elapsed_, 0, MAX_BURNTIME)); + nbt.putInt("CookTimeTotal", Mth.clamp(proc_time_needed_, 0, MAX_BURNTIME)); + nbt.putInt("SpeedSetting", Mth.clamp(speed_, -1, MAX_SPEED_SETTING)); + nbt.putFloat("XpStored", Mth.clamp(xp_stored_, 0, MAX_XP_STORED)); + battery_.save(nbt, "Energy"); + inventory_.save(nbt); } - } - return dirty; - } - private int energy_consumption() - { return energy_consumption(speed_); } - - private int energy_consumption(int speed) - { - return switch (speed) { - case 1 -> energy_consumption_; - case 2 -> energy_consumption_ * 2; - case 3 -> energy_consumption_ * 4; - default -> 0; - }; - } - - private boolean heat_up(int speed) - { - if(!enabled_) return false; - int p = energy_consumption(speed); - if((p<=0) || (!battery_.draw(p))) return false; - burntime_left_ = Math.min(burntime_left_+HEAT_INCREMENT, HEAT_CAPACITY); - return true; // returns TE dirty - } - - private void sync_blockstate() - { - final BlockState state = level.getBlockState(worldPosition); - if((state.getBlock() instanceof ElectricalFurnaceBlock) && (state.getValue(ElectricalFurnaceBlock.LIT) != burning())) { - level.setBlock(worldPosition, state.setValue(ElectricalFurnaceBlock.LIT, burning()), 2); - } - } - - //////////////////// - // Furnace ------------------------------------------------------------------------------------- - - @Nullable - public static T getSmeltingResult(RecipeType recipe_type, Level world, ItemStack stack) - { - if(stack.isEmpty()) return null; - Container inventory = new SimpleContainer(3); - inventory.setItem(0, stack); - return world.getRecipeManager().getRecipeFor(recipe_type, inventory, world).orElse(null); - } - - public boolean burning() - { return burntime_left_ > 0; } - - public int getSmeltingTimeNeeded(Level world, ItemStack stack) - { - if(stack.isEmpty()) return 0; - AbstractCookingRecipe recipe = getSmeltingResult(RECIPE_TYPE, world, stack); - if(recipe == null) return 0; - int t = recipe.getCookingTime(); - return (t<=0) ? DEFAULT_SMELTING_TIME : t; - } - - protected boolean canSmeltCurrentItem() - { - if((currentRecipe()==null) || (inventory_.getItem(SMELTING_INPUT_SLOT_NO).isEmpty())) return false; - final ItemStack recipe_result_items = getSmeltingResult(inventory_.getItem(SMELTING_INPUT_SLOT_NO)); - if(recipe_result_items.isEmpty()) return false; - final ItemStack result_stack = inventory_.getItem(SMELTING_OUTPUT_SLOT_NO); - if(result_stack.isEmpty()) return true; - if(!result_stack.sameItem(recipe_result_items)) return false; - if(result_stack.getCount() + recipe_result_items.getCount() <= inventory_.getMaxStackSize() && result_stack.getCount() + recipe_result_items.getCount() <= result_stack.getMaxStackSize()) return true; - return result_stack.getCount() + recipe_result_items.getCount() <= recipe_result_items.getMaxStackSize(); - } - - protected void smeltCurrentItem() - { - if(!canSmeltCurrentItem()) return; - final ItemStack smelting_input_stack = inventory_.getItem(SMELTING_INPUT_SLOT_NO); - final ItemStack recipe_result_items = getSmeltingResult(smelting_input_stack); - final ItemStack smelting_output_stack = inventory_.getItem(SMELTING_OUTPUT_SLOT_NO); - final float xp = getCurrentSmeltingXp(smelting_output_stack); - if(smelting_output_stack.isEmpty()) { - inventory_.setItem(SMELTING_OUTPUT_SLOT_NO, recipe_result_items.copy()); - } else if(smelting_output_stack.getItem() == recipe_result_items.getItem()) { - smelting_output_stack.grow(recipe_result_items.getCount()); - } - smelting_input_stack.shrink(1); - xp_stored_ += xp; - } - - public static int getFuelBurntime(Level world, ItemStack stack) - { - if(stack.isEmpty()) return 0; - int t = ForgeHooks.getBurnTime(stack, null); - return Math.max(t, 0); - } - - public static boolean isFuel(Level world, ItemStack stack) - { return (getFuelBurntime(world, stack) > 0) || (stack.getItem()==Items.LAVA_BUCKET); } - - public int consumeSmeltingExperience(ItemStack stack) - { - if(xp_stored_ < 1) return 0; - float xp = xp_stored_; - if(xp >= 15) xp /= 2; - xp = Math.min((float)Math.floor(xp), 150); - xp_stored_ -= xp; - return (int)xp; - } - - public ItemStack getSmeltingResult(final ItemStack stack) - { return (currentRecipe()==null) ? (ItemStack.EMPTY) : (currentRecipe().getResultItem()); } - - public float getCurrentSmeltingXp(final ItemStack stack) - { - float xp = (currentRecipe() instanceof AbstractCookingRecipe) ? (((AbstractCookingRecipe)currentRecipe()).getExperience()) : 0; - return (xp <= 0) ? 0.7f : xp; // default value for recipes without defined xp - } - - public static boolean canSmelt(Level world, final ItemStack stack) - { return getSmeltingResult(RECIPE_TYPE, world, stack) != null; } - - @Nullable - protected Recipe currentRecipe() - { return current_recipe_; } - - protected void updateCurrentRecipe() - { setCurrentRecipe(getSmeltingResult(RECIPE_TYPE, getLevel(), inventory_.getItem(SMELTING_INPUT_SLOT_NO))); } - - protected void setCurrentRecipe(Recipe recipe) - { current_recipe_ = recipe; } - - } - - //-------------------------------------------------------------------------------------------------------------------- - // Container - //-------------------------------------------------------------------------------------------------------------------- - - public static class ElectricalFurnaceContainer extends AbstractContainerMenu implements Networking.INetworkSynchronisableContainer - { - - // Slots -------------------------------------------------------------------------------------------- - - public static class OutputSlot extends Slot - { - private final Container inventory_; - private final Player player_; - private int removeCount = 0; - - public OutputSlot(Player player, Container inventory, int index, int xpos, int ypos) - { super(inventory, index, xpos, ypos); inventory_ = inventory; player_ = player; } - - @Override - public boolean mayPlace(ItemStack stack) - { return false; } - - @Override - public ItemStack remove(int amount) - { removeCount += hasItem() ? Math.min(amount, getItem().getCount()) : 0; return super.remove(amount); } - - @Override - public void onTake(Player thePlayer, ItemStack stack) - { checkTakeAchievements(stack); super.onTake(thePlayer, stack); } - - @Override - protected void onQuickCraft(ItemStack stack, int amount) - { removeCount += amount; checkTakeAchievements(stack); } - - @Override - protected void checkTakeAchievements(ItemStack stack) - { - stack.onCraftedBy(player_.level, player_, removeCount); - if((!player_.level.isClientSide()) && (inventory_ instanceof StorageInventory) && - (((StorageInventory)inventory_).getBlockEntity()) instanceof final ElectricalFurnaceTileEntity te) { - int xp = te.consumeSmeltingExperience(stack); - while(xp > 0) { - int k = ExperienceOrb.getExperienceValue(xp); - xp -= k; - player_.level.addFreshEntity((new ExperienceOrb(player_.level, player_.blockPosition().getX(), player_.blockPosition().getY()+0.5, player_.blockPosition().getZ()+0.5, k))); - } + public int getComparatorOutput() { + return (battery_.isEmpty()) ? (0) : ( + (inventory_.getItem(FIFO_INPUT_1_SLOT_NO).isEmpty() ? 0 : 5) + + (inventory_.getItem(FIFO_INPUT_0_SLOT_NO).isEmpty() ? 0 : 5) + + (inventory_.getItem(SMELTING_INPUT_SLOT_NO).isEmpty() ? 0 : 5) + ); } - removeCount = 0; - ForgeEventFactory.firePlayerSmeltedEvent(player_, stack); - } - } - private static final int PLAYER_INV_START_SLOTNO = 7; - protected final Player player_; - protected final Container inventory_; - protected final ContainerLevelAccess wpc_; - private final ContainerData fields_; - private final RecipeType recipe_type_; - - public int field(int index) { return fields_.get(index); } - public Player player() { return player_ ; } - public Container inventory() { return inventory_ ; } - public Level world() { return player_.level; } - - public ElectricalFurnaceContainer(int cid, Inventory player_inventory) - { this(cid, player_inventory, new SimpleContainer(ElectricalFurnaceTileEntity.NUM_OF_SLOTS), ContainerLevelAccess.NULL, new SimpleContainerData(ElectricalFurnaceTileEntity.NUM_OF_FIELDS)); } - - private ElectricalFurnaceContainer(int cid, Inventory player_inventory, Container block_inventory, ContainerLevelAccess wpc, ContainerData fields) - { - super(ModContent.getMenuType("small_electrical_furnace"), cid); // @todo: class mapping, - player_ = player_inventory.player; - inventory_ = block_inventory; - wpc_ = wpc; - fields_ = fields; - recipe_type_ = ElectricalFurnaceTileEntity.RECIPE_TYPE; - addSlot(new Slot(inventory_, 0, 59, 28)); // smelting input - addSlot(new Slot(inventory_, 1, 16, 52)); // aux - addSlot(new OutputSlot(player_, inventory_, 2, 101, 28)); // smelting result - addSlot(new Slot(inventory_, 3, 34, 28)); // input fifo 0 - addSlot(new Slot(inventory_, 4, 16, 28)); // input fifo 1 - addSlot(new OutputSlot(player_, inventory_, 5, 126, 28)); // out fifo 0 - addSlot(new OutputSlot(player_, inventory_, 6, 144, 28)); // out fifo 1 - for(int x=0; x<9; ++x) { - addSlot(new Slot(player_inventory, x, 8+x*18, 144)); // player slots: 0..8 - } - for(int y=0; y<3; ++y) { - for(int x=0; x<9; ++x) { - addSlot(new Slot(player_inventory, x+y*9+9, 8+x*18, 86+y*18)); // player slots: 9..35 + @Override + public void load(CompoundTag nbt) { + super.load(nbt); + readnbt(nbt); } - } - this.addDataSlots(fields_); // === Add reference holders - } - @Override - public boolean stillValid(Player player) - { return inventory_.stillValid(player); } + // Fields ----------------------------------------------------------------------------------------------- - @Override - public ItemStack quickMoveStack(Player player, int index) - { - Slot slot = getSlot(index); - if((slot==null) || (!slot.hasItem())) return ItemStack.EMPTY; - ItemStack slot_stack = slot.getItem(); - ItemStack transferred = slot_stack.copy(); - if((index==2) || (index==5) || (index==6)) { - // Output slots - if(!moveItemStackTo(slot_stack, PLAYER_INV_START_SLOTNO, PLAYER_INV_START_SLOTNO+36, true)) return ItemStack.EMPTY; - slot.onQuickCraft(slot_stack, transferred); - } else if((index==0) || (index==3) || (index==4)) { - // Input slots - if(!moveItemStackTo(slot_stack, PLAYER_INV_START_SLOTNO, PLAYER_INV_START_SLOTNO+36, false)) return ItemStack.EMPTY; - } else if(index==1) { - // Bypass slot - if(!moveItemStackTo(slot_stack, PLAYER_INV_START_SLOTNO, PLAYER_INV_START_SLOTNO+36, false)) return ItemStack.EMPTY; - } else if((index >= PLAYER_INV_START_SLOTNO) && (index <= PLAYER_INV_START_SLOTNO+36)) { - // Player inventory - if(ElectricalFurnaceTileEntity.canSmelt(world(), slot_stack)) { - if( - (!moveItemStackTo(slot_stack, 0, 1, false)) && // smelting input - (!moveItemStackTo(slot_stack, 3, 4, false)) && // fifo0 - (!moveItemStackTo(slot_stack, 4, 5, false)) // fifo1 - ) return ItemStack.EMPTY; - } else if((index >= PLAYER_INV_START_SLOTNO) && (index < PLAYER_INV_START_SLOTNO+27)) { - // player inventory --> player hotbar - if(!moveItemStackTo(slot_stack, PLAYER_INV_START_SLOTNO+27, PLAYER_INV_START_SLOTNO+36, false)) return ItemStack.EMPTY; - } else if((index >= PLAYER_INV_START_SLOTNO+27) && (index < PLAYER_INV_START_SLOTNO+36) && (!moveItemStackTo(slot_stack, PLAYER_INV_START_SLOTNO, PLAYER_INV_START_SLOTNO+27, false))) { - // player hotbar --> player inventory - return ItemStack.EMPTY; + @Override + protected void saveAdditional(CompoundTag nbt) { + super.saveAdditional(nbt); + writenbt(nbt); } - } else { - // invalid slot - return ItemStack.EMPTY; - } - if(slot_stack.isEmpty()) { - slot.set(ItemStack.EMPTY); - } else { - slot.setChanged(); - } - if(slot_stack.getCount() == transferred.getCount()) return ItemStack.EMPTY; - slot.onTake(player, slot_stack); - return transferred; + + // Capability export ---------------------------------------------------------------------------- + + @Override + public void setRemoved() { + super.setRemoved(); + item_handler_.invalidate(); + energy_handler_.invalidate(); + } + + // ----------------------------------------------------------------------------------------------------------------- + + @Override + public Component getName() { + return Auxiliaries.localizable(getBlockState().getBlock().getDescriptionId()); + } + + // Furnace -------------------------------------------------------------------------------------- + + @Override + public Component getDisplayName() { + return Nameable.super.getDisplayName(); + } + + @Override + public AbstractContainerMenu createMenu(int id, Inventory inventory, Player player) { + return new EdElectricalFurnace.ElectricalFurnaceContainer(id, inventory, inventory_, ContainerLevelAccess.create(level, worldPosition), fields); + } + + @Override + public LazyOptional getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable Direction facing) { + if (capability == ForgeCapabilities.ITEM_HANDLER) return item_handler_.cast(); + if (capability == ForgeCapabilities.ENERGY) return energy_handler_.cast(); + return super.getCapability(capability, facing); + } + + @Override + public void tick() { + if (--tick_timer_ > 0) return; + tick_timer_ = TICK_INTERVAL; + if (!(level.getBlockState(worldPosition).getBlock() instanceof ElectricalFurnaceBlock)) return; + final boolean was_burning = burning(); + if (was_burning) burntime_left_ -= TICK_INTERVAL; + if (burntime_left_ < 0) burntime_left_ = 0; + if (level.isClientSide()) return; + int battery_energy = battery_.getEnergyStored(); + boolean update_blockstate = (was_burning != burning()); + boolean dirty = update_blockstate; + boolean shift_in = false; + boolean shift_out = false; + if (--fifo_timer_ <= 0) { + fifo_timer_ = FIFO_INTERVAL / TICK_INTERVAL; + if (transferItems(FIFO_OUTPUT_0_SLOT_NO, FIFO_OUTPUT_1_SLOT_NO, 64)) { + dirty = true; + } else { + shift_out = true; + } + if (transferItems(SMELTING_OUTPUT_SLOT_NO, FIFO_OUTPUT_0_SLOT_NO, 64)) dirty = true; + if (transferItems(FIFO_INPUT_0_SLOT_NO, SMELTING_INPUT_SLOT_NO, 64)) dirty = true; + if (transferItems(FIFO_INPUT_1_SLOT_NO, FIFO_INPUT_0_SLOT_NO, 64)) { + dirty = true; + } else { + shift_in = true; + } + } + if (battery_.getEnergyStored() < energy_consumption()) { + enabled_ = false; + } else if (battery_.getEnergyStored() >= (MAX_ENERGY_BUFFER / 2)) { + enabled_ = true; + } + if ((!(inventory_.getItem(SMELTING_INPUT_SLOT_NO)).isEmpty()) && (enabled_) && (speed_ > 0)) { + Recipe last_recipe = currentRecipe(); + updateCurrentRecipe(); + if (currentRecipe() != last_recipe) { + proc_time_elapsed_ = 0; + proc_time_needed_ = getSmeltingTimeNeeded(level, inventory_.getItem(SMELTING_INPUT_SLOT_NO)); + } + final boolean can_smelt = canSmeltCurrentItem(); + if ((!can_smelt) && (getSmeltingResult(inventory_.getItem(SMELTING_INPUT_SLOT_NO)).isEmpty())) { + // bypass + if (transferItems(SMELTING_INPUT_SLOT_NO, SMELTING_OUTPUT_SLOT_NO, 1)) dirty = true; + } else { + // smelt, automatically choke speed on low power storage + final int speed = Mth.clamp((battery_.getSOC() >= 25) ? (speed_) : (1), 0, MAX_SPEED_SETTING); + if (!burning() && can_smelt) { + if (heat_up(speed)) { + dirty = true; + update_blockstate = true; + } + } + if (burning() && can_smelt) { + if (heat_up(speed)) dirty = true; + proc_time_elapsed_ += (int) (TICK_INTERVAL * proc_speed_percent_ * speed_setting_factor_[speed] / 100); + if (proc_time_elapsed_ >= proc_time_needed_) { + proc_time_elapsed_ = 0; + proc_time_needed_ = getSmeltingTimeNeeded(level, inventory_.getItem(SMELTING_INPUT_SLOT_NO)); + smeltCurrentItem(); + dirty = true; + shift_out = true; + } + } else { + proc_time_elapsed_ = 0; + } + } + } else if (proc_time_elapsed_ > 0) { + proc_time_elapsed_ -= ((inventory_.getItem(SMELTING_INPUT_SLOT_NO)).isEmpty() ? 20 : 1); + if (proc_time_elapsed_ < 0) { + proc_time_elapsed_ = 0; + shift_out = true; + update_blockstate = true; + } + } + if (update_blockstate) { + dirty = true; + sync_blockstate(); + } + if (adjacent_inventory_shift(shift_in, shift_out)) dirty = true; + field_is_burning_ = burning() ? 1 : 0; + // power consumption + power_consumption_timer_ += TICK_INTERVAL; + power_consumption_accumulator_ += (battery_.getEnergyStored() - battery_energy); + if (power_consumption_timer_ >= 20) { + field_power_consumption_ = power_consumption_accumulator_ / power_consumption_timer_; + power_consumption_accumulator_ = 0; + power_consumption_timer_ = 0; + } + if (dirty) setChanged(); + } + + private boolean is_accepted_hopper(final ItemStack stack) { + return (stack.getItem() == Blocks.HOPPER.asItem()) || (stack.getItem() == ModContent.getBlock("factory_hopper").asItem()); + } + + private boolean transferItems(final int index_from, final int index_to, int count) { + ItemStack from = inventory_.getItem(index_from); + if (from.isEmpty()) return false; + ItemStack to = inventory_.getItem(index_to); + if (from.getCount() < count) count = from.getCount(); + if (count <= 0) return false; + boolean changed = true; + if (to.isEmpty()) { + inventory_.setItem(index_to, from.split(count)); + } else if (to.getCount() >= to.getMaxStackSize()) { + changed = false; + } else if (Inventories.areItemStacksDifferent(from, to)) { + changed = false; + } else { + if ((to.getCount() + count) >= to.getMaxStackSize()) { + from.shrink(to.getMaxStackSize() - to.getCount()); + to.setCount(to.getMaxStackSize()); + } else { + from.shrink(count); + to.grow(count); + } + } + if (from.isEmpty() && from != ItemStack.EMPTY) { + inventory_.setItem(index_from, ItemStack.EMPTY); + changed = true; + } + return changed; + } + + private boolean adjacent_inventory_shift(boolean inp, boolean out) { + boolean dirty = false; + if (battery_.getEnergyStored() < transfer_energy_consumption_) return false; + final BlockState state = level.getBlockState(worldPosition); + if (!(state.getBlock() instanceof ElectricalFurnaceBlock)) return false; + final Direction out_facing = state.getValue(ElectricalFurnaceBlock.HORIZONTAL_FACING); + if (out && (!inventory_.getItem(FIFO_OUTPUT_1_SLOT_NO).isEmpty())) { + BlockEntity te = level.getBlockEntity(worldPosition.relative(out_facing)); + if (te != null) { + IItemHandler hnd = te.getCapability(ForgeCapabilities.ITEM_HANDLER, out_facing).orElse(null); + if (hnd != null) { + ItemStack remaining = ItemHandlerHelper.insertItemStacked(hnd, inventory_.getItem(FIFO_OUTPUT_1_SLOT_NO).copy(), false); + inventory_.setItem(FIFO_OUTPUT_1_SLOT_NO, remaining); + battery_.draw(transfer_energy_consumption_); + dirty = true; + } + } + } + if (with_automatic_inventory_pulling_ || is_accepted_hopper(inventory_.getItem(SMELTING_AUX_SLOT_NO))) { + final Direction inp_facing = state.getValue(ElectricalFurnaceBlock.HORIZONTAL_FACING).getOpposite(); + if (inp && (inventory_.getItem(FIFO_INPUT_1_SLOT_NO).isEmpty()) && (battery_.getEnergyStored() >= transfer_energy_consumption_)) { + final int max_count = Mth.clamp((transfer_energy_consumption_ <= 0) ? (64) : (battery_.getEnergyStored() / transfer_energy_consumption_), 1, 64); + final ItemStack retrieved = Inventories.extract(Inventories.itemhandler(level, worldPosition.relative(inp_facing), inp_facing), null, max_count, false); + if (!retrieved.isEmpty()) { + inventory_.setItem(FIFO_INPUT_1_SLOT_NO, retrieved); + battery_.draw(max_count * transfer_energy_consumption_); + dirty = true; + } + } + } + return dirty; + } + + //////////////////// + // Furnace ------------------------------------------------------------------------------------- + + private int energy_consumption() { + return energy_consumption(speed_); + } + + private int energy_consumption(int speed) { + return switch (speed) { + case 1 -> energy_consumption_; + case 2 -> energy_consumption_ * 2; + case 3 -> energy_consumption_ * 4; + default -> 0; + }; + } + + private boolean heat_up(int speed) { + if (!enabled_) return false; + int p = energy_consumption(speed); + if ((p <= 0) || (!battery_.draw(p))) return false; + burntime_left_ = Math.min(burntime_left_ + HEAT_INCREMENT, HEAT_CAPACITY); + return true; // returns TE dirty + } + + private void sync_blockstate() { + final BlockState state = level.getBlockState(worldPosition); + if ((state.getBlock() instanceof ElectricalFurnaceBlock) && (state.getValue(ElectricalFurnaceBlock.LIT) != burning())) { + level.setBlock(worldPosition, state.setValue(ElectricalFurnaceBlock.LIT, burning()), 2); + } + } + + public boolean burning() { + return burntime_left_ > 0; + } + + public int getSmeltingTimeNeeded(Level world, ItemStack stack) { + if (stack.isEmpty()) return 0; + AbstractCookingRecipe recipe = getSmeltingResult(RECIPE_TYPE, world, stack); + if (recipe == null) return 0; + int t = recipe.getCookingTime(); + return (t <= 0) ? DEFAULT_SMELTING_TIME : t; + } + + protected boolean canSmeltCurrentItem() { + if ((currentRecipe() == null) || (inventory_.getItem(SMELTING_INPUT_SLOT_NO).isEmpty())) return false; + final ItemStack recipe_result_items = getSmeltingResult(inventory_.getItem(SMELTING_INPUT_SLOT_NO)); + if (recipe_result_items.isEmpty()) return false; + final ItemStack result_stack = inventory_.getItem(SMELTING_OUTPUT_SLOT_NO); + if (result_stack.isEmpty()) return true; + if (!result_stack.is(recipe_result_items.getItem())) return false; + if (result_stack.getCount() + recipe_result_items.getCount() <= inventory_.getMaxStackSize() && result_stack.getCount() + recipe_result_items.getCount() <= result_stack.getMaxStackSize()) + return true; + return result_stack.getCount() + recipe_result_items.getCount() <= recipe_result_items.getMaxStackSize(); + } + + protected void smeltCurrentItem() { + if (!canSmeltCurrentItem()) return; + final ItemStack smelting_input_stack = inventory_.getItem(SMELTING_INPUT_SLOT_NO); + final ItemStack recipe_result_items = getSmeltingResult(smelting_input_stack); + final ItemStack smelting_output_stack = inventory_.getItem(SMELTING_OUTPUT_SLOT_NO); + final float xp = getCurrentSmeltingXp(smelting_output_stack); + if (smelting_output_stack.isEmpty()) { + inventory_.setItem(SMELTING_OUTPUT_SLOT_NO, recipe_result_items.copy()); + } else if (smelting_output_stack.getItem() == recipe_result_items.getItem()) { + smelting_output_stack.grow(recipe_result_items.getCount()); + } + smelting_input_stack.shrink(1); + xp_stored_ += xp; + } + + public int consumeSmeltingExperience(ItemStack stack) { + if (xp_stored_ < 1) return 0; + float xp = xp_stored_; + if (xp >= 15) xp /= 2; + xp = Math.min((float) Math.floor(xp), 150); + xp_stored_ -= xp; + return (int) xp; + } + + public ItemStack getSmeltingResult(final ItemStack stack) { + return (currentRecipe() == null) ? (ItemStack.EMPTY) : (currentRecipe().getResultItem(getLevel().registryAccess())); + } + + public float getCurrentSmeltingXp(final ItemStack stack) { + float xp = (currentRecipe() instanceof AbstractCookingRecipe) ? (((AbstractCookingRecipe) currentRecipe()).getExperience()) : 0; + return (xp <= 0) ? 0.7f : xp; // default value for recipes without defined xp + } + + @Nullable + protected Recipe currentRecipe() { + return current_recipe_; + } + + protected void updateCurrentRecipe() { + setCurrentRecipe(getSmeltingResult(RECIPE_TYPE, getLevel(), inventory_.getItem(SMELTING_INPUT_SLOT_NO))); + } + + protected void setCurrentRecipe(Recipe recipe) { + current_recipe_ = recipe; + } + } - // INetworkSynchronisableContainer --------------------------------------------------------- + //-------------------------------------------------------------------------------------------------------------------- + // Container + //-------------------------------------------------------------------------------------------------------------------- + + public static class ElectricalFurnaceContainer extends AbstractContainerMenu implements Networking.INetworkSynchronisableContainer { + + // Slots -------------------------------------------------------------------------------------------- + + private static final int PLAYER_INV_START_SLOTNO = 7; + protected final Player player_; + protected final Container inventory_; + protected final ContainerLevelAccess wpc_; + private final ContainerData fields_; + private final RecipeType recipe_type_; + public ElectricalFurnaceContainer(int cid, Inventory player_inventory) { + this(cid, player_inventory, new SimpleContainer(ElectricalFurnaceTileEntity.NUM_OF_SLOTS), ContainerLevelAccess.NULL, new SimpleContainerData(ElectricalFurnaceTileEntity.NUM_OF_FIELDS)); + } + + private ElectricalFurnaceContainer(int cid, Inventory player_inventory, Container block_inventory, ContainerLevelAccess wpc, ContainerData fields) { + super(ModContent.getMenuType("small_electrical_furnace"), cid); // @todo: class mapping, + player_ = player_inventory.player; + inventory_ = block_inventory; + wpc_ = wpc; + fields_ = fields; + recipe_type_ = ElectricalFurnaceTileEntity.RECIPE_TYPE; + addSlot(new Slot(inventory_, 0, 59, 28)); // smelting input + addSlot(new Slot(inventory_, 1, 16, 52)); // aux + addSlot(new OutputSlot(player_, inventory_, 2, 101, 28)); // smelting result + addSlot(new Slot(inventory_, 3, 34, 28)); // input fifo 0 + addSlot(new Slot(inventory_, 4, 16, 28)); // input fifo 1 + addSlot(new OutputSlot(player_, inventory_, 5, 126, 28)); // out fifo 0 + addSlot(new OutputSlot(player_, inventory_, 6, 144, 28)); // out fifo 1 + for (int x = 0; x < 9; ++x) { + addSlot(new Slot(player_inventory, x, 8 + x * 18, 144)); // player slots: 0..8 + } + for (int y = 0; y < 3; ++y) { + for (int x = 0; x < 9; ++x) { + addSlot(new Slot(player_inventory, x + y * 9 + 9, 8 + x * 18, 86 + y * 18)); // player slots: 9..35 + } + } + this.addDataSlots(fields_); // === Add reference holders + } + + public int field(int index) { + return fields_.get(index); + } + + public Player player() { + return player_; + } + + public Container inventory() { + return inventory_; + } + + public Level world() { + return player_.level(); + } + + @Override + public boolean stillValid(Player player) { + return inventory_.stillValid(player); + } + + @Override + public ItemStack quickMoveStack(Player player, int index) { + Slot slot = getSlot(index); + if ((slot == null) || (!slot.hasItem())) return ItemStack.EMPTY; + ItemStack slot_stack = slot.getItem(); + ItemStack transferred = slot_stack.copy(); + if ((index == 2) || (index == 5) || (index == 6)) { + // Output slots + if (!moveItemStackTo(slot_stack, PLAYER_INV_START_SLOTNO, PLAYER_INV_START_SLOTNO + 36, true)) + return ItemStack.EMPTY; + slot.onQuickCraft(slot_stack, transferred); + } else if ((index == 0) || (index == 3) || (index == 4)) { + // Input slots + if (!moveItemStackTo(slot_stack, PLAYER_INV_START_SLOTNO, PLAYER_INV_START_SLOTNO + 36, false)) + return ItemStack.EMPTY; + } else if (index == 1) { + // Bypass slot + if (!moveItemStackTo(slot_stack, PLAYER_INV_START_SLOTNO, PLAYER_INV_START_SLOTNO + 36, false)) + return ItemStack.EMPTY; + } else if ((index >= PLAYER_INV_START_SLOTNO) && (index <= PLAYER_INV_START_SLOTNO + 36)) { + // Player inventory + if (ElectricalFurnaceTileEntity.canSmelt(world(), slot_stack)) { + if ( + (!moveItemStackTo(slot_stack, 0, 1, false)) && // smelting input + (!moveItemStackTo(slot_stack, 3, 4, false)) && // fifo0 + (!moveItemStackTo(slot_stack, 4, 5, false)) // fifo1 + ) return ItemStack.EMPTY; + } else if ((index >= PLAYER_INV_START_SLOTNO) && (index < PLAYER_INV_START_SLOTNO + 27)) { + // player inventory --> player hotbar + if (!moveItemStackTo(slot_stack, PLAYER_INV_START_SLOTNO + 27, PLAYER_INV_START_SLOTNO + 36, false)) + return ItemStack.EMPTY; + } else if ((index >= PLAYER_INV_START_SLOTNO + 27) && (index < PLAYER_INV_START_SLOTNO + 36) && (!moveItemStackTo(slot_stack, PLAYER_INV_START_SLOTNO, PLAYER_INV_START_SLOTNO + 27, false))) { + // player hotbar --> player inventory + return ItemStack.EMPTY; + } + } else { + // invalid slot + return ItemStack.EMPTY; + } + if (slot_stack.isEmpty()) { + slot.set(ItemStack.EMPTY); + } else { + slot.setChanged(); + } + if (slot_stack.getCount() == transferred.getCount()) return ItemStack.EMPTY; + slot.onTake(player, slot_stack); + return transferred; + } + + @OnlyIn(Dist.CLIENT) + public void onGuiAction(CompoundTag nbt) { + Networking.PacketContainerSyncClientToServer.sendToServer(containerId, nbt); + } + + // INetworkSynchronisableContainer --------------------------------------------------------- + + @OnlyIn(Dist.CLIENT) + public void onGuiAction(String key, int value) { + CompoundTag nbt = new CompoundTag(); + nbt.putInt(key, value); + Networking.PacketContainerSyncClientToServer.sendToServer(containerId, nbt); + } + + @Override + public void onServerPacketReceived(int windowId, CompoundTag nbt) { + } + + public void onClientPacketReceived(int windowId, Player player, CompoundTag nbt) { + if (!(inventory_ instanceof Inventories.StorageInventory)) return; + if (!((((Inventories.StorageInventory) inventory_).getBlockEntity()) instanceof final ElectricalFurnaceTileEntity te)) + return; + if (nbt.contains("speed")) + te.speed_ = Mth.clamp(nbt.getInt("speed"), 0, ElectricalFurnaceTileEntity.MAX_SPEED_SETTING); + te.setChanged(); + } + + public static class OutputSlot extends Slot { + private final Container inventory_; + private final Player player_; + private int removeCount = 0; + + public OutputSlot(Player player, Container inventory, int index, int xpos, int ypos) { + super(inventory, index, xpos, ypos); + inventory_ = inventory; + player_ = player; + } + + @Override + public boolean mayPlace(ItemStack stack) { + return false; + } + + @Override + public ItemStack remove(int amount) { + removeCount += hasItem() ? Math.min(amount, getItem().getCount()) : 0; + return super.remove(amount); + } + + @Override + public void onTake(Player thePlayer, ItemStack stack) { + checkTakeAchievements(stack); + super.onTake(thePlayer, stack); + } + + @Override + protected void onQuickCraft(ItemStack stack, int amount) { + removeCount += amount; + checkTakeAchievements(stack); + } + + @Override + protected void checkTakeAchievements(ItemStack stack) { + stack.onCraftedBy(player_.level(), player_, removeCount); + if ((!player_.level().isClientSide()) && (inventory_ instanceof Inventories.StorageInventory) && + (((Inventories.StorageInventory) inventory_).getBlockEntity()) instanceof final ElectricalFurnaceTileEntity te) { + int xp = te.consumeSmeltingExperience(stack); + while (xp > 0) { + int k = ExperienceOrb.getExperienceValue(xp); + xp -= k; + player_.level().addFreshEntity((new ExperienceOrb(player_.level(), player_.blockPosition().getX(), player_.blockPosition().getY() + 0.5, player_.blockPosition().getZ() + 0.5, k))); + } + } + removeCount = 0; + ForgeEventFactory.firePlayerSmeltedEvent(player_, stack); + } + } + } + + //-------------------------------------------------------------------------------------------------------------------- + // GUI + //-------------------------------------------------------------------------------------------------------------------- @OnlyIn(Dist.CLIENT) - public void onGuiAction(CompoundTag nbt) - { Networking.PacketContainerSyncClientToServer.sendToServer(containerId, nbt); } + public static class ElectricalFurnaceGui extends Guis.ContainerGui { + public ElectricalFurnaceGui(ElectricalFurnaceContainer container, Inventory player_inventory, Component title) { + super(container, player_inventory, title, "textures/gui/small_electrical_furnace_gui.png"); + } - @OnlyIn(Dist.CLIENT) - public void onGuiAction(String key, int value) - { CompoundTag nbt=new CompoundTag(); nbt.putInt(key, value); Networking.PacketContainerSyncClientToServer.sendToServer(containerId, nbt); } + @Override + public void init() { + super.init(); + final Block block = ModContent.getBlock(Auxiliaries.getResourceLocation(getMenu().getType()).getPath().replaceAll("^ct_", "")); + final String prefix = block.getDescriptionId() + ".tooltips."; + final int x0 = getGuiLeft(), y0 = getGuiTop(); + final Slot aux = menu.getSlot(ElectricalFurnaceTileEntity.SMELTING_AUX_SLOT_NO); + tooltip_.init( + new TooltipDisplay.TipRange(x0 + 135, y0 + 50, 25, 25, Component.translatable(prefix + "speed")), + new TooltipDisplay.TipRange(x0 + aux.x, y0 + aux.y, 16, 16, Component.translatable(prefix + "auxslot")), + new TooltipDisplay.TipRange(x0 + 80, y0 + 55, 50, 14, () -> { + final int soc = getMenu().field(1) * 100 / Math.max(getMenu().field(5), 1); + final int consumption = getMenu().field(7); + return Component.translatable(prefix + "capacitors", soc, consumption); + }) + ); + } - @Override - public void onServerPacketReceived(int windowId, CompoundTag nbt) - {} + @Override + protected void renderBgWidgets(GuiGraphics mx, float partialTicks, int mouseX, int mouseY) { + final int x0 = leftPos, y0 = topPos, w = imageWidth, h = imageHeight; + mx.blit(getBackgroundImage(), x0, y0, 0, 0, w, h); + if (getMenu().field(6) != 0) { + final int hi = 13; + final int k = heat_px(hi); + mx.blit(getBackgroundImage(), x0 + 62, y0 + 55 + hi - k, 177, hi - k, 13, k); + } + mx.blit(getBackgroundImage(), x0 + 79, y0 + 30, 176, 15, 1 + progress_px(17), 15); + int we = energy_px(32, 8); + if (we > 0) mx.blit(getBackgroundImage(), x0 + 90, y0 + 54, 185, 30, we, 13); + switch (getMenu().field(4)) { + case 0 -> mx.blit(getBackgroundImage(), x0 + 144, y0 + 57, 180, 57, 6, 9); + case 1 -> mx.blit(getBackgroundImage(), x0 + 142, y0 + 58, 190, 58, 9, 6); + case 2 -> mx.blit(getBackgroundImage(), x0 + 144, y0 + 56, 200, 57, 6, 9); + case 3 -> mx.blit(getBackgroundImage(), x0 + 143, y0 + 58, 210, 58, 9, 6); + } + } - public void onClientPacketReceived(int windowId, Player player, CompoundTag nbt) - { - if(!(inventory_ instanceof StorageInventory)) return; - if(!((((StorageInventory)inventory_).getBlockEntity()) instanceof final ElectricalFurnaceTileEntity te)) return; - if(nbt.contains("speed")) te.speed_ = Mth.clamp(nbt.getInt("speed"), 0, ElectricalFurnaceTileEntity.MAX_SPEED_SETTING); - te.setChanged(); + @Override + public boolean mouseClicked(double mouseX, double mouseY, int mouseButton) { + tooltip_.resetTimer(); + ElectricalFurnaceContainer container = getMenu(); + int mx = (int) (mouseX - getGuiLeft() + .5), my = (int) (mouseY - getGuiTop() + .5); + if ((!isHovering(136, 48, 28, 28, mouseX, mouseY))) { + return super.mouseClicked(mouseX, mouseY, mouseButton); + } else if (isHovering(144, 64, 6, 10, mouseX, mouseY)) { + container.onGuiAction("speed", 0); + } else if (isHovering(134, 58, 10, 6, mouseX, mouseY)) { + container.onGuiAction("speed", 1); + } else if (isHovering(144, 48, 6, 10, mouseX, mouseY)) { + container.onGuiAction("speed", 2); + } else if (isHovering(150, 58, 10, 6, mouseX, mouseY)) { + container.onGuiAction("speed", 3); + } + return true; + } + + private int progress_px(int pixels) { + final int tc = getMenu().field(2), T = getMenu().field(3); + return ((T > 0) && (tc > 0)) ? (tc * pixels / T) : (0); + } + + private int heat_px(int pixels) { + int k = ((getMenu().field(0) * (pixels + 1)) / (EdElectricalFurnace.ElectricalFurnaceTileEntity.HEAT_CAPACITY)); + return Math.min(k, pixels); + } + + private int energy_px(int maxwidth, int quantization) { + int emax = getMenu().field(5); + int k = ((maxwidth * getMenu().field(1) * 9) / 8) / ((emax > 0 ? emax : 1) + 1); + k = (k >= maxwidth - 2) ? maxwidth : k; + if (quantization > 0) k = ((k + (quantization / 2)) / quantization) * quantization; + return k; + } } - } - - //-------------------------------------------------------------------------------------------------------------------- - // GUI - //-------------------------------------------------------------------------------------------------------------------- - - @OnlyIn(Dist.CLIENT) - public static class ElectricalFurnaceGui extends Guis.ContainerGui - { - public ElectricalFurnaceGui(ElectricalFurnaceContainer container, Inventory player_inventory, Component title) - { super(container, player_inventory, title, "textures/gui/small_electrical_furnace_gui.png"); } - - @Override - public void init() - { - super.init(); - final Block block = ModContent.getBlock(Auxiliaries.getResourceLocation(getMenu().getType()).getPath().replaceAll("^ct_","")); - final String prefix = block.getDescriptionId() + ".tooltips."; - final int x0 = getGuiLeft(), y0 = getGuiTop(); - final Slot aux = menu.getSlot(ElectricalFurnaceTileEntity.SMELTING_AUX_SLOT_NO); - tooltip_.init( - new TooltipDisplay.TipRange(x0+135, y0+50, 25, 25, Component.translatable(prefix + "speed")), - new TooltipDisplay.TipRange(x0+aux.x, y0+aux.y, 16, 16, Component.translatable(prefix + "auxslot")), - new TooltipDisplay.TipRange(x0+80, y0+55, 50, 14, ()->{ - final int soc = getMenu().field(1) * 100 / Math.max(getMenu().field(5), 1); - final int consumption = getMenu().field(7); - return Component.translatable(prefix + "capacitors", soc, consumption); - }) - ); - } - - @Override - protected void renderBgWidgets(PoseStack mx, float partialTicks, int mouseX, int mouseY) - { - final int x0=leftPos, y0=topPos, w=imageWidth, h=imageHeight; - blit(mx, x0, y0, 0, 0, w, h); - if(getMenu().field(6)!=0) { - final int hi = 13; - final int k = heat_px(hi); - blit(mx, x0+62, y0+55+hi-k, 177, hi-k, 13, k); - } - blit(mx, x0+79, y0+30, 176, 15, 1+progress_px(17), 15); - int we = energy_px(32, 8); - if(we>0) blit(mx, x0+90, y0+54, 185, 30, we, 13); - switch(getMenu().field(4)) { - case 0 -> blit(mx, x0 + 144, y0 + 57, 180, 57, 6, 9); - case 1 -> blit(mx, x0 + 142, y0 + 58, 190, 58, 9, 6); - case 2 -> blit(mx, x0 + 144, y0 + 56, 200, 57, 6, 9); - case 3 -> blit(mx, x0 + 143, y0 + 58, 210, 58, 9, 6); - } - } - - @Override - public boolean mouseClicked(double mouseX, double mouseY, int mouseButton) - { - tooltip_.resetTimer(); - ElectricalFurnaceContainer container = getMenu(); - int mx = (int)(mouseX - getGuiLeft() + .5), my = (int)(mouseY - getGuiTop() + .5); - if((!isHovering(136, 48, 28, 28, mouseX, mouseY))) { - return super.mouseClicked(mouseX, mouseY, mouseButton); - } else if(isHovering(144, 64, 6, 10, mouseX, mouseY)) { - container.onGuiAction("speed", 0); - } else if(isHovering(134, 58, 10, 6, mouseX, mouseY)) { - container.onGuiAction("speed", 1); - } else if(isHovering(144, 48, 6, 10, mouseX, mouseY)) { - container.onGuiAction("speed", 2); - } else if(isHovering(150, 58, 10, 6, mouseX, mouseY)) { - container.onGuiAction("speed", 3); - } - return true; - } - - private int progress_px(int pixels) - { final int tc=getMenu().field(2), T=getMenu().field(3); return ((T>0) && (tc>0)) ? (tc * pixels / T) : (0); } - - private int heat_px(int pixels) - { - int k = ((getMenu().field(0) * (pixels+1)) / (EdElectricalFurnace.ElectricalFurnaceTileEntity.HEAT_CAPACITY)); - return Math.min(k, pixels); - } - - private int energy_px(int maxwidth, int quantization) - { - int emax = getMenu().field(5); - int k = ((maxwidth * getMenu().field(1) * 9) / 8) / ((emax>0?emax:1)+1); - k = (k >= maxwidth-2) ? maxwidth : k; - if(quantization > 0) k = ((k+(quantization/2))/quantization) * quantization; - return k; - } - } } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdFenceBlock.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdFenceBlock.java index f2de6d8..c9cf53c 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdFenceBlock.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdFenceBlock.java @@ -8,23 +8,25 @@ */ package dev.zontreck.engineerdecor.blocks; +import dev.zontreck.libzontreck.edlibmc.StandardFenceBlock; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; -import dev.zontreck.engineerdecor.libmc.StandardFenceBlock; -public class EdFenceBlock extends StandardFenceBlock -{ - public EdFenceBlock(long config, BlockBehaviour.Properties properties) - { super(config, properties); } +public class EdFenceBlock extends StandardFenceBlock { + public EdFenceBlock(long config, BlockBehaviour.Properties properties) { + super(config, properties); + } - public EdFenceBlock(long config, BlockBehaviour.Properties properties, double pole_width, double pole_height, double side_width, double side_min_y, double side_max_low_y, double side_max_tall_y) - { super(config, properties, pole_width, pole_height, side_width, side_min_y, side_max_low_y, side_max_tall_y); } + public EdFenceBlock(long config, BlockBehaviour.Properties properties, double pole_width, double pole_height, double side_width, double side_min_y, double side_max_low_y, double side_max_tall_y) { + super(config, properties, pole_width, pole_height, side_width, side_min_y, side_max_low_y, side_max_tall_y); + } - @Override - protected boolean attachesTo(BlockState facingState, LevelReader world, BlockPos facingPos, Direction side) - { return ((facingState.getBlock()) instanceof EdDoubleGateBlock) || super.attachesTo(facingState, world, facingPos, side); } + @Override + protected boolean attachesTo(BlockState facingState, LevelReader world, BlockPos facingPos, Direction side) { + return ((facingState.getBlock()) instanceof EdDoubleGateBlock) || super.attachesTo(facingState, world, facingPos, side); + } } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdFloorGratingBlock.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdFloorGratingBlock.java index 45ad2bc..ac64240 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdFloorGratingBlock.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdFloorGratingBlock.java @@ -8,6 +8,7 @@ */ package dev.zontreck.engineerdecor.blocks; +import dev.zontreck.libzontreck.edlibmc.StandardBlocks; import net.minecraft.core.BlockPos; import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; @@ -19,55 +20,56 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; -import dev.zontreck.engineerdecor.libmc.StandardBlocks; import javax.annotation.Nullable; -public class EdFloorGratingBlock extends StandardBlocks.WaterLoggable -{ - public EdFloorGratingBlock(long config, BlockBehaviour.Properties builder, final AABB unrotatedAABB) - { super(config, builder, unrotatedAABB); } - - @Override - public RenderTypeHint getRenderTypeHint() - { return RenderTypeHint.CUTOUT; } - - @Override - public boolean propagatesSkylightDown(BlockState state, BlockGetter reader, BlockPos pos) - { return true; } - - @Override - public boolean isValidSpawn(BlockState state, BlockGetter world, BlockPos pos, SpawnPlacements.Type type, @Nullable EntityType entityType) - { return false; } - - @Override - @SuppressWarnings("deprecation") - public void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) - { - if(!(entity instanceof ItemEntity)) return; - final boolean colliding = ((entity.position().y-pos.getY()) > 0.7); - if(colliding || (entity.getDeltaMovement().y() > 0)) { - double x = pos.getX() + 0.5; - double y = Mth.clamp(entity.position().y-0.3, pos.getY(), pos.getY()+0.6); - double z = pos.getZ() + 0.5; - double vx = entity.getDeltaMovement().x(); - double vy = entity.getDeltaMovement().y(); - double vz = entity.getDeltaMovement().z(); - if(colliding) { - vx = 0; - vy = -0.3; - vz = 0; - if((entity.position().y-pos.getY()) > 0.8) y = pos.getY() + 0.6; - entity.xo = x+0.1; - entity.yo = y+0.1; - entity.zo = z+0.1; - } - vy = Mth.clamp(vy, -0.3, 0); - entity.setDeltaMovement(vx, vy, vz); - entity.fallDistance = 0; - entity.teleportTo(x,y,z); +public class EdFloorGratingBlock extends StandardBlocks.WaterLoggable { + public EdFloorGratingBlock(long config, BlockBehaviour.Properties builder, final AABB unrotatedAABB) { + super(config, builder, unrotatedAABB); + } + + @Override + public RenderTypeHint getRenderTypeHint() { + return RenderTypeHint.CUTOUT; + } + + @Override + public boolean propagatesSkylightDown(BlockState state, BlockGetter reader, BlockPos pos) { + return true; + } + + @Override + public boolean isValidSpawn(BlockState state, BlockGetter world, BlockPos pos, SpawnPlacements.Type type, @Nullable EntityType entityType) { + return false; + } + + @Override + @SuppressWarnings("deprecation") + public void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { + if (!(entity instanceof ItemEntity)) return; + final boolean colliding = ((entity.position().y - pos.getY()) > 0.7); + if (colliding || (entity.getDeltaMovement().y() > 0)) { + double x = pos.getX() + 0.5; + double y = Mth.clamp(entity.position().y - 0.3, pos.getY(), pos.getY() + 0.6); + double z = pos.getZ() + 0.5; + double vx = entity.getDeltaMovement().x(); + double vy = entity.getDeltaMovement().y(); + double vz = entity.getDeltaMovement().z(); + if (colliding) { + vx = 0; + vy = -0.3; + vz = 0; + if ((entity.position().y - pos.getY()) > 0.8) y = pos.getY() + 0.6; + entity.xo = x + 0.1; + entity.yo = y + 0.1; + entity.zo = z + 0.1; + } + vy = Mth.clamp(vy, -0.3, 0); + entity.setDeltaMovement(vx, vy, vz); + entity.fallDistance = 0; + entity.teleportTo(x, y, z); + } } - } } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdFluidBarrel.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdFluidBarrel.java index 857db4f..7a6623c 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdFluidBarrel.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdFluidBarrel.java @@ -8,6 +8,9 @@ */ package dev.zontreck.engineerdecor.blocks; +import dev.zontreck.engineerdecor.ModConfig; +import dev.zontreck.engineerdecor.ModContent; +import dev.zontreck.libzontreck.edlibmc.*; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; @@ -29,7 +32,7 @@ import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.SignalGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockBehaviour; @@ -50,349 +53,355 @@ import net.minecraftforge.common.capabilities.ICapabilityProvider; import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.IFluidHandler; -import dev.zontreck.engineerdecor.ModConfig; -import dev.zontreck.engineerdecor.ModContent; -import dev.zontreck.engineerdecor.libmc.StandardBlocks; -import dev.zontreck.engineerdecor.libmc.StandardEntityBlocks; -import dev.zontreck.engineerdecor.libmc.Auxiliaries; -import dev.zontreck.engineerdecor.libmc.Fluidics; -import dev.zontreck.engineerdecor.libmc.Overlay; import javax.annotation.Nullable; import java.util.ArrayList; import java.util.List; -public class EdFluidBarrel -{ - //-------------------------------------------------------------------------------------------------------------------- - // Config - //-------------------------------------------------------------------------------------------------------------------- +public class EdFluidBarrel { + //-------------------------------------------------------------------------------------------------------------------- + // Config + //-------------------------------------------------------------------------------------------------------------------- - private static int capacity_ = 12000; - private static int item_fluid_handler_transfer_rate_ = 1000; - private static int tile_fluid_handler_transfer_rate_ = 1000; + private static int capacity_ = 12000; + private static int item_fluid_handler_transfer_rate_ = 1000; + private static int tile_fluid_handler_transfer_rate_ = 1000; - public static void on_config(int tank_capacity, int transfer_rate) - { - capacity_ = Mth.clamp(tank_capacity, 2000, 64000); - tile_fluid_handler_transfer_rate_ = Mth.clamp(tank_capacity, 50, 4096); - item_fluid_handler_transfer_rate_ = tile_fluid_handler_transfer_rate_; - ModConfig.log("Config fluid barrel: capacity:" + capacity_ + "mb, transfer-rate:" + tile_fluid_handler_transfer_rate_ + "mb/t."); - } - - //-------------------------------------------------------------------------------------------------------------------- - // Block - //-------------------------------------------------------------------------------------------------------------------- - - public static class FluidBarrelBlock extends StandardBlocks.DirectedWaterLoggable implements StandardEntityBlocks.IStandardEntityBlock - { - public static final int FILL_LEVEL_MAX = 4; - public static final IntegerProperty FILL_LEVEL = IntegerProperty.create("level", 0, FILL_LEVEL_MAX); - - public FluidBarrelBlock(long config, BlockBehaviour.Properties builder, final AABB[] unrotatedAABB) - { - super(config, builder, unrotatedAABB); - registerDefaultState(super.defaultBlockState().setValue(FACING, Direction.UP).setValue(FILL_LEVEL, 0)); + public static void on_config(int tank_capacity, int transfer_rate) { + capacity_ = Mth.clamp(tank_capacity, 2000, 64000); + tile_fluid_handler_transfer_rate_ = Mth.clamp(tank_capacity, 50, 4096); + item_fluid_handler_transfer_rate_ = tile_fluid_handler_transfer_rate_; + ModConfig.log("Config fluid barrel: capacity:" + capacity_ + "mb, transfer-rate:" + tile_fluid_handler_transfer_rate_ + "mb/t."); } - @Override - public boolean isBlockEntityTicking(Level world, BlockState state) - { return true; } + //-------------------------------------------------------------------------------------------------------------------- + // Block + //-------------------------------------------------------------------------------------------------------------------- - @Override - public boolean hasDynamicDropList() - { return true; } + public static class FluidBarrelBlock extends StandardBlocks.DirectedWaterLoggable implements StandardEntityBlocks.IStandardEntityBlock { + public static final int FILL_LEVEL_MAX = 4; + public static final IntegerProperty FILL_LEVEL = IntegerProperty.create("level", 0, FILL_LEVEL_MAX); - @Override - public List dropList(BlockState state, Level world, final BlockEntity te, boolean explosion) - { - final List stacks = new ArrayList<>(); - if(world.isClientSide) return stacks; - if(!(te instanceof FluidBarrelTileEntity)) return stacks; - ItemStack stack = new ItemStack(this, 1); - CompoundTag te_nbt = ((FluidBarrelTileEntity) te).clear_getnbt(); - if(!te_nbt.isEmpty()) { - CompoundTag nbt = new CompoundTag(); - nbt.put("tedata", te_nbt); - stack.setTag(nbt); - } - stacks.add(stack); - return stacks; - } - - @Override - @OnlyIn(Dist.CLIENT) - public void appendHoverText(final ItemStack stack, @Nullable BlockGetter world, List tooltip, TooltipFlag flag) - { - if((!(stack.getItem() instanceof FluidBarrelItem)) || (Auxiliaries.Tooltip.helpCondition())) { - super.appendHoverText(stack, world, tooltip, flag); return; - } - FluidStack fs = FluidBarrelItem.getFluid(stack); - if(!fs.isEmpty()) { - tooltip.add(Auxiliaries.localizable(getDescriptionId()+".status.tip", Integer.toString(fs.getAmount()), Integer.toString(capacity_), Component.translatable(fs.getTranslationKey()))); - } else { - tooltip.add(Auxiliaries.localizable(getDescriptionId()+".status.tip.empty", "0", Integer.toString(capacity_))); - } - if(!Auxiliaries.Tooltip.extendedTipCondition()) { - super.appendHoverText(stack, world, tooltip, flag); - } - } - - @Override - public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) - { return Shapes.block(); } - - @Override - protected void createBlockStateDefinition(StateDefinition.Builder builder) - { super.createBlockStateDefinition(builder); builder.add(FILL_LEVEL); } - - @Override - @Nullable - public BlockState getStateForPlacement(BlockPlaceContext context) - { - BlockState state = super.getStateForPlacement(context); - if(!context.getPlayer().isShiftKeyDown()) state = state.setValue(FACING, Direction.UP); - return state; - } - - @Override - public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) - { - if(world.isClientSide) return; - if((!stack.hasTag()) || (!stack.getTag().contains("tedata"))) return; - CompoundTag te_nbt = stack.getTag().getCompound("tedata"); - if(te_nbt.isEmpty()) return; - final BlockEntity te = world.getBlockEntity(pos); - if(!(te instanceof FluidBarrelTileEntity)) return; - ((FluidBarrelTileEntity)te).readnbt(te_nbt); - te.setChanged(); - world.scheduleTick(pos, this, 4); - } - - @Override - @SuppressWarnings("deprecation") - public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult) - { - if(player.getItemInHand(hand).getItem() == asItem()) return InteractionResult.PASS; // Pass that to block placement. - if(world.isClientSide()) return InteractionResult.SUCCESS; - if(!(world.getBlockEntity(pos) instanceof final FluidBarrelTileEntity te)) return InteractionResult.FAIL; - if(!te.handlePlayerInteraction(state, world, pos, player, hand)) return InteractionResult.PASS; - world.scheduleTick(pos, this, 4); - return InteractionResult.CONSUME; - } - - @Override - @SuppressWarnings("deprecation") - public boolean hasAnalogOutputSignal(BlockState state) - { return true; } - - @Override - @SuppressWarnings("deprecation") - public int getAnalogOutputSignal(BlockState state, Level world, BlockPos pos) - { - BlockEntity te = world.getBlockEntity(pos); - if(!(te instanceof FluidBarrelTileEntity)) return 0; - return (int)Mth.clamp(((FluidBarrelTileEntity)te).getNormalizedFillLevel() * 15, 0, 15); - } - - @Override - public boolean shouldCheckWeakPower(BlockState state, LevelReader world, BlockPos pos, Direction side) - { return false; } - - } - - //-------------------------------------------------------------------------------------------------------------------- - // Tile entity - //-------------------------------------------------------------------------------------------------------------------- - - public static class FluidBarrelTileEntity extends StandardEntityBlocks.StandardBlockEntity implements ICapabilityProvider - { - private final int TICK_INTERVAL = 10; - private int tick_timer_ = 0; - private final Fluidics.Tank tank_ = (new Fluidics.Tank(capacity_)).setInteractionNotifier((t,d)->on_tank_changed()); - private final LazyOptional fluid_handler_ = tank_.createFluidHandler(); - - public FluidBarrelTileEntity(BlockPos pos, BlockState state) - { super(ModContent.getBlockEntityTypeOfBlock(state.getBlock()), pos, state); } - - public void readnbt(CompoundTag nbt) - { tank_.load(nbt); } - - public CompoundTag writenbt(CompoundTag nbt) - { tank_.save(nbt); return nbt; } - - public boolean handlePlayerInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand) - { - if(world.isClientSide()) return false; - { - Tuple transferred = Fluidics.manualTrackedFluidHandlerInteraction(world, pos, null, player, hand); - if(transferred==null) { - world.playSound(null, pos, SoundEvents.IRON_TRAPDOOR_OPEN, SoundSource.BLOCKS, 0.2f, 0.02f); - } else if(transferred.getB() > 0) { - SoundEvent se = (transferred.getA()==Fluids.LAVA) ? SoundEvents.BUCKET_EMPTY_LAVA: SoundEvents.BUCKET_EMPTY; - world.playSound(null, pos, se, SoundSource.BLOCKS, 1f, 1f); - } else { - SoundEvent se = (transferred.getA()==Fluids.LAVA) ? SoundEvents.BUCKET_FILL_LAVA : SoundEvents.BUCKET_FILL; - world.playSound(null, pos, se, SoundSource.BLOCKS, 1f, 1f); + public FluidBarrelBlock(long config, BlockBehaviour.Properties builder, final AABB[] unrotatedAABB) { + super(config, builder, unrotatedAABB); + registerDefaultState(super.defaultBlockState().setValue(FACING, Direction.UP).setValue(FILL_LEVEL, 0)); } - } - { - int vol = tank_.getFluidAmount(); - int cap = tank_.getCapacity(); - String name = (Component.translatable(tank_.getFluid().getTranslationKey())).getString(); - if((vol>0) && (cap>0)) { - Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.fluid_barrel.status", Integer.toString(vol), Integer.toString(cap), name)); - } else { - Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.fluid_barrel.status.empty", Integer.toString(vol), Integer.toString(cap))); + + @Override + public boolean isBlockEntityTicking(Level world, BlockState state) { + return true; } - } - return true; + + @Override + public boolean hasDynamicDropList() { + return true; + } + + @Override + public List dropList(BlockState state, Level world, final BlockEntity te, boolean explosion) { + final List stacks = new ArrayList<>(); + if (world.isClientSide) return stacks; + if (!(te instanceof FluidBarrelTileEntity)) return stacks; + ItemStack stack = new ItemStack(this, 1); + CompoundTag te_nbt = ((FluidBarrelTileEntity) te).clear_getnbt(); + if (!te_nbt.isEmpty()) { + CompoundTag nbt = new CompoundTag(); + nbt.put("tedata", te_nbt); + stack.setTag(nbt); + } + stacks.add(stack); + return stacks; + } + + @Override + @OnlyIn(Dist.CLIENT) + public void appendHoverText(final ItemStack stack, @Nullable BlockGetter world, List tooltip, TooltipFlag flag) { + if ((!(stack.getItem() instanceof FluidBarrelItem)) || (Auxiliaries.Tooltip.helpCondition())) { + super.appendHoverText(stack, world, tooltip, flag); + return; + } + FluidStack fs = FluidBarrelItem.getFluid(stack); + if (!fs.isEmpty()) { + tooltip.add(Auxiliaries.localizable(getDescriptionId() + ".status.tip", Integer.toString(fs.getAmount()), Integer.toString(capacity_), Component.translatable(fs.getTranslationKey()))); + } else { + tooltip.add(Auxiliaries.localizable(getDescriptionId() + ".status.tip.empty", "0", Integer.toString(capacity_))); + } + if (!Auxiliaries.Tooltip.extendedTipCondition()) { + super.appendHoverText(stack, world, tooltip, flag); + } + } + + @Override + public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) { + return Shapes.block(); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + super.createBlockStateDefinition(builder); + builder.add(FILL_LEVEL); + } + + @Override + @Nullable + public BlockState getStateForPlacement(BlockPlaceContext context) { + BlockState state = super.getStateForPlacement(context); + if (!context.getPlayer().isShiftKeyDown()) state = state.setValue(FACING, Direction.UP); + return state; + } + + @Override + public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) { + if (world.isClientSide) return; + if ((!stack.hasTag()) || (!stack.getTag().contains("tedata"))) return; + CompoundTag te_nbt = stack.getTag().getCompound("tedata"); + if (te_nbt.isEmpty()) return; + final BlockEntity te = world.getBlockEntity(pos); + if (!(te instanceof FluidBarrelTileEntity)) return; + ((FluidBarrelTileEntity) te).readnbt(te_nbt); + te.setChanged(); + world.scheduleTick(pos, this, 4); + } + + @Override + @SuppressWarnings("deprecation") + public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult) { + if (player.getItemInHand(hand).getItem() == asItem()) + return InteractionResult.PASS; // Pass that to block placement. + if (world.isClientSide()) return InteractionResult.SUCCESS; + if (!(world.getBlockEntity(pos) instanceof final FluidBarrelTileEntity te)) return InteractionResult.FAIL; + if (!te.handlePlayerInteraction(state, world, pos, player, hand)) return InteractionResult.PASS; + world.scheduleTick(pos, this, 4); + return InteractionResult.CONSUME; + } + + @Override + @SuppressWarnings("deprecation") + public boolean hasAnalogOutputSignal(BlockState state) { + return true; + } + + @Override + @SuppressWarnings("deprecation") + public int getAnalogOutputSignal(BlockState state, Level world, BlockPos pos) { + BlockEntity te = world.getBlockEntity(pos); + if (!(te instanceof FluidBarrelTileEntity)) return 0; + return (int) Mth.clamp(((FluidBarrelTileEntity) te).getNormalizedFillLevel() * 15, 0, 15); + } + + @Override + public boolean shouldCheckWeakPower(BlockState state, SignalGetter level, BlockPos pos, Direction side) { + return false; + } + } - public double getNormalizedFillLevel() - { return (tank_.isEmpty()) ? (0) : ((double)tank_.getFluidAmount()/(double)tank_.getCapacity()); } + //-------------------------------------------------------------------------------------------------------------------- + // Tile entity + //-------------------------------------------------------------------------------------------------------------------- - protected void on_tank_changed() - { if(tick_timer_ > 2) tick_timer_ = 2; } + public static class FluidBarrelTileEntity extends StandardEntityBlocks.StandardBlockEntity implements ICapabilityProvider { + private final int TICK_INTERVAL = 10; + private int tick_timer_ = 0; + private final Fluidics.Tank tank_ = (new Fluidics.Tank(capacity_)).setInteractionNotifier((t, d) -> on_tank_changed()); + private final LazyOptional fluid_handler_ = tank_.createFluidHandler(); - // BlockEntity ------------------------------------------------------------------------------ + public FluidBarrelTileEntity(BlockPos pos, BlockState state) { + super(ModContent.getBlockEntityTypeOfBlock(state.getBlock()), pos, state); + } - @Override - public void load(CompoundTag nbt) - { super.load(nbt); readnbt(nbt); } + public void readnbt(CompoundTag nbt) { + tank_.load(nbt); + } - @Override - protected void saveAdditional(CompoundTag nbt) - { super.saveAdditional(nbt); writenbt(nbt); } + public CompoundTag writenbt(CompoundTag nbt) { + tank_.save(nbt); + return nbt; + } - @Override - public void setRemoved() - { super.setRemoved(); fluid_handler_.invalidate(); } + public boolean handlePlayerInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand) { + if (world.isClientSide()) return false; + { + Tuple transferred = Fluidics.manualTrackedFluidHandlerInteraction(world, pos, null, player, hand); + if (transferred == null) { + world.playSound(null, pos, SoundEvents.IRON_TRAPDOOR_OPEN, SoundSource.BLOCKS, 0.2f, 0.02f); + } else if (transferred.getB() > 0) { + SoundEvent se = (transferred.getA() == Fluids.LAVA) ? SoundEvents.BUCKET_EMPTY_LAVA : SoundEvents.BUCKET_EMPTY; + world.playSound(null, pos, se, SoundSource.BLOCKS, 1f, 1f); + } else { + SoundEvent se = (transferred.getA() == Fluids.LAVA) ? SoundEvents.BUCKET_FILL_LAVA : SoundEvents.BUCKET_FILL; + world.playSound(null, pos, se, SoundSource.BLOCKS, 1f, 1f); + } + } + { + int vol = tank_.getFluidAmount(); + int cap = tank_.getCapacity(); + String name = (Component.translatable(tank_.getFluid().getTranslationKey())).getString(); + if ((vol > 0) && (cap > 0)) { + Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.fluid_barrel.status", Integer.toString(vol), Integer.toString(cap), name)); + } else { + Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.fluid_barrel.status.empty", Integer.toString(vol), Integer.toString(cap))); + } + } + return true; + } - public CompoundTag clear_getnbt() - { return tank_.save(new CompoundTag()); } + public double getNormalizedFillLevel() { + return (tank_.isEmpty()) ? (0) : ((double) tank_.getFluidAmount() / (double) tank_.getCapacity()); + } - // ICapabilityProvider -------------------------------------------------------------------- + protected void on_tank_changed() { + if (tick_timer_ > 2) tick_timer_ = 2; + } + + // BlockEntity ------------------------------------------------------------------------------ + + @Override + public void load(CompoundTag nbt) { + super.load(nbt); + readnbt(nbt); + } + + @Override + protected void saveAdditional(CompoundTag nbt) { + super.saveAdditional(nbt); + writenbt(nbt); + } + + @Override + public void setRemoved() { + super.setRemoved(); + fluid_handler_.invalidate(); + } + + public CompoundTag clear_getnbt() { + return tank_.save(new CompoundTag()); + } + + // ICapabilityProvider -------------------------------------------------------------------- + + @Override + public LazyOptional getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable Direction facing) { + if (capability == ForgeCapabilities.FLUID_HANDLER) return fluid_handler_.cast(); + return super.getCapability(capability, facing); + } + + // Tick -------------------------------------------------------------------- + + private boolean transfer_down() { + if (tank_.isEmpty()) return false; + final IFluidHandler fh = Fluidics.handler(level, worldPosition.below(), Direction.UP); + if (fh == null) return false; + final FluidStack fs = tank_.getFluid().copy(); + if (fs.getAmount() > tile_fluid_handler_transfer_rate_) fs.setAmount(tile_fluid_handler_transfer_rate_); + final int nfilled = fh.fill(fs, IFluidHandler.FluidAction.EXECUTE); + if (nfilled <= 0) return false; + tank_.drain(nfilled, IFluidHandler.FluidAction.EXECUTE); + return true; + } + + public void tick() { + if ((level.isClientSide()) || (--tick_timer_ >= 0)) return; + tick_timer_ = TICK_INTERVAL; + final BlockState state = getBlockState(); + final Block block = state.getBlock(); + if (!(block instanceof FluidBarrelBlock)) return; + if (state.getValue(FluidBarrelBlock.FACING).getAxis().isVertical()) + transfer_down(); // tick_timer_ ==> 1 if something was transferred, otherwise no need to waste CPU + double norm_level = getNormalizedFillLevel(); + int fill_level = (norm_level <= 0) ? 0 : ((int) Mth.clamp((norm_level * FluidBarrelBlock.FILL_LEVEL_MAX) + 0.5, 1, FluidBarrelBlock.FILL_LEVEL_MAX)); + if (fill_level != state.getValue(FluidBarrelBlock.FILL_LEVEL)) { + level.setBlock(worldPosition, state.setValue(FluidBarrelBlock.FILL_LEVEL, fill_level), 2); + level.updateNeighborsAt(worldPosition, block); + } + } - @Override - public LazyOptional getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable Direction facing) - { - if(capability == ForgeCapabilities.FLUID_HANDLER) return fluid_handler_.cast(); - return super.getCapability(capability, facing); } - // Tick -------------------------------------------------------------------- + //-------------------------------------------------------------------------------------------------------------------- + // Block item + //-------------------------------------------------------------------------------------------------------------------- - private boolean transfer_down() - { - if(tank_.isEmpty()) return false; - final IFluidHandler fh = Fluidics.handler(level, worldPosition.below(), Direction.UP); - if(fh==null) return false; - final FluidStack fs = tank_.getFluid().copy(); - if(fs.getAmount() > tile_fluid_handler_transfer_rate_) fs.setAmount(tile_fluid_handler_transfer_rate_); - final int nfilled = fh.fill(fs, IFluidHandler.FluidAction.EXECUTE); - if(nfilled <= 0) return false; - tank_.drain(nfilled, IFluidHandler.FluidAction.EXECUTE); - return true; + public static class FluidBarrelItem extends BlockItem { + public FluidBarrelItem(Block block, Item.Properties builder) { + super(block, builder); + } + + private static CompoundTag read_fluid_nbt(ItemStack stack) { + if ((!stack.hasTag()) || (!stack.getTag().contains("tedata"))) return new CompoundTag(); + final CompoundTag nbt = stack.getTag().getCompound("tedata"); + if (!nbt.contains("tank", Tag.TAG_COMPOUND)) return new CompoundTag(); + return nbt.getCompound("tank"); + } + + private static void write_fluid_nbt(ItemStack stack, CompoundTag fluid_nbt) { + if ((fluid_nbt == null) || (fluid_nbt.isEmpty())) { + if ((!stack.hasTag()) || (!stack.getTag().contains("tedata", Tag.TAG_COMPOUND))) return; + final CompoundTag tag = stack.getTag(); + final CompoundTag tedata = tag.getCompound("tedata"); + if (tedata.contains("tank")) tedata.remove("tank"); + if (tedata.isEmpty()) tag.remove("tedata"); + stack.setTag(tag.isEmpty() ? null : tag); + } else { + CompoundTag tag = stack.getTag(); + if (tag == null) tag = new CompoundTag(); + CompoundTag tedata = tag.getCompound("tedata"); + if (tedata == null) tedata = new CompoundTag(); + tedata.put("tank", fluid_nbt); + tag.put("tedata", tedata); + stack.setTag(tag); + } + } + + public static FluidStack getFluid(ItemStack stack) { + final CompoundTag nbt = read_fluid_nbt(stack); + return (nbt.isEmpty()) ? (FluidStack.EMPTY) : (FluidStack.loadFluidStackFromNBT(nbt)); + } + + public static ItemStack setFluid(ItemStack stack, FluidStack fs) { + write_fluid_nbt(stack, fs.writeToNBT(new CompoundTag())); + return stack; + } + + @Override + public int getMaxStackSize(ItemStack stack) { + return (!getFluid(stack).isEmpty()) ? 1 : super.getMaxStackSize(stack); + } + + @Override + public boolean isBarVisible(ItemStack stack) { + return (!getFluid(stack).isEmpty()); + } + + @Override + public int getBarWidth(ItemStack stack) { + return (int) Math.round(13f * Mth.clamp(((double) (getFluid(stack).getAmount())) / ((double) capacity_), 0.0, 1.0)); + } + + @Override + public int getBarColor(ItemStack stack) { + return 0x336633; + } + + @Override + public ICapabilityProvider initCapabilities(ItemStack stack, @Nullable CompoundTag nbt) { + return new Fluidics.FluidContainerItemCapabilityWrapper(stack, capacity_, item_fluid_handler_transfer_rate_, FluidBarrelItem::read_fluid_nbt, FluidBarrelItem::write_fluid_nbt, e -> true); + } + + @Override + public boolean hasCraftingRemainingItem(ItemStack stack) { + return (stack.getCount() == 1) && (!getFluid(stack).isEmpty()); + } + + @Override + public ItemStack getCraftingRemainingItem(ItemStack stack) { + if (stack.getCount() != 1) return ItemStack.EMPTY; + FluidStack fs = getFluid(stack); + if (fs.getAmount() > 1000) fs.shrink(1000); + else fs = FluidStack.EMPTY; + return setFluid(stack, fs); + } } - public void tick() - { - if((level.isClientSide()) || (--tick_timer_>=0)) return; - tick_timer_ = TICK_INTERVAL; - final BlockState state = getBlockState(); - final Block block = state.getBlock(); - if(!(block instanceof FluidBarrelBlock)) return; - if(state.getValue(FluidBarrelBlock.FACING).getAxis().isVertical()) transfer_down(); // tick_timer_ ==> 1 if something was transferred, otherwise no need to waste CPU - double norm_level = getNormalizedFillLevel(); - int fill_level = (norm_level <= 0) ? 0 : ((int)Mth.clamp((norm_level * FluidBarrelBlock.FILL_LEVEL_MAX)+0.5, 1, FluidBarrelBlock.FILL_LEVEL_MAX)); - if(fill_level != state.getValue(FluidBarrelBlock.FILL_LEVEL)) { - level.setBlock(worldPosition, state.setValue(FluidBarrelBlock.FILL_LEVEL, fill_level), 2); - level.updateNeighborsAt(worldPosition, block); - } - } - - } - - //-------------------------------------------------------------------------------------------------------------------- - // Block item - //-------------------------------------------------------------------------------------------------------------------- - - public static class FluidBarrelItem extends BlockItem - { - public FluidBarrelItem(Block block, Item.Properties builder) - { super(block, builder); } - - private static CompoundTag read_fluid_nbt(ItemStack stack) - { - if((!stack.hasTag()) || (!stack.getTag().contains("tedata"))) return new CompoundTag(); - final CompoundTag nbt = stack.getTag().getCompound("tedata"); - if(!nbt.contains("tank", Tag.TAG_COMPOUND)) return new CompoundTag(); - return nbt.getCompound("tank"); - } - - private static void write_fluid_nbt(ItemStack stack, CompoundTag fluid_nbt) - { - if((fluid_nbt==null) || (fluid_nbt.isEmpty())) { - if((!stack.hasTag()) || (!stack.getTag().contains("tedata", Tag.TAG_COMPOUND))) return; - final CompoundTag tag = stack.getTag(); - final CompoundTag tedata = tag.getCompound("tedata"); - if(tedata.contains("tank")) tedata.remove("tank"); - if(tedata.isEmpty()) tag.remove("tedata"); - stack.setTag(tag.isEmpty() ? null : tag); - } else { - CompoundTag tag = stack.getTag(); - if(tag==null) tag = new CompoundTag(); - CompoundTag tedata = tag.getCompound("tedata"); - if(tedata==null) tedata = new CompoundTag(); - tedata.put("tank", fluid_nbt); - tag.put("tedata", tedata); - stack.setTag(tag); - } - } - - public static FluidStack getFluid(ItemStack stack) - { - final CompoundTag nbt = read_fluid_nbt(stack); - return (nbt.isEmpty()) ? (FluidStack.EMPTY) : (FluidStack.loadFluidStackFromNBT(nbt)); - } - - public static ItemStack setFluid(ItemStack stack, FluidStack fs) - { write_fluid_nbt(stack, fs.writeToNBT(new CompoundTag())); return stack; } - - @Override - public int getMaxStackSize(ItemStack stack) - { return (!getFluid(stack).isEmpty()) ? 1 : super.getMaxStackSize(stack); } - - @Override - public boolean isBarVisible(ItemStack stack) - { return (!getFluid(stack).isEmpty()); } - - @Override - public int getBarWidth(ItemStack stack) - { return (int)Math.round(13f * Mth.clamp(((double)(getFluid(stack).getAmount()))/((double)capacity_), 0.0, 1.0)); } - - @Override - public int getBarColor(ItemStack stack) - { return 0x336633; } - - @Override - public ICapabilityProvider initCapabilities(ItemStack stack, @Nullable CompoundTag nbt) - { return new Fluidics.FluidContainerItemCapabilityWrapper(stack, capacity_, item_fluid_handler_transfer_rate_, FluidBarrelItem::read_fluid_nbt, FluidBarrelItem::write_fluid_nbt, e->true); } - - @Override - public boolean hasCraftingRemainingItem(ItemStack stack) - { return (stack.getCount()==1) && (!getFluid(stack).isEmpty()); } - - @Override - public ItemStack getCraftingRemainingItem(ItemStack stack) - { - if(stack.getCount()!=1) return ItemStack.EMPTY; - FluidStack fs = getFluid(stack); - if(fs.getAmount() > 1000) fs.shrink(1000); else fs = FluidStack.EMPTY; - return setFluid(stack, fs); - } - } - } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdFluidFunnel.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdFluidFunnel.java index f1a8580..f2107d7 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdFluidFunnel.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdFluidFunnel.java @@ -10,6 +10,11 @@ */ package dev.zontreck.engineerdecor.blocks; +import dev.zontreck.engineerdecor.ModConfig; +import dev.zontreck.engineerdecor.ModContent; +import dev.zontreck.libzontreck.edlibmc.Fluidics; +import dev.zontreck.libzontreck.edlibmc.StandardBlocks; +import dev.zontreck.libzontreck.edlibmc.StandardEntityBlocks; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Vec3i; @@ -23,7 +28,7 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.SignalGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; @@ -43,364 +48,374 @@ import net.minecraftforge.common.capabilities.ForgeCapabilities; import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.IFluidHandler; -import dev.zontreck.engineerdecor.ModConfig; -import dev.zontreck.engineerdecor.ModContent; -import dev.zontreck.engineerdecor.libmc.StandardBlocks; -import dev.zontreck.engineerdecor.libmc.StandardEntityBlocks; -import dev.zontreck.engineerdecor.libmc.Fluidics; import javax.annotation.Nullable; import java.util.*; -public class EdFluidFunnel -{ +public class EdFluidFunnel { - private static boolean with_device_fluid_handler_collection = false; + private static boolean with_device_fluid_handler_collection = false; - public static void on_config(boolean with_tank_fluid_collection) - { - with_device_fluid_handler_collection = with_tank_fluid_collection; - ModConfig.log("Config fluid funnel: tank-fluid-collection:" + with_device_fluid_handler_collection + "."); - } - - //-------------------------------------------------------------------------------------------------------------------- - // Block - //-------------------------------------------------------------------------------------------------------------------- - - public static class FluidFunnelBlock extends StandardBlocks.Cutout implements StandardEntityBlocks.IStandardEntityBlock - { - public static final int FILL_LEVEL_MAX = 3; - public static final IntegerProperty FILL_LEVEL = IntegerProperty.create("level", 0, FILL_LEVEL_MAX); - - public FluidFunnelBlock(long config, BlockBehaviour.Properties builder, final AABB[] unrotatedAABB) - { super(config, builder, unrotatedAABB); } - - @Override - public boolean isBlockEntityTicking(Level world, BlockState state) - { return true; } - - @Override - public RenderTypeHint getRenderTypeHint() - { return RenderTypeHint.CUTOUT; } - - @Override - public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) - { return Shapes.block(); } - - @Override - protected void createBlockStateDefinition(StateDefinition.Builder builder) - { super.createBlockStateDefinition(builder); builder.add(FILL_LEVEL); } - - @Override - @Nullable - public BlockState getStateForPlacement(BlockPlaceContext context) - { return super.getStateForPlacement(context).setValue(FILL_LEVEL, 0); } - - @Override - @SuppressWarnings("deprecation") - public boolean hasAnalogOutputSignal(BlockState state) - { return true; } - - @Override - @SuppressWarnings("deprecation") - public int getAnalogOutputSignal(BlockState state, Level world, BlockPos pos) - { return Mth.clamp((state.getValue(FILL_LEVEL)*5), 0, 15); } - - @Override - public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) - { - if(world.isClientSide) return; - if((!stack.hasTag()) || (!stack.getTag().contains("tedata"))) return; - CompoundTag te_nbt = stack.getTag().getCompound("tedata"); - if(te_nbt.isEmpty()) return; - final BlockEntity te = world.getBlockEntity(pos); - if(!(te instanceof FluidFunnelTileEntity)) return; - ((FluidFunnelTileEntity)te).readnbt(te_nbt); - te.setChanged(); - world.setBlockAndUpdate(pos, state.setValue(FILL_LEVEL, 0)); + public static void on_config(boolean with_tank_fluid_collection) { + with_device_fluid_handler_collection = with_tank_fluid_collection; + ModConfig.log("Config fluid funnel: tank-fluid-collection:" + with_device_fluid_handler_collection + "."); } - @Override - public boolean hasDynamicDropList() - { return true; } + //-------------------------------------------------------------------------------------------------------------------- + // Block + //-------------------------------------------------------------------------------------------------------------------- - @Override - public List dropList(BlockState state, Level world, final BlockEntity te, boolean explosion) - { - final List stacks = new ArrayList<>(); - if(world.isClientSide) return stacks; - if(!(te instanceof FluidFunnelTileEntity)) return stacks; - if(!explosion) { - ItemStack stack = new ItemStack(this, 1); - CompoundTag te_nbt = new CompoundTag(); - ((FluidFunnelTileEntity)te).writenbt(te_nbt); - if(!te_nbt.isEmpty()) { - CompoundTag nbt = new CompoundTag(); - nbt.put("tedata", te_nbt); - stack.setTag(nbt); + public static class FluidFunnelBlock extends StandardBlocks.Cutout implements StandardEntityBlocks.IStandardEntityBlock { + public static final int FILL_LEVEL_MAX = 3; + public static final IntegerProperty FILL_LEVEL = IntegerProperty.create("level", 0, FILL_LEVEL_MAX); + + public FluidFunnelBlock(long config, BlockBehaviour.Properties builder, final AABB[] unrotatedAABB) { + super(config, builder, unrotatedAABB); } - stacks.add(stack); - } else { - stacks.add(new ItemStack(this, 1)); - } - return stacks; - } - @Override - @SuppressWarnings("deprecation") - public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult) - { - if(world.isClientSide) return InteractionResult.SUCCESS; - return (world.getBlockEntity(pos) instanceof FluidFunnelTileEntity) && Fluidics.manualFluidHandlerInteraction(player, hand, world, pos, rayTraceResult.getDirection()) ? InteractionResult.CONSUME : InteractionResult.FAIL; - } - - @Override - @SuppressWarnings("deprecation") - public void neighborChanged(BlockState state, Level world, BlockPos pos, Block block, BlockPos fromPos, boolean unused) - { BlockEntity te = world.getBlockEntity(pos); if(te instanceof FluidFunnelTileEntity) ((FluidFunnelTileEntity)te).block_changed(); } - - @Override - public boolean shouldCheckWeakPower(BlockState state, LevelReader world, BlockPos pos, Direction side) - { return false; } - } - - //-------------------------------------------------------------------------------------------------------------------- - // Tile entity - //-------------------------------------------------------------------------------------------------------------------- - - public static class FluidFunnelTileEntity extends StandardEntityBlocks.StandardBlockEntity - { - public static final int TANK_CAPACITY = 3000; - public static final int TICK_INTERVAL = 10; // ca 500ms - public static final int COLLECTION_INTERVAL = 40; // ca 2000ms, simulates suction delay and saves CPU when not drained. - public static final int MAX_TRACK_RADIUS = 16; - public static final int MAX_TRACKING_STEPS_PER_CYCLE = 72; - public static final int MAX_TRACKING_STEPS_PER_CYCLE_INTENSIVE = 1024; - public static final int MAX_TRACK_RADIUS_SQ = MAX_TRACK_RADIUS*MAX_TRACK_RADIUS; - public static final int INTENSIVE_SEARCH_TRIGGER_THRESHOLD = 16; - private int tick_timer_ = 0; - private int collection_timer_ = 0; - private int no_fluid_found_counter_ = 0; - private int intensive_search_counter_ = 0; - private int total_pick_counter_ = 0; - private BlockPos last_pick_pos_ = BlockPos.ZERO; - private ArrayList search_offsets_ = null; - private final Fluidics.Tank tank_ = new Fluidics.Tank(TANK_CAPACITY, 0, TANK_CAPACITY); - private final LazyOptional fluid_handler_ = tank_.createOutputFluidHandler(); - - public FluidFunnelTileEntity(BlockPos pos, BlockState state) - { super(ModContent.getBlockEntityTypeOfBlock(state.getBlock()), pos, state); } - - public void readnbt(CompoundTag nbt) - { - tank_.load(nbt); - } - - public void writenbt(CompoundTag nbt) - { - tank_.save(nbt); - } - - public void block_changed() - { tick_timer_ = TICK_INTERVAL; } - - // BlockEntity ----------------------------------------------------------------------------------------- - - @Override - public void load(CompoundTag nbt) - { super.load(nbt); readnbt(nbt); } - - @Override - protected void saveAdditional(CompoundTag nbt) - { super.saveAdditional(nbt); writenbt(nbt); } - - @Override - public void setRemoved() - { - super.setRemoved(); - fluid_handler_.invalidate(); - } - - // ICapabilityProvider / Output flow handler ---------------------------------------------------------- - - @Override - public LazyOptional getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable Direction facing) - { - if(capability == ForgeCapabilities.FLUID_HANDLER) return fluid_handler_.cast(); - return super.getCapability(capability, facing); - } - - // ----------------------------------------------------------------------------------------------------------------- - - private FluidState get_fluidstate(BlockPos pos) - { - return level.getFluidState(pos); - } - - private boolean try_pick(BlockPos pos, FluidState fluidstate) - { - if(!fluidstate.isSource()) return false; - IFluidHandler hnd = Fluidics.handler(level, pos, null); - FluidStack fs; - if(hnd != null) { - fs = hnd.drain(TANK_CAPACITY, IFluidHandler.FluidAction.EXECUTE); // IFluidBlock - } else { - fs = new FluidStack(fluidstate.getType(), 1000); - BlockState state = level.getBlockState(pos); - if(state.hasProperty(BlockStateProperties.WATERLOGGED)) { - level.setBlock(pos, state.setValue(BlockStateProperties.WATERLOGGED, false), 1|2); - } else { - level.setBlock(pos, Blocks.AIR.defaultBlockState(), 1|2); // ok we can't leave the block, that would be an infinite source of an unknown fluid. + @Override + public boolean isBlockEntityTicking(Level world, BlockState state) { + return true; } - } - if((fs==null) || (fs.isEmpty())) return false; // it's marked nonnull but I don't trust every modder - including meself ... - if(tank_.isEmpty()) { - tank_.setFluid(fs.copy()); - } else if(tank_.isFluidEqual(fs)) { - tank_.fill(fs, IFluidHandler.FluidAction.EXECUTE); - } else { - return false; - } - return true; - } - private boolean can_pick(BlockPos pos, FluidState fluidstate) - { - if(fluidstate.isSource()) return true; - final IFluidHandler hnd = Fluidics.handler(level, pos, null); - if(hnd == null) return false; - final FluidStack fs = hnd.drain(TANK_CAPACITY, IFluidHandler.FluidAction.SIMULATE); // don't trust that everyone returns nonnull - return ((fs!=null) && (!fs.isEmpty())) && (fluidstate.getType().isSame(fs.getFluid())); - } + @Override + public RenderTypeHint getRenderTypeHint() { + return RenderTypeHint.CUTOUT; + } - private void rebuild_search_offsets(boolean intensive) - { - search_offsets_ = new ArrayList<>(9); - search_offsets_.add(new Vec3i(0, 1, 0)); // up first - { - ArrayList ofs = new ArrayList<>(Arrays.asList(new Vec3i(-1, 0, 0), new Vec3i( 1, 0, 0), new Vec3i( 0, 0,-1), new Vec3i( 0, 0, 1))); - if(intensive || (total_pick_counter_ > 50)) Collections.shuffle(ofs); - search_offsets_.addAll(ofs); - } - if(intensive) { - ArrayList ofs = new ArrayList<>(Arrays.asList(new Vec3i(-1, 1, 0), new Vec3i( 1, 1, 0), new Vec3i( 0, 1,-1), new Vec3i( 0, 1, 1))); - Collections.shuffle(ofs); - search_offsets_.addAll(ofs); - } - } + @Override + public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) { + return Shapes.block(); + } - private boolean try_collect(final BlockPos collection_pos) - { - FluidState collection_fluidstate = get_fluidstate(collection_pos); - if(collection_fluidstate.isEmpty()) return false; - Fluid fluid_to_collect = collection_fluidstate.getType(); - if((!tank_.isEmpty()) && (!tank_.getFluid().getFluid().isSame(fluid_to_collect))) return false; - if(try_pick(collection_pos, collection_fluidstate)) { last_pick_pos_ = collection_pos; return true; } // Blocks directly always first. Allows water source blocks to recover/reflow to source blocks. - if((last_pick_pos_==null) || (last_pick_pos_.distSqr(collection_pos) > MAX_TRACK_RADIUS_SQ)) { last_pick_pos_ = collection_pos; search_offsets_ = null; } - BlockPos pos = last_pick_pos_; - HashSet checked = new HashSet<>(); - Stack trail = new Stack<>(); - trail.add(pos); - checked.add(pos); - int steps=0; - boolean intensive = (no_fluid_found_counter_ >= INTENSIVE_SEARCH_TRIGGER_THRESHOLD); - if(intensive) { no_fluid_found_counter_ = 0; ++intensive_search_counter_; } - if(search_offsets_ == null) rebuild_search_offsets(intensive); - int max = intensive ? MAX_TRACKING_STEPS_PER_CYCLE_INTENSIVE : MAX_TRACKING_STEPS_PER_CYCLE; - while(++steps <= max) { - int num_adjacent = 0; - for(int i=0; i builder) { + super.createBlockStateDefinition(builder); + builder.add(FILL_LEVEL); + } + + @Override + @Nullable + public BlockState getStateForPlacement(BlockPlaceContext context) { + return super.getStateForPlacement(context).setValue(FILL_LEVEL, 0); + } + + @Override + @SuppressWarnings("deprecation") + public boolean hasAnalogOutputSignal(BlockState state) { + return true; + } + + @Override + @SuppressWarnings("deprecation") + public int getAnalogOutputSignal(BlockState state, Level world, BlockPos pos) { + return Mth.clamp((state.getValue(FILL_LEVEL) * 5), 0, 15); + } + + @Override + public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) { + if (world.isClientSide) return; + if ((!stack.hasTag()) || (!stack.getTag().contains("tedata"))) return; + CompoundTag te_nbt = stack.getTag().getCompound("tedata"); + if (te_nbt.isEmpty()) return; + final BlockEntity te = world.getBlockEntity(pos); + if (!(te instanceof FluidFunnelTileEntity)) return; + ((FluidFunnelTileEntity) te).readnbt(te_nbt); + te.setChanged(); + world.setBlockAndUpdate(pos, state.setValue(FILL_LEVEL, 0)); + } + + @Override + public boolean hasDynamicDropList() { + return true; + } + + @Override + public List dropList(BlockState state, Level world, final BlockEntity te, boolean explosion) { + final List stacks = new ArrayList<>(); + if (world.isClientSide) return stacks; + if (!(te instanceof FluidFunnelTileEntity)) return stacks; + if (!explosion) { + ItemStack stack = new ItemStack(this, 1); + CompoundTag te_nbt = new CompoundTag(); + ((FluidFunnelTileEntity) te).writenbt(te_nbt); + if (!te_nbt.isEmpty()) { + CompoundTag nbt = new CompoundTag(); + nbt.put("tedata", te_nbt); + stack.setTag(nbt); + } + stacks.add(stack); + } else { + stacks.add(new ItemStack(this, 1)); } - if(try_pick(pos, fluidstate)) { - last_pick_pos_ = pos; - no_fluid_found_counter_ = 0; - search_offsets_ = null; - // probability reset, so it's not turteling too far away, mainly for large nether lava seas, not desert lakes. - if((++total_pick_counter_ > 50) && level.random.nextInt(10)==0) last_pick_pos_ = collection_pos; - //println("PASS " + steps + " - " + (pos.subtract(collection_pos))); - return true; - } - } + return stacks; } - if(trail.isEmpty()) break; // reset search - if(num_adjacent==0) pos = trail.pop(); - } - //println("FAIL=" + steps + " - " + (pos.subtract(collection_pos))); - //String s = new String(); for(BlockPos p:checked) s += "\n" + p; println(s); - if(intensive_search_counter_ > 2) level.removeBlock(pos, false); - last_pick_pos_ = collection_pos; - search_offsets_ = null; // try other search order - ++no_fluid_found_counter_; - return false; + + @Override + @SuppressWarnings("deprecation") + public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult) { + if (world.isClientSide) return InteractionResult.SUCCESS; + return (world.getBlockEntity(pos) instanceof FluidFunnelTileEntity) && Fluidics.manualFluidHandlerInteraction(player, hand, world, pos, rayTraceResult.getDirection()) ? InteractionResult.CONSUME : InteractionResult.FAIL; + } + + @Override + @SuppressWarnings("deprecation") + public void neighborChanged(BlockState state, Level world, BlockPos pos, Block block, BlockPos fromPos, boolean unused) { + BlockEntity te = world.getBlockEntity(pos); + if (te instanceof FluidFunnelTileEntity) ((FluidFunnelTileEntity) te).block_changed(); + } + + @Override + public boolean shouldCheckWeakPower(BlockState state, SignalGetter level, BlockPos pos, Direction side) { + return false; + } + } - public void tick() - { - if((level.isClientSide) || (--tick_timer_ > 0)) return; - tick_timer_ = TICK_INTERVAL; - collection_timer_ += TICK_INTERVAL; - final BlockState funnel_state = level.getBlockState(worldPosition); - if(!(funnel_state.getBlock() instanceof FluidFunnelBlock)) return; - boolean dirty = false; - // Collection - if((collection_timer_ >= COLLECTION_INTERVAL) && ((tank_==null) || (tank_.getFluidAmount() <= (TANK_CAPACITY-1000)))) { - collection_timer_ = 0; - if(!level.hasNeighborSignal(worldPosition)) { // redstone disable feature - if(last_pick_pos_==null) last_pick_pos_ = worldPosition.above(); - BlockEntity te = with_device_fluid_handler_collection ? (level.getBlockEntity(worldPosition.above())) : (null); - if(te != null) { - IFluidHandler fh = te.getCapability(ForgeCapabilities.FLUID_HANDLER, Direction.DOWN).orElse(null); - if(fh == null) { - te = null; - } else if(tank_.isEmpty()) { - FluidStack fs = fh.drain(1000, IFluidHandler.FluidAction.EXECUTE); - if((fs!=null) && (!fs.isEmpty())) tank_.setFluid(fs.copy()); - dirty = true; - } else if (!tank_.isFull()) { - FluidStack todrain = new FluidStack(tank_.getFluid(), Math.min(tank_.getCapacity()-tank_.getFluidAmount(), 1000)); - tank_.fill(fh.drain(todrain, IFluidHandler.FluidAction.EXECUTE), IFluidHandler.FluidAction.EXECUTE); - dirty = true; + //-------------------------------------------------------------------------------------------------------------------- + // Tile entity + //-------------------------------------------------------------------------------------------------------------------- + + public static class FluidFunnelTileEntity extends StandardEntityBlocks.StandardBlockEntity { + public static final int TANK_CAPACITY = 3000; + public static final int TICK_INTERVAL = 10; // ca 500ms + public static final int COLLECTION_INTERVAL = 40; // ca 2000ms, simulates suction delay and saves CPU when not drained. + public static final int MAX_TRACK_RADIUS = 16; + public static final int MAX_TRACKING_STEPS_PER_CYCLE = 72; + public static final int MAX_TRACKING_STEPS_PER_CYCLE_INTENSIVE = 1024; + public static final int MAX_TRACK_RADIUS_SQ = MAX_TRACK_RADIUS * MAX_TRACK_RADIUS; + public static final int INTENSIVE_SEARCH_TRIGGER_THRESHOLD = 16; + private final Fluidics.Tank tank_ = new Fluidics.Tank(TANK_CAPACITY, 0, TANK_CAPACITY); + private final LazyOptional fluid_handler_ = tank_.createOutputFluidHandler(); + private int tick_timer_ = 0; + private int collection_timer_ = 0; + private int no_fluid_found_counter_ = 0; + private int intensive_search_counter_ = 0; + private int total_pick_counter_ = 0; + private BlockPos last_pick_pos_ = BlockPos.ZERO; + private ArrayList search_offsets_ = null; + + public FluidFunnelTileEntity(BlockPos pos, BlockState state) { + super(ModContent.getBlockEntityTypeOfBlock(state.getBlock()), pos, state); + } + + public void readnbt(CompoundTag nbt) { + tank_.load(nbt); + } + + public void writenbt(CompoundTag nbt) { + tank_.save(nbt); + } + + public void block_changed() { + tick_timer_ = TICK_INTERVAL; + } + + // BlockEntity ----------------------------------------------------------------------------------------- + + @Override + public void load(CompoundTag nbt) { + super.load(nbt); + readnbt(nbt); + } + + @Override + protected void saveAdditional(CompoundTag nbt) { + super.saveAdditional(nbt); + writenbt(nbt); + } + + @Override + public void setRemoved() { + super.setRemoved(); + fluid_handler_.invalidate(); + } + + // ICapabilityProvider / Output flow handler ---------------------------------------------------------- + + @Override + public LazyOptional getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable Direction facing) { + if (capability == ForgeCapabilities.FLUID_HANDLER) return fluid_handler_.cast(); + return super.getCapability(capability, facing); + } + + // ----------------------------------------------------------------------------------------------------------------- + + private FluidState get_fluidstate(BlockPos pos) { + return level.getFluidState(pos); + } + + private boolean try_pick(BlockPos pos, FluidState fluidstate) { + if (!fluidstate.isSource()) return false; + IFluidHandler hnd = Fluidics.handler(level, pos, null); + FluidStack fs; + if (hnd != null) { + fs = hnd.drain(TANK_CAPACITY, IFluidHandler.FluidAction.EXECUTE); // IFluidBlock + } else { + fs = new FluidStack(fluidstate.getType(), 1000); + BlockState state = level.getBlockState(pos); + if (state.hasProperty(BlockStateProperties.WATERLOGGED)) { + level.setBlock(pos, state.setValue(BlockStateProperties.WATERLOGGED, false), 1 | 2); + } else { + level.setBlock(pos, Blocks.AIR.defaultBlockState(), 1 | 2); // ok we can't leave the block, that would be an infinite source of an unknown fluid. + } } - } - if(te==null) { - if(try_collect(worldPosition.above())) dirty = true; - } + if ((fs == null) || (fs.isEmpty())) + return false; // it's marked nonnull but I don't trust every modder - including meself ... + if (tank_.isEmpty()) { + tank_.setFluid(fs.copy()); + } else if (tank_.isFluidEqual(fs)) { + tank_.fill(fs, IFluidHandler.FluidAction.EXECUTE); + } else { + return false; + } + return true; } - } - // Gravity fluid transfer - if((tank_.getFluidAmount() >= 1000)) { - final IFluidHandler fh = Fluidics.handler(level, worldPosition.below(), Direction.UP); - if(fh != null) { - FluidStack fs = new FluidStack(tank_.getFluid().getFluid(), 1000); - int nfilled = Mth.clamp(fh.fill(fs, IFluidHandler.FluidAction.EXECUTE), 0, 1000); - tank_.drain(nfilled); - dirty = true; + + private boolean can_pick(BlockPos pos, FluidState fluidstate) { + if (fluidstate.isSource()) return true; + final IFluidHandler hnd = Fluidics.handler(level, pos, null); + if (hnd == null) return false; + final FluidStack fs = hnd.drain(TANK_CAPACITY, IFluidHandler.FluidAction.SIMULATE); // don't trust that everyone returns nonnull + return ((fs != null) && (!fs.isEmpty())) && (fluidstate.getType().isSame(fs.getFluid())); + } + + private void rebuild_search_offsets(boolean intensive) { + search_offsets_ = new ArrayList<>(9); + search_offsets_.add(new Vec3i(0, 1, 0)); // up first + { + ArrayList ofs = new ArrayList<>(Arrays.asList(new Vec3i(-1, 0, 0), new Vec3i(1, 0, 0), new Vec3i(0, 0, -1), new Vec3i(0, 0, 1))); + if (intensive || (total_pick_counter_ > 50)) Collections.shuffle(ofs); + search_offsets_.addAll(ofs); + } + if (intensive) { + ArrayList ofs = new ArrayList<>(Arrays.asList(new Vec3i(-1, 1, 0), new Vec3i(1, 1, 0), new Vec3i(0, 1, -1), new Vec3i(0, 1, 1))); + Collections.shuffle(ofs); + search_offsets_.addAll(ofs); + } + } + + private boolean try_collect(final BlockPos collection_pos) { + FluidState collection_fluidstate = get_fluidstate(collection_pos); + if (collection_fluidstate.isEmpty()) return false; + Fluid fluid_to_collect = collection_fluidstate.getType(); + if ((!tank_.isEmpty()) && (!tank_.getFluid().getFluid().isSame(fluid_to_collect))) return false; + if (try_pick(collection_pos, collection_fluidstate)) { + last_pick_pos_ = collection_pos; + return true; + } // Blocks directly always first. Allows water source blocks to recover/reflow to source blocks. + if ((last_pick_pos_ == null) || (last_pick_pos_.distSqr(collection_pos) > MAX_TRACK_RADIUS_SQ)) { + last_pick_pos_ = collection_pos; + search_offsets_ = null; + } + BlockPos pos = last_pick_pos_; + HashSet checked = new HashSet<>(); + Stack trail = new Stack<>(); + trail.add(pos); + checked.add(pos); + int steps = 0; + boolean intensive = (no_fluid_found_counter_ >= INTENSIVE_SEARCH_TRIGGER_THRESHOLD); + if (intensive) { + no_fluid_found_counter_ = 0; + ++intensive_search_counter_; + } + if (search_offsets_ == null) rebuild_search_offsets(intensive); + int max = intensive ? MAX_TRACKING_STEPS_PER_CYCLE_INTENSIVE : MAX_TRACKING_STEPS_PER_CYCLE; + while (++steps <= max) { + int num_adjacent = 0; + for (int i = 0; i < search_offsets_.size(); ++i) { + BlockPos p = pos.offset(search_offsets_.get(i)); + if (checked.contains(p)) continue; + checked.add(p); + ++steps; + FluidState fluidstate = get_fluidstate(p); + if (fluidstate.getType().isSame(fluid_to_collect)) { + ++num_adjacent; + pos = p; + trail.push(pos); + if (steps < MAX_TRACKING_STEPS_PER_CYCLE_INTENSIVE / 2) { + // check for same fluid above (only source blocks) + final int max_surface_search = (MAX_TRACKING_STEPS_PER_CYCLE_INTENSIVE / 2) - steps; + for (int k = 0; k < max_surface_search; ++k) { + FluidState fs = get_fluidstate(pos.above()); + if (!can_pick(pos.above(), fs)) break; + fluidstate = fs; + pos = pos.above(); + trail.push(pos); + } + } + if (try_pick(pos, fluidstate)) { + last_pick_pos_ = pos; + no_fluid_found_counter_ = 0; + search_offsets_ = null; + // probability reset, so it's not turteling too far away, mainly for large nether lava seas, not desert lakes. + if ((++total_pick_counter_ > 50) && level.random.nextInt(10) == 0) + last_pick_pos_ = collection_pos; + //println("PASS " + steps + " - " + (pos.subtract(collection_pos))); + return true; + } + } + } + if (trail.isEmpty()) break; // reset search + if (num_adjacent == 0) pos = trail.pop(); + } + //println("FAIL=" + steps + " - " + (pos.subtract(collection_pos))); + //String s = new String(); for(BlockPos p:checked) s += "\n" + p; println(s); + if (intensive_search_counter_ > 2) level.removeBlock(pos, false); + last_pick_pos_ = collection_pos; + search_offsets_ = null; // try other search order + ++no_fluid_found_counter_; + return false; + } + + public void tick() { + if ((level.isClientSide) || (--tick_timer_ > 0)) return; + tick_timer_ = TICK_INTERVAL; + collection_timer_ += TICK_INTERVAL; + final BlockState funnel_state = level.getBlockState(worldPosition); + if (!(funnel_state.getBlock() instanceof FluidFunnelBlock)) return; + boolean dirty = false; + // Collection + if ((collection_timer_ >= COLLECTION_INTERVAL) && ((tank_ == null) || (tank_.getFluidAmount() <= (TANK_CAPACITY - 1000)))) { + collection_timer_ = 0; + if (!level.hasNeighborSignal(worldPosition)) { // redstone disable feature + if (last_pick_pos_ == null) last_pick_pos_ = worldPosition.above(); + BlockEntity te = with_device_fluid_handler_collection ? (level.getBlockEntity(worldPosition.above())) : (null); + if (te != null) { + IFluidHandler fh = te.getCapability(ForgeCapabilities.FLUID_HANDLER, Direction.DOWN).orElse(null); + if (fh == null) { + te = null; + } else if (tank_.isEmpty()) { + FluidStack fs = fh.drain(1000, IFluidHandler.FluidAction.EXECUTE); + if ((fs != null) && (!fs.isEmpty())) tank_.setFluid(fs.copy()); + dirty = true; + } else if (!tank_.isFull()) { + FluidStack todrain = new FluidStack(tank_.getFluid(), Math.min(tank_.getCapacity() - tank_.getFluidAmount(), 1000)); + tank_.fill(fh.drain(todrain, IFluidHandler.FluidAction.EXECUTE), IFluidHandler.FluidAction.EXECUTE); + dirty = true; + } + } + if (te == null) { + if (try_collect(worldPosition.above())) dirty = true; + } + } + } + // Gravity fluid transfer + if ((tank_.getFluidAmount() >= 1000)) { + final IFluidHandler fh = Fluidics.handler(level, worldPosition.below(), Direction.UP); + if (fh != null) { + FluidStack fs = new FluidStack(tank_.getFluid().getFluid(), 1000); + int nfilled = Mth.clamp(fh.fill(fs, IFluidHandler.FluidAction.EXECUTE), 0, 1000); + tank_.drain(nfilled); + dirty = true; + } + } + // Block state + int fill_level = (tank_ == null) ? 0 : (Mth.clamp(tank_.getFluidAmount() / 1000, 0, FluidFunnelBlock.FILL_LEVEL_MAX)); + if (funnel_state.getValue(FluidFunnelBlock.FILL_LEVEL) != fill_level) + level.setBlock(worldPosition, funnel_state.setValue(FluidFunnelBlock.FILL_LEVEL, fill_level), 2 | 16); + if (dirty) setChanged(); } - } - // Block state - int fill_level = (tank_==null) ? 0 : (Mth.clamp(tank_.getFluidAmount()/1000,0, FluidFunnelBlock.FILL_LEVEL_MAX)); - if(funnel_state.getValue(FluidFunnelBlock.FILL_LEVEL) != fill_level) level.setBlock(worldPosition, funnel_state.setValue(FluidFunnelBlock.FILL_LEVEL, fill_level), 2|16); - if(dirty) setChanged(); } - } } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdFreezer.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdFreezer.java index 23ff41b..dc6305c 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdFreezer.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdFreezer.java @@ -9,6 +9,11 @@ */ package dev.zontreck.engineerdecor.blocks; +import dev.zontreck.engineerdecor.ModConfig; +import dev.zontreck.engineerdecor.ModContent; +import dev.zontreck.libzontreck.edlibmc.Fluidics; +import dev.zontreck.libzontreck.edlibmc.StandardBlocks; +import dev.zontreck.libzontreck.edlibmc.StandardEntityBlocks; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; @@ -25,7 +30,7 @@ import net.minecraft.world.item.Items; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.SignalGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockBehaviour; @@ -45,357 +50,375 @@ import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.energy.IEnergyStorage; import net.minecraftforge.fluids.capability.IFluidHandler; import net.minecraftforge.items.IItemHandler; -import dev.zontreck.engineerdecor.ModConfig; -import dev.zontreck.engineerdecor.ModContent; -import dev.zontreck.engineerdecor.libmc.StandardBlocks; -import dev.zontreck.engineerdecor.libmc.StandardEntityBlocks; -import dev.zontreck.engineerdecor.libmc.Fluidics; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.ArrayList; import java.util.List; -public class EdFreezer -{ - public static void on_config(int consumption, int cooldown_per_second) - { FreezerTileEntity.on_config(consumption, cooldown_per_second); } - - //-------------------------------------------------------------------------------------------------------------------- - // Block - //-------------------------------------------------------------------------------------------------------------------- - - public static class FreezerBlock extends StandardBlocks.Horizontal implements StandardEntityBlocks.IStandardEntityBlock - { - public static final int PHASE_MAX = 4; - public static final IntegerProperty PHASE = IntegerProperty.create("phase", 0, PHASE_MAX); - - public FreezerBlock(long config, BlockBehaviour.Properties builder, final AABB unrotatedAABB) - { super(config, builder, unrotatedAABB); } - - @Override - public boolean isBlockEntityTicking(Level world, BlockState state) - { return true; } - - @Override - public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) - { return Shapes.block(); } - - @Override - protected void createBlockStateDefinition(StateDefinition.Builder builder) - { super.createBlockStateDefinition(builder); builder.add(PHASE); } - - @Override - @Nullable - public BlockState getStateForPlacement(BlockPlaceContext context) - { return super.getStateForPlacement(context).setValue(PHASE, 0); } - - @Override - @SuppressWarnings("deprecation") - public boolean hasAnalogOutputSignal(BlockState state) - { return true; } - - @Override - @SuppressWarnings("deprecation") - public int getAnalogOutputSignal(BlockState state, Level world, BlockPos pos) - { return Mth.clamp((state.getValue(PHASE)*4), 0, 15); } - - @Override - public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) - {} - - @Override - public boolean hasDynamicDropList() - { return true; } - - @Override - public List dropList(BlockState state, Level world, BlockEntity te, boolean explosion) - { - final List stacks = new ArrayList<>(); - if(world.isClientSide) return stacks; - if(!(te instanceof FreezerTileEntity)) return stacks; - ((FreezerTileEntity)te).reset_process(); - stacks.add(new ItemStack(this, 1)); - return stacks; +public class EdFreezer { + public static void on_config(int consumption, int cooldown_per_second) { + FreezerTileEntity.on_config(consumption, cooldown_per_second); } - @Override - @SuppressWarnings("deprecation") - public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult) - { - if(player.isShiftKeyDown()) return InteractionResult.PASS; - if(world.isClientSide()) return InteractionResult.SUCCESS; - FreezerTileEntity te = getTe(world, pos); - if(te==null) return InteractionResult.FAIL; - final ItemStack stack = player.getItemInHand(hand); - boolean dirty = false; - if(Fluidics.manualFluidHandlerInteraction(world, pos, null, player, hand)) { - world.playSound(null, pos, SoundEvents.BUCKET_EMPTY, SoundSource.BLOCKS, 0.5f, 1.4f); - return InteractionResult.CONSUME; - } - if(stack.getItem()==Items.WATER_BUCKET) { - return InteractionResult.CONSUME; // would be already handled - } else if(stack.isEmpty()) { - ItemStack ice = te.getIceItem(true); - if(!ice.isEmpty()) { - player.addItem(ice); - world.playSound(null, pos, SoundEvents.ITEM_PICKUP, SoundSource.BLOCKS, 0.3f, 1.1f); - } else { - world.playSound(null, pos, SoundEvents.IRON_TRAPDOOR_OPEN, SoundSource.BLOCKS, 0.2f, 0.02f); + //-------------------------------------------------------------------------------------------------------------------- + // Block + //-------------------------------------------------------------------------------------------------------------------- + + public static class FreezerBlock extends StandardBlocks.Horizontal implements StandardEntityBlocks.IStandardEntityBlock { + public static final int PHASE_MAX = 4; + public static final IntegerProperty PHASE = IntegerProperty.create("phase", 0, PHASE_MAX); + + public FreezerBlock(long config, BlockBehaviour.Properties builder, final AABB unrotatedAABB) { + super(config, builder, unrotatedAABB); + } + + @Override + public boolean isBlockEntityTicking(Level world, BlockState state) { + return true; + } + + @Override + public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) { + return Shapes.block(); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + super.createBlockStateDefinition(builder); + builder.add(PHASE); + } + + @Override + @Nullable + public BlockState getStateForPlacement(BlockPlaceContext context) { + return super.getStateForPlacement(context).setValue(PHASE, 0); + } + + @Override + @SuppressWarnings("deprecation") + public boolean hasAnalogOutputSignal(BlockState state) { + return true; + } + + @Override + @SuppressWarnings("deprecation") + public int getAnalogOutputSignal(BlockState state, Level world, BlockPos pos) { + return Mth.clamp((state.getValue(PHASE) * 4), 0, 15); + } + + @Override + public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) { + } + + @Override + public boolean hasDynamicDropList() { + return true; + } + + @Override + public List dropList(BlockState state, Level world, BlockEntity te, boolean explosion) { + final List stacks = new ArrayList<>(); + if (world.isClientSide) return stacks; + if (!(te instanceof FreezerTileEntity)) return stacks; + ((FreezerTileEntity) te).reset_process(); + stacks.add(new ItemStack(this, 1)); + return stacks; + } + + @Override + @SuppressWarnings("deprecation") + public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult) { + if (player.isShiftKeyDown()) return InteractionResult.PASS; + if (world.isClientSide()) return InteractionResult.SUCCESS; + FreezerTileEntity te = getTe(world, pos); + if (te == null) return InteractionResult.FAIL; + final ItemStack stack = player.getItemInHand(hand); + boolean dirty = false; + if (Fluidics.manualFluidHandlerInteraction(world, pos, null, player, hand)) { + world.playSound(null, pos, SoundEvents.BUCKET_EMPTY, SoundSource.BLOCKS, 0.5f, 1.4f); + return InteractionResult.CONSUME; + } + if (stack.getItem() == Items.WATER_BUCKET) { + return InteractionResult.CONSUME; // would be already handled + } else if (stack.isEmpty()) { + ItemStack ice = te.getIceItem(true); + if (!ice.isEmpty()) { + player.addItem(ice); + world.playSound(null, pos, SoundEvents.ITEM_PICKUP, SoundSource.BLOCKS, 0.3f, 1.1f); + } else { + world.playSound(null, pos, SoundEvents.IRON_TRAPDOOR_OPEN, SoundSource.BLOCKS, 0.2f, 0.02f); + } + return InteractionResult.CONSUME; + } else { + return InteractionResult.PASS; + } + } + + @Override + public boolean shouldCheckWeakPower(BlockState state, SignalGetter level, BlockPos pos, Direction side) { + return false; + } + + @Override + @OnlyIn(Dist.CLIENT) + public void animateTick(BlockState state, Level world, BlockPos pos, RandomSource rnd) { + } + + @Nullable + private FreezerTileEntity getTe(Level world, BlockPos pos) { + final BlockEntity te = world.getBlockEntity(pos); + return (!(te instanceof FreezerTileEntity)) ? (null) : ((FreezerTileEntity) te); } - return InteractionResult.CONSUME; - } else { - return InteractionResult.PASS; - } } - @Override - public boolean shouldCheckWeakPower(BlockState state, LevelReader world, BlockPos pos, Direction side) - { return false; } + //-------------------------------------------------------------------------------------------------------------------- + // Tile entity + //-------------------------------------------------------------------------------------------------------------------- - @Override - @OnlyIn(Dist.CLIENT) - public void animateTick(BlockState state, Level world, BlockPos pos, RandomSource rnd) - {} + public static class FreezerTileEntity extends StandardEntityBlocks.StandardBlockEntity implements IEnergyStorage { + public static final int TICK_INTERVAL = 20; + public static final int MAX_FLUID_LEVEL = 2000; + public static final int MAX_ENERGY_BUFFER = 32000; + public static final int MAX_ENERGY_TRANSFER = 8192; + public static final int TANK_CAPACITY = 2000; + public static final int DEFAULT_ENERGY_CONSUMPTION = 92; + public static final int DEFAULT_COOLDOWN_RATE = 2; + public static final int PHASE_EMPTY = 0; + public static final int PHASE_WATER = 1; + public static final int PHASE_ICE = 2; + public static final int PHASE_PACKEDICE = 3; + public static final int PHASE_BLUEICE = 4; - @Nullable - private FreezerTileEntity getTe(Level world, BlockPos pos) - { final BlockEntity te=world.getBlockEntity(pos); return (!(te instanceof FreezerTileEntity)) ? (null) : ((FreezerTileEntity)te); } - } + private static int energy_consumption = DEFAULT_ENERGY_CONSUMPTION; + private static int cooldown_rate = DEFAULT_COOLDOWN_RATE; + private static int reheat_rate = 1; + private final Fluidics.Tank tank_ = new Fluidics.Tank(TANK_CAPACITY, TANK_CAPACITY, TANK_CAPACITY, fs -> fs.getFluid() == Fluids.WATER); + private final LazyOptional item_handler_ = LazyOptional.of(() -> new FreezerItemHandler(this)); + private final LazyOptional fluid_handler_ = LazyOptional.of(() -> new Fluidics.SingleTankFluidHandler(tank_)); + protected LazyOptional energy_handler_ = LazyOptional.of(() -> this); + private int tick_timer_; + private int energy_stored_; + private int progress_; + private boolean force_block_update_; - //-------------------------------------------------------------------------------------------------------------------- - // Tile entity - //-------------------------------------------------------------------------------------------------------------------- + public FreezerTileEntity(BlockPos pos, BlockState state) { + super(ModContent.getBlockEntityTypeOfBlock(state.getBlock()), pos, state); + } - public static class FreezerTileEntity extends StandardEntityBlocks.StandardBlockEntity implements IEnergyStorage - { - public static final int TICK_INTERVAL = 20; - public static final int MAX_FLUID_LEVEL = 2000; - public static final int MAX_ENERGY_BUFFER = 32000; - public static final int MAX_ENERGY_TRANSFER = 8192; - public static final int TANK_CAPACITY = 2000; - public static final int DEFAULT_ENERGY_CONSUMPTION = 92; - public static final int DEFAULT_COOLDOWN_RATE = 2; - public static final int PHASE_EMPTY = 0; - public static final int PHASE_WATER = 1; - public static final int PHASE_ICE = 2; - public static final int PHASE_PACKEDICE = 3; - public static final int PHASE_BLUEICE = 4; + public static void on_config(int consumption, int cooldown_per_second) { + energy_consumption = Mth.clamp(consumption, 8, 4096); + cooldown_rate = Mth.clamp(cooldown_per_second, 1, 5); + reheat_rate = Mth.clamp(cooldown_per_second / 2, 1, 5); + ModConfig.log("Config freezer energy consumption:" + energy_consumption + "rf/t, cooldown-rate: " + cooldown_rate + "%/s."); + } - private static int energy_consumption = DEFAULT_ENERGY_CONSUMPTION; - private static int cooldown_rate = DEFAULT_COOLDOWN_RATE; - private static int reheat_rate = 1; - private final Fluidics.Tank tank_ = new Fluidics.Tank(TANK_CAPACITY, TANK_CAPACITY, TANK_CAPACITY, fs->fs.getFluid()==Fluids.WATER); - private int tick_timer_; - private int energy_stored_; - private int progress_; - private boolean force_block_update_; + public int progress() { + return progress_; + } - public static void on_config(int consumption, int cooldown_per_second) - { - energy_consumption = Mth.clamp(consumption, 8, 4096); - cooldown_rate = Mth.clamp(cooldown_per_second, 1, 5); - reheat_rate = Mth.clamp(cooldown_per_second/2, 1, 5); - ModConfig.log("Config freezer energy consumption:" + energy_consumption + "rf/t, cooldown-rate: " + cooldown_rate + "%/s."); + public int phase() { + if (tank_.getFluidAmount() < 1000) return PHASE_EMPTY; + if (progress_ >= 100) return PHASE_BLUEICE; + if (progress_ >= 70) return PHASE_PACKEDICE; + if (progress_ >= 30) return PHASE_ICE; + return PHASE_WATER; + } + + public ItemStack getIceItem(boolean extract) { + ItemStack stack; + switch (phase()) { + case PHASE_ICE: + stack = new ItemStack(Items.ICE); + break; + case PHASE_PACKEDICE: + stack = new ItemStack(Items.PACKED_ICE); + break; + case PHASE_BLUEICE: + stack = new ItemStack(Items.BLUE_ICE); + break; + default: + return ItemStack.EMPTY; + } + if (extract) reset_process(); + return stack; + } + + public int comparator_signal() { + return phase() * 4; + } + + // BlockEntity ------------------------------------------------------------------------------ + + protected void reset_process() { + force_block_update_ = true; + tank_.drain(1000); + tick_timer_ = 0; + progress_ = 0; + } + + public void readnbt(CompoundTag nbt) { + energy_stored_ = nbt.getInt("energy"); + progress_ = nbt.getInt("progress"); + tank_.load(nbt); + } + + protected void writenbt(CompoundTag nbt) { + nbt.putInt("energy", Mth.clamp(energy_stored_, 0, MAX_ENERGY_BUFFER)); + nbt.putInt("progress", Mth.clamp(progress_, 0, 100)); + tank_.save(nbt); + } + + // IItemHandler -------------------------------------------------------------------------------- + + @Override + public void load(CompoundTag nbt) { + super.load(nbt); + readnbt(nbt); + } + + @Override + protected void saveAdditional(CompoundTag nbt) { + super.saveAdditional(nbt); + writenbt(nbt); + } + + // IFluidHandler -------------------------------------------------------------------------------- + + @Override + public void setRemoved() { + super.setRemoved(); + energy_handler_.invalidate(); + fluid_handler_.invalidate(); + item_handler_.invalidate(); + } + + // IEnergyStorage ---------------------------------------------------------------------------- + + @Override + public boolean canExtract() { + return false; + } + + @Override + public boolean canReceive() { + return true; + } + + @Override + public int getMaxEnergyStored() { + return MAX_ENERGY_BUFFER; + } + + @Override + public int getEnergyStored() { + return energy_stored_; + } + + @Override + public int extractEnergy(int maxExtract, boolean simulate) { + return 0; + } + + @Override + public int receiveEnergy(int maxReceive, boolean simulate) { + if (energy_stored_ >= MAX_ENERGY_BUFFER) return 0; + int n = Math.min(maxReceive, (MAX_ENERGY_BUFFER - energy_stored_)); + if (n > MAX_ENERGY_TRANSFER) n = MAX_ENERGY_TRANSFER; + if (!simulate) { + energy_stored_ += n; + setChanged(); + } + return n; + } + + @Override + public LazyOptional getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable Direction facing) { + if (capability == ForgeCapabilities.ITEM_HANDLER) return item_handler_.cast(); + if (capability == ForgeCapabilities.FLUID_HANDLER) return fluid_handler_.cast(); + if (capability == ForgeCapabilities.ENERGY) return energy_handler_.cast(); + return super.getCapability(capability, facing); + } + + // Capability export ---------------------------------------------------------------------------- + + @Override + public void tick() { + if (level.isClientSide) return; + if (--tick_timer_ > 0) return; + tick_timer_ = TICK_INTERVAL; + BlockState state = level.getBlockState(worldPosition); + if (!(state.getBlock() instanceof FreezerBlock)) return; + boolean dirty = false; + final int last_phase = phase(); + if (tank_.getFluidAmount() < 1000) { + progress_ = 0; + } else if ((energy_stored_ <= 0) || (level.hasNeighborSignal(worldPosition))) { + progress_ = Mth.clamp(progress_ - reheat_rate, 0, 100); + } else if (progress_ >= 100) { + progress_ = 100; + energy_stored_ = Mth.clamp(energy_stored_ - ((energy_consumption * TICK_INTERVAL) / 20), 0, MAX_ENERGY_BUFFER); + } else { + energy_stored_ = Mth.clamp(energy_stored_ - (energy_consumption * TICK_INTERVAL), 0, MAX_ENERGY_BUFFER); + progress_ = Mth.clamp(progress_ + cooldown_rate, 0, 100); + } + int new_phase = phase(); + if (new_phase > last_phase) { + level.playSound(null, worldPosition, SoundEvents.SAND_FALL, SoundSource.BLOCKS, 0.2f, 0.7f); + } else if (new_phase < last_phase) { + level.playSound(null, worldPosition, SoundEvents.SAND_FALL, SoundSource.BLOCKS, 0.2f, 0.7f); + } + // Block state + if ((force_block_update_ || (state.getValue(FreezerBlock.PHASE) != new_phase))) { + state = state.setValue(FreezerBlock.PHASE, new_phase); + level.setBlock(worldPosition, state, 3 | 16); + level.updateNeighborsAt(getBlockPos(), state.getBlock()); + force_block_update_ = false; + } + if (dirty) setChanged(); + } + + // ITickable ------------------------------------------------------------------------------------ + + protected static class FreezerItemHandler implements IItemHandler { + private final FreezerTileEntity te; + + FreezerItemHandler(FreezerTileEntity te) { + this.te = te; + } + + @Override + public int getSlots() { + return 1; + } + + @Override + public int getSlotLimit(int index) { + return 1; + } + + @Override + public boolean isItemValid(int slot, @Nonnull ItemStack stack) { + return false; + } + + @Override + @Nonnull + public ItemStack getStackInSlot(int index) { + return (index != 0) ? ItemStack.EMPTY : te.getIceItem(false); + } + + @Override + @Nonnull + public ItemStack insertItem(int index, @Nonnull ItemStack stack, boolean simulate) { + return ItemStack.EMPTY; + } + + @Override + @Nonnull + public ItemStack extractItem(int index, int amount, boolean simulate) { + return te.getIceItem(!simulate); + } + } } - - public FreezerTileEntity(BlockPos pos, BlockState state) - { super(ModContent.getBlockEntityTypeOfBlock(state.getBlock()), pos, state); } - - public int progress() - { return progress_; } - - public int phase() - { - if(tank_.getFluidAmount() < 1000) return PHASE_EMPTY; - if(progress_ >= 100) return PHASE_BLUEICE; - if(progress_ >= 70) return PHASE_PACKEDICE; - if(progress_ >= 30) return PHASE_ICE; - return PHASE_WATER; - } - - public ItemStack getIceItem(boolean extract) - { - ItemStack stack; - switch(phase()) { - case PHASE_ICE: stack = new ItemStack(Items.ICE); break; - case PHASE_PACKEDICE: stack = new ItemStack(Items.PACKED_ICE); break; - case PHASE_BLUEICE: stack = new ItemStack(Items.BLUE_ICE); break; - default: return ItemStack.EMPTY; - } - if(extract) reset_process(); - return stack; - } - - public int comparator_signal() - { return phase() * 4; } - - protected void reset_process() - { - force_block_update_ = true; - tank_.drain(1000); - tick_timer_ = 0; - progress_ = 0; - } - - public void readnbt(CompoundTag nbt) - { - energy_stored_ = nbt.getInt("energy"); - progress_ = nbt.getInt("progress"); - tank_.load(nbt); - } - - protected void writenbt(CompoundTag nbt) - { - nbt.putInt("energy", Mth.clamp(energy_stored_,0 , MAX_ENERGY_BUFFER)); - nbt.putInt("progress", Mth.clamp(progress_,0 , 100)); - tank_.save(nbt); - } - - // BlockEntity ------------------------------------------------------------------------------ - - @Override - public void load(CompoundTag nbt) - { super.load(nbt); readnbt(nbt); } - - @Override - protected void saveAdditional(CompoundTag nbt) - { super.saveAdditional(nbt); writenbt(nbt); } - - @Override - public void setRemoved() - { - super.setRemoved(); - energy_handler_.invalidate(); - fluid_handler_.invalidate(); - item_handler_.invalidate(); - } - - // IItemHandler -------------------------------------------------------------------------------- - - private final LazyOptional item_handler_ = LazyOptional.of(() -> new FreezerItemHandler(this)); - - protected static class FreezerItemHandler implements IItemHandler - { - private final FreezerTileEntity te; - - FreezerItemHandler(FreezerTileEntity te) - { this.te = te; } - - @Override - public int getSlots() - { return 1; } - - @Override - public int getSlotLimit(int index) - { return 1; } - - @Override - public boolean isItemValid(int slot, @Nonnull ItemStack stack) - { return false; } - - @Override - @Nonnull - public ItemStack getStackInSlot(int index) - { return (index!=0) ? ItemStack.EMPTY : te.getIceItem(false); } - - @Override - @Nonnull - public ItemStack insertItem(int index, @Nonnull ItemStack stack, boolean simulate) - { return ItemStack.EMPTY; } - - @Override - @Nonnull - public ItemStack extractItem(int index, int amount, boolean simulate) - { return te.getIceItem(!simulate); } - } - - // IFluidHandler -------------------------------------------------------------------------------- - - private final LazyOptional fluid_handler_ = LazyOptional.of(() -> new Fluidics.SingleTankFluidHandler(tank_)); - - // IEnergyStorage ---------------------------------------------------------------------------- - - protected LazyOptional energy_handler_ = LazyOptional.of(() -> this); - - @Override - public boolean canExtract() - { return false; } - - @Override - public boolean canReceive() - { return true; } - - @Override - public int getMaxEnergyStored() - { return MAX_ENERGY_BUFFER; } - - @Override - public int getEnergyStored() - { return energy_stored_; } - - @Override - public int extractEnergy(int maxExtract, boolean simulate) - { return 0; } - - @Override - public int receiveEnergy(int maxReceive, boolean simulate) - { - if(energy_stored_ >= MAX_ENERGY_BUFFER) return 0; - int n = Math.min(maxReceive, (MAX_ENERGY_BUFFER - energy_stored_)); - if(n > MAX_ENERGY_TRANSFER) n = MAX_ENERGY_TRANSFER; - if(!simulate) {energy_stored_ += n; setChanged(); } - return n; - } - - // Capability export ---------------------------------------------------------------------------- - - @Override - public LazyOptional getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable Direction facing) - { - if(capability == ForgeCapabilities.ITEM_HANDLER) return item_handler_.cast(); - if(capability == ForgeCapabilities.FLUID_HANDLER) return fluid_handler_.cast(); - if(capability == ForgeCapabilities.ENERGY) return energy_handler_.cast(); - return super.getCapability(capability, facing); - } - - // ITickable ------------------------------------------------------------------------------------ - - @Override - public void tick() - { - if(level.isClientSide) return; - if(--tick_timer_ > 0) return; - tick_timer_ = TICK_INTERVAL; - BlockState state = level.getBlockState(worldPosition); - if(!(state.getBlock() instanceof FreezerBlock)) return; - boolean dirty = false; - final int last_phase = phase(); - if(tank_.getFluidAmount() < 1000) { - progress_ = 0; - } else if((energy_stored_ <= 0) || (level.hasNeighborSignal(worldPosition))) { - progress_ = Mth.clamp(progress_-reheat_rate, 0,100); - } else if(progress_ >= 100) { - progress_ = 100; - energy_stored_ = Mth.clamp(energy_stored_-((energy_consumption*TICK_INTERVAL)/20), 0, MAX_ENERGY_BUFFER); - } else { - energy_stored_ = Mth.clamp(energy_stored_-(energy_consumption*TICK_INTERVAL), 0, MAX_ENERGY_BUFFER); - progress_ = Mth.clamp(progress_+cooldown_rate, 0, 100); - } - int new_phase = phase(); - if(new_phase > last_phase) { - level.playSound(null, worldPosition, SoundEvents.SAND_FALL, SoundSource.BLOCKS, 0.2f, 0.7f); - } else if(new_phase < last_phase) { - level.playSound(null, worldPosition, SoundEvents.SAND_FALL, SoundSource.BLOCKS, 0.2f, 0.7f); - } - // Block state - if((force_block_update_ || (state.getValue(FreezerBlock.PHASE) != new_phase))) { - state = state.setValue(FreezerBlock.PHASE, new_phase); - level.setBlock(worldPosition, state,3|16); - level.updateNeighborsAt(getBlockPos(), state.getBlock()); - force_block_update_ = false; - } - if(dirty) setChanged(); - } - } } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdFurnace.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdFurnace.java index 18a5187..f7c439f 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdFurnace.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdFurnace.java @@ -8,7 +8,11 @@ */ package dev.zontreck.engineerdecor.blocks; -import com.mojang.blaze3d.vertex.PoseStack; +import dev.zontreck.engineerdecor.ModConfig; +import dev.zontreck.engineerdecor.ModContent; +import dev.zontreck.libzontreck.edlibmc.*; +import dev.zontreck.libzontreck.edlibmc.Inventories.MappedItemHandler; +import net.minecraft.client.gui.GuiGraphics; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.particles.ParticleTypes; @@ -35,7 +39,7 @@ import net.minecraft.world.item.crafting.RecipeType; import net.minecraft.world.item.crafting.SmeltingRecipe; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.SignalGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockBehaviour; @@ -54,843 +58,862 @@ import net.minecraftforge.energy.IEnergyStorage; import net.minecraftforge.event.ForgeEventFactory; import net.minecraftforge.items.IItemHandler; import net.minecraftforge.registries.ForgeRegistries; -import dev.zontreck.engineerdecor.ModConfig; -import dev.zontreck.engineerdecor.ModContent; -import dev.zontreck.engineerdecor.libmc.StandardBlocks; -import dev.zontreck.engineerdecor.libmc.StandardEntityBlocks; -import dev.zontreck.engineerdecor.libmc.Auxiliaries; -import dev.zontreck.engineerdecor.libmc.Inventories; -import dev.zontreck.engineerdecor.libmc.Inventories.MappedItemHandler; -import dev.zontreck.engineerdecor.libmc.Inventories.StorageInventory; -import dev.zontreck.engineerdecor.libmc.Networking; -import dev.zontreck.engineerdecor.libmc.RfEnergy; -import dev.zontreck.engineerdecor.libmc.Guis; import javax.annotation.Nullable; import java.util.*; import java.util.stream.Collectors; -public class EdFurnace -{ - public static void on_config(int speed_percent, int fuel_efficiency_percent, int boost_energy_per_tick, String accepted_heaters_csv) - { FurnaceTileEntity.on_config(speed_percent, fuel_efficiency_percent, boost_energy_per_tick, accepted_heaters_csv); } - - //-------------------------------------------------------------------------------------------------------------------- - // Block - //-------------------------------------------------------------------------------------------------------------------- - - public static class FurnaceBlock extends StandardBlocks.Horizontal implements StandardEntityBlocks.IStandardEntityBlock - { - public static final BooleanProperty LIT = BlockStateProperties.LIT; - - public FurnaceBlock(long config, BlockBehaviour.Properties properties, final AABB[] unrotatedAABB) - { super(config, properties, unrotatedAABB); registerDefaultState(super.defaultBlockState().setValue(LIT, false)); } - - @Override - public boolean isBlockEntityTicking(Level world, BlockState state) - { return true; } - - @Override - protected void createBlockStateDefinition(StateDefinition.Builder builder) - { super.createBlockStateDefinition(builder); builder.add(LIT); } - - @Override - @SuppressWarnings("deprecation") - public int getLightEmission(BlockState state, BlockGetter world, BlockPos pos) - { return state.getValue(LIT) ? super.getLightEmission(state, world, pos) : 0; } - - @Override - @Nullable - public BlockState getStateForPlacement(BlockPlaceContext context) - { return super.getStateForPlacement(context).setValue(LIT, false); } - - @Override - @SuppressWarnings("deprecation") - public boolean hasAnalogOutputSignal(BlockState state) - { return true; } - - @Override - @SuppressWarnings("deprecation") - public int getAnalogOutputSignal(BlockState blockState, Level world, BlockPos pos) - { - BlockEntity te = world.getBlockEntity(pos); - return (te instanceof FurnaceTileEntity) ? ((FurnaceTileEntity)te).getComparatorOutput() : 0; +public class EdFurnace { + public static void on_config(int speed_percent, int fuel_efficiency_percent, int boost_energy_per_tick, String accepted_heaters_csv) { + FurnaceTileEntity.on_config(speed_percent, fuel_efficiency_percent, boost_energy_per_tick, accepted_heaters_csv); } - @Override - public boolean shouldCheckWeakPower(BlockState state, LevelReader world, BlockPos pos, Direction side) - { return false; } + //-------------------------------------------------------------------------------------------------------------------- + // Block + //-------------------------------------------------------------------------------------------------------------------- - @Override - public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) - { - world.setBlockAndUpdate(pos, state.setValue(LIT, false)); - if(world.isClientSide) return; - if((!stack.hasTag()) || (!stack.getTag().contains("inventory"))) return; - CompoundTag inventory_nbt = stack.getTag().getCompound("inventory"); - if(inventory_nbt.isEmpty()) return; - final BlockEntity te = world.getBlockEntity(pos); - if(!(te instanceof final FurnaceTileEntity bte)) return; - bte.readnbt(inventory_nbt); - bte.setChanged(); - world.setBlockAndUpdate(pos, state.setValue(LIT, bte.burning())); - } + public static class FurnaceBlock extends StandardBlocks.Horizontal implements StandardEntityBlocks.IStandardEntityBlock { + public static final BooleanProperty LIT = BlockStateProperties.LIT; - @Override - public boolean hasDynamicDropList() - { return true; } - - @Override - public List dropList(BlockState state, Level world, final BlockEntity te, boolean explosion) { - final List stacks = new ArrayList<>(); - if(world.isClientSide) return stacks; - if(!(te instanceof FurnaceTileEntity)) return stacks; - if(!explosion) { - ItemStack stack = new ItemStack(this, 1); - CompoundTag inventory_nbt = ((FurnaceTileEntity)te).reset_getnbt(); - if(!inventory_nbt.isEmpty()) { - CompoundTag nbt = new CompoundTag(); - nbt.put("inventory", inventory_nbt); - stack.setTag(nbt); + public FurnaceBlock(long config, BlockBehaviour.Properties properties, final AABB[] unrotatedAABB) { + super(config, properties, unrotatedAABB); + registerDefaultState(super.defaultBlockState().setValue(LIT, false)); } - stacks.add(stack); - } else { - for(ItemStack stack: ((FurnaceTileEntity)te).inventory_) stacks.add(stack); - ((FurnaceTileEntity)te).reset(); - } - return stacks; - } - @Override - @SuppressWarnings("deprecation") - public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult) - { return useOpenGui(state, world, pos, player); } - - @Override - @OnlyIn(Dist.CLIENT) - public void animateTick(BlockState state, Level world, BlockPos pos, RandomSource rnd) - { - if((state.getBlock()!=this) || (!state.getValue(LIT))) return; - final double rv = rnd.nextDouble(); - if(rv > 0.5) return; - final double x=0.5+pos.getX(), y=0.5+pos.getY(), z=0.5+pos.getZ(); - final double xc=0.52, xr=rnd.nextDouble()*0.4-0.2, yr=(y-0.3+rnd.nextDouble()*0.2); - if(rv < 0.1d) world.playLocalSound(x, y, z, SoundEvents.FURNACE_FIRE_CRACKLE, SoundSource.BLOCKS, 0.4f, 0.5f, false); - switch(state.getValue(HORIZONTAL_FACING)) { - case WEST -> world.addParticle(ParticleTypes.SMOKE, x - xc, yr, z + xr, 0.0, 0.0, 0.0); - case EAST -> world.addParticle(ParticleTypes.SMOKE, x + xc, yr, z + xr, 0.0, 0.0, 0.0); - case NORTH -> world.addParticle(ParticleTypes.SMOKE, x + xr, yr, z - xc, 0.0, 0.0, 0.0); - default -> world.addParticle(ParticleTypes.SMOKE, x + xr, yr, z + xc, 0.0, 0.0, 0.0); - } - } - - } - - //-------------------------------------------------------------------------------------------------------------------- - // Tile entity - //-------------------------------------------------------------------------------------------------------------------- - - public static class FurnaceTileEntity extends StandardEntityBlocks.StandardBlockEntity implements MenuProvider, Nameable - { - private static final RecipeType RECIPE_TYPE = RecipeType.SMELTING; - private static final int MAX_BURNTIME = 0x7fff; - private static final int MAX_XP_STORED = 65535; - private static final int NUM_OF_FIELDS = 5; - private static final int TICK_INTERVAL = 4; - private static final int FIFO_INTERVAL = 20; - private static final int DEFAULT_SMELTING_TIME = 200; - private static final int DEFAULT_BOOST_ENERGY = 32; - private static final int NUM_OF_SLOTS = 11; - private static final int SMELTING_INPUT_SLOT_NO = 0; - private static final int SMELTING_FUEL_SLOT_NO = 1; - private static final int SMELTING_OUTPUT_SLOT_NO = 2; - private static final int FIFO_INPUT_0_SLOT_NO = 3; - private static final int FIFO_INPUT_1_SLOT_NO = 4; - private static final int FIFO_FUEL_0_SLOT_NO = 5; - private static final int FIFO_FUEL_1_SLOT_NO = 6; - private static final int FIFO_OUTPUT_0_SLOT_NO = 7; - private static final int FIFO_OUTPUT_1_SLOT_NO = 8; - private static final int AUX_0_SLOT_NO = 9; - private static final int AUX_1_SLOT_NO =10; - - // Config ---------------------------------------------------------------------------------- - - private static double proc_fuel_efficiency_ = 1.0; - private static double proc_speed_ = 1.2; - private static int boost_energy_consumption = DEFAULT_BOOST_ENERGY * TICK_INTERVAL; - private static final Set accepted_heaters_ = new HashSet<>(); - - public static void on_config(int speed_percent, int fuel_efficiency_percent, int boost_energy_per_tick, String accepted_heaters_csv) - { - proc_speed_ = ((double)Mth.clamp(speed_percent, 10, 500)) / 100; - proc_fuel_efficiency_ = ((double) Mth.clamp(fuel_efficiency_percent, 10, 500)) / 100; - boost_energy_consumption = TICK_INTERVAL * Mth.clamp(boost_energy_per_tick, 4, 4096); - { - List heater_resource_locations = Arrays.stream(accepted_heaters_csv.toLowerCase().split("[\\s,;]+")).map(String::trim).toList(); - accepted_heaters_.clear(); - for(String rlstr: heater_resource_locations) { - try { - ResourceLocation rl = new ResourceLocation(rlstr); - Item heater = ForgeRegistries.ITEMS.getValue(rl); - if((heater==null) || (heater==Items.AIR)) { - ModConfig.log("Furnace accepted heater config: Skipped '" + rl + "', item not found/mod not installed."); - } else { - accepted_heaters_.add(heater); - } - } catch(Throwable e) { - Auxiliaries.logError("Furnace accepted heater config invalid: '" + rlstr + "', not added."); - } - } - } - Auxiliaries.logInfo("Config lab furnace speed:" + (proc_speed_*100) + "%, efficiency:" + (proc_fuel_efficiency_*100) + "%, boost: " + (boost_energy_consumption/TICK_INTERVAL) + "rf/t."); - Auxiliaries.logInfo("Config lab furnace accepted heaters: " + accepted_heaters_.stream().map(item->Auxiliaries.getResourceLocation(item).toString()).collect(Collectors.joining(",")) + "."); - } - - // DecorFurnaceTileEntity ----------------------------------------------------------------------------- - - private int tick_timer_; - private int fifo_timer_; - private double proc_time_elapsed_; - private int proc_time_needed_; - private int burntime_left_; - private int field_is_burning_; - private float xp_stored_; - private @Nullable Recipe current_recipe_ = null; - private int fuel_burntime_; - private int field_proc_time_elapsed_; - private boolean heater_inserted_ = false; - private final StorageInventory inventory_; - private final LazyOptional item_extraction_handler_; - private final LazyOptional item_insertion_handler_; - private final LazyOptional item_fuel_insertion_handler_; - private final RfEnergy.Battery battery_ = new RfEnergy.Battery(boost_energy_consumption * 16, boost_energy_consumption, 0); - private final LazyOptional energy_handler_ = battery_.createEnergyHandler(); - - public FurnaceTileEntity(BlockPos pos, BlockState state) - { - super(ModContent.getBlockEntityTypeOfBlock(state.getBlock()), pos, state); - inventory_ = new StorageInventory(this, NUM_OF_SLOTS) { @Override - public void setItem(int index, ItemStack stack) - { - ItemStack slot_stack = stacks_.get(index); - boolean already_in_slot = (!stack.isEmpty()) && (Inventories.areItemStacksIdentical(stack, slot_stack)); - stacks_.set(index, stack); - if(stack.getCount() > getMaxStackSize()) stack.setCount(getMaxStackSize()); - if((index == SMELTING_INPUT_SLOT_NO) && (!already_in_slot)) { - proc_time_needed_ = getSmeltingTimeNeeded(level, stack); - proc_time_elapsed_ = 0; - setChanged(); - } + public boolean isBlockEntityTicking(Level world, BlockState state) { + return true; } - }; - inventory_.setValidator((index, stack)->{ - // applies to gui and handlers - switch(index) { - case SMELTING_OUTPUT_SLOT_NO: - case FIFO_OUTPUT_0_SLOT_NO: - case FIFO_OUTPUT_1_SLOT_NO: + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + super.createBlockStateDefinition(builder); + builder.add(LIT); + } + + @Override + @SuppressWarnings("deprecation") + public int getLightEmission(BlockState state, BlockGetter world, BlockPos pos) { + return state.getValue(LIT) ? super.getLightEmission(state, world, pos) : 0; + } + + @Override + @Nullable + public BlockState getStateForPlacement(BlockPlaceContext context) { + return super.getStateForPlacement(context).setValue(LIT, false); + } + + @Override + @SuppressWarnings("deprecation") + public boolean hasAnalogOutputSignal(BlockState state) { + return true; + } + + @Override + @SuppressWarnings("deprecation") + public int getAnalogOutputSignal(BlockState blockState, Level world, BlockPos pos) { + BlockEntity te = world.getBlockEntity(pos); + return (te instanceof FurnaceTileEntity) ? ((FurnaceTileEntity) te).getComparatorOutput() : 0; + } + + @Override + public boolean shouldCheckWeakPower(BlockState state, SignalGetter level, BlockPos pos, Direction side) { return false; - case SMELTING_INPUT_SLOT_NO: - case FIFO_INPUT_0_SLOT_NO: - case FIFO_INPUT_1_SLOT_NO: + } + + @Override + public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) { + world.setBlockAndUpdate(pos, state.setValue(LIT, false)); + if (world.isClientSide) return; + if ((!stack.hasTag()) || (!stack.getTag().contains("inventory"))) return; + CompoundTag inventory_nbt = stack.getTag().getCompound("inventory"); + if (inventory_nbt.isEmpty()) return; + final BlockEntity te = world.getBlockEntity(pos); + if (!(te instanceof final FurnaceTileEntity bte)) return; + bte.readnbt(inventory_nbt); + bte.setChanged(); + world.setBlockAndUpdate(pos, state.setValue(LIT, bte.burning())); + } + + @Override + public boolean hasDynamicDropList() { return true; - case AUX_0_SLOT_NO: - case AUX_1_SLOT_NO: - return true; - default: { - ItemStack slot_stack = inventory_.getItem(FIFO_FUEL_1_SLOT_NO); - return isFuel(level, stack) || FurnaceFuelSlot.isBucket(stack) && (slot_stack.getItem() != Items.BUCKET); - } } - }); - item_extraction_handler_ = MappedItemHandler.createExtractionHandler(inventory_, - (slot,stack)->(slot!=SMELTING_FUEL_SLOT_NO) || (stack.getItem()==Items.BUCKET) || (!isFuel(getLevel(), stack)), - Arrays.asList(FIFO_OUTPUT_0_SLOT_NO, FIFO_OUTPUT_1_SLOT_NO, SMELTING_FUEL_SLOT_NO) - ); - item_insertion_handler_ = MappedItemHandler.createInsertionHandler(inventory_, - FIFO_INPUT_1_SLOT_NO,FIFO_INPUT_0_SLOT_NO,SMELTING_INPUT_SLOT_NO - ); - item_fuel_insertion_handler_ = MappedItemHandler.createInsertionHandler(inventory_, - FIFO_FUEL_1_SLOT_NO,FIFO_FUEL_0_SLOT_NO,SMELTING_FUEL_SLOT_NO - ); - } - public CompoundTag reset_getnbt() - { - CompoundTag nbt = new CompoundTag(); - writenbt(nbt); - reset(); - return nbt; - } - - public void reset() - { - inventory_.clearContent(); - proc_time_elapsed_ = 0; - proc_time_needed_ = 0; - burntime_left_ = 0; - fuel_burntime_ = 0; - fifo_timer_ = 0; - tick_timer_ = 0; - xp_stored_ = 0; - current_recipe_ = null; - } - - public void readnbt(CompoundTag nbt) - { - burntime_left_ = nbt.getInt("BurnTime"); - proc_time_elapsed_ = nbt.getInt("CookTime"); - proc_time_needed_ = nbt.getInt("CookTimeTotal"); - fuel_burntime_ = nbt.getInt("FuelBurnTime"); - xp_stored_ = nbt.getFloat("XpStored"); - battery_.load(nbt, "Energy"); - inventory_.load(nbt); - } - - protected void writenbt(CompoundTag nbt) - { - nbt.putInt("BurnTime", Mth.clamp(burntime_left_,0 , MAX_BURNTIME)); - nbt.putInt("CookTime", Mth.clamp((int)proc_time_elapsed_, 0, MAX_BURNTIME)); - nbt.putInt("CookTimeTotal", Mth.clamp(proc_time_needed_, 0, MAX_BURNTIME)); - nbt.putInt("FuelBurnTime", Mth.clamp(fuel_burntime_, 0, MAX_BURNTIME)); - nbt.putFloat("XpStored", Mth.clamp(xp_stored_, 0, MAX_XP_STORED)); - battery_.save(nbt, "Energy"); - inventory_.save(nbt); - } - - public int getComparatorOutput() - { - if(inventory_.getItem(FIFO_FUEL_0_SLOT_NO).isEmpty() && inventory_.getItem(FIFO_FUEL_1_SLOT_NO).isEmpty() && inventory_.getItem(SMELTING_FUEL_SLOT_NO).isEmpty()) { - return 0; // fuel completely empty - } else { - return ( - (inventory_.getItem(FIFO_INPUT_1_SLOT_NO).isEmpty() ? 0 : 5) + - (inventory_.getItem(FIFO_INPUT_0_SLOT_NO).isEmpty() ? 0 : 5) + - (inventory_.getItem(SMELTING_INPUT_SLOT_NO).isEmpty() ? 0 : 5) - ); - } - } - - // BlockEntity ------------------------------------------------------------------------------ - - @Override - public void load(CompoundTag nbt) - { super.load(nbt); readnbt(nbt); } - - @Override - protected void saveAdditional(CompoundTag nbt) - { super.saveAdditional(nbt); writenbt(nbt); } - - @Override - public void setRemoved() - { - super.setRemoved(); - item_extraction_handler_.invalidate(); - item_insertion_handler_.invalidate(); - item_fuel_insertion_handler_.invalidate(); - energy_handler_.invalidate(); - } - - // INamedContainerProvider / Nameable ------------------------------------------------------ - - @Override - public Component getName() - { return Auxiliaries.localizable(getBlockState().getBlock().getDescriptionId()); } - - @Override - public boolean hasCustomName() - { return false; } - - @Override - public Component getCustomName() - { return getName(); } - - // IContainerProvider ---------------------------------------------------------------------- - - @Override - public Component getDisplayName() - { return Nameable.super.getDisplayName(); } - - @Override - public AbstractContainerMenu createMenu(int id, Inventory inventory, Player player ) - { return new FurnaceContainer(id, inventory, inventory_, ContainerLevelAccess.create(level, worldPosition), fields); } - - // Fields ----------------------------------------------------------------------------------------------- - - protected final ContainerData fields = new ContainerData() - { - @Override - public int getCount() - { return FurnaceTileEntity.NUM_OF_FIELDS; } - - @Override - public int get(int id) - { - return switch(id) { - case 0 -> FurnaceTileEntity.this.burntime_left_; - case 1 -> FurnaceTileEntity.this.fuel_burntime_; - case 2 -> FurnaceTileEntity.this.field_proc_time_elapsed_; - case 3 -> FurnaceTileEntity.this.proc_time_needed_; - case 4 -> FurnaceTileEntity.this.field_is_burning_; - default -> 0; - }; - } - @Override - public void set(int id, int value) - { - switch(id) { - case 0 -> FurnaceTileEntity.this.burntime_left_ = value; - case 1 -> FurnaceTileEntity.this.fuel_burntime_ = value; - case 2 -> FurnaceTileEntity.this.field_proc_time_elapsed_ = value; - case 3 -> FurnaceTileEntity.this.proc_time_needed_ = value; - case 4 -> FurnaceTileEntity.this.field_is_burning_ = value; - } - } - }; - - // Capability export ---------------------------------------------------------------------------- - - @Override - public LazyOptional getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable Direction facing) - { - if(capability == ForgeCapabilities.ITEM_HANDLER) { - if(facing == Direction.UP) return item_insertion_handler_.cast(); - if(facing == Direction.DOWN) return item_extraction_handler_.cast(); - return item_fuel_insertion_handler_.cast(); - } else if(capability == ForgeCapabilities.ENERGY) { - return energy_handler_.cast(); - } - return super.getCapability(capability, facing); - } - - // ITickableTileEntity ------------------------------------------------------------------------- - - @Override - public void tick() - { - if(--tick_timer_ > 0) return; - tick_timer_ = TICK_INTERVAL; - final BlockState state = level.getBlockState(worldPosition); - if(!(state.getBlock() instanceof FurnaceBlock)) return; - final boolean was_burning = burning(); - if(was_burning) burntime_left_ -= TICK_INTERVAL; - if(burntime_left_ < 0) burntime_left_ = 0; - if(level.isClientSide) return; - boolean dirty = false; - if(--fifo_timer_ <= 0) { - fifo_timer_ = FIFO_INTERVAL/TICK_INTERVAL; - // note, intentionally not using bitwise OR piping. - if(transferItems(FIFO_OUTPUT_0_SLOT_NO, FIFO_OUTPUT_1_SLOT_NO, 1)) dirty = true; - if(transferItems(SMELTING_OUTPUT_SLOT_NO, FIFO_OUTPUT_0_SLOT_NO, 1)) dirty = true; - if(transferItems(FIFO_FUEL_0_SLOT_NO, SMELTING_FUEL_SLOT_NO, 1)) dirty = true; - if(transferItems(FIFO_FUEL_1_SLOT_NO, FIFO_FUEL_0_SLOT_NO, 1)) dirty = true; - if(transferItems(FIFO_INPUT_0_SLOT_NO, SMELTING_INPUT_SLOT_NO, 1)) dirty = true; - if(transferItems(FIFO_INPUT_1_SLOT_NO, FIFO_INPUT_0_SLOT_NO, 1)) dirty = true; - heater_inserted_ = accepted_heaters_.isEmpty() // without IE always allow electrical boost - || accepted_heaters_.contains(inventory_.getItem(AUX_0_SLOT_NO).getItem()) - || accepted_heaters_.contains(inventory_.getItem(AUX_1_SLOT_NO).getItem()); - } - ItemStack fuel = inventory_.getItem(SMELTING_FUEL_SLOT_NO); - if(burning() || (!fuel.isEmpty()) && (!(inventory_.getItem(SMELTING_INPUT_SLOT_NO)).isEmpty())) { - Recipe last_recipe = currentRecipe(); - updateCurrentRecipe(); - if(currentRecipe() != last_recipe) { - proc_time_elapsed_ = 0; - proc_time_needed_ = getSmeltingTimeNeeded(level, inventory_.getItem(SMELTING_INPUT_SLOT_NO)); - } - if(!burning() && canSmeltCurrentItem()) { - burntime_left_ = (int)Mth.clamp((proc_fuel_efficiency_ * getFuelBurntime(level, fuel)), 0, MAX_BURNTIME); - fuel_burntime_ = (int)Mth.clamp(((double)burntime_left_)/((proc_speed_ > 0) ? proc_speed_ : 1), 1, MAX_BURNTIME); - if(burning()) { - dirty = true; - if(!fuel.isEmpty()) { - Item fuel_item = fuel.getItem(); - fuel.shrink(1); - if(fuel.isEmpty()) inventory_.setItem(SMELTING_FUEL_SLOT_NO, fuel_item.getCraftingRemainingItem(fuel)); + @Override + public List dropList(BlockState state, Level world, final BlockEntity te, boolean explosion) { + final List stacks = new ArrayList<>(); + if (world.isClientSide) return stacks; + if (!(te instanceof FurnaceTileEntity)) return stacks; + if (!explosion) { + ItemStack stack = new ItemStack(this, 1); + CompoundTag inventory_nbt = ((FurnaceTileEntity) te).reset_getnbt(); + if (!inventory_nbt.isEmpty()) { + CompoundTag nbt = new CompoundTag(); + nbt.put("inventory", inventory_nbt); + stack.setTag(nbt); + } + stacks.add(stack); + } else { + for (ItemStack stack : ((FurnaceTileEntity) te).inventory_) stacks.add(stack); + ((FurnaceTileEntity) te).reset(); } - } + return stacks; } - if(burning() && canSmeltCurrentItem()) { - proc_time_elapsed_ += TICK_INTERVAL * proc_speed_; - if(heater_inserted_ && battery_.draw(boost_energy_consumption)) { - proc_time_elapsed_ += (TICK_INTERVAL * proc_speed_) * 2; - } - if(proc_time_elapsed_ >= proc_time_needed_) { + + @Override + @SuppressWarnings("deprecation") + public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult) { + return useOpenGui(state, world, pos, player); + } + + @Override + @OnlyIn(Dist.CLIENT) + public void animateTick(BlockState state, Level world, BlockPos pos, RandomSource rnd) { + if ((state.getBlock() != this) || (!state.getValue(LIT))) return; + final double rv = rnd.nextDouble(); + if (rv > 0.5) return; + final double x = 0.5 + pos.getX(), y = 0.5 + pos.getY(), z = 0.5 + pos.getZ(); + final double xc = 0.52, xr = rnd.nextDouble() * 0.4 - 0.2, yr = (y - 0.3 + rnd.nextDouble() * 0.2); + if (rv < 0.1d) + world.playLocalSound(x, y, z, SoundEvents.FURNACE_FIRE_CRACKLE, SoundSource.BLOCKS, 0.4f, 0.5f, false); + switch (state.getValue(HORIZONTAL_FACING)) { + case WEST -> world.addParticle(ParticleTypes.SMOKE, x - xc, yr, z + xr, 0.0, 0.0, 0.0); + case EAST -> world.addParticle(ParticleTypes.SMOKE, x + xc, yr, z + xr, 0.0, 0.0, 0.0); + case NORTH -> world.addParticle(ParticleTypes.SMOKE, x + xr, yr, z - xc, 0.0, 0.0, 0.0); + default -> world.addParticle(ParticleTypes.SMOKE, x + xr, yr, z + xc, 0.0, 0.0, 0.0); + } + } + + } + + //-------------------------------------------------------------------------------------------------------------------- + // Tile entity + //-------------------------------------------------------------------------------------------------------------------- + + public static class FurnaceTileEntity extends StandardEntityBlocks.StandardBlockEntity implements MenuProvider, Nameable { + private static final RecipeType RECIPE_TYPE = RecipeType.SMELTING; + private static final int MAX_BURNTIME = 0x7fff; + private static final int MAX_XP_STORED = 65535; + private static final int NUM_OF_FIELDS = 5; + private static final int TICK_INTERVAL = 4; + private static final int FIFO_INTERVAL = 20; + private static final int DEFAULT_SMELTING_TIME = 200; + private static final int DEFAULT_BOOST_ENERGY = 32; + private static final int NUM_OF_SLOTS = 11; + private static final int SMELTING_INPUT_SLOT_NO = 0; + private static final int SMELTING_FUEL_SLOT_NO = 1; + private static final int SMELTING_OUTPUT_SLOT_NO = 2; + private static final int FIFO_INPUT_0_SLOT_NO = 3; + private static final int FIFO_INPUT_1_SLOT_NO = 4; + private static final int FIFO_FUEL_0_SLOT_NO = 5; + private static final int FIFO_FUEL_1_SLOT_NO = 6; + private static final int FIFO_OUTPUT_0_SLOT_NO = 7; + private static final int FIFO_OUTPUT_1_SLOT_NO = 8; + private static final int AUX_0_SLOT_NO = 9; + private static final int AUX_1_SLOT_NO = 10; + + // Config ---------------------------------------------------------------------------------- + private static final Set accepted_heaters_ = new HashSet<>(); + private static double proc_fuel_efficiency_ = 1.0; + private static double proc_speed_ = 1.2; + private static int boost_energy_consumption = DEFAULT_BOOST_ENERGY * TICK_INTERVAL; + private final Inventories.StorageInventory inventory_; + + // DecorFurnaceTileEntity ----------------------------------------------------------------------------- + private final LazyOptional item_extraction_handler_; + private final LazyOptional item_insertion_handler_; + private final LazyOptional item_fuel_insertion_handler_; + private final RfEnergy.Battery battery_ = new RfEnergy.Battery(boost_energy_consumption * 16, boost_energy_consumption, 0); + private final LazyOptional energy_handler_ = battery_.createEnergyHandler(); + private int tick_timer_; + private int fifo_timer_; + private double proc_time_elapsed_; + private int proc_time_needed_; + private int burntime_left_; + private int field_is_burning_; + private float xp_stored_; + private @Nullable Recipe current_recipe_ = null; + private int fuel_burntime_; + private int field_proc_time_elapsed_; + protected final ContainerData fields = new ContainerData() { + @Override + public int getCount() { + return FurnaceTileEntity.NUM_OF_FIELDS; + } + + @Override + public int get(int id) { + return switch (id) { + case 0 -> FurnaceTileEntity.this.burntime_left_; + case 1 -> FurnaceTileEntity.this.fuel_burntime_; + case 2 -> FurnaceTileEntity.this.field_proc_time_elapsed_; + case 3 -> FurnaceTileEntity.this.proc_time_needed_; + case 4 -> FurnaceTileEntity.this.field_is_burning_; + default -> 0; + }; + } + + @Override + public void set(int id, int value) { + switch (id) { + case 0 -> FurnaceTileEntity.this.burntime_left_ = value; + case 1 -> FurnaceTileEntity.this.fuel_burntime_ = value; + case 2 -> FurnaceTileEntity.this.field_proc_time_elapsed_ = value; + case 3 -> FurnaceTileEntity.this.proc_time_needed_ = value; + case 4 -> FurnaceTileEntity.this.field_is_burning_ = value; + } + } + }; + private boolean heater_inserted_ = false; + + public FurnaceTileEntity(BlockPos pos, BlockState state) { + super(ModContent.getBlockEntityTypeOfBlock(state.getBlock()), pos, state); + inventory_ = new Inventories.StorageInventory(this, NUM_OF_SLOTS) { + @Override + public void setItem(int index, ItemStack stack) { + ItemStack slot_stack = stacks_.get(index); + boolean already_in_slot = (!stack.isEmpty()) && (Inventories.areItemStacksIdentical(stack, slot_stack)); + stacks_.set(index, stack); + if (stack.getCount() > getMaxStackSize()) stack.setCount(getMaxStackSize()); + if ((index == SMELTING_INPUT_SLOT_NO) && (!already_in_slot)) { + proc_time_needed_ = getSmeltingTimeNeeded(level, stack); + proc_time_elapsed_ = 0; + setChanged(); + } + } + }; + inventory_.setValidator((index, stack) -> { + // applies to gui and handlers + switch (index) { + case SMELTING_OUTPUT_SLOT_NO: + case FIFO_OUTPUT_0_SLOT_NO: + case FIFO_OUTPUT_1_SLOT_NO: + return false; + case SMELTING_INPUT_SLOT_NO: + case FIFO_INPUT_0_SLOT_NO: + case FIFO_INPUT_1_SLOT_NO: + return true; + case AUX_0_SLOT_NO: + case AUX_1_SLOT_NO: + return true; + default: { + ItemStack slot_stack = inventory_.getItem(FIFO_FUEL_1_SLOT_NO); + return isFuel(level, stack) || FurnaceFuelSlot.isBucket(stack) && (slot_stack.getItem() != Items.BUCKET); + } + } + }); + item_extraction_handler_ = MappedItemHandler.createExtractionHandler(inventory_, + (slot, stack) -> (slot != SMELTING_FUEL_SLOT_NO) || (stack.getItem() == Items.BUCKET) || (!isFuel(getLevel(), stack)), + Arrays.asList(FIFO_OUTPUT_0_SLOT_NO, FIFO_OUTPUT_1_SLOT_NO, SMELTING_FUEL_SLOT_NO) + ); + item_insertion_handler_ = MappedItemHandler.createInsertionHandler(inventory_, + FIFO_INPUT_1_SLOT_NO, FIFO_INPUT_0_SLOT_NO, SMELTING_INPUT_SLOT_NO + ); + item_fuel_insertion_handler_ = MappedItemHandler.createInsertionHandler(inventory_, + FIFO_FUEL_1_SLOT_NO, FIFO_FUEL_0_SLOT_NO, SMELTING_FUEL_SLOT_NO + ); + } + + public static void on_config(int speed_percent, int fuel_efficiency_percent, int boost_energy_per_tick, String accepted_heaters_csv) { + proc_speed_ = ((double) Mth.clamp(speed_percent, 10, 500)) / 100; + proc_fuel_efficiency_ = ((double) Mth.clamp(fuel_efficiency_percent, 10, 500)) / 100; + boost_energy_consumption = TICK_INTERVAL * Mth.clamp(boost_energy_per_tick, 4, 4096); + { + List heater_resource_locations = Arrays.stream(accepted_heaters_csv.toLowerCase().split("[\\s,;]+")).map(String::trim).toList(); + accepted_heaters_.clear(); + for (String rlstr : heater_resource_locations) { + try { + ResourceLocation rl = new ResourceLocation(rlstr); + Item heater = ForgeRegistries.ITEMS.getValue(rl); + if ((heater == null) || (heater == Items.AIR)) { + ModConfig.log("Furnace accepted heater config: Skipped '" + rl + "', item not found/mod not installed."); + } else { + accepted_heaters_.add(heater); + } + } catch (Throwable e) { + Auxiliaries.logError("Furnace accepted heater config invalid: '" + rlstr + "', not added."); + } + } + } + Auxiliaries.logInfo("Config lab furnace speed:" + (proc_speed_ * 100) + "%, efficiency:" + (proc_fuel_efficiency_ * 100) + "%, boost: " + (boost_energy_consumption / TICK_INTERVAL) + "rf/t."); + Auxiliaries.logInfo("Config lab furnace accepted heaters: " + accepted_heaters_.stream().map(item -> Auxiliaries.getResourceLocation(item).toString()).collect(Collectors.joining(",")) + "."); + } + + @Nullable + public static T getSmeltingResult(RecipeType recipe_type, Level world, ItemStack stack) { + if (stack.isEmpty()) return null; + Container inventory = new SimpleContainer(3); + inventory.setItem(0, stack); + return world.getRecipeManager().getRecipeFor(recipe_type, inventory, world).orElse(null); + } + + public static int getFuelBurntime(Level world, ItemStack stack) { + if (stack.isEmpty()) return 0; + int t = ForgeHooks.getBurnTime(stack, null); + return Math.max(t, 0); + } + + public static boolean isFuel(Level world, ItemStack stack) { + return (getFuelBurntime(world, stack) > 0) || (stack.getItem() == Items.LAVA_BUCKET); + } + + public static boolean canSmelt(Level world, final ItemStack stack) { + return getSmeltingResult(RECIPE_TYPE, world, stack) != null; + } + + // BlockEntity ------------------------------------------------------------------------------ + + public CompoundTag reset_getnbt() { + CompoundTag nbt = new CompoundTag(); + writenbt(nbt); + reset(); + return nbt; + } + + public void reset() { + inventory_.clearContent(); proc_time_elapsed_ = 0; - proc_time_needed_ = getSmeltingTimeNeeded(level, inventory_.getItem(SMELTING_INPUT_SLOT_NO)); - smeltCurrentItem(); - dirty = true; - } - } else { - proc_time_elapsed_ = 0; + proc_time_needed_ = 0; + burntime_left_ = 0; + fuel_burntime_ = 0; + fifo_timer_ = 0; + tick_timer_ = 0; + xp_stored_ = 0; + current_recipe_ = null; } - } else if((!burning()) && (proc_time_elapsed_ > 0)) { - proc_time_elapsed_ = Mth.clamp(proc_time_elapsed_-2, 0, proc_time_needed_); - } - if(was_burning != burning()) { - dirty = true; - level.setBlockAndUpdate(worldPosition, state.setValue(FurnaceBlock.LIT, burning())); - } - if(dirty) { - setChanged(); - } - field_is_burning_ = this.burning() ? 1 : 0; - field_proc_time_elapsed_ = (int)proc_time_elapsed_; - } - // Furnace ------------------------------------------------------------------------------------- - - @Nullable - public static T getSmeltingResult(RecipeType recipe_type, Level world, ItemStack stack) - { - if(stack.isEmpty()) return null; - Container inventory = new SimpleContainer(3); - inventory.setItem(0, stack); - return world.getRecipeManager().getRecipeFor(recipe_type, inventory, world).orElse(null); - } - - public boolean burning() - { return burntime_left_ > 0; } - - public int getSmeltingTimeNeeded(Level world, ItemStack stack) - { - if(stack.isEmpty()) return 0; - AbstractCookingRecipe recipe = getSmeltingResult(RECIPE_TYPE, world, stack); - if(recipe == null) return 0; - int t = recipe.getCookingTime(); - return (t<=0) ? DEFAULT_SMELTING_TIME : t; - } - - private boolean transferItems(final int index_from, final int index_to, int count) - { - ItemStack from = inventory_.getItem(index_from); - if(from.isEmpty()) return false; - ItemStack to = inventory_.getItem(index_to); - if(from.getCount() < count) count = from.getCount(); - if(count <= 0) return false; - boolean changed = true; - if(to.isEmpty()) { - inventory_.setItem(index_to, from.split(count)); - } else if(to.getCount() >= to.getMaxStackSize()) { - changed = false; - } else if(Inventories.areItemStacksDifferent(from, to)) { - changed = false; - } else { - if((to.getCount()+count) >= to.getMaxStackSize()) { - from.shrink(to.getMaxStackSize()-to.getCount()); - to.setCount(to.getMaxStackSize()); - } else { - from.shrink(count); - to.grow(count); + public void readnbt(CompoundTag nbt) { + burntime_left_ = nbt.getInt("BurnTime"); + proc_time_elapsed_ = nbt.getInt("CookTime"); + proc_time_needed_ = nbt.getInt("CookTimeTotal"); + fuel_burntime_ = nbt.getInt("FuelBurnTime"); + xp_stored_ = nbt.getFloat("XpStored"); + battery_.load(nbt, "Energy"); + inventory_.load(nbt); } - } - if(from.isEmpty() && from!=ItemStack.EMPTY) { - inventory_.setItem(index_from, ItemStack.EMPTY); - changed = true; - } - return changed; - } - protected boolean canSmeltCurrentItem() - { - if((currentRecipe()==null) || (inventory_.getItem(SMELTING_INPUT_SLOT_NO).isEmpty())) return false; - final ItemStack recipe_result_items = getSmeltingResult(inventory_.getItem(SMELTING_INPUT_SLOT_NO)); - if(recipe_result_items.isEmpty()) return false; - final ItemStack result_stack = inventory_.getItem(SMELTING_OUTPUT_SLOT_NO); - if(result_stack.isEmpty()) return true; - if(!result_stack.sameItem(recipe_result_items)) return false; - if(result_stack.getCount() + recipe_result_items.getCount() <= inventory_.getMaxStackSize() && result_stack.getCount() + recipe_result_items.getCount() <= result_stack.getMaxStackSize()) return true; - return result_stack.getCount() + recipe_result_items.getCount() <= recipe_result_items.getMaxStackSize(); - } + // INamedContainerProvider / Nameable ------------------------------------------------------ - protected void smeltCurrentItem() - { - if(!canSmeltCurrentItem()) return; - final ItemStack smelting_input_stack = inventory_.getItem(SMELTING_INPUT_SLOT_NO); - final ItemStack recipe_result_items = getSmeltingResult(smelting_input_stack); - final ItemStack smelting_output_stack = inventory_.getItem(SMELTING_OUTPUT_SLOT_NO); - final float xp = getCurrentSmeltingXp(smelting_output_stack); - if(smelting_output_stack.isEmpty()) { - inventory_.setItem(SMELTING_OUTPUT_SLOT_NO, recipe_result_items.copy()); - } else if(smelting_output_stack.getItem() == recipe_result_items.getItem()) { - smelting_output_stack.grow(recipe_result_items.getCount()); - } - smelting_input_stack.shrink(1); - xp_stored_ += xp; - } - - public static int getFuelBurntime(Level world, ItemStack stack) - { - if(stack.isEmpty()) return 0; - int t = ForgeHooks.getBurnTime(stack, null); - return Math.max(t, 0); - } - - public static boolean isFuel(Level world, ItemStack stack) - { return (getFuelBurntime(world, stack) > 0) || (stack.getItem()==Items.LAVA_BUCKET); } - - public int consumeSmeltingExperience(ItemStack stack) - { - if(xp_stored_ < 1) return 0; - float xp = xp_stored_; - if(xp >= 15) xp /= 2; - xp = Math.min((float)Math.floor(xp), 150); - xp_stored_ -= xp; - return (int)xp; - } - - public ItemStack getSmeltingResult(final ItemStack stack) - { return (currentRecipe()==null) ? (ItemStack.EMPTY) : (currentRecipe().getResultItem()); } - - public float getCurrentSmeltingXp(final ItemStack stack) - { - float xp = (currentRecipe() instanceof AbstractCookingRecipe) ? (((AbstractCookingRecipe)currentRecipe()).getExperience()) : (0); - return (xp <= 0) ? 0.7f : xp; // default value for recipes without defined xp - } - - public static boolean canSmelt(Level world, final ItemStack stack) - { return getSmeltingResult(RECIPE_TYPE, world, stack) != null; } - - @Nullable - protected Recipe currentRecipe() - { return current_recipe_; } - - protected void updateCurrentRecipe() - { setCurrentRecipe(getSmeltingResult(RECIPE_TYPE, getLevel(), inventory_.getItem(SMELTING_INPUT_SLOT_NO))); } - - protected void setCurrentRecipe(Recipe recipe) - { current_recipe_ = recipe; } - } - - //-------------------------------------------------------------------------------------------------------------------- - // Container slots - //-------------------------------------------------------------------------------------------------------------------- - - public static class FurnaceContainer extends AbstractContainerMenu implements Networking.INetworkSynchronisableContainer - { - // Slots -------------------------------------------------------------------------------------------- - - public static class OutputSlot extends Slot - { - private final Container inventory_; - private final Player player_; - private int removeCount = 0; - - public OutputSlot(Player player, Container inventory, int index, int xpos, int ypos) - { super(inventory, index, xpos, ypos); inventory_ = inventory; player_ = player; } - - @Override - public boolean mayPlace(ItemStack stack) - { return false; } - - @Override - public ItemStack remove(int amount) - { removeCount += hasItem() ? Math.min(amount, getItem().getCount()) : 0; return super.remove(amount); } - - @Override - public void onTake(Player thePlayer, ItemStack stack) - { checkTakeAchievements(stack); super.onTake(thePlayer, stack); } - - @Override - protected void onQuickCraft(ItemStack stack, int amount) - { removeCount += amount; checkTakeAchievements(stack); } - - @Override - protected void checkTakeAchievements(ItemStack stack) - { - stack.onCraftedBy(player_.level, player_, removeCount); - if((!player_.level.isClientSide()) && (inventory_ instanceof StorageInventory) && - ((((StorageInventory)inventory_).getBlockEntity()) instanceof final FurnaceTileEntity te) - ) { - int xp = te.consumeSmeltingExperience(stack); - while(xp > 0) { - int k = ExperienceOrb.getExperienceValue(xp); - xp -= k; - player_.level.addFreshEntity((new ExperienceOrb(player_.level, player_.blockPosition().getX(), player_.blockPosition().getY()+0.5, player_.blockPosition().getZ()+0.5, k))); - } + protected void writenbt(CompoundTag nbt) { + nbt.putInt("BurnTime", Mth.clamp(burntime_left_, 0, MAX_BURNTIME)); + nbt.putInt("CookTime", Mth.clamp((int) proc_time_elapsed_, 0, MAX_BURNTIME)); + nbt.putInt("CookTimeTotal", Mth.clamp(proc_time_needed_, 0, MAX_BURNTIME)); + nbt.putInt("FuelBurnTime", Mth.clamp(fuel_burntime_, 0, MAX_BURNTIME)); + nbt.putFloat("XpStored", Mth.clamp(xp_stored_, 0, MAX_XP_STORED)); + battery_.save(nbt, "Energy"); + inventory_.save(nbt); } - removeCount = 0; - ForgeEventFactory.firePlayerSmeltedEvent(player_, stack); - } - } - public static class FuelSlot extends Slot - { - private final FurnaceContainer container_; - - public FuelSlot(Container inventory, int index, int xpos, int ypos, FurnaceContainer container) - { super(inventory, index, xpos, ypos); container_=container; } - - @Override - public boolean mayPlace(ItemStack stack) - { return isBucket(stack) || (FurnaceTileEntity.isFuel(container_.world(), stack)); } - - @Override - public int getMaxStackSize(ItemStack stack) - { return isBucket(stack) ? 1 : super.getMaxStackSize(stack); } - - protected static boolean isBucket(ItemStack stack) - { return (stack.getItem()==Items.BUCKET); } - } - - // Container ---------------------------------------------------------------------------------------- - - private static final int PLAYER_INV_START_SLOTNO = 11; - protected final Player player_; - protected final Container inventory_; - protected final ContainerLevelAccess wpc_; - private final ContainerData fields_; - private final RecipeType recipe_type_; - - public int field(int index) { return fields_.get(index); } - public Player player() { return player_ ; } - public Container inventory() { return inventory_ ; } - public Level world() { return player_.level; } - - public FurnaceContainer(int cid, Inventory player_inventory) - { this(cid, player_inventory, new SimpleContainer(FurnaceTileEntity.NUM_OF_SLOTS), ContainerLevelAccess.NULL, new SimpleContainerData(FurnaceTileEntity.NUM_OF_FIELDS)); } - - private FurnaceContainer(int cid, Inventory player_inventory, Container block_inventory, ContainerLevelAccess wpc, ContainerData fields) - { - super(ModContent.getMenuType("small_lab_furnace"), cid); // @todo: class mapping - player_ = player_inventory.player; - inventory_ = block_inventory; - wpc_ = wpc; - fields_ = fields; - recipe_type_ = FurnaceTileEntity.RECIPE_TYPE; - addSlot(new Slot(inventory_, 0, 59, 17)); // smelting input - addSlot(new FuelSlot(inventory_, 1, 59, 53, this)); // fuel - addSlot(new OutputSlot(player_, inventory_, 2, 101, 35)); // smelting result - addSlot(new Slot(inventory_, 3, 34, 17)); // input fifo 0 - addSlot(new Slot(inventory_, 4, 16, 17)); // input fifo 1 - addSlot(new Slot(inventory_, 5, 34, 53)); // fuel fifo 0 - addSlot(new Slot(inventory_, 6, 16, 53)); // fuel fifo 1 - addSlot(new OutputSlot(player_inventory.player, inventory_, 7, 126, 35)); // out fifo 0 - addSlot(new OutputSlot(player_inventory.player, inventory_, 8, 144, 35)); // out fifo 1 - addSlot(new Slot(inventory_, 9, 126, 61)); // aux slot 1 - addSlot(new Slot(inventory_, 10, 144, 61)); // aux slot 2 - for(int x=0; x<9; ++x) { - addSlot(new Slot(player_inventory, x, 8+x*18, 144)); // player slots: 0..8 - } - for(int y=0; y<3; ++y) { - for(int x=0; x<9; ++x) { - addSlot(new Slot(player_inventory, x+y*9+9, 8+x*18, 86+y*18)); // player slots: 9..35 + public int getComparatorOutput() { + if (inventory_.getItem(FIFO_FUEL_0_SLOT_NO).isEmpty() && inventory_.getItem(FIFO_FUEL_1_SLOT_NO).isEmpty() && inventory_.getItem(SMELTING_FUEL_SLOT_NO).isEmpty()) { + return 0; // fuel completely empty + } else { + return ( + (inventory_.getItem(FIFO_INPUT_1_SLOT_NO).isEmpty() ? 0 : 5) + + (inventory_.getItem(FIFO_INPUT_0_SLOT_NO).isEmpty() ? 0 : 5) + + (inventory_.getItem(SMELTING_INPUT_SLOT_NO).isEmpty() ? 0 : 5) + ); + } } - } - this.addDataSlots(fields_); // === Add reference holders - } - @Override - public boolean stillValid(Player player) - { return inventory_.stillValid(player); } - - @Override - public ItemStack quickMoveStack(Player player, int index) - { - Slot slot = getSlot(index); - if((slot==null) || (!slot.hasItem())) return ItemStack.EMPTY; - ItemStack slot_stack = slot.getItem(); - ItemStack transferred = slot_stack.copy(); - if((index==2) || (index==7) || (index==8)) { - // Output slots - if(!moveItemStackTo(slot_stack, PLAYER_INV_START_SLOTNO, PLAYER_INV_START_SLOTNO+36, true)) return ItemStack.EMPTY; - slot.onQuickCraft(slot_stack, transferred); - } else if((index==0) || (index==3) || (index==4)) { - // Input slots - if(!moveItemStackTo(slot_stack, PLAYER_INV_START_SLOTNO, PLAYER_INV_START_SLOTNO+36, false)) return ItemStack.EMPTY; - } else if((index==1) || (index==5) || (index==6)) { - // Fuel slots - if(!moveItemStackTo(slot_stack, PLAYER_INV_START_SLOTNO, PLAYER_INV_START_SLOTNO+36, false)) return ItemStack.EMPTY; - } else if((index==9) || (index==10)) { - if(!moveItemStackTo(slot_stack, PLAYER_INV_START_SLOTNO, PLAYER_INV_START_SLOTNO+36, false)) return ItemStack.EMPTY; - } else if((index >= PLAYER_INV_START_SLOTNO) && (index <= PLAYER_INV_START_SLOTNO+36)) { - // Player inventory - if(FurnaceTileEntity.canSmelt(world(), slot_stack)) { - if( - (!moveItemStackTo(slot_stack, 0, 1, false)) && // smelting input - (!moveItemStackTo(slot_stack, 3, 4, false)) && // fifo0 - (!moveItemStackTo(slot_stack, 4, 5, false)) // fifo1 - ) return ItemStack.EMPTY; - } else if(FurnaceTileEntity.isFuel(player_.level, slot_stack)) { - if( - (!moveItemStackTo(slot_stack, 1, 2, false)) && // fuel input - (!moveItemStackTo(slot_stack, 5, 6, false)) && // fuel fifo0 - (!moveItemStackTo(slot_stack, 6, 7, false)) // fuel fifo1 - ) return ItemStack.EMPTY; - } else if((index >= PLAYER_INV_START_SLOTNO) && (index < PLAYER_INV_START_SLOTNO+27)) { - // player inventory --> player hotbar - if(!moveItemStackTo(slot_stack, PLAYER_INV_START_SLOTNO+27, PLAYER_INV_START_SLOTNO+36, false)) return ItemStack.EMPTY; - } else if((index >= PLAYER_INV_START_SLOTNO+27) && (index < PLAYER_INV_START_SLOTNO+36) && (!moveItemStackTo(slot_stack, PLAYER_INV_START_SLOTNO, PLAYER_INV_START_SLOTNO+27, false))) { - // player hotbar --> player inventory - return ItemStack.EMPTY; + @Override + public void load(CompoundTag nbt) { + super.load(nbt); + readnbt(nbt); + } + + // IContainerProvider ---------------------------------------------------------------------- + + @Override + protected void saveAdditional(CompoundTag nbt) { + super.saveAdditional(nbt); + writenbt(nbt); + } + + @Override + public void setRemoved() { + super.setRemoved(); + item_extraction_handler_.invalidate(); + item_insertion_handler_.invalidate(); + item_fuel_insertion_handler_.invalidate(); + energy_handler_.invalidate(); + } + + // Fields ----------------------------------------------------------------------------------------------- + + @Override + public Component getName() { + return Auxiliaries.localizable(getBlockState().getBlock().getDescriptionId()); + } + + // Capability export ---------------------------------------------------------------------------- + + @Override + public boolean hasCustomName() { + return false; + } + + // ITickableTileEntity ------------------------------------------------------------------------- + + @Override + public Component getCustomName() { + return getName(); + } + + // Furnace ------------------------------------------------------------------------------------- + + @Override + public Component getDisplayName() { + return Nameable.super.getDisplayName(); + } + + @Override + public AbstractContainerMenu createMenu(int id, Inventory inventory, Player player) { + return new FurnaceContainer(id, inventory, inventory_, ContainerLevelAccess.create(level, worldPosition), fields); + } + + @Override + public LazyOptional getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable Direction facing) { + if (capability == ForgeCapabilities.ITEM_HANDLER) { + if (facing == Direction.UP) return item_insertion_handler_.cast(); + if (facing == Direction.DOWN) return item_extraction_handler_.cast(); + return item_fuel_insertion_handler_.cast(); + } else if (capability == ForgeCapabilities.ENERGY) { + return energy_handler_.cast(); + } + return super.getCapability(capability, facing); + } + + @Override + public void tick() { + if (--tick_timer_ > 0) return; + tick_timer_ = TICK_INTERVAL; + final BlockState state = level.getBlockState(worldPosition); + if (!(state.getBlock() instanceof FurnaceBlock)) return; + final boolean was_burning = burning(); + if (was_burning) burntime_left_ -= TICK_INTERVAL; + if (burntime_left_ < 0) burntime_left_ = 0; + if (level.isClientSide) return; + boolean dirty = false; + if (--fifo_timer_ <= 0) { + fifo_timer_ = FIFO_INTERVAL / TICK_INTERVAL; + // note, intentionally not using bitwise OR piping. + if (transferItems(FIFO_OUTPUT_0_SLOT_NO, FIFO_OUTPUT_1_SLOT_NO, 1)) dirty = true; + if (transferItems(SMELTING_OUTPUT_SLOT_NO, FIFO_OUTPUT_0_SLOT_NO, 1)) dirty = true; + if (transferItems(FIFO_FUEL_0_SLOT_NO, SMELTING_FUEL_SLOT_NO, 1)) dirty = true; + if (transferItems(FIFO_FUEL_1_SLOT_NO, FIFO_FUEL_0_SLOT_NO, 1)) dirty = true; + if (transferItems(FIFO_INPUT_0_SLOT_NO, SMELTING_INPUT_SLOT_NO, 1)) dirty = true; + if (transferItems(FIFO_INPUT_1_SLOT_NO, FIFO_INPUT_0_SLOT_NO, 1)) dirty = true; + heater_inserted_ = accepted_heaters_.isEmpty() // without IE always allow electrical boost + || accepted_heaters_.contains(inventory_.getItem(AUX_0_SLOT_NO).getItem()) + || accepted_heaters_.contains(inventory_.getItem(AUX_1_SLOT_NO).getItem()); + } + ItemStack fuel = inventory_.getItem(SMELTING_FUEL_SLOT_NO); + if (burning() || (!fuel.isEmpty()) && (!(inventory_.getItem(SMELTING_INPUT_SLOT_NO)).isEmpty())) { + Recipe last_recipe = currentRecipe(); + updateCurrentRecipe(); + if (currentRecipe() != last_recipe) { + proc_time_elapsed_ = 0; + proc_time_needed_ = getSmeltingTimeNeeded(level, inventory_.getItem(SMELTING_INPUT_SLOT_NO)); + } + if (!burning() && canSmeltCurrentItem()) { + burntime_left_ = (int) Mth.clamp((proc_fuel_efficiency_ * getFuelBurntime(level, fuel)), 0, MAX_BURNTIME); + fuel_burntime_ = (int) Mth.clamp(((double) burntime_left_) / ((proc_speed_ > 0) ? proc_speed_ : 1), 1, MAX_BURNTIME); + if (burning()) { + dirty = true; + if (!fuel.isEmpty()) { + Item fuel_item = fuel.getItem(); + fuel.shrink(1); + if (fuel.isEmpty()) + inventory_.setItem(SMELTING_FUEL_SLOT_NO, fuel_item.getCraftingRemainingItem(fuel)); + } + } + } + if (burning() && canSmeltCurrentItem()) { + proc_time_elapsed_ += TICK_INTERVAL * proc_speed_; + if (heater_inserted_ && battery_.draw(boost_energy_consumption)) { + proc_time_elapsed_ += (TICK_INTERVAL * proc_speed_) * 2; + } + if (proc_time_elapsed_ >= proc_time_needed_) { + proc_time_elapsed_ = 0; + proc_time_needed_ = getSmeltingTimeNeeded(level, inventory_.getItem(SMELTING_INPUT_SLOT_NO)); + smeltCurrentItem(); + dirty = true; + } + } else { + proc_time_elapsed_ = 0; + } + } else if ((!burning()) && (proc_time_elapsed_ > 0)) { + proc_time_elapsed_ = Mth.clamp(proc_time_elapsed_ - 2, 0, proc_time_needed_); + } + if (was_burning != burning()) { + dirty = true; + level.setBlockAndUpdate(worldPosition, state.setValue(FurnaceBlock.LIT, burning())); + } + if (dirty) { + setChanged(); + } + field_is_burning_ = this.burning() ? 1 : 0; + field_proc_time_elapsed_ = (int) proc_time_elapsed_; + } + + public boolean burning() { + return burntime_left_ > 0; + } + + public int getSmeltingTimeNeeded(Level world, ItemStack stack) { + if (stack.isEmpty()) return 0; + AbstractCookingRecipe recipe = getSmeltingResult(RECIPE_TYPE, world, stack); + if (recipe == null) return 0; + int t = recipe.getCookingTime(); + return (t <= 0) ? DEFAULT_SMELTING_TIME : t; + } + + private boolean transferItems(final int index_from, final int index_to, int count) { + ItemStack from = inventory_.getItem(index_from); + if (from.isEmpty()) return false; + ItemStack to = inventory_.getItem(index_to); + if (from.getCount() < count) count = from.getCount(); + if (count <= 0) return false; + boolean changed = true; + if (to.isEmpty()) { + inventory_.setItem(index_to, from.split(count)); + } else if (to.getCount() >= to.getMaxStackSize()) { + changed = false; + } else if (Inventories.areItemStacksDifferent(from, to)) { + changed = false; + } else { + if ((to.getCount() + count) >= to.getMaxStackSize()) { + from.shrink(to.getMaxStackSize() - to.getCount()); + to.setCount(to.getMaxStackSize()); + } else { + from.shrink(count); + to.grow(count); + } + } + if (from.isEmpty() && from != ItemStack.EMPTY) { + inventory_.setItem(index_from, ItemStack.EMPTY); + changed = true; + } + return changed; + } + + protected boolean canSmeltCurrentItem() { + if ((currentRecipe() == null) || (inventory_.getItem(SMELTING_INPUT_SLOT_NO).isEmpty())) return false; + final ItemStack recipe_result_items = getSmeltingResult(inventory_.getItem(SMELTING_INPUT_SLOT_NO)); + if (recipe_result_items.isEmpty()) return false; + final ItemStack result_stack = inventory_.getItem(SMELTING_OUTPUT_SLOT_NO); + if (result_stack.isEmpty()) return true; + if (!result_stack.is(recipe_result_items.getItem())) return false; + if (result_stack.getCount() + recipe_result_items.getCount() <= inventory_.getMaxStackSize() && result_stack.getCount() + recipe_result_items.getCount() <= result_stack.getMaxStackSize()) + return true; + return result_stack.getCount() + recipe_result_items.getCount() <= recipe_result_items.getMaxStackSize(); + } + + protected void smeltCurrentItem() { + if (!canSmeltCurrentItem()) return; + final ItemStack smelting_input_stack = inventory_.getItem(SMELTING_INPUT_SLOT_NO); + final ItemStack recipe_result_items = getSmeltingResult(smelting_input_stack); + final ItemStack smelting_output_stack = inventory_.getItem(SMELTING_OUTPUT_SLOT_NO); + final float xp = getCurrentSmeltingXp(smelting_output_stack); + if (smelting_output_stack.isEmpty()) { + inventory_.setItem(SMELTING_OUTPUT_SLOT_NO, recipe_result_items.copy()); + } else if (smelting_output_stack.getItem() == recipe_result_items.getItem()) { + smelting_output_stack.grow(recipe_result_items.getCount()); + } + smelting_input_stack.shrink(1); + xp_stored_ += xp; + } + + public int consumeSmeltingExperience(ItemStack stack) { + if (xp_stored_ < 1) return 0; + float xp = xp_stored_; + if (xp >= 15) xp /= 2; + xp = Math.min((float) Math.floor(xp), 150); + xp_stored_ -= xp; + return (int) xp; + } + + public ItemStack getSmeltingResult(final ItemStack stack) { + return (currentRecipe() == null) ? (ItemStack.EMPTY) : (currentRecipe().getResultItem(getLevel().registryAccess())); + } + + public float getCurrentSmeltingXp(final ItemStack stack) { + float xp = (currentRecipe() instanceof AbstractCookingRecipe) ? (((AbstractCookingRecipe) currentRecipe()).getExperience()) : (0); + return (xp <= 0) ? 0.7f : xp; // default value for recipes without defined xp + } + + @Nullable + protected Recipe currentRecipe() { + return current_recipe_; + } + + protected void updateCurrentRecipe() { + setCurrentRecipe(getSmeltingResult(RECIPE_TYPE, getLevel(), inventory_.getItem(SMELTING_INPUT_SLOT_NO))); + } + + protected void setCurrentRecipe(Recipe recipe) { + current_recipe_ = recipe; } - } else { - // invalid slot - return ItemStack.EMPTY; - } - if(slot_stack.isEmpty()) { - slot.set(ItemStack.EMPTY); - } else { - slot.setChanged(); - } - if(slot_stack.getCount() == transferred.getCount()) return ItemStack.EMPTY; - slot.onTake(player, slot_stack); - //if(!player.world.isRemote) detectAndSendChanges(); - return transferred; } - // INetworkSynchronisableContainer --------------------------------------------------------- + //-------------------------------------------------------------------------------------------------------------------- + // Container slots + //-------------------------------------------------------------------------------------------------------------------- + + public static class FurnaceContainer extends AbstractContainerMenu implements Networking.INetworkSynchronisableContainer { + // Slots -------------------------------------------------------------------------------------------- + + private static final int PLAYER_INV_START_SLOTNO = 11; + protected final Player player_; + + // Container ---------------------------------------------------------------------------------------- + protected final Container inventory_; + protected final ContainerLevelAccess wpc_; + private final ContainerData fields_; + private final RecipeType recipe_type_; + public FurnaceContainer(int cid, Inventory player_inventory) { + this(cid, player_inventory, new SimpleContainer(FurnaceTileEntity.NUM_OF_SLOTS), ContainerLevelAccess.NULL, new SimpleContainerData(FurnaceTileEntity.NUM_OF_FIELDS)); + } + private FurnaceContainer(int cid, Inventory player_inventory, Container block_inventory, ContainerLevelAccess wpc, ContainerData fields) { + super(ModContent.getMenuType("small_lab_furnace"), cid); // @todo: class mapping + player_ = player_inventory.player; + inventory_ = block_inventory; + wpc_ = wpc; + fields_ = fields; + recipe_type_ = FurnaceTileEntity.RECIPE_TYPE; + addSlot(new Slot(inventory_, 0, 59, 17)); // smelting input + addSlot(new FuelSlot(inventory_, 1, 59, 53, this)); // fuel + addSlot(new OutputSlot(player_, inventory_, 2, 101, 35)); // smelting result + addSlot(new Slot(inventory_, 3, 34, 17)); // input fifo 0 + addSlot(new Slot(inventory_, 4, 16, 17)); // input fifo 1 + addSlot(new Slot(inventory_, 5, 34, 53)); // fuel fifo 0 + addSlot(new Slot(inventory_, 6, 16, 53)); // fuel fifo 1 + addSlot(new OutputSlot(player_inventory.player, inventory_, 7, 126, 35)); // out fifo 0 + addSlot(new OutputSlot(player_inventory.player, inventory_, 8, 144, 35)); // out fifo 1 + addSlot(new Slot(inventory_, 9, 126, 61)); // aux slot 1 + addSlot(new Slot(inventory_, 10, 144, 61)); // aux slot 2 + for (int x = 0; x < 9; ++x) { + addSlot(new Slot(player_inventory, x, 8 + x * 18, 144)); // player slots: 0..8 + } + for (int y = 0; y < 3; ++y) { + for (int x = 0; x < 9; ++x) { + addSlot(new Slot(player_inventory, x + y * 9 + 9, 8 + x * 18, 86 + y * 18)); // player slots: 9..35 + } + } + this.addDataSlots(fields_); // === Add reference holders + } + + public int field(int index) { + return fields_.get(index); + } + + public Player player() { + return player_; + } + + public Container inventory() { + return inventory_; + } + + public Level world() { + return player_.level(); + } + + @Override + public boolean stillValid(Player player) { + return inventory_.stillValid(player); + } + + @Override + public ItemStack quickMoveStack(Player player, int index) { + Slot slot = getSlot(index); + if ((slot == null) || (!slot.hasItem())) return ItemStack.EMPTY; + ItemStack slot_stack = slot.getItem(); + ItemStack transferred = slot_stack.copy(); + if ((index == 2) || (index == 7) || (index == 8)) { + // Output slots + if (!moveItemStackTo(slot_stack, PLAYER_INV_START_SLOTNO, PLAYER_INV_START_SLOTNO + 36, true)) + return ItemStack.EMPTY; + slot.onQuickCraft(slot_stack, transferred); + } else if ((index == 0) || (index == 3) || (index == 4)) { + // Input slots + if (!moveItemStackTo(slot_stack, PLAYER_INV_START_SLOTNO, PLAYER_INV_START_SLOTNO + 36, false)) + return ItemStack.EMPTY; + } else if ((index == 1) || (index == 5) || (index == 6)) { + // Fuel slots + if (!moveItemStackTo(slot_stack, PLAYER_INV_START_SLOTNO, PLAYER_INV_START_SLOTNO + 36, false)) + return ItemStack.EMPTY; + } else if ((index == 9) || (index == 10)) { + if (!moveItemStackTo(slot_stack, PLAYER_INV_START_SLOTNO, PLAYER_INV_START_SLOTNO + 36, false)) + return ItemStack.EMPTY; + } else if ((index >= PLAYER_INV_START_SLOTNO) && (index <= PLAYER_INV_START_SLOTNO + 36)) { + // Player inventory + if (FurnaceTileEntity.canSmelt(world(), slot_stack)) { + if ( + (!moveItemStackTo(slot_stack, 0, 1, false)) && // smelting input + (!moveItemStackTo(slot_stack, 3, 4, false)) && // fifo0 + (!moveItemStackTo(slot_stack, 4, 5, false)) // fifo1 + ) return ItemStack.EMPTY; + } else if (FurnaceTileEntity.isFuel(player_.level(), slot_stack)) { + if ( + (!moveItemStackTo(slot_stack, 1, 2, false)) && // fuel input + (!moveItemStackTo(slot_stack, 5, 6, false)) && // fuel fifo0 + (!moveItemStackTo(slot_stack, 6, 7, false)) // fuel fifo1 + ) return ItemStack.EMPTY; + } else if ((index >= PLAYER_INV_START_SLOTNO) && (index < PLAYER_INV_START_SLOTNO + 27)) { + // player inventory --> player hotbar + if (!moveItemStackTo(slot_stack, PLAYER_INV_START_SLOTNO + 27, PLAYER_INV_START_SLOTNO + 36, false)) + return ItemStack.EMPTY; + } else if ((index >= PLAYER_INV_START_SLOTNO + 27) && (index < PLAYER_INV_START_SLOTNO + 36) && (!moveItemStackTo(slot_stack, PLAYER_INV_START_SLOTNO, PLAYER_INV_START_SLOTNO + 27, false))) { + // player hotbar --> player inventory + return ItemStack.EMPTY; + } + } else { + // invalid slot + return ItemStack.EMPTY; + } + if (slot_stack.isEmpty()) { + slot.set(ItemStack.EMPTY); + } else { + slot.setChanged(); + } + if (slot_stack.getCount() == transferred.getCount()) return ItemStack.EMPTY; + slot.onTake(player, slot_stack); + //if(!player.world.isRemote) detectAndSendChanges(); + return transferred; + } + + @OnlyIn(Dist.CLIENT) + public void onGuiAction(CompoundTag nbt) { + Networking.PacketContainerSyncClientToServer.sendToServer(containerId, nbt); + } + + @OnlyIn(Dist.CLIENT) + public void onGuiAction(String key, int value) { + CompoundTag nbt = new CompoundTag(); + nbt.putInt(key, value); + Networking.PacketContainerSyncClientToServer.sendToServer(containerId, nbt); + } + + // INetworkSynchronisableContainer --------------------------------------------------------- + + @Override + public void onServerPacketReceived(int windowId, CompoundTag nbt) { + } + + @Override + public void onClientPacketReceived(int windowId, Player player, CompoundTag nbt) { + } + + public static class OutputSlot extends Slot { + private final Container inventory_; + private final Player player_; + private int removeCount = 0; + + public OutputSlot(Player player, Container inventory, int index, int xpos, int ypos) { + super(inventory, index, xpos, ypos); + inventory_ = inventory; + player_ = player; + } + + @Override + public boolean mayPlace(ItemStack stack) { + return false; + } + + @Override + public ItemStack remove(int amount) { + removeCount += hasItem() ? Math.min(amount, getItem().getCount()) : 0; + return super.remove(amount); + } + + @Override + public void onTake(Player thePlayer, ItemStack stack) { + checkTakeAchievements(stack); + super.onTake(thePlayer, stack); + } + + @Override + protected void onQuickCraft(ItemStack stack, int amount) { + removeCount += amount; + checkTakeAchievements(stack); + } + + @Override + protected void checkTakeAchievements(ItemStack stack) { + stack.onCraftedBy(player_.level(), player_, removeCount); + if ((!player_.level().isClientSide()) && (inventory_ instanceof Inventories.StorageInventory) && + ((((Inventories.StorageInventory) inventory_).getBlockEntity()) instanceof final FurnaceTileEntity te) + ) { + int xp = te.consumeSmeltingExperience(stack); + while (xp > 0) { + int k = ExperienceOrb.getExperienceValue(xp); + xp -= k; + player_.level().addFreshEntity((new ExperienceOrb(player_.level(), player_.blockPosition().getX(), player_.blockPosition().getY() + 0.5, player_.blockPosition().getZ() + 0.5, k))); + } + } + removeCount = 0; + ForgeEventFactory.firePlayerSmeltedEvent(player_, stack); + } + } + + public static class FuelSlot extends Slot { + private final FurnaceContainer container_; + + public FuelSlot(Container inventory, int index, int xpos, int ypos, FurnaceContainer container) { + super(inventory, index, xpos, ypos); + container_ = container; + } + + protected static boolean isBucket(ItemStack stack) { + return (stack.getItem() == Items.BUCKET); + } + + @Override + public boolean mayPlace(ItemStack stack) { + return isBucket(stack) || (FurnaceTileEntity.isFuel(container_.world(), stack)); + } + + @Override + public int getMaxStackSize(ItemStack stack) { + return isBucket(stack) ? 1 : super.getMaxStackSize(stack); + } + } + + } + + //-------------------------------------------------------------------------------------------------------------------- + // GUI + //-------------------------------------------------------------------------------------------------------------------- @OnlyIn(Dist.CLIENT) - public void onGuiAction(CompoundTag nbt) - { Networking.PacketContainerSyncClientToServer.sendToServer(containerId, nbt); } + public static class FurnaceGui extends Guis.ContainerGui { + public FurnaceGui(FurnaceContainer container, Inventory player_inventory, Component title) { + super(container, player_inventory, title, "textures/gui/small_lab_furnace_gui.png"); + } - @OnlyIn(Dist.CLIENT) - public void onGuiAction(String key, int value) - { - CompoundTag nbt = new CompoundTag(); - nbt.putInt(key, value); - Networking.PacketContainerSyncClientToServer.sendToServer(containerId, nbt); + @Override + protected void renderBgWidgets(GuiGraphics mx, float partialTicks, int mouseX, int mouseY) { + final int x0 = leftPos, y0 = topPos, w = imageWidth, h = imageHeight; + if (getMenu().field(4) != 0) { + final int k = flame_px(13); + mx.blit(getBackgroundImage(), x0 + 59, y0 + 36 + 12 - k, 176, 12 - k, 14, k + 1); + } + mx.blit(getBackgroundImage(), x0 + 79, y0 + 36, 176, 15, 1 + progress_px(17), 15); + } + + private int progress_px(int pixels) { + final int tc = getMenu().field(2), T = getMenu().field(3); + return ((T > 0) && (tc > 0)) ? (tc * pixels / T) : (0); + } + + private int flame_px(int pixels) { + int ibt = getMenu().field(1); + return ((getMenu().field(0) * pixels) / ((ibt > 0) ? (ibt) : (FurnaceTileEntity.DEFAULT_SMELTING_TIME))); + } } - @Override - public void onServerPacketReceived(int windowId, CompoundTag nbt) - {} - - @Override - public void onClientPacketReceived(int windowId, Player player, CompoundTag nbt) - {} - - } - - //-------------------------------------------------------------------------------------------------------------------- - // GUI - //-------------------------------------------------------------------------------------------------------------------- - - @OnlyIn(Dist.CLIENT) - public static class FurnaceGui extends Guis.ContainerGui - { - public FurnaceGui(FurnaceContainer container, Inventory player_inventory, Component title) - { super(container, player_inventory, title, "textures/gui/small_lab_furnace_gui.png"); } - - @Override - protected void renderBgWidgets(PoseStack mx, float partialTicks, int mouseX, int mouseY) - { - final int x0=leftPos, y0=topPos, w=imageWidth, h=imageHeight; - if(getMenu().field(4) != 0) { - final int k = flame_px(13); - blit(mx, x0+59, y0+36+12-k, 176, 12-k, 14, k+1); - } - blit(mx, x0+79, y0+36, 176, 15, 1+progress_px(17), 15); - } - - private int progress_px(int pixels) - { final int tc=getMenu().field(2), T=getMenu().field(3); return ((T>0) && (tc>0)) ? (tc * pixels / T) : (0); } - - private int flame_px(int pixels) - { int ibt = getMenu().field(1); return ((getMenu().field(0) * pixels) / ((ibt>0) ? (ibt) : (FurnaceTileEntity.DEFAULT_SMELTING_TIME))); } - } - } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdGlassBlock.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdGlassBlock.java index 6de1b58..0e29cd1 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdGlassBlock.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdGlassBlock.java @@ -9,6 +9,8 @@ */ package dev.zontreck.engineerdecor.blocks; +import dev.zontreck.libzontreck.edlibmc.Auxiliaries; +import dev.zontreck.libzontreck.edlibmc.StandardBlocks; import net.minecraft.core.Direction; import net.minecraft.network.chat.Component; import net.minecraft.world.item.DyeColor; @@ -20,35 +22,37 @@ import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -import dev.zontreck.engineerdecor.libmc.StandardBlocks; -import dev.zontreck.engineerdecor.libmc.Auxiliaries; import javax.annotation.Nullable; import java.util.List; -public class EdGlassBlock extends StainedGlassBlock implements StandardBlocks.IStandardBlock -{ - public EdGlassBlock(long config, BlockBehaviour.Properties properties) - { super(DyeColor.BLACK, properties); } +public class EdGlassBlock extends StainedGlassBlock implements StandardBlocks.IStandardBlock { + public EdGlassBlock(long config, BlockBehaviour.Properties properties) { + super(DyeColor.BLACK, properties); + } - @Override - @OnlyIn(Dist.CLIENT) - public void appendHoverText(ItemStack stack, @Nullable BlockGetter world, List tooltip, TooltipFlag flag) - { Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true); } + @Override + @OnlyIn(Dist.CLIENT) + public void appendHoverText(ItemStack stack, @Nullable BlockGetter world, List tooltip, TooltipFlag flag) { + Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true); + } - @Override - public StandardBlocks.IStandardBlock.RenderTypeHint getRenderTypeHint() - { return StandardBlocks.IStandardBlock.RenderTypeHint.TRANSLUCENT; } + @Override + public StandardBlocks.IStandardBlock.RenderTypeHint getRenderTypeHint() { + return StandardBlocks.IStandardBlock.RenderTypeHint.TRANSLUCENT; + } - @Override - @OnlyIn(Dist.CLIENT) - @SuppressWarnings("deprecation") - public boolean skipRendering(BlockState state, BlockState adjacentBlockState, Direction side) - { return (adjacentBlockState.getBlock()==this) || (super.skipRendering(state, adjacentBlockState, side)); } + @Override + @OnlyIn(Dist.CLIENT) + @SuppressWarnings("deprecation") + public boolean skipRendering(BlockState state, BlockState adjacentBlockState, Direction side) { + return (adjacentBlockState.getBlock() == this) || (super.skipRendering(state, adjacentBlockState, side)); + } - @Override - public boolean isPossibleToRespawnInThis() - { return false; } + @Override + public boolean isPossibleToRespawnInThis(BlockState p_279289_) { + return false; + } } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdHatchBlock.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdHatchBlock.java index 9f368f5..5227fb2 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdHatchBlock.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdHatchBlock.java @@ -8,6 +8,8 @@ */ package dev.zontreck.engineerdecor.blocks; +import dev.zontreck.libzontreck.edlibmc.Auxiliaries; +import dev.zontreck.libzontreck.edlibmc.StandardBlocks; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.sounds.SoundEvents; @@ -22,6 +24,7 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.SignalGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; @@ -35,137 +38,141 @@ import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; -import dev.zontreck.engineerdecor.libmc.StandardBlocks; -import dev.zontreck.engineerdecor.libmc.Auxiliaries; import javax.annotation.Nullable; import java.util.List; -public class EdHatchBlock extends StandardBlocks.HorizontalWaterLoggable -{ - public static final BooleanProperty OPEN = BlockStateProperties.OPEN; - public static final BooleanProperty POWERED = BlockStateProperties.POWERED; - protected final List vshapes_open; +public class EdHatchBlock extends StandardBlocks.HorizontalWaterLoggable { + public static final BooleanProperty OPEN = BlockStateProperties.OPEN; + public static final BooleanProperty POWERED = BlockStateProperties.POWERED; + protected final List vshapes_open; - public EdHatchBlock(long config, BlockBehaviour.Properties builder, final AABB unrotatedAABBClosed, final AABB unrotatedAABBOpen) - { - super(config, builder, unrotatedAABBClosed); vshapes_open = makeHorizontalShapeLookup(new AABB[]{unrotatedAABBOpen}); - registerDefaultState(super.defaultBlockState().setValue(OPEN, false).setValue(POWERED, false)); - } - - public EdHatchBlock(long config, BlockBehaviour.Properties builder, final AABB[] unrotatedAABBsClosed, final AABB[] unrotatedAABBsOpen) - { super(config, builder, unrotatedAABBsClosed); vshapes_open = makeHorizontalShapeLookup(unrotatedAABBsOpen); } - - protected static List makeHorizontalShapeLookup(final AABB[] unrotatedAABBs) - { - return List.of( - Shapes.block(), - Shapes.block(), - Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(unrotatedAABBs, Direction.NORTH, true)), - Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(unrotatedAABBs, Direction.SOUTH, true)), - Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(unrotatedAABBs, Direction.WEST, true)), - Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(unrotatedAABBs, Direction.EAST, true)) - ); - } - - @Override - public RenderTypeHint getRenderTypeHint() - { return RenderTypeHint.CUTOUT; } - - @Override - public VoxelShape getShape(BlockState state, BlockGetter source, BlockPos pos, CollisionContext selectionContext) - { return state.getValue(OPEN) ? vshapes_open.get((state.getValue(HORIZONTAL_FACING)).get3DDataValue()) : super.getShape(state, source, pos, selectionContext); } - - @Override - public VoxelShape getCollisionShape(BlockState state, BlockGetter source, BlockPos pos, CollisionContext selectionContext) - { return getShape(state, source, pos, selectionContext); } - - @Override - public boolean propagatesSkylightDown(BlockState state, BlockGetter reader, BlockPos pos) - { return state.getValue(OPEN); } - - @Override - @SuppressWarnings("deprecation") - public boolean isPathfindable(BlockState state, BlockGetter world, BlockPos pos, PathComputationType type) - { return !state.getValue(OPEN); } - - @Override - public boolean isLadder(BlockState state, LevelReader world, BlockPos pos, LivingEntity entity) - { - if(!state.getValue(OPEN)) return false; - { - final BlockState up_state = world.getBlockState(pos.above()); - if(up_state.is(this) && (up_state.getValue(OPEN))) return true; - if(up_state.isLadder(world, pos.above(), entity)) return true; + public EdHatchBlock(long config, BlockBehaviour.Properties builder, final AABB unrotatedAABBClosed, final AABB unrotatedAABBOpen) { + super(config, builder, unrotatedAABBClosed); + vshapes_open = makeHorizontalShapeLookup(new AABB[]{unrotatedAABBOpen}); + registerDefaultState(super.defaultBlockState().setValue(OPEN, false).setValue(POWERED, false)); } - { - final BlockState down_state = world.getBlockState(pos.below()); - if(down_state.is(this) && (down_state.getValue(OPEN))) return true; - if(down_state.isLadder(world, pos.below(), entity)) return true; + + public EdHatchBlock(long config, BlockBehaviour.Properties builder, final AABB[] unrotatedAABBsClosed, final AABB[] unrotatedAABBsOpen) { + super(config, builder, unrotatedAABBsClosed); + vshapes_open = makeHorizontalShapeLookup(unrotatedAABBsOpen); } - return false; - } - @Override - public boolean isValidSpawn(BlockState state, BlockGetter world, BlockPos pos, SpawnPlacements.Type type, @Nullable EntityType entityType) - { return false; } + protected static List makeHorizontalShapeLookup(final AABB[] unrotatedAABBs) { + return List.of( + Shapes.block(), + Shapes.block(), + Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(unrotatedAABBs, Direction.NORTH, true)), + Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(unrotatedAABBs, Direction.SOUTH, true)), + Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(unrotatedAABBs, Direction.WEST, true)), + Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(unrotatedAABBs, Direction.EAST, true)) + ); + } - @Override - protected void createBlockStateDefinition(StateDefinition.Builder builder) - { super.createBlockStateDefinition(builder); builder.add(OPEN, POWERED); } + @Override + public RenderTypeHint getRenderTypeHint() { + return RenderTypeHint.CUTOUT; + } - @Override - @SuppressWarnings("deprecation") - public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult) - { - if(world.isClientSide()) return InteractionResult.SUCCESS; - boolean open = !state.getValue(OPEN); - world.setBlock(pos, state.setValue(OPEN, open), 1|2); - world.playSound(null, pos, open?SoundEvents.IRON_DOOR_OPEN:SoundEvents.IRON_DOOR_CLOSE, SoundSource.BLOCKS, 0.7f, 1.4f); - return InteractionResult.CONSUME; - } + @Override + public VoxelShape getShape(BlockState state, BlockGetter source, BlockPos pos, CollisionContext selectionContext) { + return state.getValue(OPEN) ? vshapes_open.get((state.getValue(HORIZONTAL_FACING)).get3DDataValue()) : super.getShape(state, source, pos, selectionContext); + } - @Override - @SuppressWarnings("deprecation") - public void neighborChanged(BlockState state, Level world, BlockPos pos, Block block, BlockPos fromPos, boolean isMoving) - { - if((world.isClientSide) || (!(state.getBlock() instanceof EdHatchBlock))) return; - boolean powered = world.hasNeighborSignal(pos); - if(powered == state.getValue(POWERED)) return; - if(powered != state.getValue(OPEN)) world.playSound(null, pos, powered?SoundEvents.IRON_DOOR_OPEN:SoundEvents.IRON_DOOR_CLOSE, SoundSource.BLOCKS, 0.7f, 1.4f); - world.setBlock(pos, state.setValue(OPEN, powered).setValue(POWERED, powered), 1|2); - } + @Override + public VoxelShape getCollisionShape(BlockState state, BlockGetter source, BlockPos pos, CollisionContext selectionContext) { + return getShape(state, source, pos, selectionContext); + } - @Override - @SuppressWarnings("deprecation") - public boolean useShapeForLightOcclusion(BlockState state) { - return !state.getValue(OPEN); - } + @Override + public boolean propagatesSkylightDown(BlockState state, BlockGetter reader, BlockPos pos) { + return state.getValue(OPEN); + } - @Override - @SuppressWarnings("deprecation") - public VoxelShape getOcclusionShape(BlockState state, BlockGetter world, BlockPos pos) { - return state.getValue(OPEN) ? Shapes.empty() : super.getOcclusionShape(state, world, pos); - } + @Override + @SuppressWarnings("deprecation") + public boolean isPathfindable(BlockState state, BlockGetter world, BlockPos pos, PathComputationType type) { + return !state.getValue(OPEN); + } - @Override - public boolean shouldCheckWeakPower(BlockState state, LevelReader world, BlockPos pos, Direction side) - { return false; } + @Override + public boolean isLadder(BlockState state, LevelReader world, BlockPos pos, LivingEntity entity) { + if (!state.getValue(OPEN)) return false; + { + final BlockState up_state = world.getBlockState(pos.above()); + if (up_state.is(this) && (up_state.getValue(OPEN))) return true; + if (up_state.isLadder(world, pos.above(), entity)) return true; + } + { + final BlockState down_state = world.getBlockState(pos.below()); + if (down_state.is(this) && (down_state.getValue(OPEN))) return true; + if (down_state.isLadder(world, pos.below(), entity)) return true; + } + return false; + } - @Override - @SuppressWarnings("deprecation") - public void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) - { - if((!state.getValue(OPEN)) || (!(entity instanceof final Player player))) return; - if(player.isSteppingCarefully()) return; - if(entity.getLookAngle().y() > -0.75) return; - if(player.getDirection() != state.getValue(HORIZONTAL_FACING)) return; - Vec3 ppos = player.position(); - Vec3 centre = Vec3.atBottomCenterOf(pos); - Vec3 v = centre.subtract(ppos); - if(ppos.y() < (centre.y()-0.1) || (v.lengthSqr() > 0.3)) return; - v = v.scale(0.2); - player.push(v.x, -0.1, v.z); - } + @Override + public boolean isValidSpawn(BlockState state, BlockGetter world, BlockPos pos, SpawnPlacements.Type type, @Nullable EntityType entityType) { + return false; + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + super.createBlockStateDefinition(builder); + builder.add(OPEN, POWERED); + } + + @Override + @SuppressWarnings("deprecation") + public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult) { + if (world.isClientSide()) return InteractionResult.SUCCESS; + boolean open = !state.getValue(OPEN); + world.setBlock(pos, state.setValue(OPEN, open), 1 | 2); + world.playSound(null, pos, open ? SoundEvents.IRON_DOOR_OPEN : SoundEvents.IRON_DOOR_CLOSE, SoundSource.BLOCKS, 0.7f, 1.4f); + return InteractionResult.CONSUME; + } + + @Override + @SuppressWarnings("deprecation") + public void neighborChanged(BlockState state, Level world, BlockPos pos, Block block, BlockPos fromPos, boolean isMoving) { + if ((world.isClientSide) || (!(state.getBlock() instanceof EdHatchBlock))) return; + boolean powered = world.hasNeighborSignal(pos); + if (powered == state.getValue(POWERED)) return; + if (powered != state.getValue(OPEN)) + world.playSound(null, pos, powered ? SoundEvents.IRON_DOOR_OPEN : SoundEvents.IRON_DOOR_CLOSE, SoundSource.BLOCKS, 0.7f, 1.4f); + world.setBlock(pos, state.setValue(OPEN, powered).setValue(POWERED, powered), 1 | 2); + } + + @Override + @SuppressWarnings("deprecation") + public boolean useShapeForLightOcclusion(BlockState state) { + return !state.getValue(OPEN); + } + + @Override + @SuppressWarnings("deprecation") + public VoxelShape getOcclusionShape(BlockState state, BlockGetter world, BlockPos pos) { + return state.getValue(OPEN) ? Shapes.empty() : super.getOcclusionShape(state, world, pos); + } + + @Override + public boolean shouldCheckWeakPower(BlockState state, SignalGetter level, BlockPos pos, Direction side) { + return false; + } + + @Override + @SuppressWarnings("deprecation") + public void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { + if ((!state.getValue(OPEN)) || (!(entity instanceof final Player player))) return; + if (player.isSteppingCarefully()) return; + if (entity.getLookAngle().y() > -0.75) return; + if (player.getDirection() != state.getValue(HORIZONTAL_FACING)) return; + Vec3 ppos = player.position(); + Vec3 centre = Vec3.atBottomCenterOf(pos); + Vec3 v = centre.subtract(ppos); + if (ppos.y() < (centre.y() - 0.1) || (v.lengthSqr() > 0.3)) return; + v = v.scale(0.2); + player.push(v.x, -0.1, v.z); + } } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdHopper.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdHopper.java index 717cdfe..3e93c31 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdHopper.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdHopper.java @@ -9,7 +9,9 @@ package dev.zontreck.engineerdecor.blocks; import com.mojang.blaze3d.vertex.PoseStack; -import dev.zontreck.engineerdecor.libmc.*; +import dev.zontreck.engineerdecor.ModContent; +import dev.zontreck.libzontreck.edlibmc.*; +import net.minecraft.client.gui.GuiGraphics; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; @@ -27,6 +29,7 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.SignalGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockBehaviour; @@ -42,8 +45,6 @@ import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.common.capabilities.ForgeCapabilities; import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.items.IItemHandler; -import dev.zontreck.engineerdecor.ModContent; -import wile.engineersdecor.libmc.*; import javax.annotation.Nullable; import java.util.ArrayList; @@ -51,255 +52,288 @@ import java.util.List; import java.util.function.Supplier; -public class EdHopper -{ - public static void on_config() - {} - - //-------------------------------------------------------------------------------------------------------------------- - // Block - //-------------------------------------------------------------------------------------------------------------------- - - public static class HopperBlock extends StandardBlocks.Directed implements StandardEntityBlocks.IStandardEntityBlock - { - public HopperBlock(long config, BlockBehaviour.Properties builder, final Supplier> shape_supplier) - { super(config, builder, shape_supplier); } - - @Override - public boolean isBlockEntityTicking(Level world, BlockState state) - { return true; } - - @Override - public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) - { return Shapes.block(); } - - @Override - @SuppressWarnings("deprecation") - public boolean hasAnalogOutputSignal(BlockState state) - { return true; } - - @Override - @SuppressWarnings("deprecation") - public int getAnalogOutputSignal(BlockState blockState, Level world, BlockPos pos) - { return (world.getBlockEntity(pos) instanceof EdHopper.HopperTileEntity te) ? RsSignals.fromContainer(te.storage_slot_range_) : 0; } - - @Override - public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) - { - if(world.isClientSide) return; - if((!stack.hasTag()) || (!stack.getTag().contains("tedata"))) return; - CompoundTag te_nbt = stack.getTag().getCompound("tedata"); - if(te_nbt.isEmpty()) return; - final BlockEntity te = world.getBlockEntity(pos); - if(!(te instanceof HopperTileEntity)) return; - ((HopperTileEntity)te).readnbt(te_nbt, false); - ((HopperTileEntity)te).reset_rtstate(); - te.setChanged(); +public class EdHopper { + public static void on_config() { } - @Override - public boolean hasDynamicDropList() - { return true; } + //-------------------------------------------------------------------------------------------------------------------- + // Block + //-------------------------------------------------------------------------------------------------------------------- - @Override - public List dropList(BlockState state, Level world, final BlockEntity te, boolean explosion) - { - final List stacks = new ArrayList<>(); - if(world.isClientSide) return stacks; - if(!(te instanceof HopperTileEntity)) return stacks; - if(!explosion) { - ItemStack stack = new ItemStack(this, 1); - CompoundTag te_nbt = ((HopperTileEntity)te).clear_getnbt(); - if(!te_nbt.isEmpty()) { - CompoundTag nbt = new CompoundTag(); - nbt.put("tedata", te_nbt); - stack.setTag(nbt); + public static class HopperBlock extends StandardBlocks.Directed implements StandardEntityBlocks.IStandardEntityBlock { + public HopperBlock(long config, BlockBehaviour.Properties builder, final Supplier> shape_supplier) { + super(config, builder, shape_supplier); } - stacks.add(stack); - } else { - for(ItemStack stack: ((HopperTileEntity)te).main_inventory_) { - if(!stack.isEmpty()) stacks.add(stack); + + @Override + public boolean isBlockEntityTicking(Level world, BlockState state) { + return true; } - ((HopperTileEntity)te).reset_rtstate(); - } - return stacks; + + @Override + public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) { + return Shapes.block(); + } + + @Override + @SuppressWarnings("deprecation") + public boolean hasAnalogOutputSignal(BlockState state) { + return true; + } + + @Override + @SuppressWarnings("deprecation") + public int getAnalogOutputSignal(BlockState blockState, Level world, BlockPos pos) { + return (world.getBlockEntity(pos) instanceof EdHopper.HopperTileEntity te) ? RsSignals.fromContainer(te.storage_slot_range_) : 0; + } + + @Override + public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) { + if (world.isClientSide) return; + if ((!stack.hasTag()) || (!stack.getTag().contains("tedata"))) return; + CompoundTag te_nbt = stack.getTag().getCompound("tedata"); + if (te_nbt.isEmpty()) return; + final BlockEntity te = world.getBlockEntity(pos); + if (!(te instanceof HopperTileEntity)) return; + ((HopperTileEntity) te).readnbt(te_nbt, false); + ((HopperTileEntity) te).reset_rtstate(); + te.setChanged(); + } + + @Override + public boolean hasDynamicDropList() { + return true; + } + + @Override + public List dropList(BlockState state, Level world, final BlockEntity te, boolean explosion) { + final List stacks = new ArrayList<>(); + if (world.isClientSide) return stacks; + if (!(te instanceof HopperTileEntity)) return stacks; + if (!explosion) { + ItemStack stack = new ItemStack(this, 1); + CompoundTag te_nbt = ((HopperTileEntity) te).clear_getnbt(); + if (!te_nbt.isEmpty()) { + CompoundTag nbt = new CompoundTag(); + nbt.put("tedata", te_nbt); + stack.setTag(nbt); + } + stacks.add(stack); + } else { + for (ItemStack stack : ((HopperTileEntity) te).main_inventory_) { + if (!stack.isEmpty()) stacks.add(stack); + } + ((HopperTileEntity) te).reset_rtstate(); + } + return stacks; + } + + @Override + @SuppressWarnings("deprecation") + public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult) { + return useOpenGui(state, world, pos, player); + } + + @Override + @SuppressWarnings("deprecation") + public void neighborChanged(BlockState state, Level world, BlockPos pos, Block block, BlockPos fromPos, boolean unused) { + if (!(world instanceof Level) || (world.isClientSide)) return; + BlockEntity te = world.getBlockEntity(pos); + if (!(te instanceof HopperTileEntity)) return; + ((HopperTileEntity) te).block_updated(); + } + + @Override + public void fallOn(Level world, BlockState state, BlockPos pos, Entity entity, float fallDistance) { + super.fallOn(world, state, pos, entity, fallDistance); + if (!(entity instanceof ItemEntity)) return; + if (!(world.getBlockEntity(pos) instanceof HopperTileEntity te)) return; + te.collection_timer_ = 0; + } + + @Override + public boolean shouldCheckWeakPower(BlockState state, SignalGetter level, BlockPos pos, Direction side) { + return false; + } + + @Override + @SuppressWarnings("deprecation") + public boolean isSignalSource(BlockState state) { + return true; + } + + @Override + @SuppressWarnings("deprecation") + public int getSignal(BlockState blockState, BlockGetter blockAccess, BlockPos pos, Direction side) { + return 0; + } + + @Override + @SuppressWarnings("deprecation") + public int getDirectSignal(BlockState blockState, BlockGetter blockAccess, BlockPos pos, Direction side) { + return 0; + } + } - @Override - @SuppressWarnings("deprecation") - public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult) - { return useOpenGui(state, world, pos, player); } + //-------------------------------------------------------------------------------------------------------------------- + // Tile entity + //-------------------------------------------------------------------------------------------------------------------- - @Override - @SuppressWarnings("deprecation") - public void neighborChanged(BlockState state, Level world, BlockPos pos, Block block, BlockPos fromPos, boolean unused) - { - if(!(world instanceof Level) || (world.isClientSide)) return; - BlockEntity te = world.getBlockEntity(pos); - if(!(te instanceof HopperTileEntity)) return; - ((HopperTileEntity)te).block_updated(); - } + public static class HopperTileEntity extends StandardEntityBlocks.StandardBlockEntity implements MenuProvider, Nameable { + public static final int NUM_OF_FIELDS = 7; + public static final int TICK_INTERVAL = 10; + public static final int COLLECTION_INTERVAL = 50; + public static final int NUM_OF_SLOTS = 18; + public static final int NUM_OF_STORAGE_SLOTS = NUM_OF_SLOTS; + public static final int MAX_TRANSFER_COUNT = 32; + public static final int MAX_COLLECTION_RANGE = 4; + public static final int PERIOD_OFFSET = 10; + /// + public static final int LOGIC_NOT_INVERTED = 0x00; + public static final int LOGIC_INVERTED = 0x01; + public static final int LOGIC_CONTINUOUS = 0x02; + public static final int LOGIC_IGNORE_EXT = 0x04; + protected final Inventories.StorageInventory main_inventory_ = new Inventories.StorageInventory(this, NUM_OF_SLOTS, 1); + protected final Inventories.InventoryRange storage_slot_range_ = new Inventories.InventoryRange(main_inventory_, 0, NUM_OF_STORAGE_SLOTS); + protected LazyOptional item_handler_ = Inventories.MappedItemHandler.createGenericHandler(storage_slot_range_); + /// + private boolean block_power_signal_ = false; + private boolean block_power_updated_ = false; + private int collection_timer_ = 0; + private int delay_timer_ = 0; + private int transfer_count_ = 1; + private int logic_ = LOGIC_INVERTED | LOGIC_CONTINUOUS; + private int transfer_period_ = 0; + private int collection_range_ = 0; + private int current_slot_index_ = 0; + protected final ContainerData fields = new ContainerData() { + @Override + public int getCount() { + return HopperTileEntity.NUM_OF_FIELDS; + } - @Override - public void fallOn(Level world, BlockState state, BlockPos pos, Entity entity, float fallDistance) - { - super.fallOn(world, state, pos, entity, fallDistance); - if(!(entity instanceof ItemEntity)) return; - if(!(world.getBlockEntity(pos) instanceof HopperTileEntity te)) return; - te.collection_timer_ = 0; - } + @Override + public int get(int id) { + return switch (id) { + case 0 -> collection_range_; + case 1 -> transfer_count_; + case 2 -> logic_; + case 3 -> transfer_period_; + case 4 -> delay_timer_; + case 5 -> block_power_signal_ ? 1 : 0; + case 6 -> current_slot_index_; + default -> 0; + }; + } - @Override - public boolean shouldCheckWeakPower(BlockState state, LevelReader world, BlockPos pos, Direction side) - { return false; } + @Override + public void set(int id, int value) { + switch (id) { + case 0 -> collection_range_ = Mth.clamp(value, 0, MAX_COLLECTION_RANGE); + case 1 -> transfer_count_ = Mth.clamp(value, 1, MAX_TRANSFER_COUNT); + case 2 -> logic_ = value; + case 3 -> transfer_period_ = Mth.clamp(value, 0, 100); + case 4 -> delay_timer_ = Mth.clamp(value, 0, 400); + case 5 -> block_power_signal_ = (value != 0); + case 6 -> current_slot_index_ = Mth.clamp(value, 0, NUM_OF_STORAGE_SLOTS - 1); + } + } + }; + private int tick_timer_ = 0; - @Override - @SuppressWarnings("deprecation") - public boolean isSignalSource(BlockState state) - { return true; } + public HopperTileEntity(BlockPos pos, BlockState state) { + super(ModContent.getBlockEntityTypeOfBlock(state.getBlock()), pos, state); + main_inventory_.setSlotChangeAction((slot, stack) -> tick_timer_ = Math.min(tick_timer_, 8)); + } - @Override - @SuppressWarnings("deprecation") - public int getSignal(BlockState blockState, BlockGetter blockAccess, BlockPos pos, Direction side) - { return 0; } + private static int next_slot(int i) { + return (i < NUM_OF_STORAGE_SLOTS - 1) ? (i + 1) : 0; + } - @Override - @SuppressWarnings("deprecation") - public int getDirectSignal(BlockState blockState, BlockGetter blockAccess, BlockPos pos, Direction side) - { return 0; } + public void reset_rtstate() { + block_power_signal_ = false; + block_power_updated_ = false; + } - } + public CompoundTag clear_getnbt() { + CompoundTag nbt = new CompoundTag(); + block_power_signal_ = false; + writenbt(nbt, false); + boolean is_empty = main_inventory_.isEmpty(); + main_inventory_.clearContent(); + reset_rtstate(); + block_power_updated_ = false; + if (is_empty) nbt = new CompoundTag(); + return nbt; + } - //-------------------------------------------------------------------------------------------------------------------- - // Tile entity - //-------------------------------------------------------------------------------------------------------------------- + public void readnbt(CompoundTag nbt, boolean update_packet) { + main_inventory_.load(nbt); + block_power_signal_ = nbt.getBoolean("powered"); + current_slot_index_ = nbt.getInt("act_slot_index"); + transfer_count_ = Mth.clamp(nbt.getInt("xsize"), 1, MAX_TRANSFER_COUNT); + logic_ = nbt.getInt("logic"); + transfer_period_ = nbt.getInt("period"); + collection_range_ = nbt.getInt("range"); + } - public static class HopperTileEntity extends StandardEntityBlocks.StandardBlockEntity implements MenuProvider, Nameable - { - public static final int NUM_OF_FIELDS = 7; - public static final int TICK_INTERVAL = 10; - public static final int COLLECTION_INTERVAL = 50; - public static final int NUM_OF_SLOTS = 18; - public static final int NUM_OF_STORAGE_SLOTS = NUM_OF_SLOTS; - public static final int MAX_TRANSFER_COUNT = 32; - public static final int MAX_COLLECTION_RANGE = 4; - public static final int PERIOD_OFFSET = 10; - /// - public static final int LOGIC_NOT_INVERTED = 0x00; - public static final int LOGIC_INVERTED = 0x01; - public static final int LOGIC_CONTINUOUS = 0x02; - public static final int LOGIC_IGNORE_EXT = 0x04; - /// - private boolean block_power_signal_ = false; - private boolean block_power_updated_ = false; - private int collection_timer_ = 0; - private int delay_timer_ = 0; - private int transfer_count_ = 1; - private int logic_ = LOGIC_INVERTED|LOGIC_CONTINUOUS; - private int transfer_period_ = 0; - private int collection_range_ = 0; - private int current_slot_index_ = 0; - private int tick_timer_ = 0; - protected final Inventories.StorageInventory main_inventory_ = new Inventories.StorageInventory(this, NUM_OF_SLOTS, 1); - protected final Inventories.InventoryRange storage_slot_range_ = new Inventories.InventoryRange(main_inventory_, 0, NUM_OF_STORAGE_SLOTS); - protected LazyOptional item_handler_ = Inventories.MappedItemHandler.createGenericHandler(storage_slot_range_); + // BlockEntity -------------------------------------------------------------------------------------------- - public HopperTileEntity(BlockPos pos, BlockState state) - { - super(ModContent.getBlockEntityTypeOfBlock(state.getBlock()), pos, state); - main_inventory_.setSlotChangeAction((slot,stack)->tick_timer_ = Math.min(tick_timer_, 8)); - } + protected void writenbt(CompoundTag nbt, boolean update_packet) { + main_inventory_.save(nbt); + nbt.putBoolean("powered", block_power_signal_); + nbt.putInt("act_slot_index", current_slot_index_); + nbt.putInt("xsize", transfer_count_); + nbt.putInt("logic", logic_); + nbt.putInt("period", transfer_period_); + nbt.putInt("range", collection_range_); + } - public void reset_rtstate() - { - block_power_signal_ = false; - block_power_updated_ = false; - } + public void block_updated() { + // RS power check, both edges + boolean powered = level.hasNeighborSignal(worldPosition); + if (block_power_signal_ != powered) block_power_updated_ = true; + block_power_signal_ = powered; + tick_timer_ = 1; + } - public CompoundTag clear_getnbt() - { - CompoundTag nbt = new CompoundTag(); - block_power_signal_ = false; - writenbt(nbt, false); - boolean is_empty = main_inventory_.isEmpty(); - main_inventory_.clearContent(); - reset_rtstate(); - block_power_updated_ = false; - if(is_empty) nbt = new CompoundTag(); - return nbt; - } + @Override + public void load(CompoundTag nbt) { + super.load(nbt); + readnbt(nbt, false); + } - public void readnbt(CompoundTag nbt, boolean update_packet) - { - main_inventory_.load(nbt); - block_power_signal_ = nbt.getBoolean("powered"); - current_slot_index_ = nbt.getInt("act_slot_index"); - transfer_count_ = Mth.clamp(nbt.getInt("xsize"), 1, MAX_TRANSFER_COUNT); - logic_ = nbt.getInt("logic"); - transfer_period_ = nbt.getInt("period"); - collection_range_ = nbt.getInt("range"); - } + // Namable ---------------------------------------------------------------------------------------------- - protected void writenbt(CompoundTag nbt, boolean update_packet) - { - main_inventory_.save(nbt); - nbt.putBoolean("powered", block_power_signal_); - nbt.putInt("act_slot_index", current_slot_index_); - nbt.putInt("xsize", transfer_count_); - nbt.putInt("logic", logic_); - nbt.putInt("period", transfer_period_); - nbt.putInt("range", collection_range_); - } + @Override + protected void saveAdditional(CompoundTag nbt) { + super.saveAdditional(nbt); + writenbt(nbt, false); + } - public void block_updated() - { - // RS power check, both edges - boolean powered = level.hasNeighborSignal(worldPosition); - if(block_power_signal_ != powered) block_power_updated_ = true; - block_power_signal_ = powered; - tick_timer_ = 1; - } + @Override + public void setRemoved() { + super.setRemoved(); + item_handler_.invalidate(); + } - // BlockEntity -------------------------------------------------------------------------------------------- + @Override + public Component getName() { + return Auxiliaries.localizable(getBlockState().getBlock().getDescriptionId()); + } - @Override - public void load(CompoundTag nbt) - { super.load(nbt); readnbt(nbt, false); } + // INamedContainerProvider ------------------------------------------------------------------------------ - @Override - protected void saveAdditional(CompoundTag nbt) - { super.saveAdditional(nbt); writenbt(nbt, false); } + @Override + public boolean hasCustomName() { + return false; + } - @Override - public void setRemoved() - { - super.setRemoved(); - item_handler_.invalidate(); - } - - // Namable ---------------------------------------------------------------------------------------------- - - @Override - public Component getName() - { return Auxiliaries.localizable(getBlockState().getBlock().getDescriptionId()); } - - @Override - public boolean hasCustomName() - { return false; } - - @Override - public Component getCustomName() - { return getName(); } - - // INamedContainerProvider ------------------------------------------------------------------------------ - - @Override - public Component getDisplayName() - { return Nameable.super.getDisplayName(); } - - @Override - public AbstractContainerMenu createMenu(int id, Inventory inventory, Player player ) - { return new HopperContainer(id, inventory, main_inventory_, ContainerLevelAccess.create(level, worldPosition), fields); } + @Override + public Component getCustomName() { + return getName(); + } /// CONTAINER SETITEM // @Override @@ -311,587 +345,566 @@ public class EdHopper // setChanged(); // } - // Fields ----------------------------------------------------------------------------------------------- + // Fields ----------------------------------------------------------------------------------------------- - protected final ContainerData fields = new ContainerData() - { - @Override - public int getCount() - { return HopperTileEntity.NUM_OF_FIELDS; } - - @Override - public int get(int id) - { - return switch(id) { - case 0 -> collection_range_; - case 1 -> transfer_count_; - case 2 -> logic_; - case 3 -> transfer_period_; - case 4 -> delay_timer_; - case 5 -> block_power_signal_ ? 1 : 0; - case 6 -> current_slot_index_; - default -> 0; - }; - } - @Override - public void set(int id, int value) - { - switch (id) { - case 0 -> collection_range_ = Mth.clamp(value, 0, MAX_COLLECTION_RANGE); - case 1 -> transfer_count_ = Mth.clamp(value, 1, MAX_TRANSFER_COUNT); - case 2 -> logic_ = value; - case 3 -> transfer_period_ = Mth.clamp(value, 0, 100); - case 4 -> delay_timer_ = Mth.clamp(value, 0, 400); - case 5 -> block_power_signal_ = (value != 0); - case 6 -> current_slot_index_ = Mth.clamp(value, 0, NUM_OF_STORAGE_SLOTS - 1); + @Override + public Component getDisplayName() { + return Nameable.super.getDisplayName(); } - } - }; - // Capability export ------------------------------------------------------------------------------------ + // Capability export ------------------------------------------------------------------------------------ - @Override - public LazyOptional getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable Direction facing) - { - if(capability == ForgeCapabilities.ITEM_HANDLER) return item_handler_.cast(); - return super.getCapability(capability, facing); - } - - // ITickable and aux methods --------------------------------------------------------------------- - - private IItemHandler inventory_entity_handler(BlockPos where) - { - final List entities = level.getEntities((Entity)null, (new AABB(where)), EntitySelector.CONTAINER_ENTITY_SELECTOR); - return entities.isEmpty() ? null : Inventories.itemhandler(entities.get(0)); - } - - private static int next_slot(int i) - { return (i= n_to_insert) { - slotstack.grow(n_to_insert); - n_to_insert = 0; - break; - } else { - slotstack.grow(nspace); - n_to_insert -= nspace; + @Override + public AbstractContainerMenu createMenu(int id, Inventory inventory, Player player) { + return new HopperContainer(id, inventory, main_inventory_, ContainerLevelAccess.create(level, worldPosition), fields); } - } - if((n_to_insert > 0) && (first_empty_slot >= 0)) { - ItemStack new_stack = stack.copy(); - new_stack.setCount(n_to_insert); - storage_slot_range_.set(first_empty_slot, new_stack); - n_to_insert = 0; - } - return max_to_insert - n_to_insert; - } - private boolean try_insert(Direction facing) - { - ItemStack current_stack = ItemStack.EMPTY; - for(int i=0; i= NUM_OF_STORAGE_SLOTS) current_slot_index_ = 0; - current_stack = storage_slot_range_.get(current_slot_index_); - if(!current_stack.isEmpty()) break; - current_slot_index_ = next_slot(current_slot_index_); - } - if(current_stack.isEmpty()) { - current_slot_index_ = 0; - return false; - } - final BlockPos facing_pos = worldPosition.relative(facing); - IItemHandler ih = null; - // Tile entity insertion check - { - final BlockEntity te = level.getBlockEntity(facing_pos); - if(te != null) { - ih = te.getCapability(ForgeCapabilities.ITEM_HANDLER, facing.getOpposite()).orElse(null); - if(ih == null) { delay_timer_ = TICK_INTERVAL+2; return false; } - if(te instanceof net.minecraft.world.level.block.entity.HopperBlockEntity) { - Direction f = level.getBlockState(facing_pos).getValue(net.minecraft.world.level.block.HopperBlock.FACING); - if(f==facing.getOpposite()) return false; // no back transfer - } else if(te instanceof EdHopper.HopperTileEntity) { - Direction f = level.getBlockState(facing_pos).getValue(EdHopper.HopperBlock.FACING); - if(f==facing.getOpposite()) return false; - } + // ITickable and aux methods --------------------------------------------------------------------- + + @Override + public LazyOptional getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable Direction facing) { + if (capability == ForgeCapabilities.ITEM_HANDLER) return item_handler_.cast(); + return super.getCapability(capability, facing); } - } - // Entity insertion check - if(ih == null) ih = inventory_entity_handler(facing_pos); - if(ih == null) { delay_timer_ = TICK_INTERVAL+2; return false; } // no reason to recalculate this all the time if there is nowhere to insert. - // Handler insertion - { - ItemStack insert_stack = current_stack.copy(); - if(insert_stack.getCount() > transfer_count_) insert_stack.setCount(transfer_count_); - final int initial_insert_stack_size = insert_stack.getCount(); - if((ih == null) || ih.getSlots() <= 0) return false; - // First stack comletion insert run. - for(int i=0; i entities = level.getEntities((Entity) null, (new AABB(where)), EntitySelector.CONTAINER_ENTITY_SELECTOR); + return entities.isEmpty() ? null : Inventories.itemhandler(entities.get(0)); } - // First-available insert run. - if(!insert_stack.isEmpty()) { - for(int i=0; i 0) { - current_stack.shrink(num_inserted); - storage_slot_range_.set(current_slot_index_, current_stack); - } - if(!insert_stack.isEmpty()) current_slot_index_ = next_slot(current_slot_index_); - return (num_inserted > 0); - } - } - private boolean try_item_handler_extract(final IItemHandler ih) - { - final int end = ih.getSlots(); - int n_to_extract = transfer_count_; - for(int i=0; i 0) { - ItemStack test = ih.extractItem(i, n_accepted, false); - n_to_extract -= n_accepted; - if(n_to_extract <= 0) break; - } - } - return (n_to_extract < transfer_count_); - } - - private boolean try_inventory_extract(final Container inv) - { - final int end = inv.getContainerSize(); - int n_to_extract = transfer_count_; - for(int i=0; i 0) { - stack.shrink(n_accepted); - n_to_extract -= n_accepted; - if(stack.isEmpty()) stack = ItemStack.EMPTY; - inv.setItem(i, stack); - if(n_to_extract <= 0) break; - } - } - if(n_to_extract < transfer_count_) { - inv.setChanged(); - return true; - } else { - return false; - } - } - - private boolean try_collect(Direction facing) - { - AABB collection_volume; - Vec3 rpos; - if(facing==Direction.UP) { - rpos = new Vec3(0.5+worldPosition.getX(),1.5+worldPosition.getY(),0.5+worldPosition.getZ()); - collection_volume = (new AABB(worldPosition.above())).inflate(0.1+collection_range_, 0.6, 0.1+collection_range_); - } else { - rpos = new Vec3(0.5+worldPosition.getX(),-1.5+worldPosition.getY(),0.5+worldPosition.getZ()); - collection_volume = (new AABB(worldPosition.below(2))).inflate(0.1+collection_range_, 1, 0.1+collection_range_); - } - final List items = level.getEntitiesOfClass(ItemEntity.class, collection_volume, e->(e.isAlive() && e.isOnGround())); - if(items.size() <= 0) return false; - final int max_to_collect = 3; - int n_collected = 0; - for(ItemEntity ie:items) { - boolean is_direct_collection_tange = ie.distanceToSqr(rpos)<0.7; - if(!is_direct_collection_tange && (ie.hasPickUpDelay())) continue; - ItemStack stack = ie.getItem(); - if(stack.isEmpty()) continue; - int n_accepted = try_insert_into_hopper(stack); - if(n_accepted <= 0) continue; - if(n_accepted >= stack.getCount()) { - stack.setCount(0); - ie.setItem(stack); - ie.remove(Entity.RemovalReason.DISCARDED); - } else { - stack.shrink(n_accepted); - ie.setItem(stack); - } - if((!is_direct_collection_tange) && (++n_collected >= max_to_collect)) break; - } - return (n_collected > 0); - } - - @Override - public void tick() - { - // Tick cycle pre-conditions - if(level.isClientSide) return; - if((delay_timer_ > 0) && ((--delay_timer_) == 0)) setChanged(); - if(--tick_timer_ > 0) return; - tick_timer_ = TICK_INTERVAL; - // Cycle init - boolean dirty = block_power_updated_; - final boolean rssignal = ((logic_ & LOGIC_IGNORE_EXT)!=0) || ((logic_ & LOGIC_INVERTED)!=0)==(!block_power_signal_); - final boolean pulse_mode = ((logic_ & (LOGIC_CONTINUOUS|LOGIC_IGNORE_EXT))==0); - boolean trigger = ((logic_ & LOGIC_IGNORE_EXT)!=0) || (rssignal && ((block_power_updated_) || (!pulse_mode))); - final BlockState state = level.getBlockState(worldPosition); - if(!(state.getBlock() instanceof HopperBlock)) { block_power_signal_= false; return; } - final Direction hopper_facing = state.getValue(HopperBlock.FACING); - // Trigger edge detection for next cycle - { - boolean tr = level.hasNeighborSignal(worldPosition); - block_power_updated_ = (block_power_signal_ != tr); - block_power_signal_ = tr; - if(block_power_updated_) dirty = true; - } - // Collection - if(rssignal || pulse_mode) { - Direction hopper_input_facing = (hopper_facing==Direction.UP) ? Direction.DOWN : Direction.UP; - BlockEntity te = level.getBlockEntity(worldPosition.relative(hopper_input_facing)); - IItemHandler ih = (te==null) ? (null) : (te.getCapability(ForgeCapabilities.ITEM_HANDLER, hopper_input_facing.getOpposite()).orElse(null)); - if((ih != null) || (te instanceof WorldlyContainer)) { - // Tile Entity pulling - if((ih != null)) { - if(try_item_handler_extract(ih)) dirty = true; - } else { - if(try_inventory_extract((WorldlyContainer)te)) dirty = true; - } - } - if(ih==null) { - ih = inventory_entity_handler(worldPosition.relative(hopper_input_facing)); - if((ih!=null) && (try_item_handler_extract(ih))) dirty = true; - } - if((ih==null) && (collection_timer_ -= TICK_INTERVAL) <= 0) { - // Ranged collection - collection_timer_ = COLLECTION_INTERVAL; - if(try_collect(hopper_input_facing)) dirty = true; - } - } - // Insertion - if(trigger && (delay_timer_ <= 0)) { - delay_timer_ = PERIOD_OFFSET + transfer_period_ * 2; - if(try_insert(hopper_facing)) dirty = true; - } - if(dirty) setChanged(); - if(trigger && (tick_timer_ > TICK_INTERVAL)) tick_timer_ = TICK_INTERVAL; - } - } - - //-------------------------------------------------------------------------------------------------------------------- - // Container - //-------------------------------------------------------------------------------------------------------------------- - - public static class HopperContainer extends AbstractContainerMenu implements Networking.INetworkSynchronisableContainer - { - protected static final String QUICK_MOVE_ALL = "quick-move-all"; - private static final int PLAYER_INV_START_SLOTNO = HopperTileEntity.NUM_OF_SLOTS; - private static final int NUM_OF_CONTAINER_SLOTS = HopperTileEntity.NUM_OF_SLOTS + 36; - protected static final int STORAGE_SLOT_BEGIN = 0; - protected static final int STORAGE_SLOT_END = HopperTileEntity.NUM_OF_SLOTS; - protected static final int PLAYER_SLOT_BEGIN = HopperTileEntity.NUM_OF_SLOTS; - protected static final int PLAYER_SLOT_END = HopperTileEntity.NUM_OF_SLOTS+36; - private final Inventories.InventoryRange player_inventory_range_; - private final Inventories.InventoryRange block_storage_range_; - private final Player player_; - private final Container inventory_; - private final ContainerLevelAccess wpc_; - private final ContainerData fields_; - - public final int field(int index) { return fields_.get(index); } - - public HopperContainer(int cid, Inventory player_inventory) - { this(cid, player_inventory, new SimpleContainer(HopperTileEntity.NUM_OF_SLOTS), ContainerLevelAccess.NULL, new SimpleContainerData(HopperTileEntity.NUM_OF_FIELDS)); } - - private HopperContainer(int cid, Inventory player_inventory, Container block_inventory, ContainerLevelAccess wpc, ContainerData fields) - { - super(ModContent.getMenuType("factory_hopper"), cid); // @todo: class mapping - fields_ = fields; - wpc_ = wpc; - player_ = player_inventory.player; - inventory_ = block_inventory; - block_storage_range_ = new Inventories.InventoryRange(inventory_, 0, HopperTileEntity.NUM_OF_SLOTS); - player_inventory_range_ = Inventories.InventoryRange.fromPlayerInventory(player_); - int i=-1; - // input slots (stacks 0 to 17) - for(int y=0; y<3; ++y) { - for(int x=0; x<6; ++x) { - int xpos = 11+x*18, ypos = 9+y*17; - addSlot(new Slot(inventory_, ++i, xpos, ypos)); - } - } - // player slots - for(int x=0; x<9; ++x) { - addSlot(new Slot(player_inventory, x, 8+x*18, 129)); // player slots: 0..8 - } - for(int y=0; y<3; ++y) { - for(int x=0; x<9; ++x) { - addSlot(new Slot(player_inventory, x+y*9+9, 8+x*18, 71+y*18)); // player slots: 9..35 - } - } - this.addDataSlots(fields_); // === Add reference holders - } - - @Override - public boolean stillValid(Player player) - { return inventory_.stillValid(player); } - - @Override - public ItemStack quickMoveStack(Player player, int index) - { - Slot slot = getSlot(index); - if((slot==null) || (!slot.hasItem())) return ItemStack.EMPTY; - ItemStack slot_stack = slot.getItem(); - ItemStack transferred = slot_stack.copy(); - if((index>=0) && (index= PLAYER_INV_START_SLOTNO) && (index <= PLAYER_INV_START_SLOTNO+36)) { - // Player slot - if(!moveItemStackTo(slot_stack, 0, HopperTileEntity.NUM_OF_SLOTS, false)) return ItemStack.EMPTY; - } else { - // invalid slot - return ItemStack.EMPTY; - } - if(slot_stack.isEmpty()) { - slot.set(ItemStack.EMPTY); - } else { - slot.setChanged(); - } - if(slot_stack.getCount() == transferred.getCount()) return ItemStack.EMPTY; - slot.onTake(player, slot_stack); - return transferred; - } - - // INetworkSynchronisableContainer --------------------------------------------------------- - - @OnlyIn(Dist.CLIENT) - public void onGuiAction(CompoundTag nbt) - { Networking.PacketContainerSyncClientToServer.sendToServer(containerId, nbt); } - - @OnlyIn(Dist.CLIENT) - public void onGuiAction(String key, int value) - { - CompoundTag nbt = new CompoundTag(); - nbt.putInt(key, value); - Networking.PacketContainerSyncClientToServer.sendToServer(containerId, nbt); - } - - @OnlyIn(Dist.CLIENT) - public void onGuiAction(String message, CompoundTag nbt) - { - nbt.putString("action", message); - Networking.PacketContainerSyncClientToServer.sendToServer(containerId, nbt); - } - - @Override - public void onServerPacketReceived(int windowId, CompoundTag nbt) - {} - - @Override - public void onClientPacketReceived(int windowId, Player player, CompoundTag nbt) - { - if(!(inventory_ instanceof Inventories.StorageInventory)) return; - if(!((((((Inventories.StorageInventory)inventory_).getBlockEntity())) instanceof final EdHopper.HopperTileEntity te))) return; - if(nbt.contains("xsize")) te.transfer_count_ = Mth.clamp(nbt.getInt("xsize"), 1, HopperTileEntity.MAX_TRANSFER_COUNT); - if(nbt.contains("period")) te.transfer_period_ = Mth.clamp(nbt.getInt("period"), 0, 100); - if(nbt.contains("range")) te.collection_range_ = Mth.clamp(nbt.getInt("range"), 0, HopperTileEntity.MAX_COLLECTION_RANGE); - if(nbt.contains("logic")) te.logic_ = nbt.getInt("logic"); - if(nbt.contains("manual_trigger") && (nbt.getInt("manual_trigger")!=0)) { te.block_power_signal_=true; te.block_power_updated_=true; te.tick_timer_=1; } - if(nbt.contains("action")) { - boolean changed = false; - final int slotId = nbt.contains("slot") ? nbt.getInt("slot") : -1; - switch (nbt.getString("action")) { - case QUICK_MOVE_ALL -> { - if ((slotId >= STORAGE_SLOT_BEGIN) && (slotId < STORAGE_SLOT_END) && (getSlot(slotId).hasItem())) { - changed = block_storage_range_.move(getSlot(slotId).getSlotIndex(), player_inventory_range_, true, false, true, true); - } else if ((slotId >= PLAYER_SLOT_BEGIN) && (slotId < PLAYER_SLOT_END) && (getSlot(slotId).hasItem())) { - changed = player_inventory_range_.move(getSlot(slotId).getSlotIndex(), block_storage_range_, true, false, false, true); + private int try_insert_into_hopper(final ItemStack stack) { + final int max_to_insert = stack.getCount(); + int n_to_insert = max_to_insert; + int first_empty_slot = -1; + for (int i = 0; i < storage_slot_range_.size(); ++i) { + final ItemStack slotstack = storage_slot_range_.get(i); + if ((first_empty_slot < 0) && slotstack.isEmpty()) { + first_empty_slot = i; + continue; + } + if (Inventories.areItemStacksDifferent(stack, slotstack)) continue; + int nspace = slotstack.getMaxStackSize() - slotstack.getCount(); + if (nspace <= 0) { + continue; + } else if (nspace >= n_to_insert) { + slotstack.grow(n_to_insert); + n_to_insert = 0; + break; + } else { + slotstack.grow(nspace); + n_to_insert -= nspace; + } } - } + if ((n_to_insert > 0) && (first_empty_slot >= 0)) { + ItemStack new_stack = stack.copy(); + new_stack.setCount(n_to_insert); + storage_slot_range_.set(first_empty_slot, new_stack); + n_to_insert = 0; + } + return max_to_insert - n_to_insert; } - if(changed) { - inventory_.setChanged(); - player.getInventory().setChanged(); - broadcastChanges(); + + private boolean try_insert(Direction facing) { + ItemStack current_stack = ItemStack.EMPTY; + for (int i = 0; i < NUM_OF_STORAGE_SLOTS; ++i) { + if (current_slot_index_ >= NUM_OF_STORAGE_SLOTS) current_slot_index_ = 0; + current_stack = storage_slot_range_.get(current_slot_index_); + if (!current_stack.isEmpty()) break; + current_slot_index_ = next_slot(current_slot_index_); + } + if (current_stack.isEmpty()) { + current_slot_index_ = 0; + return false; + } + final BlockPos facing_pos = worldPosition.relative(facing); + IItemHandler ih = null; + // Tile entity insertion check + { + final BlockEntity te = level.getBlockEntity(facing_pos); + if (te != null) { + ih = te.getCapability(ForgeCapabilities.ITEM_HANDLER, facing.getOpposite()).orElse(null); + if (ih == null) { + delay_timer_ = TICK_INTERVAL + 2; + return false; + } + if (te instanceof net.minecraft.world.level.block.entity.HopperBlockEntity) { + Direction f = level.getBlockState(facing_pos).getValue(net.minecraft.world.level.block.HopperBlock.FACING); + if (f == facing.getOpposite()) return false; // no back transfer + } else if (te instanceof EdHopper.HopperTileEntity) { + Direction f = level.getBlockState(facing_pos).getValue(EdHopper.HopperBlock.FACING); + if (f == facing.getOpposite()) return false; + } + } + } + // Entity insertion check + if (ih == null) ih = inventory_entity_handler(facing_pos); + if (ih == null) { + delay_timer_ = TICK_INTERVAL + 2; + return false; + } // no reason to recalculate this all the time if there is nowhere to insert. + // Handler insertion + { + ItemStack insert_stack = current_stack.copy(); + if (insert_stack.getCount() > transfer_count_) insert_stack.setCount(transfer_count_); + final int initial_insert_stack_size = insert_stack.getCount(); + if ((ih == null) || ih.getSlots() <= 0) return false; + // First stack comletion insert run. + for (int i = 0; i < ih.getSlots(); ++i) { + final ItemStack target_stack = ih.getStackInSlot(i); + if (Inventories.areItemStacksDifferent(target_stack, insert_stack)) continue; + insert_stack = ih.insertItem(i, insert_stack.copy(), false); + if (insert_stack.isEmpty()) break; + } + // First-available insert run. + if (!insert_stack.isEmpty()) { + for (int i = 0; i < ih.getSlots(); ++i) { + insert_stack = ih.insertItem(i, insert_stack.copy(), false); + if (insert_stack.isEmpty()) break; + } + } + final int num_inserted = initial_insert_stack_size - insert_stack.getCount(); + if (num_inserted > 0) { + current_stack.shrink(num_inserted); + storage_slot_range_.set(current_slot_index_, current_stack); + } + if (!insert_stack.isEmpty()) current_slot_index_ = next_slot(current_slot_index_); + return (num_inserted > 0); + } } - } - te.setChanged(); - } - } - //-------------------------------------------------------------------------------------------------------------------- - // GUI - //-------------------------------------------------------------------------------------------------------------------- + private boolean try_item_handler_extract(final IItemHandler ih) { + final int end = ih.getSlots(); + int n_to_extract = transfer_count_; + for (int i = 0; i < end; ++i) { + if (ih.getStackInSlot(i).isEmpty()) continue; + ItemStack stack = ih.extractItem(i, n_to_extract, true); + if (stack.isEmpty()) continue; + int n_accepted = try_insert_into_hopper(stack); + if (n_accepted > 0) { + ItemStack test = ih.extractItem(i, n_accepted, false); + n_to_extract -= n_accepted; + if (n_to_extract <= 0) break; + } + } + return (n_to_extract < transfer_count_); + } - @OnlyIn(Dist.CLIENT) - public static class HopperGui extends Guis.ContainerGui - { - public HopperGui(HopperContainer container, Inventory player_inventory, Component title) - { super(container, player_inventory, title, "textures/gui/factory_hopper_gui.png"); } + private boolean try_inventory_extract(final Container inv) { + final int end = inv.getContainerSize(); + int n_to_extract = transfer_count_; + for (int i = 0; i < end; ++i) { + ItemStack stack = inv.getItem(i).copy(); + if (stack.isEmpty()) continue; + int n_accepted = try_insert_into_hopper(stack); + if (n_accepted > 0) { + stack.shrink(n_accepted); + n_to_extract -= n_accepted; + if (stack.isEmpty()) stack = ItemStack.EMPTY; + inv.setItem(i, stack); + if (n_to_extract <= 0) break; + } + } + if (n_to_extract < transfer_count_) { + inv.setChanged(); + return true; + } else { + return false; + } + } - @Override - public void init() - { - super.init(); - { - final Block block = ModContent.getBlock( Auxiliaries.getResourceLocation(getMenu().getType()).getPath().replaceAll("^ct_","")); - final String prefix = block.getDescriptionId() + ".tooltips."; - final int x0 = getGuiLeft(), y0 = getGuiTop(); - tooltip_.init( - new TooltipDisplay.TipRange(x0+148, y0+22, 3, 3, Component.translatable(prefix + "delayindicator")), - new TooltipDisplay.TipRange(x0+130, y0+ 9, 40, 10, Component.translatable(prefix + "range")), - new TooltipDisplay.TipRange(x0+130, y0+22, 40, 10, Component.translatable(prefix + "period")), - new TooltipDisplay.TipRange(x0+130, y0+35, 40, 10, Component.translatable(prefix + "count")), - new TooltipDisplay.TipRange(x0+133, y0+49, 9, 9, Component.translatable(prefix + "rssignal")), - new TooltipDisplay.TipRange(x0+145, y0+49, 9, 9, Component.translatable(prefix + "inversion")), - new TooltipDisplay.TipRange(x0+159, y0+49, 9, 9, Component.translatable(prefix + "triggermode")) - ); - } + private boolean try_collect(Direction facing) { + AABB collection_volume; + Vec3 rpos; + if (facing == Direction.UP) { + rpos = new Vec3(0.5 + worldPosition.getX(), 1.5 + worldPosition.getY(), 0.5 + worldPosition.getZ()); + collection_volume = (new AABB(worldPosition.above())).inflate(0.1 + collection_range_, 0.6, 0.1 + collection_range_); + } else { + rpos = new Vec3(0.5 + worldPosition.getX(), -1.5 + worldPosition.getY(), 0.5 + worldPosition.getZ()); + collection_volume = (new AABB(worldPosition.below(2))).inflate(0.1 + collection_range_, 1, 0.1 + collection_range_); + } + final List items = level.getEntitiesOfClass(ItemEntity.class, collection_volume, e -> (e.isAlive() && e.onGround())); + if (items.size() <= 0) return false; + final int max_to_collect = 3; + int n_collected = 0; + for (ItemEntity ie : items) { + boolean is_direct_collection_tange = ie.distanceToSqr(rpos) < 0.7; + if (!is_direct_collection_tange && (ie.hasPickUpDelay())) continue; + ItemStack stack = ie.getItem(); + if (stack.isEmpty()) continue; + int n_accepted = try_insert_into_hopper(stack); + if (n_accepted <= 0) continue; + if (n_accepted >= stack.getCount()) { + stack.setCount(0); + ie.setItem(stack); + ie.remove(Entity.RemovalReason.DISCARDED); + } else { + stack.shrink(n_accepted); + ie.setItem(stack); + } + if ((!is_direct_collection_tange) && (++n_collected >= max_to_collect)) break; + } + return (n_collected > 0); + } + + @Override + public void tick() { + // Tick cycle pre-conditions + if (level.isClientSide) return; + if ((delay_timer_ > 0) && ((--delay_timer_) == 0)) setChanged(); + if (--tick_timer_ > 0) return; + tick_timer_ = TICK_INTERVAL; + // Cycle init + boolean dirty = block_power_updated_; + final boolean rssignal = ((logic_ & LOGIC_IGNORE_EXT) != 0) || ((logic_ & LOGIC_INVERTED) != 0) == (!block_power_signal_); + final boolean pulse_mode = ((logic_ & (LOGIC_CONTINUOUS | LOGIC_IGNORE_EXT)) == 0); + boolean trigger = ((logic_ & LOGIC_IGNORE_EXT) != 0) || (rssignal && ((block_power_updated_) || (!pulse_mode))); + final BlockState state = level.getBlockState(worldPosition); + if (!(state.getBlock() instanceof HopperBlock)) { + block_power_signal_ = false; + return; + } + final Direction hopper_facing = state.getValue(HopperBlock.FACING); + // Trigger edge detection for next cycle + { + boolean tr = level.hasNeighborSignal(worldPosition); + block_power_updated_ = (block_power_signal_ != tr); + block_power_signal_ = tr; + if (block_power_updated_) dirty = true; + } + // Collection + if (rssignal || pulse_mode) { + Direction hopper_input_facing = (hopper_facing == Direction.UP) ? Direction.DOWN : Direction.UP; + BlockEntity te = level.getBlockEntity(worldPosition.relative(hopper_input_facing)); + IItemHandler ih = (te == null) ? (null) : (te.getCapability(ForgeCapabilities.ITEM_HANDLER, hopper_input_facing.getOpposite()).orElse(null)); + if ((ih != null) || (te instanceof WorldlyContainer)) { + // Tile Entity pulling + if ((ih != null)) { + if (try_item_handler_extract(ih)) dirty = true; + } else { + if (try_inventory_extract((WorldlyContainer) te)) dirty = true; + } + } + if (ih == null) { + ih = inventory_entity_handler(worldPosition.relative(hopper_input_facing)); + if ((ih != null) && (try_item_handler_extract(ih))) dirty = true; + } + if ((ih == null) && (collection_timer_ -= TICK_INTERVAL) <= 0) { + // Ranged collection + collection_timer_ = COLLECTION_INTERVAL; + if (try_collect(hopper_input_facing)) dirty = true; + } + } + // Insertion + if (trigger && (delay_timer_ <= 0)) { + delay_timer_ = PERIOD_OFFSET + transfer_period_ * 2; + if (try_insert(hopper_facing)) dirty = true; + } + if (dirty) setChanged(); + if (trigger && (tick_timer_ > TICK_INTERVAL)) tick_timer_ = TICK_INTERVAL; + } } - @Override - protected void renderBgWidgets(PoseStack mx, float partialTicks, int mouseX, int mouseY) - { - final int x0=getGuiLeft(), y0=getGuiTop(), w=getXSize(), h=getYSize(); - HopperContainer container = getMenu(); - // active slot - { - int slot_index = container.field(6); - if((slot_index < 0) || (slot_index >= HopperTileEntity.NUM_OF_SLOTS)) slot_index = 0; - int x = (x0+10+((slot_index % 6) * 18)); - int y = (y0+8+((slot_index / 6) * 17)); - blit(mx, x, y, 200, 8, 18, 18); - } - // collection range - { - int[] lut = { 133, 141, 149, 157, 166 }; - int px = lut[Mth.clamp(container.field(0), 0, HopperTileEntity.MAX_COLLECTION_RANGE)]; - int x = x0 + px - 2; - int y = y0 + 14; - blit(mx, x, y, 179, 40, 5, 5); - } - // transfer period - { - int px = (int)Math.round(((33.5 * container.field(3)) / 100) + 1); - int x = x0 + 132 - 2 + Mth.clamp(px, 0, 34); - int y = y0 + 27; - blit(mx, x, y, 179, 40, 5, 5); - } - // transfer count - { - int x = x0 + 133 - 2 + (container.field(1)); - int y = y0 + 40; - blit(mx, x, y, 179, 40, 5, 5); - } - // redstone input - { - if(container.field(5) != 0) { - blit(mx, x0+133, y0+49, 217, 49, 9, 9); + //-------------------------------------------------------------------------------------------------------------------- + // Container + //-------------------------------------------------------------------------------------------------------------------- + + public static class HopperContainer extends AbstractContainerMenu implements Networking.INetworkSynchronisableContainer { + protected static final String QUICK_MOVE_ALL = "quick-move-all"; + protected static final int STORAGE_SLOT_BEGIN = 0; + protected static final int STORAGE_SLOT_END = HopperTileEntity.NUM_OF_SLOTS; + protected static final int PLAYER_SLOT_BEGIN = HopperTileEntity.NUM_OF_SLOTS; + protected static final int PLAYER_SLOT_END = HopperTileEntity.NUM_OF_SLOTS + 36; + private static final int PLAYER_INV_START_SLOTNO = HopperTileEntity.NUM_OF_SLOTS; + private static final int NUM_OF_CONTAINER_SLOTS = HopperTileEntity.NUM_OF_SLOTS + 36; + private final Inventories.InventoryRange player_inventory_range_; + private final Inventories.InventoryRange block_storage_range_; + private final Player player_; + private final Container inventory_; + private final ContainerLevelAccess wpc_; + private final ContainerData fields_; + + public HopperContainer(int cid, Inventory player_inventory) { + this(cid, player_inventory, new SimpleContainer(HopperTileEntity.NUM_OF_SLOTS), ContainerLevelAccess.NULL, new SimpleContainerData(HopperTileEntity.NUM_OF_FIELDS)); } - } - // trigger logic - { - int inverter_offset_x = ((container.field(2) & HopperTileEntity.LOGIC_INVERTED) != 0) ? 11 : 0; - int inverter_offset_y = ((container.field(2) & HopperTileEntity.LOGIC_IGNORE_EXT) != 0) ? 10 : 0; - blit(mx, x0+145, y0+49, 177+inverter_offset_x, 49+inverter_offset_y, 9, 9); - int pulse_mode_offset = ((container.field(2) & HopperTileEntity.LOGIC_CONTINUOUS ) != 0) ? 9 : 0; - blit(mx, x0+159, y0+49, 199+pulse_mode_offset, 49, 9, 9); - } - // delay timer running indicator - { - if((container.field(4) > HopperTileEntity.PERIOD_OFFSET) && ((System.currentTimeMillis() % 1000) < 500)) { - blit(mx, x0+148, y0+22, 187, 22, 3, 3); + + private HopperContainer(int cid, Inventory player_inventory, Container block_inventory, ContainerLevelAccess wpc, ContainerData fields) { + super(ModContent.getMenuType("factory_hopper"), cid); // @todo: class mapping + fields_ = fields; + wpc_ = wpc; + player_ = player_inventory.player; + inventory_ = block_inventory; + block_storage_range_ = new Inventories.InventoryRange(inventory_, 0, HopperTileEntity.NUM_OF_SLOTS); + player_inventory_range_ = Inventories.InventoryRange.fromPlayerInventory(player_); + int i = -1; + // input slots (stacks 0 to 17) + for (int y = 0; y < 3; ++y) { + for (int x = 0; x < 6; ++x) { + int xpos = 11 + x * 18, ypos = 9 + y * 17; + addSlot(new Slot(inventory_, ++i, xpos, ypos)); + } + } + // player slots + for (int x = 0; x < 9; ++x) { + addSlot(new Slot(player_inventory, x, 8 + x * 18, 129)); // player slots: 0..8 + } + for (int y = 0; y < 3; ++y) { + for (int x = 0; x < 9; ++x) { + addSlot(new Slot(player_inventory, x + y * 9 + 9, 8 + x * 18, 71 + y * 18)); // player slots: 9..35 + } + } + this.addDataSlots(fields_); // === Add reference holders + } + + public final int field(int index) { + return fields_.get(index); + } + + @Override + public boolean stillValid(Player player) { + return inventory_.stillValid(player); + } + + @Override + public ItemStack quickMoveStack(Player player, int index) { + Slot slot = getSlot(index); + if ((slot == null) || (!slot.hasItem())) return ItemStack.EMPTY; + ItemStack slot_stack = slot.getItem(); + ItemStack transferred = slot_stack.copy(); + if ((index >= 0) && (index < PLAYER_INV_START_SLOTNO)) { + // Device slots + if (!moveItemStackTo(slot_stack, PLAYER_INV_START_SLOTNO, PLAYER_INV_START_SLOTNO + 36, false)) + return ItemStack.EMPTY; + } else if ((index >= PLAYER_INV_START_SLOTNO) && (index <= PLAYER_INV_START_SLOTNO + 36)) { + // Player slot + if (!moveItemStackTo(slot_stack, 0, HopperTileEntity.NUM_OF_SLOTS, false)) return ItemStack.EMPTY; + } else { + // invalid slot + return ItemStack.EMPTY; + } + if (slot_stack.isEmpty()) { + slot.set(ItemStack.EMPTY); + } else { + slot.setChanged(); + } + if (slot_stack.getCount() == transferred.getCount()) return ItemStack.EMPTY; + slot.onTake(player, slot_stack); + return transferred; + } + + // INetworkSynchronisableContainer --------------------------------------------------------- + + @OnlyIn(Dist.CLIENT) + public void onGuiAction(CompoundTag nbt) { + Networking.PacketContainerSyncClientToServer.sendToServer(containerId, nbt); + } + + @OnlyIn(Dist.CLIENT) + public void onGuiAction(String key, int value) { + CompoundTag nbt = new CompoundTag(); + nbt.putInt(key, value); + Networking.PacketContainerSyncClientToServer.sendToServer(containerId, nbt); + } + + @OnlyIn(Dist.CLIENT) + public void onGuiAction(String message, CompoundTag nbt) { + nbt.putString("action", message); + Networking.PacketContainerSyncClientToServer.sendToServer(containerId, nbt); + } + + @Override + public void onServerPacketReceived(int windowId, CompoundTag nbt) { + } + + @Override + public void onClientPacketReceived(int windowId, Player player, CompoundTag nbt) { + if (!(inventory_ instanceof Inventories.StorageInventory)) return; + if (!((((((Inventories.StorageInventory) inventory_).getBlockEntity())) instanceof final EdHopper.HopperTileEntity te))) + return; + if (nbt.contains("xsize")) + te.transfer_count_ = Mth.clamp(nbt.getInt("xsize"), 1, HopperTileEntity.MAX_TRANSFER_COUNT); + if (nbt.contains("period")) te.transfer_period_ = Mth.clamp(nbt.getInt("period"), 0, 100); + if (nbt.contains("range")) + te.collection_range_ = Mth.clamp(nbt.getInt("range"), 0, HopperTileEntity.MAX_COLLECTION_RANGE); + if (nbt.contains("logic")) te.logic_ = nbt.getInt("logic"); + if (nbt.contains("manual_trigger") && (nbt.getInt("manual_trigger") != 0)) { + te.block_power_signal_ = true; + te.block_power_updated_ = true; + te.tick_timer_ = 1; + } + if (nbt.contains("action")) { + boolean changed = false; + final int slotId = nbt.contains("slot") ? nbt.getInt("slot") : -1; + switch (nbt.getString("action")) { + case QUICK_MOVE_ALL -> { + if ((slotId >= STORAGE_SLOT_BEGIN) && (slotId < STORAGE_SLOT_END) && (getSlot(slotId).hasItem())) { + changed = block_storage_range_.move(getSlot(slotId).getSlotIndex(), player_inventory_range_, true, false, true, true); + } else if ((slotId >= PLAYER_SLOT_BEGIN) && (slotId < PLAYER_SLOT_END) && (getSlot(slotId).hasItem())) { + changed = player_inventory_range_.move(getSlot(slotId).getSlotIndex(), block_storage_range_, true, false, false, true); + } + } + } + if (changed) { + inventory_.setChanged(); + player.getInventory().setChanged(); + broadcastChanges(); + } + } + te.setChanged(); } - } } - @Override - protected void slotClicked(Slot slot, int slotId, int button, ClickType type) - { - tooltip_.resetTimer(); - if((type == ClickType.QUICK_MOVE) && (slot!=null) && slot.hasItem() && Auxiliaries.isShiftDown() && Auxiliaries.isCtrlDown()) { - CompoundTag nbt = new CompoundTag(); - nbt.putInt("slot", slotId); - menu.onGuiAction(HopperContainer.QUICK_MOVE_ALL, nbt); - } else { - super.slotClicked(slot, slotId, button, type); - } - } + //-------------------------------------------------------------------------------------------------------------------- + // GUI + //-------------------------------------------------------------------------------------------------------------------- - @Override - public boolean mouseClicked(double mouseX, double mouseY, int mouseButton) - { - tooltip_.resetTimer(); - HopperContainer container = getMenu(); - int mx = (int)(mouseX - getGuiLeft() + .5), my = (int)(mouseY - getGuiTop() + .5); - if((!isHovering(126, 1, 49, 60, mouseX, mouseY))) { - return super.mouseClicked(mouseX, mouseY, mouseButton); - } else if(isHovering(128, 9, 44, 10, mouseX, mouseY)) { - int range = (mx-133); - if(range < -1) { - range = container.field(0) - 1; // - - } else if(range >= 34) { - range = container.field(0) + 1; // + - } else { - range = (int)(0.5 + ((((double)HopperTileEntity.MAX_COLLECTION_RANGE) * range)/34)); // slider - range = Mth.clamp(range, 0, HopperTileEntity.MAX_COLLECTION_RANGE); + @OnlyIn(Dist.CLIENT) + public static class HopperGui extends Guis.ContainerGui { + public HopperGui(HopperContainer container, Inventory player_inventory, Component title) { + super(container, player_inventory, title, "textures/gui/factory_hopper_gui.png"); } - container.onGuiAction("range", range); - } else if(isHovering(128, 21, 44, 10, mouseX, mouseY)) { - int period = (mx-133); - if(period < -1) { - period = container.field(3) - 3; // - - } else if(period >= 35) { - period = container.field(3) + 3; // + - } else { - period = (int)(0.5 + ((100.0 * period)/34)); - } - period = Mth.clamp(period, 0, 100); - container.onGuiAction("period", period); - } else if(isHovering(128, 34, 44, 10, mouseX, mouseY)) { - int ndrop = (mx-134); - if(ndrop < -1) { - ndrop = container.field(1) - 1; // - - } else if(ndrop >= 34) { - ndrop = container.field(1) + 1; // + - } else { - ndrop = Mth.clamp(1+ndrop, 1, HopperTileEntity.MAX_TRANSFER_COUNT); // slider - } - container.onGuiAction("xsize", ndrop); - } else if(isHovering(133, 49, 9, 9, mouseX, mouseY)) { - container.onGuiAction("manual_trigger", 1); - } else if(isHovering(145, 49, 9, 9, mouseX, mouseY)) { - final int mask = (HopperTileEntity.LOGIC_INVERTED|HopperTileEntity.LOGIC_IGNORE_EXT|HopperTileEntity.LOGIC_NOT_INVERTED); - final int logic = switch (container.field(2) & mask) { - case HopperTileEntity.LOGIC_NOT_INVERTED -> HopperTileEntity.LOGIC_INVERTED; - case HopperTileEntity.LOGIC_INVERTED -> HopperTileEntity.LOGIC_IGNORE_EXT; - case HopperTileEntity.LOGIC_IGNORE_EXT -> HopperTileEntity.LOGIC_NOT_INVERTED; - default -> HopperTileEntity.LOGIC_IGNORE_EXT; - }; - container.onGuiAction("logic", (container.field(2) & (~mask)) | logic); - } else if(isHovering(159, 49, 7, 9, mouseX, mouseY)) { - container.onGuiAction("logic", container.field(2) ^ HopperTileEntity.LOGIC_CONTINUOUS); - } - return true; - } - } + @Override + public void init() { + super.init(); + { + final Block block = ModContent.getBlock(Auxiliaries.getResourceLocation(getMenu().getType()).getPath().replaceAll("^ct_", "")); + final String prefix = block.getDescriptionId() + ".tooltips."; + final int x0 = getGuiLeft(), y0 = getGuiTop(); + tooltip_.init( + new TooltipDisplay.TipRange(x0 + 148, y0 + 22, 3, 3, Component.translatable(prefix + "delayindicator")), + new TooltipDisplay.TipRange(x0 + 130, y0 + 9, 40, 10, Component.translatable(prefix + "range")), + new TooltipDisplay.TipRange(x0 + 130, y0 + 22, 40, 10, Component.translatable(prefix + "period")), + new TooltipDisplay.TipRange(x0 + 130, y0 + 35, 40, 10, Component.translatable(prefix + "count")), + new TooltipDisplay.TipRange(x0 + 133, y0 + 49, 9, 9, Component.translatable(prefix + "rssignal")), + new TooltipDisplay.TipRange(x0 + 145, y0 + 49, 9, 9, Component.translatable(prefix + "inversion")), + new TooltipDisplay.TipRange(x0 + 159, y0 + 49, 9, 9, Component.translatable(prefix + "triggermode")) + ); + } + } + + @Override + protected void renderBgWidgets(GuiGraphics mx, float partialTicks, int mouseX, int mouseY) { + final int x0 = getGuiLeft(), y0 = getGuiTop(), w = getXSize(), h = getYSize(); + HopperContainer container = getMenu(); + // active slot + { + int slot_index = container.field(6); + if ((slot_index < 0) || (slot_index >= HopperTileEntity.NUM_OF_SLOTS)) slot_index = 0; + int x = (x0 + 10 + ((slot_index % 6) * 18)); + int y = (y0 + 8 + ((slot_index / 6) * 17)); + mx.blit(getBackgroundImage(), x, y, 200, 8, 18, 18); + } + // collection range + { + int[] lut = {133, 141, 149, 157, 166}; + int px = lut[Mth.clamp(container.field(0), 0, HopperTileEntity.MAX_COLLECTION_RANGE)]; + int x = x0 + px - 2; + int y = y0 + 14; + mx.blit(getBackgroundImage(), x, y, 179, 40, 5, 5); + } + // transfer period + { + int px = (int) Math.round(((33.5 * container.field(3)) / 100) + 1); + int x = x0 + 132 - 2 + Mth.clamp(px, 0, 34); + int y = y0 + 27; + mx.blit(getBackgroundImage(), x, y, 179, 40, 5, 5); + } + // transfer count + { + int x = x0 + 133 - 2 + (container.field(1)); + int y = y0 + 40; + mx.blit(getBackgroundImage(), x, y, 179, 40, 5, 5); + } + // redstone input + { + if (container.field(5) != 0) { + mx.blit(getBackgroundImage(), x0 + 133, y0 + 49, 217, 49, 9, 9); + } + } + // trigger logic + { + int inverter_offset_x = ((container.field(2) & HopperTileEntity.LOGIC_INVERTED) != 0) ? 11 : 0; + int inverter_offset_y = ((container.field(2) & HopperTileEntity.LOGIC_IGNORE_EXT) != 0) ? 10 : 0; + mx.blit(getBackgroundImage(), x0 + 145, y0 + 49, 177 + inverter_offset_x, 49 + inverter_offset_y, 9, 9); + int pulse_mode_offset = ((container.field(2) & HopperTileEntity.LOGIC_CONTINUOUS) != 0) ? 9 : 0; + mx.blit(getBackgroundImage(), x0 + 159, y0 + 49, 199 + pulse_mode_offset, 49, 9, 9); + } + // delay timer running indicator + { + if ((container.field(4) > HopperTileEntity.PERIOD_OFFSET) && ((System.currentTimeMillis() % 1000) < 500)) { + mx.blit(getBackgroundImage(), x0 + 148, y0 + 22, 187, 22, 3, 3); + } + } + } + + @Override + protected void slotClicked(Slot slot, int slotId, int button, ClickType type) { + tooltip_.resetTimer(); + if ((type == ClickType.QUICK_MOVE) && (slot != null) && slot.hasItem() && Auxiliaries.isShiftDown() && Auxiliaries.isCtrlDown()) { + CompoundTag nbt = new CompoundTag(); + nbt.putInt("slot", slotId); + menu.onGuiAction(HopperContainer.QUICK_MOVE_ALL, nbt); + } else { + super.slotClicked(slot, slotId, button, type); + } + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int mouseButton) { + tooltip_.resetTimer(); + HopperContainer container = getMenu(); + int mx = (int) (mouseX - getGuiLeft() + .5), my = (int) (mouseY - getGuiTop() + .5); + if ((!isHovering(126, 1, 49, 60, mouseX, mouseY))) { + return super.mouseClicked(mouseX, mouseY, mouseButton); + } else if (isHovering(128, 9, 44, 10, mouseX, mouseY)) { + int range = (mx - 133); + if (range < -1) { + range = container.field(0) - 1; // - + } else if (range >= 34) { + range = container.field(0) + 1; // + + } else { + range = (int) (0.5 + ((((double) HopperTileEntity.MAX_COLLECTION_RANGE) * range) / 34)); // slider + range = Mth.clamp(range, 0, HopperTileEntity.MAX_COLLECTION_RANGE); + } + container.onGuiAction("range", range); + } else if (isHovering(128, 21, 44, 10, mouseX, mouseY)) { + int period = (mx - 133); + if (period < -1) { + period = container.field(3) - 3; // - + } else if (period >= 35) { + period = container.field(3) + 3; // + + } else { + period = (int) (0.5 + ((100.0 * period) / 34)); + } + period = Mth.clamp(period, 0, 100); + container.onGuiAction("period", period); + } else if (isHovering(128, 34, 44, 10, mouseX, mouseY)) { + int ndrop = (mx - 134); + if (ndrop < -1) { + ndrop = container.field(1) - 1; // - + } else if (ndrop >= 34) { + ndrop = container.field(1) + 1; // + + } else { + ndrop = Mth.clamp(1 + ndrop, 1, HopperTileEntity.MAX_TRANSFER_COUNT); // slider + } + container.onGuiAction("xsize", ndrop); + } else if (isHovering(133, 49, 9, 9, mouseX, mouseY)) { + container.onGuiAction("manual_trigger", 1); + } else if (isHovering(145, 49, 9, 9, mouseX, mouseY)) { + final int mask = (HopperTileEntity.LOGIC_INVERTED | HopperTileEntity.LOGIC_IGNORE_EXT | HopperTileEntity.LOGIC_NOT_INVERTED); + final int logic = switch (container.field(2) & mask) { + case HopperTileEntity.LOGIC_NOT_INVERTED -> HopperTileEntity.LOGIC_INVERTED; + case HopperTileEntity.LOGIC_INVERTED -> HopperTileEntity.LOGIC_IGNORE_EXT; + case HopperTileEntity.LOGIC_IGNORE_EXT -> HopperTileEntity.LOGIC_NOT_INVERTED; + default -> HopperTileEntity.LOGIC_IGNORE_EXT; + }; + container.onGuiAction("logic", (container.field(2) & (~mask)) | logic); + } else if (isHovering(159, 49, 7, 9, mouseX, mouseY)) { + container.onGuiAction("logic", container.field(2) ^ HopperTileEntity.LOGIC_CONTINUOUS); + } + return true; + } + + } } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdHorizontalSupportBlock.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdHorizontalSupportBlock.java index 5a8ec13..631db19 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdHorizontalSupportBlock.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdHorizontalSupportBlock.java @@ -9,6 +9,9 @@ */ package dev.zontreck.engineerdecor.blocks; +import dev.zontreck.libzontreck.edlibmc.Auxiliaries; +import dev.zontreck.libzontreck.edlibmc.Inventories; +import dev.zontreck.libzontreck.edlibmc.StandardBlocks; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.sounds.SoundEvents; @@ -39,9 +42,6 @@ import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; import dev.zontreck.engineerdecor.ModContent; -import dev.zontreck.engineerdecor.libmc.StandardBlocks; -import dev.zontreck.engineerdecor.libmc.Auxiliaries; -import dev.zontreck.engineerdecor.libmc.Inventories; import javax.annotation.Nullable; import java.util.HashMap; @@ -85,8 +85,9 @@ public class EdHorizontalSupportBlock extends StandardBlocks.WaterLoggable { return RenderTypeHint.CUTOUT; } @Override - public boolean isPossibleToRespawnInThis() - { return false; } + public boolean isPossibleToRespawnInThis(BlockState p_279289_) { + return false; + } @Override public boolean isValidSpawn(BlockState state, BlockGetter world, BlockPos pos, SpawnPlacements.Type type, @Nullable EntityType entityType) diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdLadderBlock.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdLadderBlock.java index cfb5750..d994119 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdLadderBlock.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdLadderBlock.java @@ -12,6 +12,9 @@ */ package dev.zontreck.engineerdecor.blocks; +import dev.zontreck.engineerdecor.ModConfig; +import dev.zontreck.libzontreck.edlibmc.Auxiliaries; +import dev.zontreck.libzontreck.edlibmc.StandardBlocks; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.network.chat.Component; @@ -34,90 +37,92 @@ import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -import dev.zontreck.engineerdecor.ModConfig; -import dev.zontreck.engineerdecor.libmc.StandardBlocks; -import dev.zontreck.engineerdecor.libmc.Auxiliaries; import javax.annotation.Nullable; import java.util.List; -public class EdLadderBlock extends LadderBlock implements StandardBlocks.IStandardBlock -{ - protected static final AABB EDLADDER_UNROTATED_AABB = Auxiliaries.getPixeledAABB(3, 0, 0, 13, 16, 3); - protected static final VoxelShape EDLADDER_SOUTH_AABB = Shapes.create(Auxiliaries.getRotatedAABB(EDLADDER_UNROTATED_AABB, Direction.SOUTH, false)); - protected static final VoxelShape EDLADDER_EAST_AABB = Shapes.create(Auxiliaries.getRotatedAABB(EDLADDER_UNROTATED_AABB, Direction.EAST, false)); - protected static final VoxelShape EDLADDER_WEST_AABB = Shapes.create(Auxiliaries.getRotatedAABB(EDLADDER_UNROTATED_AABB, Direction.WEST, false)); - protected static final VoxelShape EDLADDER_NORTH_AABB = Shapes.create(Auxiliaries.getRotatedAABB(EDLADDER_UNROTATED_AABB, Direction.NORTH, false)); - private static boolean without_speed_boost_ = false; +public class EdLadderBlock extends LadderBlock implements StandardBlocks.IStandardBlock { + protected static final AABB EDLADDER_UNROTATED_AABB = Auxiliaries.getPixeledAABB(3, 0, 0, 13, 16, 3); + protected static final VoxelShape EDLADDER_SOUTH_AABB = Shapes.create(Auxiliaries.getRotatedAABB(EDLADDER_UNROTATED_AABB, Direction.SOUTH, false)); + protected static final VoxelShape EDLADDER_EAST_AABB = Shapes.create(Auxiliaries.getRotatedAABB(EDLADDER_UNROTATED_AABB, Direction.EAST, false)); + protected static final VoxelShape EDLADDER_WEST_AABB = Shapes.create(Auxiliaries.getRotatedAABB(EDLADDER_UNROTATED_AABB, Direction.WEST, false)); + protected static final VoxelShape EDLADDER_NORTH_AABB = Shapes.create(Auxiliaries.getRotatedAABB(EDLADDER_UNROTATED_AABB, Direction.NORTH, false)); + private static boolean without_speed_boost_ = false; - public static void on_config(boolean without_speed_boost) - { - without_speed_boost_ = without_speed_boost; - ModConfig.log("Config ladder: without-speed-boost:" + without_speed_boost_); - } - - public EdLadderBlock(long config, BlockBehaviour.Properties builder) - { super(builder); } - - @Override - public RenderTypeHint getRenderTypeHint() - { return RenderTypeHint.CUTOUT; } - - @Override - @OnlyIn(Dist.CLIENT) - public void appendHoverText(ItemStack stack, @Nullable BlockGetter world, List tooltip, TooltipFlag flag) - { Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true); } - - public VoxelShape getShape(BlockState state, BlockGetter worldIn, BlockPos pos) - { - return switch(state.getValue(FACING)) { - case NORTH -> EDLADDER_NORTH_AABB; - case SOUTH -> EDLADDER_SOUTH_AABB; - case WEST -> EDLADDER_WEST_AABB; - default -> EDLADDER_EAST_AABB; - }; - } - - @Override - public boolean isPossibleToRespawnInThis() - { return false; } - - @Override - public boolean isValidSpawn(BlockState state, BlockGetter world, BlockPos pos, SpawnPlacements.Type type, @Nullable EntityType entityType) - { return false; } - - @Override - @SuppressWarnings("deprecation") - public PushReaction getPistonPushReaction(BlockState state) - { return PushReaction.NORMAL; } - - @Override - public boolean isLadder(BlockState state, LevelReader world, BlockPos pos, LivingEntity entity) - { return true; } - - // Player update event, forwarded from the main mod instance. - public static void onPlayerUpdateEvent(final Player player) - { - if((without_speed_boost_) || (player.isOnGround()) || (!player.onClimbable()) || (player.isSteppingCarefully()) || (player.isSpectator())) return; - double lvy = player.getLookAngle().y; - if(Math.abs(lvy) < 0.92) return; - final BlockPos pos = player.blockPosition(); - final BlockState state = player.getLevel().getBlockState(pos); - final Block block = state.getBlock(); - if(!(block instanceof EdLadderBlock || block instanceof EdHatchBlock && state.getValue(EdHatchBlock.OPEN))) return; - player.resetFallDistance(); - if((player.getDeltaMovement().y() < 0) == (player.getLookAngle().y < 0)) { - player.makeStuckInBlock(state, new Vec3(0.2, (lvy>0)?(3):(6), 0.2)); - if(Math.abs(player.getDeltaMovement().y()) > 0.1) { - Vec3 vdiff = Vec3.atBottomCenterOf(pos).subtract(player.position()).scale(1); - vdiff.add(Vec3.atBottomCenterOf(state.getValue(FACING).getNormal()).scale(0.5)); - vdiff = new Vec3(vdiff.x, player.getDeltaMovement().y, vdiff.z); - player.setDeltaMovement(vdiff); - } - } else if(player.getLookAngle().y > 0) { - player.makeStuckInBlock(state, new Vec3(1, 0.05, 1)); + public EdLadderBlock(long config, BlockBehaviour.Properties builder) { + super(builder); + } + + public static void on_config(boolean without_speed_boost) { + without_speed_boost_ = without_speed_boost; + ModConfig.log("Config ladder: without-speed-boost:" + without_speed_boost_); + } + + // Player update event, forwarded from the main mod instance. + public static void onPlayerUpdateEvent(final Player player) { + if ((without_speed_boost_) || (player.onGround()) || (!player.onClimbable()) || (player.isSteppingCarefully()) || (player.isSpectator())) + return; + double lvy = player.getLookAngle().y; + if (Math.abs(lvy) < 0.92) return; + final BlockPos pos = player.blockPosition(); + final BlockState state = player.level().getBlockState(pos); + final Block block = state.getBlock(); + if (!(block instanceof EdLadderBlock || block instanceof EdHatchBlock && state.getValue(EdHatchBlock.OPEN))) + return; + player.resetFallDistance(); + if ((player.getDeltaMovement().y() < 0) == (player.getLookAngle().y < 0)) { + player.makeStuckInBlock(state, new Vec3(0.2, (lvy > 0) ? (3) : (6), 0.2)); + if (Math.abs(player.getDeltaMovement().y()) > 0.1) { + Vec3 vdiff = Vec3.atBottomCenterOf(pos).subtract(player.position()).scale(1); + vdiff.add(Vec3.atBottomCenterOf(state.getValue(FACING).getNormal()).scale(0.5)); + vdiff = new Vec3(vdiff.x, player.getDeltaMovement().y, vdiff.z); + player.setDeltaMovement(vdiff); + } + } else if (player.getLookAngle().y > 0) { + player.makeStuckInBlock(state, new Vec3(1, 0.05, 1)); + } + } + + @Override + public RenderTypeHint getRenderTypeHint() { + return RenderTypeHint.CUTOUT; + } + + @Override + @OnlyIn(Dist.CLIENT) + public void appendHoverText(ItemStack stack, @Nullable BlockGetter world, List tooltip, TooltipFlag flag) { + Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true); + } + + public VoxelShape getShape(BlockState state, BlockGetter worldIn, BlockPos pos) { + return switch (state.getValue(FACING)) { + case NORTH -> EDLADDER_NORTH_AABB; + case SOUTH -> EDLADDER_SOUTH_AABB; + case WEST -> EDLADDER_WEST_AABB; + default -> EDLADDER_EAST_AABB; + }; + } + + @Override + public boolean isPossibleToRespawnInThis(BlockState p_279289_) { + return false; + } + + @Override + public boolean isValidSpawn(BlockState state, BlockGetter world, BlockPos pos, SpawnPlacements.Type type, @Nullable EntityType entityType) { + return false; + } + + @Override + @SuppressWarnings("deprecation") + public PushReaction getPistonPushReaction(BlockState state) { + return PushReaction.NORMAL; + } + + @Override + public boolean isLadder(BlockState state, LevelReader world, BlockPos pos, LivingEntity entity) { + return true; } - } } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdMilker.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdMilker.java index 7302bbf..7026528 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdMilker.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdMilker.java @@ -8,7 +8,10 @@ */ package dev.zontreck.engineerdecor.blocks; -import dev.zontreck.engineerdecor.libmc.*; + +import dev.zontreck.engineerdecor.ModConfig; +import dev.zontreck.engineerdecor.ModContent; +import dev.zontreck.libzontreck.edlibmc.*; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; @@ -30,6 +33,7 @@ import net.minecraft.world.item.Items; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.SignalGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockBehaviour; @@ -50,9 +54,6 @@ import net.minecraftforge.fluids.capability.IFluidHandler; import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.ItemHandlerHelper; import net.minecraftforge.registries.ForgeRegistries; -import dev.zontreck.engineerdecor.ModConfig; -import dev.zontreck.engineerdecor.ModContent; -import wile.engineersdecor.libmc.*; import javax.annotation.Nullable; import java.util.HashMap; @@ -62,669 +63,694 @@ import java.util.Map.Entry; import java.util.UUID; -public class EdMilker -{ - public static final int BUCKET_SIZE = 1000; - public static final int TICK_INTERVAL = 80; - public static final int PROCESSING_TICK_INTERVAL = 20; - public static final int TANK_CAPACITY = BUCKET_SIZE * 12; - public static final int MAX_MILKING_TANK_LEVEL = TANK_CAPACITY-500; - public static final int FILLED_INDICATION_THRESHOLD = BUCKET_SIZE; - public static final int MAX_ENERGY_BUFFER = 16000; - public static final int MAX_ENERGY_TRANSFER = 512; - public static final int DEFAULT_ENERGY_CONSUMPTION = 0; - public static final int DEFAULT_MILKING_DELAY_PER_COW = 4000; - private static final FluidStack NO_MILK_FLUID = new FluidStack(Fluids.WATER, 1000); +public class EdMilker { + public static final int BUCKET_SIZE = 1000; + public static final int TICK_INTERVAL = 80; + public static final int PROCESSING_TICK_INTERVAL = 20; + public static final int TANK_CAPACITY = BUCKET_SIZE * 12; + public static final int MAX_MILKING_TANK_LEVEL = TANK_CAPACITY - 500; + public static final int FILLED_INDICATION_THRESHOLD = BUCKET_SIZE; + public static final int MAX_ENERGY_BUFFER = 16000; + public static final int MAX_ENERGY_TRANSFER = 512; + public static final int DEFAULT_ENERGY_CONSUMPTION = 0; + public static final int DEFAULT_MILKING_DELAY_PER_COW = 4000; + private static final FluidStack NO_MILK_FLUID = new FluidStack(Fluids.WATER, 1000); + private static final HashMap milk_containers_ = new HashMap<>(); + private static FluidStack milk_fluid_ = NO_MILK_FLUID; + private static int energy_consumption_ = DEFAULT_ENERGY_CONSUMPTION; + private static long min_milking_delay_per_cow_ticks_ = DEFAULT_MILKING_DELAY_PER_COW; - private static FluidStack milk_fluid_ = NO_MILK_FLUID; - private static final HashMap milk_containers_ = new HashMap<>(); - private static int energy_consumption_ = DEFAULT_ENERGY_CONSUMPTION; - private static long min_milking_delay_per_cow_ticks_ = DEFAULT_MILKING_DELAY_PER_COW; - - public static void on_config(int energy_consumption_per_tick, int min_milking_delay_per_cow) - { - energy_consumption_ = Mth.clamp(energy_consumption_per_tick, 0, 1024); - min_milking_delay_per_cow_ticks_ = Mth.clamp(min_milking_delay_per_cow, 1000, 24000); - { - ResourceLocation milk_rl = ForgeRegistries.FLUIDS.getKeys().stream().filter(rl->rl.getPath().equals("milk")).findFirst().orElse(null); - if(milk_rl != null) { - Fluid milk = ForgeRegistries.FLUIDS.getValue(milk_rl); - if(milk != null) milk_fluid_ = new FluidStack(milk, BUCKET_SIZE); - } - } - { - milk_containers_.put(new ItemStack(Items.BUCKET), new ItemStack(Items.MILK_BUCKET)); - } - ModConfig.log( - "Config milker: energy consumption:" + energy_consumption_ + "rf/t" - + ((milk_fluid_==NO_MILK_FLUID)?"[no milk fluid registered]":" [milk fluid available]") - ); - } - - //-------------------------------------------------------------------------------------------------------------------- - // Block - //-------------------------------------------------------------------------------------------------------------------- - - public static class MilkerBlock extends StandardBlocks.Horizontal implements StandardEntityBlocks.IStandardEntityBlock - { - public static final BooleanProperty FILLED = BooleanProperty.create("filled"); - public static final BooleanProperty ACTIVE = BooleanProperty.create("active"); - - public MilkerBlock(long config, BlockBehaviour.Properties builder, final AABB[] unrotatedAABBs) - { - super(config, builder, unrotatedAABBs); - cshapes.replaceAll((state,shape)->Shapes.create(Auxiliaries.getPixeledAABB(0,0,0, 16,24,16))); - } - - @Override - public boolean isBlockEntityTicking(Level world, BlockState state) - { return true; } - - @Override - protected void createBlockStateDefinition(StateDefinition.Builder builder) - { super.createBlockStateDefinition(builder); builder.add(ACTIVE); builder.add(FILLED); } - - @Override - @Nullable - public BlockState getStateForPlacement(BlockPlaceContext context) - { return super.getStateForPlacement(context).setValue(FILLED, false).setValue(ACTIVE, false); } - - @Override - @SuppressWarnings("deprecation") - public boolean hasAnalogOutputSignal(BlockState state) - { return true; } - - @Override - @SuppressWarnings("deprecation") - public int getAnalogOutputSignal(BlockState state, Level world, BlockPos pos) - { - MilkerTileEntity te = getTe(world, pos); - return (te==null) ? 0 : Mth.clamp((16 * te.fluid_level())/TANK_CAPACITY, 0, 15); - } - - @Override - public boolean shouldCheckWeakPower(BlockState state, LevelReader world, BlockPos pos, Direction side) - { return false; } - - @Override - @SuppressWarnings("deprecation") - public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) - { - if(world.isClientSide()) return InteractionResult.SUCCESS; - MilkerTileEntity te = getTe(world, pos); - if(te==null) return InteractionResult.FAIL; - final ItemStack in_stack = player.getItemInHand(hand); - final ItemStack out_stack = MilkerTileEntity.milk_filled_container_item(in_stack); - if(in_stack.isEmpty()) { - te.state_message(player); - return InteractionResult.CONSUME; - } else if(out_stack.isEmpty() && (te.fluid_handler()!=null)) { - return Fluidics.manualFluidHandlerInteraction(player, hand, te.fluid_handler()) ? InteractionResult.CONSUME : InteractionResult.FAIL; - } else { - boolean drained = false; - IItemHandler player_inventory = Inventories.itemhandler(player); - if(te.fluid_level() >= BUCKET_SIZE) { - final ItemStack insert_stack = out_stack.copy(); - ItemStack remainder = ItemHandlerHelper.insertItemStacked(player_inventory, insert_stack, false); - if(remainder.getCount() < insert_stack.getCount()) { - te.drain(BUCKET_SIZE); - in_stack.shrink(1); - drained = true; - if(remainder.getCount() > 0) { - final ItemEntity ei = new ItemEntity(world, player.position().x(), player.position().y()+0.5, player.position().z(), remainder); - ei.setPickUpDelay(40); - ei.setDeltaMovement(0,0,0); - world.addFreshEntity(ei); + public static void on_config(int energy_consumption_per_tick, int min_milking_delay_per_cow) { + energy_consumption_ = Mth.clamp(energy_consumption_per_tick, 0, 1024); + min_milking_delay_per_cow_ticks_ = Mth.clamp(min_milking_delay_per_cow, 1000, 24000); + { + ResourceLocation milk_rl = ForgeRegistries.FLUIDS.getKeys().stream().filter(rl -> rl.getPath().equals("milk")).findFirst().orElse(null); + if (milk_rl != null) { + Fluid milk = ForgeRegistries.FLUIDS.getValue(milk_rl); + if (milk != null) milk_fluid_ = new FluidStack(milk, BUCKET_SIZE); } - } } - if(drained) { - world.playSound(null, pos, SoundEvents.BUCKET_FILL, SoundSource.BLOCKS, 0.8f, 1f); + { + milk_containers_.put(new ItemStack(Items.BUCKET), new ItemStack(Items.MILK_BUCKET)); } - } - return InteractionResult.CONSUME; - } - - @Nullable - private MilkerTileEntity getTe(Level world, BlockPos pos) - { final BlockEntity te=world.getBlockEntity(pos); return (!(te instanceof MilkerTileEntity)) ? (null) : ((MilkerTileEntity)te); } - } - - //-------------------------------------------------------------------------------------------------------------------- - // Tile entity - //-------------------------------------------------------------------------------------------------------------------- - - public static class MilkerTileEntity extends StandardEntityBlocks.StandardBlockEntity // implements IFluidTank - { - private static final Direction[] FLUID_TRANSFER_DIRECTRIONS = {Direction.DOWN,Direction.EAST,Direction.SOUTH,Direction.WEST,Direction.NORTH}; - private enum MilkingState { IDLE, PICKED, COMING, POSITIONING, MILKING, LEAVING, WAITING } - - private int tick_timer_; - private UUID tracked_cow_ = null; - private MilkingState state_ = MilkingState.IDLE; - private int state_timeout_ = 0; - private int state_timer_ = 0; - private BlockPos tracked_cow_original_position_ = null; - private final RfEnergy.Battery battery_; - private final LazyOptional energy_handler_; - private final Fluidics.Tank tank_; - private final LazyOptional fluid_handler_; - - public MilkerTileEntity(BlockPos pos, BlockState state) - { - super(ModContent.getBlockEntityTypeOfBlock(state.getBlock()), pos, state); - tank_ = new Fluidics.Tank(TANK_CAPACITY, 0, BUCKET_SIZE, fs->fs.isFluidEqual(milk_fluid_)); - fluid_handler_ = tank_.createOutputFluidHandler(); - battery_ = new RfEnergy.Battery(MAX_ENERGY_BUFFER, MAX_ENERGY_TRANSFER, 0); - energy_handler_ = battery_.createEnergyHandler(); - reset(); - } - - public void reset() - { - tank_.clear(); - battery_.clear(); - tick_timer_ = 0; - tracked_cow_ = null; - state_ = MilkingState.IDLE; - state_timeout_ = 0; - } - - public CompoundTag destroy_getnbt() - { - final UUID cowuid = tracked_cow_; - CompoundTag nbt = new CompoundTag(); - writenbt(nbt, false); reset(); - if(cowuid == null) return nbt; - level.getEntitiesOfClass(Cow.class, new AABB(worldPosition).inflate(16, 16, 16), e->e.getUUID().equals(cowuid)).forEach(e->e.setNoAi(false)); - return nbt; - } - - public void readnbt(CompoundTag nbt, boolean update_packet) - { - battery_.load(nbt); - tank_.load(nbt); - } - - protected void writenbt(CompoundTag nbt, boolean update_packet) - { - tank_.save(nbt); - if(!battery_.isEmpty()) battery_.save(nbt); - } - - private boolean has_milk_fluid() - { return !(NO_MILK_FLUID.isFluidEqual(milk_fluid_)); } - - private IFluidHandler fluid_handler() - { return fluid_handler_.orElse(null); } - - private int fluid_level() - { return tank_.getFluidAmount(); } - - private FluidStack drain(int amount) - { return tank_.drain(amount); } - - public void state_message(Player player) - { - Component rf = (energy_consumption_ <= 0) ? (Component.empty()) : (Auxiliaries.localizable("block.engineersdecor.small_milking_machine.status.rf", battery_.getEnergyStored())); - Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.small_milking_machine.status", tank_.getFluidAmount(), rf)); - } - - // BlockEntity ------------------------------------------------------------------------------ - - @Override - public void load(CompoundTag nbt) - { super.load(nbt); readnbt(nbt, false); } - - @Override - protected void saveAdditional(CompoundTag nbt) - { super.saveAdditional(nbt); writenbt(nbt, false); } - - @Override - public void setRemoved() - { - super.setRemoved(); - energy_handler_.invalidate(); - fluid_handler_.invalidate(); - } - - // ICapabilityProvider --------------------------------------------------------------------------- - - @Override - public LazyOptional getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable Direction facing) - { - if((capability == ForgeCapabilities.FLUID_HANDLER) && has_milk_fluid()) return fluid_handler_.cast(); - if((capability == ForgeCapabilities.ENERGY) && (energy_consumption_>0)) return energy_handler_.cast(); - return super.getCapability(capability, facing); - } - - // ITickable ------------------------------------------------------------------------------------ - - private static final HashMap tracked_cows_ = new HashMap<>(); - - private void log(String s) - {} // println("Milker|" + s); may be enabled with config, for dev was println - - private static ItemStack milk_filled_container_item(ItemStack stack) - { return milk_containers_.entrySet().stream().filter(e-> Inventories.areItemStacksIdentical(e.getKey(), stack)).map(Map.Entry::getValue).findFirst().orElse(ItemStack.EMPTY); } - - private boolean fill_adjacent_inventory_item_containers(Direction block_facing) - { - // Check inventory existence, back to down is preferred, otherwise sort back into same inventory. - IItemHandler src = Inventories.itemhandler(level, worldPosition.relative(block_facing), block_facing.getOpposite()); - IItemHandler dst = Inventories.itemhandler(level, worldPosition.below(), Direction.UP); - if(src==null) { src = dst; } else if(dst==null) { dst = src; } - if((src==null) || (dst==null)) return false; - boolean dirty = false; - while((tank_.getFluidAmount() >= BUCKET_SIZE)) { - boolean inserted = false; - for(Entry e:milk_containers_.entrySet()) { - if(Inventories.extract(src, e.getKey(), 1, true).isEmpty()) continue; - if(!Inventories.insert(dst, e.getValue().copy(), false).isEmpty()) continue; - Inventories.extract(src, e.getKey(), 1, false); - tank_.drain(BUCKET_SIZE); - inserted = true; - dirty = true; - break; - } - if(!inserted) break; - } - return dirty; - } - - private boolean fill_adjacent_tank() - { - if((fluid_level()<=0) || (!has_milk_fluid())) return false; - final FluidStack fs = new FluidStack(milk_fluid_, Math.max(fluid_level(), BUCKET_SIZE)); - for(Direction dir:Direction.values()) { - int amount = Fluidics.fill(getLevel(), getBlockPos().relative(dir), dir.getOpposite(), fs); - if(amount > 0) { - tank_.drain(amount); - return true; - } - } - return false; - } - - private void release_cow(Cow cow) - { - log("release cow"); - if(cow != null) { - cow.setNoAi(false); - SingleMoveGoal.abortFor(cow); - tracked_cows_.remove(cow.getId()); - for(int id: tracked_cows_.keySet().stream().filter(i->cow.getCommandSenderWorld().getEntity(i) == null).toList()) { - tracked_cows_.remove(id); - } - } - tracked_cow_ = null; - state_ = MilkingState.IDLE; - tick_timer_ = TICK_INTERVAL; - } - - private boolean milking_process() - { - if((tracked_cow_ == null) && (fluid_level() >= MAX_MILKING_TANK_LEVEL)) return false; // nothing to do - final Direction facing = level.getBlockState(getBlockPos()).getValue(MilkerBlock.HORIZONTAL_FACING).getOpposite(); - final Vec3 target_pos = Vec3.atLowerCornerOf(getBlockPos().relative(facing)).add(0.5,0,0.5); - Cow cow = null; - { - AABB aabb = new AABB(worldPosition.relative(facing, 3)).inflate(4, 2, 4); - final long t = level.getGameTime(); - final List cows = level.getEntitiesOfClass(Cow.class, aabb, - e-> { - if(e.getUUID().equals(tracked_cow_)) return true; - if((tracked_cow_!=null) || e.isBaby() || e.isInLove() || e.isVehicle()) return false; - if(!e.getNavigation().isDone()) return false; - if(Math.abs(tracked_cows_.getOrDefault(e.getId(), 0L)-t) < min_milking_delay_per_cow_ticks_) return false; - return true; - } + ModConfig.log( + "Config milker: energy consumption:" + energy_consumption_ + "rf/t" + + ((milk_fluid_ == NO_MILK_FLUID) ? "[no milk fluid registered]" : " [milk fluid available]") ); - if(cows.size() == 1) { - cow = cows.get(0); // tracked or only one - } else if(cows.size() > 1) { - cow = cows.get(level.random.nextInt(cows.size()-1)); // pick one + } + + //-------------------------------------------------------------------------------------------------------------------- + // Block + //-------------------------------------------------------------------------------------------------------------------- + + public static class MilkerBlock extends StandardBlocks.Horizontal implements StandardEntityBlocks.IStandardEntityBlock { + public static final BooleanProperty FILLED = BooleanProperty.create("filled"); + public static final BooleanProperty ACTIVE = BooleanProperty.create("active"); + + public MilkerBlock(long config, BlockBehaviour.Properties builder, final AABB[] unrotatedAABBs) { + super(config, builder, unrotatedAABBs); + cshapes.replaceAll((state, shape) -> Shapes.create(Auxiliaries.getPixeledAABB(0, 0, 0, 16, 24, 16))); } - } - if((state_ != MilkingState.IDLE) && ((state_timeout_ -= PROCESSING_TICK_INTERVAL) <= 0)) { release_cow(cow); log("Cow motion timeout"); cow = null; } - if((cow == null) || (!cow.isAlive())) { release_cow(cow); cow = null; } - if(tracked_cow_ == null) state_ = MilkingState.IDLE; - if(cow == null) { log("Init: No cow"); return false; } // retry next cycle - tick_timer_ = PROCESSING_TICK_INTERVAL; - state_timer_ -= PROCESSING_TICK_INTERVAL; - if(state_timer_ > 0) return false; - switch(state_) { // Let's do this the old school FSA sequencing way ... - case IDLE -> { - final List blocking_entities = level.getEntitiesOfClass(LivingEntity.class, new AABB(worldPosition.relative(facing)).inflate(0.5, 0.5, 0.5)); - if(blocking_entities.size() > 0) { - tick_timer_ = TICK_INTERVAL; - log("Idle: Position blocked"); - if(blocking_entities.get(0) instanceof final Cow blocker) { - BlockPos p = getBlockPos().relative(facing, 2); - log("Idle: Shove off"); - blocker.setNoAi(false); - SingleMoveGoal.startFor(blocker, p, 2, 1.0, (goal, world, pos) -> (pos.distSqr(goal.getCreature().blockPosition()) > 100)); + + @Override + public boolean isBlockEntityTicking(Level world, BlockState state) { + return true; + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + super.createBlockStateDefinition(builder); + builder.add(ACTIVE); + builder.add(FILLED); + } + + @Override + @Nullable + public BlockState getStateForPlacement(BlockPlaceContext context) { + return super.getStateForPlacement(context).setValue(FILLED, false).setValue(ACTIVE, false); + } + + @Override + @SuppressWarnings("deprecation") + public boolean hasAnalogOutputSignal(BlockState state) { + return true; + } + + @Override + @SuppressWarnings("deprecation") + public int getAnalogOutputSignal(BlockState state, Level world, BlockPos pos) { + MilkerTileEntity te = getTe(world, pos); + return (te == null) ? 0 : Mth.clamp((16 * te.fluid_level()) / TANK_CAPACITY, 0, 15); + } + + @Override + public boolean shouldCheckWeakPower(BlockState state, SignalGetter level, BlockPos pos, Direction side) { + return false; + } + + @Override + @SuppressWarnings("deprecation") + public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { + if (world.isClientSide()) return InteractionResult.SUCCESS; + MilkerTileEntity te = getTe(world, pos); + if (te == null) return InteractionResult.FAIL; + final ItemStack in_stack = player.getItemInHand(hand); + final ItemStack out_stack = MilkerTileEntity.milk_filled_container_item(in_stack); + if (in_stack.isEmpty()) { + te.state_message(player); + return InteractionResult.CONSUME; + } else if (out_stack.isEmpty() && (te.fluid_handler() != null)) { + return Fluidics.manualFluidHandlerInteraction(player, hand, te.fluid_handler()) ? InteractionResult.CONSUME : InteractionResult.FAIL; + } else { + boolean drained = false; + IItemHandler player_inventory = Inventories.itemhandler(player); + if (te.fluid_level() >= BUCKET_SIZE) { + final ItemStack insert_stack = out_stack.copy(); + ItemStack remainder = ItemHandlerHelper.insertItemStacked(player_inventory, insert_stack, false); + if (remainder.getCount() < insert_stack.getCount()) { + te.drain(BUCKET_SIZE); + in_stack.shrink(1); + drained = true; + if (remainder.getCount() > 0) { + final ItemEntity ei = new ItemEntity(world, player.position().x(), player.position().y() + 0.5, player.position().z(), remainder); + ei.setPickUpDelay(40); + ei.setDeltaMovement(0, 0, 0); + world.addFreshEntity(ei); + } + } + } + if (drained) { + world.playSound(null, pos, SoundEvents.BUCKET_FILL, SoundSource.BLOCKS, 0.8f, 1f); + } + } + return InteractionResult.CONSUME; + } + + @Nullable + private MilkerTileEntity getTe(Level world, BlockPos pos) { + final BlockEntity te = world.getBlockEntity(pos); + return (!(te instanceof MilkerTileEntity)) ? (null) : ((MilkerTileEntity) te); + } + } + + //-------------------------------------------------------------------------------------------------------------------- + // Tile entity + //-------------------------------------------------------------------------------------------------------------------- + + public static class MilkerTileEntity extends StandardEntityBlocks.StandardBlockEntity // implements IFluidTank + { + private static final Direction[] FLUID_TRANSFER_DIRECTRIONS = {Direction.DOWN, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.NORTH}; + private static final HashMap tracked_cows_ = new HashMap<>(); + private final RfEnergy.Battery battery_; + private final LazyOptional energy_handler_; + private final Fluidics.Tank tank_; + private final LazyOptional fluid_handler_; + private int tick_timer_; + private UUID tracked_cow_ = null; + private MilkingState state_ = MilkingState.IDLE; + private int state_timeout_ = 0; + private int state_timer_ = 0; + private BlockPos tracked_cow_original_position_ = null; + + public MilkerTileEntity(BlockPos pos, BlockState state) { + super(ModContent.getBlockEntityTypeOfBlock(state.getBlock()), pos, state); + tank_ = new Fluidics.Tank(TANK_CAPACITY, 0, BUCKET_SIZE, fs -> fs.isFluidEqual(milk_fluid_)); + fluid_handler_ = tank_.createOutputFluidHandler(); + battery_ = new RfEnergy.Battery(MAX_ENERGY_BUFFER, MAX_ENERGY_TRANSFER, 0); + energy_handler_ = battery_.createEnergyHandler(); + reset(); + } + + private static ItemStack milk_filled_container_item(ItemStack stack) { + return milk_containers_.entrySet().stream().filter(e -> Inventories.areItemStacksIdentical(e.getKey(), stack)).map(Map.Entry::getValue).findFirst().orElse(ItemStack.EMPTY); + } + + public void reset() { + tank_.clear(); + battery_.clear(); + tick_timer_ = 0; + tracked_cow_ = null; + state_ = MilkingState.IDLE; + state_timeout_ = 0; + } + + public CompoundTag destroy_getnbt() { + final UUID cowuid = tracked_cow_; + CompoundTag nbt = new CompoundTag(); + writenbt(nbt, false); + reset(); + if (cowuid == null) return nbt; + level.getEntitiesOfClass(Cow.class, new AABB(worldPosition).inflate(16, 16, 16), e -> e.getUUID().equals(cowuid)).forEach(e -> e.setNoAi(false)); + return nbt; + } + + public void readnbt(CompoundTag nbt, boolean update_packet) { + battery_.load(nbt); + tank_.load(nbt); + } + + protected void writenbt(CompoundTag nbt, boolean update_packet) { + tank_.save(nbt); + if (!battery_.isEmpty()) battery_.save(nbt); + } + + private boolean has_milk_fluid() { + return !(NO_MILK_FLUID.isFluidEqual(milk_fluid_)); + } + + private IFluidHandler fluid_handler() { + return fluid_handler_.orElse(null); + } + + private int fluid_level() { + return tank_.getFluidAmount(); + } + + private FluidStack drain(int amount) { + return tank_.drain(amount); + } + + // BlockEntity ------------------------------------------------------------------------------ + + public void state_message(Player player) { + Component rf = (energy_consumption_ <= 0) ? (Component.empty()) : (Auxiliaries.localizable("block.engineersdecor.small_milking_machine.status.rf", battery_.getEnergyStored())); + Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.small_milking_machine.status", tank_.getFluidAmount(), rf)); + } + + @Override + public void load(CompoundTag nbt) { + super.load(nbt); + readnbt(nbt, false); + } + + @Override + protected void saveAdditional(CompoundTag nbt) { + super.saveAdditional(nbt); + writenbt(nbt, false); + } + + // ICapabilityProvider --------------------------------------------------------------------------- + + @Override + public void setRemoved() { + super.setRemoved(); + energy_handler_.invalidate(); + fluid_handler_.invalidate(); + } + + // ITickable ------------------------------------------------------------------------------------ + + @Override + public LazyOptional getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable Direction facing) { + if ((capability == ForgeCapabilities.FLUID_HANDLER) && has_milk_fluid()) return fluid_handler_.cast(); + if ((capability == ForgeCapabilities.ENERGY) && (energy_consumption_ > 0)) return energy_handler_.cast(); + return super.getCapability(capability, facing); + } + + private void log(String s) { + } // println("Milker|" + s); may be enabled with config, for dev was println + + private boolean fill_adjacent_inventory_item_containers(Direction block_facing) { + // Check inventory existence, back to down is preferred, otherwise sort back into same inventory. + IItemHandler src = Inventories.itemhandler(level, worldPosition.relative(block_facing), block_facing.getOpposite()); + IItemHandler dst = Inventories.itemhandler(level, worldPosition.below(), Direction.UP); + if (src == null) { + src = dst; + } else if (dst == null) { + dst = src; + } + if ((src == null) || (dst == null)) return false; + boolean dirty = false; + while ((tank_.getFluidAmount() >= BUCKET_SIZE)) { + boolean inserted = false; + for (Entry e : milk_containers_.entrySet()) { + if (Inventories.extract(src, e.getKey(), 1, true).isEmpty()) continue; + if (!Inventories.insert(dst, e.getValue().copy(), false).isEmpty()) continue; + Inventories.extract(src, e.getKey(), 1, false); + tank_.drain(BUCKET_SIZE); + inserted = true; + dirty = true; + break; + } + if (!inserted) break; + } + return dirty; + } + + private boolean fill_adjacent_tank() { + if ((fluid_level() <= 0) || (!has_milk_fluid())) return false; + final FluidStack fs = new FluidStack(milk_fluid_, Math.max(fluid_level(), BUCKET_SIZE)); + for (Direction dir : Direction.values()) { + int amount = Fluidics.fill(getLevel(), getBlockPos().relative(dir), dir.getOpposite(), fs); + if (amount > 0) { + tank_.drain(amount); + return true; + } } return false; - } - if(cow.isLeashed() || cow.isBaby() || cow.isInLove() || (!cow.isOnGround()) || cow.isVehicle() || cow.isSprinting()) - return false; - tracked_cows_.put(cow.getId(), cow.getCommandSenderWorld().getGameTime()); - tracked_cow_ = cow.getUUID(); - state_ = MilkingState.PICKED; - state_timeout_ = 200; - tracked_cow_original_position_ = cow.blockPosition(); - log("Idle: Picked cow " + tracked_cow_); - return false; } - case PICKED -> { - SingleMoveGoal.startFor( - cow, target_pos, 2, 1.0, - (goal, world, pos) -> (pos.distSqr(goal.getCreature().blockPosition()) > 100), - (goal, world, pos) -> { - log("move: position reached"); - goal.getCreature().moveTo(goal.getTargetPosition().x(), goal.getTargetPosition().y(), goal.getTargetPosition().z(), facing.toYRot(), 0); - }, - (goal, world, pos) -> log("move: aborted") - ); - state_ = MilkingState.COMING; - state_timeout_ = 400; // 15s should be enough - log("Picked: coming to " + target_pos); - return false; - } - case COMING -> { - if(target_pos.distanceToSqr(cow.position()) <= 1) { - log("Coming: position reached"); - state_ = MilkingState.POSITIONING; - state_timeout_ = 100; // 5s - } else if((!SingleMoveGoal.isActiveFor(cow))) { - release_cow(cow); - log("Coming: aborted"); - } else { - state_timeout_ -= 100; - } - return false; - } - case POSITIONING -> { - log("Positioning: start milking"); - SingleMoveGoal.abortFor(cow); - cow.setNoAi(true); - cow.moveTo(target_pos.x(), target_pos.y(), target_pos.z(), facing.toYRot(), 0); - level.playSound(null, worldPosition, SoundEvents.COW_MILK, SoundSource.BLOCKS, 0.5f, 1f); - state_timeout_ = 600; - state_ = MilkingState.MILKING; - state_timer_ = 30; - return false; - } - case MILKING -> { - tank_.fill(milk_fluid_.copy(), IFluidHandler.FluidAction.EXECUTE); - state_timeout_ = 600; - state_ = MilkingState.LEAVING; - state_timer_ = 20; - cow.setNoAi(false); - cow.getNavigation().stop(); - log("Milking: done, leave"); - return true; - } - case LEAVING -> { - BlockPos p = (tracked_cow_original_position_ != null) ? (tracked_cow_original_position_) : getBlockPos().relative(facing, 2).relative(facing.getCounterClockWise()); - SingleMoveGoal.startFor(cow, p, 2, 1.0, (goal, world, pos) -> (pos.distSqr(goal.getCreature().blockPosition()) > 100)); - state_timeout_ = 600; - state_timer_ = 500; - tick_timer_ = TICK_INTERVAL; - state_ = MilkingState.WAITING; - tracked_cows_.put(cow.getId(), cow.getCommandSenderWorld().getGameTime()); - log("Leaving: process done"); - return true; - } - case WAITING -> { - // wait for the timeout to kick in until starting with the next. - tick_timer_ = TICK_INTERVAL; - if(state_timer_ < 40) { + + private void release_cow(Cow cow) { + log("release cow"); + if (cow != null) { + cow.setNoAi(false); + SingleMoveGoal.abortFor(cow); + tracked_cows_.remove(cow.getId()); + for (int id : tracked_cows_.keySet().stream().filter(i -> cow.getCommandSenderWorld().getEntity(i) == null).toList()) { + tracked_cows_.remove(id); + } + } tracked_cow_ = null; - release_cow(null); - } - log("Waiting time elapsed"); - return true; + state_ = MilkingState.IDLE; + tick_timer_ = TICK_INTERVAL; } - default -> release_cow(cow); - } - return (tracked_cow_ != null); - } - @Override - public void tick() - { - if((level.isClientSide) || ((--tick_timer_ > 0))) return; - tick_timer_ = TICK_INTERVAL; - boolean dirty = false; - final BlockState block_state = level.getBlockState(worldPosition); - if(!(block_state.getBlock() instanceof MilkerBlock)) return; - if(!level.hasNeighborSignal(worldPosition) || (state_ != MilkingState.IDLE)) { - if((energy_consumption_ > 0) && (!battery_.draw(energy_consumption_))) return; - // Track and milk cows - if(milking_process()) dirty = true; - // Fluid transfer - if(has_milk_fluid() && (!tank_.isEmpty())) { - log("Fluid transfer"); - for(Direction facing: FLUID_TRANSFER_DIRECTRIONS) { - final IFluidHandler fh = Fluidics.handler(level, worldPosition.relative(facing), facing.getOpposite()); - if(fh == null) continue; - final FluidStack fs = tank_.drain(BUCKET_SIZE, IFluidHandler.FluidAction.SIMULATE); - int nfilled = fh.fill(fs, IFluidHandler.FluidAction.EXECUTE); - if(nfilled <= 0) continue; - tank_.drain(nfilled, IFluidHandler.FluidAction.EXECUTE); - dirty = true; - break; - } + private boolean milking_process() { + if ((tracked_cow_ == null) && (fluid_level() >= MAX_MILKING_TANK_LEVEL)) return false; // nothing to do + final Direction facing = level.getBlockState(getBlockPos()).getValue(MilkerBlock.HORIZONTAL_FACING).getOpposite(); + final Vec3 target_pos = Vec3.atLowerCornerOf(getBlockPos().relative(facing)).add(0.5, 0, 0.5); + Cow cow = null; + { + AABB aabb = new AABB(worldPosition.relative(facing, 3)).inflate(4, 2, 4); + final long t = level.getGameTime(); + final List cows = level.getEntitiesOfClass(Cow.class, aabb, + e -> { + if (e.getUUID().equals(tracked_cow_)) return true; + if ((tracked_cow_ != null) || e.isBaby() || e.isInLove() || e.isVehicle()) return false; + if (!e.getNavigation().isDone()) return false; + if (Math.abs(tracked_cows_.getOrDefault(e.getId(), 0L) - t) < min_milking_delay_per_cow_ticks_) + return false; + return true; + } + ); + if (cows.size() == 1) { + cow = cows.get(0); // tracked or only one + } else if (cows.size() > 1) { + cow = cows.get(level.random.nextInt(cows.size() - 1)); // pick one + } + } + if ((state_ != MilkingState.IDLE) && ((state_timeout_ -= PROCESSING_TICK_INTERVAL) <= 0)) { + release_cow(cow); + log("Cow motion timeout"); + cow = null; + } + if ((cow == null) || (!cow.isAlive())) { + release_cow(cow); + cow = null; + } + if (tracked_cow_ == null) state_ = MilkingState.IDLE; + if (cow == null) { + log("Init: No cow"); + return false; + } // retry next cycle + tick_timer_ = PROCESSING_TICK_INTERVAL; + state_timer_ -= PROCESSING_TICK_INTERVAL; + if (state_timer_ > 0) return false; + switch (state_) { // Let's do this the old school FSA sequencing way ... + case IDLE -> { + final List blocking_entities = level.getEntitiesOfClass(LivingEntity.class, new AABB(worldPosition.relative(facing)).inflate(0.5, 0.5, 0.5)); + if (blocking_entities.size() > 0) { + tick_timer_ = TICK_INTERVAL; + log("Idle: Position blocked"); + if (blocking_entities.get(0) instanceof final Cow blocker) { + BlockPos p = getBlockPos().relative(facing, 2); + log("Idle: Shove off"); + blocker.setNoAi(false); + SingleMoveGoal.startFor(blocker, p, 2, 1.0, (goal, world, pos) -> (pos.distSqr(goal.getCreature().blockPosition()) > 100)); + } + return false; + } + if (cow.isLeashed() || cow.isBaby() || cow.isInLove() || (!cow.onGround()) || cow.isVehicle() || cow.isSprinting()) + return false; + tracked_cows_.put(cow.getId(), cow.getCommandSenderWorld().getGameTime()); + tracked_cow_ = cow.getUUID(); + state_ = MilkingState.PICKED; + state_timeout_ = 200; + tracked_cow_original_position_ = cow.blockPosition(); + log("Idle: Picked cow " + tracked_cow_); + return false; + } + case PICKED -> { + SingleMoveGoal.startFor( + cow, target_pos, 2, 1.0, + (goal, world, pos) -> (pos.distSqr(goal.getCreature().blockPosition()) > 100), + (goal, world, pos) -> { + log("move: position reached"); + goal.getCreature().moveTo(goal.getTargetPosition().x(), goal.getTargetPosition().y(), goal.getTargetPosition().z(), facing.toYRot(), 0); + }, + (goal, world, pos) -> log("move: aborted") + ); + state_ = MilkingState.COMING; + state_timeout_ = 400; // 15s should be enough + log("Picked: coming to " + target_pos); + return false; + } + case COMING -> { + if (target_pos.distanceToSqr(cow.position()) <= 1) { + log("Coming: position reached"); + state_ = MilkingState.POSITIONING; + state_timeout_ = 100; // 5s + } else if ((!SingleMoveGoal.isActiveFor(cow))) { + release_cow(cow); + log("Coming: aborted"); + } else { + state_timeout_ -= 100; + } + return false; + } + case POSITIONING -> { + log("Positioning: start milking"); + SingleMoveGoal.abortFor(cow); + cow.setNoAi(true); + cow.moveTo(target_pos.x(), target_pos.y(), target_pos.z(), facing.toYRot(), 0); + level.playSound(null, worldPosition, SoundEvents.COW_MILK, SoundSource.BLOCKS, 0.5f, 1f); + state_timeout_ = 600; + state_ = MilkingState.MILKING; + state_timer_ = 30; + return false; + } + case MILKING -> { + tank_.fill(milk_fluid_.copy(), IFluidHandler.FluidAction.EXECUTE); + state_timeout_ = 600; + state_ = MilkingState.LEAVING; + state_timer_ = 20; + cow.setNoAi(false); + cow.getNavigation().stop(); + log("Milking: done, leave"); + return true; + } + case LEAVING -> { + BlockPos p = (tracked_cow_original_position_ != null) ? (tracked_cow_original_position_) : getBlockPos().relative(facing, 2).relative(facing.getCounterClockWise()); + SingleMoveGoal.startFor(cow, p, 2, 1.0, (goal, world, pos) -> (pos.distSqr(goal.getCreature().blockPosition()) > 100)); + state_timeout_ = 600; + state_timer_ = 500; + tick_timer_ = TICK_INTERVAL; + state_ = MilkingState.WAITING; + tracked_cows_.put(cow.getId(), cow.getCommandSenderWorld().getGameTime()); + log("Leaving: process done"); + return true; + } + case WAITING -> { + // wait for the timeout to kick in until starting with the next. + tick_timer_ = TICK_INTERVAL; + if (state_timer_ < 40) { + tracked_cow_ = null; + release_cow(null); + } + log("Waiting time elapsed"); + return true; + } + default -> release_cow(cow); + } + return (tracked_cow_ != null); } - // Adjacent inventory update, only done just after milking to prevent waste of server cpu. - if((!dirty) && (fluid_level() > 0)) { - log("Try item transfer"); - if(fill_adjacent_tank() || ((fluid_level() >= BUCKET_SIZE) && fill_adjacent_inventory_item_containers(block_state.getValue(MilkerBlock.HORIZONTAL_FACING)))) dirty = true; + + @Override + public void tick() { + if ((level.isClientSide) || ((--tick_timer_ > 0))) return; + tick_timer_ = TICK_INTERVAL; + boolean dirty = false; + final BlockState block_state = level.getBlockState(worldPosition); + if (!(block_state.getBlock() instanceof MilkerBlock)) return; + if (!level.hasNeighborSignal(worldPosition) || (state_ != MilkingState.IDLE)) { + if ((energy_consumption_ > 0) && (!battery_.draw(energy_consumption_))) return; + // Track and milk cows + if (milking_process()) dirty = true; + // Fluid transfer + if (has_milk_fluid() && (!tank_.isEmpty())) { + log("Fluid transfer"); + for (Direction facing : FLUID_TRANSFER_DIRECTRIONS) { + final IFluidHandler fh = Fluidics.handler(level, worldPosition.relative(facing), facing.getOpposite()); + if (fh == null) continue; + final FluidStack fs = tank_.drain(BUCKET_SIZE, IFluidHandler.FluidAction.SIMULATE); + int nfilled = fh.fill(fs, IFluidHandler.FluidAction.EXECUTE); + if (nfilled <= 0) continue; + tank_.drain(nfilled, IFluidHandler.FluidAction.EXECUTE); + dirty = true; + break; + } + } + // Adjacent inventory update, only done just after milking to prevent waste of server cpu. + if ((!dirty) && (fluid_level() > 0)) { + log("Try item transfer"); + if (fill_adjacent_tank() || ((fluid_level() >= BUCKET_SIZE) && fill_adjacent_inventory_item_containers(block_state.getValue(MilkerBlock.HORIZONTAL_FACING)))) + dirty = true; + } + } + // State update + BlockState new_state = block_state.setValue(MilkerBlock.FILLED, fluid_level() >= FILLED_INDICATION_THRESHOLD).setValue(MilkerBlock.ACTIVE, state_ == MilkingState.MILKING); + if (block_state != new_state) level.setBlock(worldPosition, new_state, 1 | 2 | 16); + if (dirty) setChanged(); } - } - // State update - BlockState new_state = block_state.setValue(MilkerBlock.FILLED, fluid_level()>=FILLED_INDICATION_THRESHOLD).setValue(MilkerBlock.ACTIVE, state_==MilkingState.MILKING); - if(block_state != new_state) level.setBlock(worldPosition, new_state,1|2|16); - if(dirty) setChanged(); - } - } - public static class SingleMoveGoal extends MoveToBlockGoal - { - @FunctionalInterface public interface TargetPositionInValidCheck { boolean test(SingleMoveGoal goal, LevelReader world, BlockPos pos); } - @FunctionalInterface public interface StrollEvent { void apply(SingleMoveGoal goal, LevelReader world, Vec3 pos); } - private static void log(String s) {} // println("SingleMoveGoal: "+s); - - private static final HashMap tracked_entities_ = new HashMap<>(); - private static final int motion_timeout = 20*20; - private boolean aborted_; - private boolean in_position_; - private boolean was_aborted_; - private Vec3 target_pos_; - private TargetPositionInValidCheck abort_condition_; - private StrollEvent on_target_position_reached_; - private StrollEvent on_aborted_; - - public SingleMoveGoal(PathfinderMob creature, Vec3 pos, double speed, TargetPositionInValidCheck abort_condition, @Nullable StrollEvent on_position_reached, @Nullable StrollEvent on_aborted) - { - super(creature, speed, 32, 32); - abort_condition_ = abort_condition; - on_target_position_reached_ = on_position_reached; - on_aborted_ = on_aborted; - blockPos = new BlockPos(pos.x(), pos.y(), pos.z()); - tryTicks = 0; - nextStartTick = 0; - aborted_ = false; - was_aborted_ = false; - target_pos_ = pos; + private enum MilkingState {IDLE, PICKED, COMING, POSITIONING, MILKING, LEAVING, WAITING} } - public static void startFor(PathfinderMob entity, BlockPos target_pos, int priority, double speed, TargetPositionInValidCheck abort_condition) - { startFor(entity, new Vec3(target_pos.getX(),target_pos.getY(),target_pos.getZ()), priority, speed, abort_condition, null, null); } - - public static boolean startFor(PathfinderMob entity, Vec3 target_pos, int priority, double speed, TargetPositionInValidCheck abort_condition, @Nullable StrollEvent on_position_reached, @Nullable StrollEvent on_aborted) - { - synchronized(tracked_entities_) { - SingleMoveGoal goal = tracked_entities_.getOrDefault(entity.getId(), null); - if(goal != null) { - if(!goal.aborted()) return false; // that is still running. - entity.goalSelector.removeGoal(goal); + public static class SingleMoveGoal extends MoveToBlockGoal { + private static final HashMap tracked_entities_ = new HashMap<>(); + private static final int motion_timeout = 20 * 20; + private boolean aborted_; + private boolean in_position_; + private boolean was_aborted_; + private Vec3 target_pos_; + private TargetPositionInValidCheck abort_condition_; + private StrollEvent on_target_position_reached_; + private StrollEvent on_aborted_; + public SingleMoveGoal(PathfinderMob creature, Vec3 pos, double speed, TargetPositionInValidCheck abort_condition, @Nullable StrollEvent on_position_reached, @Nullable StrollEvent on_aborted) { + super(creature, speed, 32, 32); + abort_condition_ = abort_condition; + on_target_position_reached_ = on_position_reached; + on_aborted_ = on_aborted; + blockPos = new BlockPos(pos.x(), pos.y(), pos.z()); + tryTicks = 0; + nextStartTick = 0; + aborted_ = false; + was_aborted_ = false; + target_pos_ = pos; } - log("::start("+entity.getId()+")"); - goal = new SingleMoveGoal(entity, target_pos, speed, abort_condition, on_position_reached, on_aborted); - tracked_entities_.put(entity.getId(), goal); - entity.goalSelector.addGoal(priority, goal); - return true; - } - } - public static boolean isActiveFor(PathfinderMob entity) - { return (entity != null) && (entity.goalSelector.getRunningGoals().anyMatch( - g->((g.getGoal()) instanceof SingleMoveGoal) && (!((SingleMoveGoal)(g.getGoal())).aborted()) - )); } + private static void log(String s) { + } // println("SingleMoveGoal: "+s); - public static void abortFor(PathfinderMob entity) - { - log("::abort("+entity.getId()+")"); - if(entity.isAlive()) { - entity.goalSelector.getRunningGoals().filter(g->(g.getGoal()) instanceof SingleMoveGoal).forEach(g->((SingleMoveGoal)g.getGoal()).abort()); - } - final Level world = entity.getCommandSenderWorld(); - if(world != null) { - // @todo: check nicer way to filter a map. - List to_remove = tracked_entities_.keySet().stream().filter(i->(world.getEntity(i) == null)).toList(); - for(int id:to_remove)tracked_entities_.remove(id); - } - } - - public Vec3 getTargetPosition() - { return target_pos_; } - - public PathfinderMob getCreature() - { return mob; } - - public synchronized void abort() - { aborted_ = true; } - - public synchronized boolean aborted() - { return aborted_; } - - public synchronized void initialize(Vec3 target_pos, double speed, TargetPositionInValidCheck abort_condition, @Nullable StrollEvent on_position_reached, @Nullable StrollEvent on_aborted) - { - abort_condition_ = abort_condition; - on_target_position_reached_ = on_position_reached; - on_aborted_ = on_aborted; - blockPos = new BlockPos(target_pos.x(), target_pos.y(), target_pos.z()); - tryTicks = 0; - nextStartTick = 0; - aborted_ = false; - was_aborted_ = false; - target_pos_ = new Vec3(target_pos.x(), target_pos.y(), target_pos.z()); - // this.movementSpeed = speed; -> that is final, need to override tick and func_whatever - } - - @Override - public void stop() - { nextStartTick = 0; tryTicks = 0; } - - @Override - public double acceptedDistance() - { return 0.7; } - - @Override - public boolean shouldRecalculatePath() - { return (!aborted()) && (tryTicks & 0x7) == 0; } - - @Override - public boolean canUse() - { - if(aborted_) { - if((!was_aborted_) && (on_aborted_!=null)) on_aborted_.apply(this, mob.level, target_pos_); - was_aborted_ = true; - return false; - } else if(!isValidTarget(mob.level, blockPos)) { - synchronized(this) { aborted_ = true; } - return false; - } else if(--nextStartTick > 0) { - return false; - } else { - nextStartTick = 10; - return true; - } - } - - @Override - public void start() - { - tryTicks = 0; - if(!mob.getNavigation().moveTo(target_pos_.x(), target_pos_.y(), target_pos_.z(), this.speedModifier)) { - abort(); - log("startExecuting() -> abort, no path"); - } else { - log("startExecuting() -> started"); - } - } - - public boolean canContinueToUse() - { - if(aborted()) { - log("shouldContinueExecuting() -> already aborted"); - return false; - } else if(mob.getNavigation().isDone()) { - if((!mob.getNavigation().moveTo(mob.getNavigation().createPath(target_pos_.x(), target_pos_.y(), target_pos_.z(), 0), speedModifier))) { - log("shouldContinueExecuting() -> abort, no path"); - abort(); - return false; - } else { - return true; + public static void startFor(PathfinderMob entity, BlockPos target_pos, int priority, double speed, TargetPositionInValidCheck abort_condition) { + startFor(entity, new Vec3(target_pos.getX(), target_pos.getY(), target_pos.getZ()), priority, speed, abort_condition, null, null); } - } else if(tryTicks > motion_timeout) { - log("shouldContinueExecuting() -> abort, timeout"); - abort(); - return false; - } else if(!isValidTarget(mob.level, blockPos)) { - log("shouldContinueExecuting() -> abort, !shouldMoveTo()"); - abort(); - return false; - } else { - log("shouldContinueExecuting() -> yes"); - return true; - } - } - @Override - protected boolean isValidTarget(LevelReader world, BlockPos pos) - { - if(abort_condition_.test(this, world, pos)) { - log("shouldMoveTo() -> abort_condition"); - return false; - } else { - return true; - } - } + public static boolean startFor(PathfinderMob entity, Vec3 target_pos, int priority, double speed, TargetPositionInValidCheck abort_condition, @Nullable StrollEvent on_position_reached, @Nullable StrollEvent on_aborted) { + synchronized (tracked_entities_) { + SingleMoveGoal goal = tracked_entities_.getOrDefault(entity.getId(), null); + if (goal != null) { + if (!goal.aborted()) return false; // that is still running. + entity.goalSelector.removeGoal(goal); + } + log("::start(" + entity.getId() + ")"); + goal = new SingleMoveGoal(entity, target_pos, speed, abort_condition, on_position_reached, on_aborted); + tracked_entities_.put(entity.getId(), goal); + entity.goalSelector.addGoal(priority, goal); + return true; + } + } - @Override - public void tick() - { - final BlockPos testpos = new BlockPos(target_pos_.x(), mob.position().y(), target_pos_.z()); - if(!testpos.closerToCenterThan(mob.position(), acceptedDistance())) { - if((++tryTicks > motion_timeout)) { - log("tick() -> abort, timeoutCounter"); - abort(); - return; + public static boolean isActiveFor(PathfinderMob entity) { + return (entity != null) && (entity.goalSelector.getRunningGoals().anyMatch( + g -> ((g.getGoal()) instanceof SingleMoveGoal) && (!((SingleMoveGoal) (g.getGoal())).aborted()) + )); } - if(shouldRecalculatePath() && (!mob.getNavigation().moveTo(target_pos_.x(), target_pos_.y(), target_pos_.z(), speedModifier))) { - log("tick() -> abort, !tryMoveToXYZ()"); - abort(); + + public static void abortFor(PathfinderMob entity) { + log("::abort(" + entity.getId() + ")"); + if (entity.isAlive()) { + entity.goalSelector.getRunningGoals().filter(g -> (g.getGoal()) instanceof SingleMoveGoal).forEach(g -> ((SingleMoveGoal) g.getGoal()).abort()); + } + final Level world = entity.getCommandSenderWorld(); + if (world != null) { + // @todo: check nicer way to filter a map. + List to_remove = tracked_entities_.keySet().stream().filter(i -> (world.getEntity(i) == null)).toList(); + for (int id : to_remove) tracked_entities_.remove(id); + } + } + + public Vec3 getTargetPosition() { + return target_pos_; + } + + public PathfinderMob getCreature() { + return mob; + } + + public synchronized void abort() { + aborted_ = true; + } + + public synchronized boolean aborted() { + return aborted_; + } + + public synchronized void initialize(Vec3 target_pos, double speed, TargetPositionInValidCheck abort_condition, @Nullable StrollEvent on_position_reached, @Nullable StrollEvent on_aborted) { + abort_condition_ = abort_condition; + on_target_position_reached_ = on_position_reached; + on_aborted_ = on_aborted; + blockPos = new BlockPos(target_pos.x(), target_pos.y(), target_pos.z()); + tryTicks = 0; + nextStartTick = 0; + aborted_ = false; + was_aborted_ = false; + target_pos_ = new Vec3(target_pos.x(), target_pos.y(), target_pos.z()); + // this.movementSpeed = speed; -> that is final, need to override tick and func_whatever + } + + @Override + public void stop() { + nextStartTick = 0; + tryTicks = 0; + } + + @Override + public double acceptedDistance() { + return 0.7; + } + + @Override + public boolean shouldRecalculatePath() { + return (!aborted()) && (tryTicks & 0x7) == 0; + } + + @Override + public boolean canUse() { + if (aborted_) { + if ((!was_aborted_) && (on_aborted_ != null)) on_aborted_.apply(this, mob.level(), target_pos_); + was_aborted_ = true; + return false; + } else if (!isValidTarget(mob.level(), blockPos)) { + synchronized (this) { + aborted_ = true; + } + return false; + } else if (--nextStartTick > 0) { + return false; + } else { + nextStartTick = 10; + return true; + } + } + + @Override + public void start() { + tryTicks = 0; + if (!mob.getNavigation().moveTo(target_pos_.x(), target_pos_.y(), target_pos_.z(), this.speedModifier)) { + abort(); + log("startExecuting() -> abort, no path"); + } else { + log("startExecuting() -> started"); + } + } + + public boolean canContinueToUse() { + if (aborted()) { + log("shouldContinueExecuting() -> already aborted"); + return false; + } else if (mob.getNavigation().isDone()) { + if ((!mob.getNavigation().moveTo(mob.getNavigation().createPath(target_pos_.x(), target_pos_.y(), target_pos_.z(), 0), speedModifier))) { + log("shouldContinueExecuting() -> abort, no path"); + abort(); + return false; + } else { + return true; + } + } else if (tryTicks > motion_timeout) { + log("shouldContinueExecuting() -> abort, timeout"); + abort(); + return false; + } else if (!isValidTarget(mob.level(), blockPos)) { + log("shouldContinueExecuting() -> abort, !shouldMoveTo()"); + abort(); + return false; + } else { + log("shouldContinueExecuting() -> yes"); + return true; + } + } + + @Override + protected boolean isValidTarget(LevelReader world, BlockPos pos) { + if (abort_condition_.test(this, world, pos)) { + log("shouldMoveTo() -> abort_condition"); + return false; + } else { + return true; + } + } + + @Override + public void tick() { + final BlockPos testpos = new BlockPos((int) target_pos_.x(), (int) mob.position().y(), (int) target_pos_.z()); + if (!testpos.closerToCenterThan(mob.position(), acceptedDistance())) { + if ((++tryTicks > motion_timeout)) { + log("tick() -> abort, timeoutCounter"); + abort(); + return; + } + if (shouldRecalculatePath() && (!mob.getNavigation().moveTo(target_pos_.x(), target_pos_.y(), target_pos_.z(), speedModifier))) { + log("tick() -> abort, !tryMoveToXYZ()"); + abort(); + } + } else { + log("tick() -> abort, in position)"); + in_position_ = true; + abort(); + if (on_target_position_reached_ != null) + on_target_position_reached_.apply(this, mob.level(), target_pos_); + } + } + + @FunctionalInterface + public interface TargetPositionInValidCheck { + boolean test(SingleMoveGoal goal, LevelReader world, BlockPos pos); + } + + @FunctionalInterface + public interface StrollEvent { + void apply(SingleMoveGoal goal, LevelReader world, Vec3 pos); } - } else { - log("tick() -> abort, in position)"); - in_position_ = true; - abort(); - if(on_target_position_reached_ != null) on_target_position_reached_.apply(this, mob.level, target_pos_); - } } - } } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdMineralSmelter.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdMineralSmelter.java index 373855c..7a969bb 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdMineralSmelter.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdMineralSmelter.java @@ -9,6 +9,9 @@ */ package dev.zontreck.engineerdecor.blocks; +import dev.zontreck.engineerdecor.ModConfig; +import dev.zontreck.engineerdecor.ModContent; +import dev.zontreck.libzontreck.edlibmc.*; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.particles.ParticleOptions; @@ -29,7 +32,7 @@ import net.minecraft.world.item.Items; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.SignalGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; @@ -51,458 +54,459 @@ import net.minecraftforge.energy.IEnergyStorage; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.IFluidHandler; import net.minecraftforge.items.IItemHandler; -import dev.zontreck.engineerdecor.ModConfig; -import dev.zontreck.engineerdecor.ModContent; -import dev.zontreck.engineerdecor.libmc.StandardBlocks; -import dev.zontreck.engineerdecor.libmc.StandardEntityBlocks; -import dev.zontreck.engineerdecor.libmc.Auxiliaries; -import dev.zontreck.engineerdecor.libmc.Fluidics; -import dev.zontreck.engineerdecor.libmc.Inventories; -import dev.zontreck.engineerdecor.libmc.RfEnergy; import javax.annotation.Nullable; -import java.util.*; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; -public class EdMineralSmelter -{ - public static void on_config(int consumption, int heatup_per_second) - { MineralSmelterTileEntity.on_config(consumption, heatup_per_second); } - - //-------------------------------------------------------------------------------------------------------------------- - // Block - //-------------------------------------------------------------------------------------------------------------------- - - public static class MineralSmelterBlock extends StandardBlocks.Horizontal implements StandardEntityBlocks.IStandardEntityBlock - { - public static final int PHASE_MAX = 3; - public static final IntegerProperty PHASE = IntegerProperty.create("phase", 0, PHASE_MAX); - - public MineralSmelterBlock(long config, BlockBehaviour.Properties builder, final AABB unrotatedAABB) - { super(config, builder, unrotatedAABB); } - - @Override - public boolean isBlockEntityTicking(Level world, BlockState state) - { return true; } - - @Override - public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) - { return Shapes.block(); } - - @Override - protected void createBlockStateDefinition(StateDefinition.Builder builder) - { super.createBlockStateDefinition(builder); builder.add(PHASE); } - - @Override - @Nullable - public BlockState getStateForPlacement(BlockPlaceContext context) - { return super.getStateForPlacement(context).setValue(PHASE, 0); } - - @Override - @SuppressWarnings("deprecation") - public boolean hasAnalogOutputSignal(BlockState state) - { return true; } - - @Override - @SuppressWarnings("deprecation") - public int getAnalogOutputSignal(BlockState state, Level world, BlockPos pos) - { return Mth.clamp((state.getValue(PHASE)*5), 0, 15); } - - @Override - public boolean shouldCheckWeakPower(BlockState state, LevelReader world, BlockPos pos, Direction side) - { return false; } - - @Override - public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) - {} - - @Override - public boolean hasDynamicDropList() - { return true; } - - @Override - public List dropList(BlockState state, Level world, BlockEntity te, boolean explosion) - { - final List stacks = new ArrayList<>(); - if(world.isClientSide) return stacks; - if(!(te instanceof MineralSmelterTileEntity)) return stacks; - ((MineralSmelterTileEntity)te).reset_process(); - stacks.add(new ItemStack(this, 1)); - return stacks; +public class EdMineralSmelter { + public static void on_config(int consumption, int heatup_per_second) { + MineralSmelterTileEntity.on_config(consumption, heatup_per_second); } - @Override - @SuppressWarnings("deprecation") - public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult) - { - if(player.isShiftKeyDown()) return InteractionResult.PASS; - if(world.isClientSide()) return InteractionResult.SUCCESS; - MineralSmelterTileEntity te = getTe(world, pos); - if(te==null) return InteractionResult.FAIL; - final ItemStack stack = player.getItemInHand(hand); - boolean dirty = false; - if(te.accepts_lava_container(stack)) { - if(stack.sameItemStackIgnoreDurability(MineralSmelterTileEntity.BUCKET_STACK)) { // check how this works with item capabilities or so - if(te.bucket_extraction_possible()) { - if(stack.getCount() > 1) { - int target_stack_index = -1; - for(int i=0; i { + public static final int PHASE_MAX = 3; + public static final IntegerProperty PHASE = IntegerProperty.create("phase", 0, PHASE_MAX); + + public MineralSmelterBlock(long config, BlockBehaviour.Properties builder, final AABB unrotatedAABB) { + super(config, builder, unrotatedAABB); + } + + @Override + public boolean isBlockEntityTicking(Level world, BlockState state) { + return true; + } + + @Override + public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) { + return Shapes.block(); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + super.createBlockStateDefinition(builder); + builder.add(PHASE); + } + + @Override + @Nullable + public BlockState getStateForPlacement(BlockPlaceContext context) { + return super.getStateForPlacement(context).setValue(PHASE, 0); + } + + @Override + @SuppressWarnings("deprecation") + public boolean hasAnalogOutputSignal(BlockState state) { + return true; + } + + @Override + @SuppressWarnings("deprecation") + public int getAnalogOutputSignal(BlockState state, Level world, BlockPos pos) { + return Mth.clamp((state.getValue(PHASE) * 5), 0, 15); + } + + @Override + public boolean shouldCheckWeakPower(BlockState state, SignalGetter level, BlockPos pos, Direction side) { + return false; + } + + @Override + public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) { + } + + @Override + public boolean hasDynamicDropList() { + return true; + } + + @Override + public List dropList(BlockState state, Level world, BlockEntity te, boolean explosion) { + final List stacks = new ArrayList<>(); + if (world.isClientSide) return stacks; + if (!(te instanceof MineralSmelterTileEntity)) return stacks; + ((MineralSmelterTileEntity) te).reset_process(); + stacks.add(new ItemStack(this, 1)); + return stacks; + } + + @Override + @SuppressWarnings("deprecation") + public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult) { + if (player.isShiftKeyDown()) return InteractionResult.PASS; + if (world.isClientSide()) return InteractionResult.SUCCESS; + MineralSmelterTileEntity te = getTe(world, pos); + if (te == null) return InteractionResult.FAIL; + final ItemStack stack = player.getItemInHand(hand); + boolean dirty = false; + if (te.accepts_lava_container(stack)) { + if (stack.is(MineralSmelterTileEntity.BUCKET_STACK.getItem())) { // check how this works with item capabilities or so + if (te.bucket_extraction_possible()) { + if (stack.getCount() > 1) { + int target_stack_index = -1; + for (int i = 0; i < player.getInventory().getContainerSize(); ++i) { + if (player.getInventory().getItem(i).isEmpty()) { + target_stack_index = i; + break; + } + } + if (target_stack_index >= 0) { + te.reset_process(); + stack.shrink(1); + player.setItemInHand(hand, stack); + player.getInventory().setItem(target_stack_index, MineralSmelterTileEntity.LAVA_BUCKET_STACK.copy()); + world.playSound(null, pos, SoundEvents.BUCKET_FILL_LAVA, SoundSource.BLOCKS, 1f, 1f); + dirty = true; + } + } else { + te.reset_process(); + player.setItemInHand(hand, MineralSmelterTileEntity.LAVA_BUCKET_STACK.copy()); + world.playSound(null, pos, SoundEvents.BUCKET_FILL_LAVA, SoundSource.BLOCKS, 1f, 1f); + dirty = true; + } + } } - } - if(target_stack_index >= 0) { - te.reset_process(); + } else if (stack.isEmpty()) { + final ItemStack istack = te.extract(true); + if (te.phase() > MineralSmelterTileEntity.PHASE_WARMUP) player.setSecondsOnFire(1); + if (!istack.isEmpty()) { + player.setItemInHand(hand, te.extract(false)); + dirty = true; + } + } else if (te.insert(stack, false)) { stack.shrink(1); - player.setItemInHand(hand, stack); - player.getInventory().setItem(target_stack_index, MineralSmelterTileEntity.LAVA_BUCKET_STACK.copy()); - world.playSound(null, pos, SoundEvents.BUCKET_FILL_LAVA, SoundSource.BLOCKS, 1f, 1f); dirty = true; - } - } else { - te.reset_process(); - player.setItemInHand(hand, MineralSmelterTileEntity.LAVA_BUCKET_STACK.copy()); - world.playSound(null, pos, SoundEvents.BUCKET_FILL_LAVA, SoundSource.BLOCKS, 1f, 1f); - dirty = true; } - } + if (dirty) player.getInventory().setChanged(); + return InteractionResult.CONSUME; } - } else if(stack.isEmpty()) { - final ItemStack istack = te.extract(true); - if(te.phase() > MineralSmelterTileEntity.PHASE_WARMUP) player.setSecondsOnFire(1); - if(!istack.isEmpty()) { - player.setItemInHand(hand, te.extract(false)); - dirty = true; - } - } else if(te.insert(stack,false)) { - stack.shrink(1); - dirty = true; - } - if(dirty) player.getInventory().setChanged(); - return InteractionResult.CONSUME; - } - @Override - @OnlyIn(Dist.CLIENT) - public void animateTick(BlockState state, Level world, BlockPos pos, RandomSource rnd) - { - if(state.getBlock()!=this) return; - ParticleOptions particle = ParticleTypes.SMOKE; - switch(state.getValue(PHASE)) { - case MineralSmelterTileEntity.PHASE_WARMUP: - return; - case MineralSmelterTileEntity.PHASE_HOT: - if(rnd.nextInt(10) > 4) return; - break; - case MineralSmelterTileEntity.PHASE_MAGMABLOCK: - if(rnd.nextInt(10) > 7) return; - particle = ParticleTypes.LARGE_SMOKE; - break; - case MineralSmelterTileEntity.PHASE_LAVA: - if(rnd.nextInt(10) > 2) return; - particle = ParticleTypes.LAVA; - break; - default: - return; - } - final double x=0.5+pos.getX(), y=0.5+pos.getY(), z=0.5+pos.getZ(); - final double xr=rnd.nextDouble()*0.4-0.2, yr=rnd.nextDouble()*0.5, zr=rnd.nextDouble()*0.4-0.2; - world.addParticle(particle, x+xr, y+yr, z+zr, 0.0, 0.0, 0.0); - } - - @Nullable - private MineralSmelterTileEntity getTe(Level world, BlockPos pos) - { final BlockEntity te=world.getBlockEntity(pos); return (!(te instanceof MineralSmelterTileEntity)) ? (null) : ((MineralSmelterTileEntity)te); } - } - - //-------------------------------------------------------------------------------------------------------------------- - // Tile entity - //-------------------------------------------------------------------------------------------------------------------- - - public static class MineralSmelterTileEntity extends StandardEntityBlocks.StandardBlockEntity - { - public static final int NUM_OF_SLOTS = 2; - public static final int TICK_INTERVAL = 20; - public static final int MAX_FLUID_LEVEL = 2000; - public static final int MAX_BUCKET_EXTRACT_FLUID_LEVEL = 900; - public static final int MAX_ENERGY_BUFFER = 32000; - public static final int MAX_ENERGY_TRANSFER = 8192; - public static final int DEFAULT_ENERGY_CONSUMPTION = 92; - public static final int DEFAULT_HEATUP_RATE = 2; // -> 50s for one smelting process - public static final int PHASE_WARMUP = 0; - public static final int PHASE_HOT = 1; - public static final int PHASE_MAGMABLOCK = 2; - public static final int PHASE_LAVA = 3; - private static final ItemStack MAGMA_STACK = new ItemStack(Blocks.MAGMA_BLOCK); - private static final ItemStack BUCKET_STACK = new ItemStack(Items.BUCKET); - private static final ItemStack LAVA_BUCKET_STACK = new ItemStack(Items.LAVA_BUCKET); - private static final FluidStack LAVA_BUCKET_FLUID_STACK = new FluidStack(Fluids.LAVA, 1000); - private static final Set accepted_minerals = new HashSet<>(); - private static final Set accepted_lava_contrainers = new HashSet<>(); - private static int energy_consumption = DEFAULT_ENERGY_CONSUMPTION; - private static int heatup_rate = DEFAULT_HEATUP_RATE; - private static int cooldown_rate = 1; - private int tick_timer_; - private int progress_; - private boolean force_block_update_; - - private final RfEnergy.Battery battery_ = new RfEnergy.Battery(MAX_ENERGY_BUFFER, MAX_ENERGY_TRANSFER, 0); - private final LazyOptional energy_handler_ = battery_.createEnergyHandler(); - private final Fluidics.Tank tank_ = new Fluidics.Tank(MAX_FLUID_LEVEL, 0, 100); - private final LazyOptional fluid_handler_ = tank_.createOutputFluidHandler(); - private final Inventories.StorageInventory main_inventory_; - private final LazyOptional item_handler_; - - static { - accepted_lava_contrainers.add(Items.BUCKET); - } - - public static void on_config(int consumption, int heatup_per_second) - { - energy_consumption = Mth.clamp(consumption, 8, 4096); - heatup_rate = Mth.clamp(heatup_per_second, 1, 5); - cooldown_rate = Mth.clamp(heatup_per_second/2, 1, 5); - ModConfig.log("Config mineal smelter: energy consumption:" + energy_consumption + "rf/t, heat-up rate: " + heatup_rate + "%/s."); - } - - public MineralSmelterTileEntity(BlockPos pos, BlockState state) - { - super(ModContent.getBlockEntityTypeOfBlock(state.getBlock()), pos, state); - main_inventory_ = (new Inventories.StorageInventory(this, NUM_OF_SLOTS, 1)).setStackLimit(1); - item_handler_ = Inventories.MappedItemHandler.createGenericHandler( - main_inventory_, - (index,stack)->((index==1) && (phase()!=PHASE_LAVA)), - (index,stack)->((index==0) && (progress_==0) && accepts_input(stack)), - (index,stack)->{}, - (index,stack)->{ if(index!=0) reset_process(); } - ); - } - - public int progress() - { return progress_; } - - public int phase() - { - if(progress_ >= 100) return PHASE_LAVA; - if(progress_ >= 90) return PHASE_MAGMABLOCK; - if(progress_ >= 5) return PHASE_HOT; - return PHASE_WARMUP; - } - - public boolean bucket_extraction_possible() - { return tank_.getFluidAmount() >= MAX_BUCKET_EXTRACT_FLUID_LEVEL; } - - public int comparator_signal() - { return phase() * 5; } - - private boolean accepts_lava_container(ItemStack stack) - { return accepted_lava_contrainers.contains(stack.getItem()); } - - private boolean accepts_input(ItemStack stack) - { - if(!main_inventory_.isEmpty()) { - return false; - } else if(bucket_extraction_possible()) { - return accepts_lava_container(stack); - } else { - return (accepted_minerals.contains(stack.getItem())) || (Auxiliaries.isInItemTag(stack.getItem(), new ResourceLocation(Auxiliaries.modid(), "accepted_mineral_smelter_input"))); - } - } - - public boolean insert(final ItemStack stack, boolean simulate) - { - if(stack.isEmpty() || (!accepts_input(stack))) return false; - if(!simulate) { - final ItemStack st = stack.copy(); - st.setCount(1); - main_inventory_.setItem(0, st); - if(!accepts_lava_container(stack)) progress_ = 0; - force_block_update_ = true; - } - return true; - } - - public ItemStack extract(boolean simulate) - { - final ItemStack stack = main_inventory_.getItem(1).copy(); - if(stack.isEmpty()) return ItemStack.EMPTY; - if(!simulate) reset_process(); - return stack; - } - - protected void reset_process() - { - main_inventory_.setItem(0, ItemStack.EMPTY); - main_inventory_.setItem(1, ItemStack.EMPTY); - tank_.clear(); - force_block_update_ = true; - tick_timer_ = 0; - progress_ = 0; - } - - public void readnbt(CompoundTag nbt) - { - main_inventory_.load(nbt); - battery_.load(nbt); - tank_.load(nbt); - progress_ = nbt.getInt("progress"); - } - - protected void writenbt(CompoundTag nbt) - { - main_inventory_.save(nbt); - battery_.save(nbt); - tank_.save(nbt); - nbt.putInt("progress", Mth.clamp(progress_,0 , 100)); - } - - // BlockEntity ------------------------------------------------------------------------------ - - @Override - public void load(CompoundTag nbt) - { super.load(nbt); readnbt(nbt); } - - @Override - protected void saveAdditional(CompoundTag nbt) - { super.saveAdditional(nbt); writenbt(nbt); } - - @Override - public void setRemoved() - { - super.setRemoved(); - energy_handler_.invalidate(); - fluid_handler_.invalidate(); - item_handler_.invalidate(); - } - - // Capability export ---------------------------------------------------------------------------- - - @Override - public LazyOptional getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable Direction facing) - { - if(capability == ForgeCapabilities.ITEM_HANDLER) return item_handler_.cast(); - if(capability == ForgeCapabilities.FLUID_HANDLER) return fluid_handler_.cast(); - if(capability == ForgeCapabilities.ENERGY) return energy_handler_.cast(); - return super.getCapability(capability, facing); - } - - // ITickable ------------------------------------------------------------------------------------ - - @Override - public void tick() - { - if(--tick_timer_ > 0) return; - tick_timer_ = TICK_INTERVAL; - BlockState state = level.getBlockState(worldPosition); - if(!(state.getBlock() instanceof MineralSmelterBlock)) return; - boolean dirty = false; - final int last_phase = phase(); - final ItemStack istack = main_inventory_.getItem(0); - if(istack.isEmpty() && (tank_.getFluidAmount()= 100) { - progress_ = 100; - if(!battery_.draw(energy_consumption*TICK_INTERVAL/20)) battery_.clear(); - } else if((phase()>=PHASE_LAVA) || (!istack.isEmpty())) { - if(!battery_.draw(energy_consumption*TICK_INTERVAL)) battery_.clear(); - progress_ = Mth.clamp(progress_+heatup_rate, 0, 100); - } - final int new_phase = phase(); - if(accepts_lava_container(istack)) { - // That stays in the slot until its extracted or somone takes it out. - if(istack.sameItem(BUCKET_STACK)) { - if(!main_inventory_.getItem(1).sameItem(LAVA_BUCKET_STACK)) { - if(bucket_extraction_possible()) { - reset_process(); - main_inventory_.setItem(1, LAVA_BUCKET_STACK); - level.playSound(null, worldPosition, SoundEvents.BUCKET_FILL_LAVA, SoundSource.BLOCKS, 0.2f, 1.3f); - } else { - main_inventory_.setItem(1, istack.copy()); + @Override + @OnlyIn(Dist.CLIENT) + public void animateTick(BlockState state, Level world, BlockPos pos, RandomSource rnd) { + if (state.getBlock() != this) return; + ParticleOptions particle = ParticleTypes.SMOKE; + switch (state.getValue(PHASE)) { + case MineralSmelterTileEntity.PHASE_WARMUP: + return; + case MineralSmelterTileEntity.PHASE_HOT: + if (rnd.nextInt(10) > 4) return; + break; + case MineralSmelterTileEntity.PHASE_MAGMABLOCK: + if (rnd.nextInt(10) > 7) return; + particle = ParticleTypes.LARGE_SMOKE; + break; + case MineralSmelterTileEntity.PHASE_LAVA: + if (rnd.nextInt(10) > 2) return; + particle = ParticleTypes.LAVA; + break; + default: + return; } - dirty = true; - } - } else { - main_inventory_.setItem(1, istack.copy()); - // Out stack -> Somehow the filled container or container with fluid+fluid_level(). + final double x = 0.5 + pos.getX(), y = 0.5 + pos.getY(), z = 0.5 + pos.getZ(); + final double xr = rnd.nextDouble() * 0.4 - 0.2, yr = rnd.nextDouble() * 0.5, zr = rnd.nextDouble() * 0.4 - 0.2; + world.addParticle(particle, x + xr, y + yr, z + zr, 0.0, 0.0, 0.0); } - } else if(new_phase > last_phase) { - // Heat-up to next phase happened. - switch(new_phase) { - case PHASE_LAVA -> { - tank_.fill(new FluidStack(Fluids.LAVA, 1000), IFluidHandler.FluidAction.EXECUTE); - main_inventory_.setItem(1, ItemStack.EMPTY); + + @Nullable + private MineralSmelterTileEntity getTe(Level world, BlockPos pos) { + final BlockEntity te = world.getBlockEntity(pos); + return (!(te instanceof MineralSmelterTileEntity)) ? (null) : ((MineralSmelterTileEntity) te); + } + } + + //-------------------------------------------------------------------------------------------------------------------- + // Tile entity + //-------------------------------------------------------------------------------------------------------------------- + + public static class MineralSmelterTileEntity extends StandardEntityBlocks.StandardBlockEntity { + public static final int NUM_OF_SLOTS = 2; + public static final int TICK_INTERVAL = 20; + public static final int MAX_FLUID_LEVEL = 2000; + public static final int MAX_BUCKET_EXTRACT_FLUID_LEVEL = 900; + public static final int MAX_ENERGY_BUFFER = 32000; + public static final int MAX_ENERGY_TRANSFER = 8192; + public static final int DEFAULT_ENERGY_CONSUMPTION = 92; + public static final int DEFAULT_HEATUP_RATE = 2; // -> 50s for one smelting process + public static final int PHASE_WARMUP = 0; + public static final int PHASE_HOT = 1; + public static final int PHASE_MAGMABLOCK = 2; + public static final int PHASE_LAVA = 3; + private static final ItemStack MAGMA_STACK = new ItemStack(Blocks.MAGMA_BLOCK); + private static final ItemStack BUCKET_STACK = new ItemStack(Items.BUCKET); + private static final ItemStack LAVA_BUCKET_STACK = new ItemStack(Items.LAVA_BUCKET); + private static final FluidStack LAVA_BUCKET_FLUID_STACK = new FluidStack(Fluids.LAVA, 1000); + private static final Set accepted_minerals = new HashSet<>(); + private static final Set accepted_lava_contrainers = new HashSet<>(); + private static int energy_consumption = DEFAULT_ENERGY_CONSUMPTION; + private static int heatup_rate = DEFAULT_HEATUP_RATE; + private static int cooldown_rate = 1; + + static { + accepted_lava_contrainers.add(Items.BUCKET); + } + + private final RfEnergy.Battery battery_ = new RfEnergy.Battery(MAX_ENERGY_BUFFER, MAX_ENERGY_TRANSFER, 0); + private final LazyOptional energy_handler_ = battery_.createEnergyHandler(); + private final Fluidics.Tank tank_ = new Fluidics.Tank(MAX_FLUID_LEVEL, 0, 100); + private final LazyOptional fluid_handler_ = tank_.createOutputFluidHandler(); + private final Inventories.StorageInventory main_inventory_; + private final LazyOptional item_handler_; + private int tick_timer_; + private int progress_; + private boolean force_block_update_; + + public MineralSmelterTileEntity(BlockPos pos, BlockState state) { + super(ModContent.getBlockEntityTypeOfBlock(state.getBlock()), pos, state); + main_inventory_ = (new Inventories.StorageInventory(this, NUM_OF_SLOTS, 1)).setStackLimit(1); + item_handler_ = Inventories.MappedItemHandler.createGenericHandler( + main_inventory_, + (index, stack) -> ((index == 1) && (phase() != PHASE_LAVA)), + (index, stack) -> ((index == 0) && (progress_ == 0) && accepts_input(stack)), + (index, stack) -> { + }, + (index, stack) -> { + if (index != 0) reset_process(); + } + ); + } + + public static void on_config(int consumption, int heatup_per_second) { + energy_consumption = Mth.clamp(consumption, 8, 4096); + heatup_rate = Mth.clamp(heatup_per_second, 1, 5); + cooldown_rate = Mth.clamp(heatup_per_second / 2, 1, 5); + ModConfig.log("Config mineal smelter: energy consumption:" + energy_consumption + "rf/t, heat-up rate: " + heatup_rate + "%/s."); + } + + public int progress() { + return progress_; + } + + public int phase() { + if (progress_ >= 100) return PHASE_LAVA; + if (progress_ >= 90) return PHASE_MAGMABLOCK; + if (progress_ >= 5) return PHASE_HOT; + return PHASE_WARMUP; + } + + public boolean bucket_extraction_possible() { + return tank_.getFluidAmount() >= MAX_BUCKET_EXTRACT_FLUID_LEVEL; + } + + public int comparator_signal() { + return phase() * 5; + } + + private boolean accepts_lava_container(ItemStack stack) { + return accepted_lava_contrainers.contains(stack.getItem()); + } + + private boolean accepts_input(ItemStack stack) { + if (!main_inventory_.isEmpty()) { + return false; + } else if (bucket_extraction_possible()) { + return accepts_lava_container(stack); + } else { + return (accepted_minerals.contains(stack.getItem())) || (Auxiliaries.isInItemTag(stack.getItem(), new ResourceLocation(Auxiliaries.modid(), "accepted_mineral_smelter_input"))); + } + } + + public boolean insert(final ItemStack stack, boolean simulate) { + if (stack.isEmpty() || (!accepts_input(stack))) return false; + if (!simulate) { + final ItemStack st = stack.copy(); + st.setCount(1); + main_inventory_.setItem(0, st); + if (!accepts_lava_container(stack)) progress_ = 0; + force_block_update_ = true; + } + return true; + } + + public ItemStack extract(boolean simulate) { + final ItemStack stack = main_inventory_.getItem(1).copy(); + if (stack.isEmpty()) return ItemStack.EMPTY; + if (!simulate) reset_process(); + return stack; + } + + protected void reset_process() { main_inventory_.setItem(0, ItemStack.EMPTY); - level.playSound(null, worldPosition, SoundEvents.LAVA_AMBIENT, SoundSource.BLOCKS, 0.2f, 1.0f); - dirty = true; - } - case PHASE_MAGMABLOCK -> { - main_inventory_.setItem(1, MAGMA_STACK.copy()); - level.playSound(null, worldPosition, SoundEvents.FIRE_AMBIENT, SoundSource.BLOCKS, 0.2f, 0.8f); - dirty = true; - } - case PHASE_HOT -> { - level.playSound(null, worldPosition, SoundEvents.FIRE_AMBIENT, SoundSource.BLOCKS, 0.2f, 0.8f); - } + main_inventory_.setItem(1, ItemStack.EMPTY); + tank_.clear(); + force_block_update_ = true; + tick_timer_ = 0; + progress_ = 0; } - } else if(new_phase < last_phase) { - // Cool-down to prev phase happened. - switch(new_phase) { - case PHASE_MAGMABLOCK -> { - if(tank_.getFluidAmount() < MAX_BUCKET_EXTRACT_FLUID_LEVEL) { - reset_process(); - } else { - main_inventory_.setItem(0, MAGMA_STACK.copy()); - main_inventory_.setItem(1, MAGMA_STACK.copy()); - tank_.drain(1000); - } - level.playSound(null, worldPosition, SoundEvents.LAVA_EXTINGUISH, SoundSource.BLOCKS, 0.5f, 1.1f); - dirty = true; - } - case PHASE_HOT -> { - if(istack.sameItem(MAGMA_STACK)) { - main_inventory_.setItem(1, new ItemStack(Blocks.OBSIDIAN)); - } else { - main_inventory_.setItem(1, new ItemStack(Blocks.COBBLESTONE)); - } - level.playSound(null, worldPosition, SoundEvents.LAVA_EXTINGUISH, SoundSource.BLOCKS, 0.3f, 0.9f); - dirty = true; - } - case PHASE_WARMUP -> { - level.playSound(null, worldPosition, SoundEvents.LAVA_EXTINGUISH, SoundSource.BLOCKS, 0.3f, 0.7f); - } + + public void readnbt(CompoundTag nbt) { + main_inventory_.load(nbt); + battery_.load(nbt); + tank_.load(nbt); + progress_ = nbt.getInt("progress"); } - } else if(phase()>=PHASE_LAVA) { - if(tank_.getFluidAmount()<=0) { - reset_process(); - level.playSound(null, worldPosition, SoundEvents.LAVA_EXTINGUISH, SoundSource.BLOCKS, 0.3f, 0.7f); - } else { - // Phase unchanged, fluid transfer check. - FluidStack fs = tank_.getFluid().copy(); - if(fs.getAmount() > 100) fs.setAmount(100); - final int n = Fluidics.fill(level, getBlockPos().below(), Direction.UP, fs); - if(n > 0) { - tank_.drain(n); - if(tank_.isEmpty()) { - final ItemStack prev = main_inventory_.getItem(0); - reset_process(); - main_inventory_.setItem(0, prev); - level.playSound(null, worldPosition, SoundEvents.LAVA_EXTINGUISH, SoundSource.BLOCKS, 0.3f, 0.7f); - } - } + + protected void writenbt(CompoundTag nbt) { + main_inventory_.save(nbt); + battery_.save(nbt); + tank_.save(nbt); + nbt.putInt("progress", Mth.clamp(progress_, 0, 100)); + } + + // BlockEntity ------------------------------------------------------------------------------ + + @Override + public void load(CompoundTag nbt) { + super.load(nbt); + readnbt(nbt); + } + + @Override + protected void saveAdditional(CompoundTag nbt) { + super.saveAdditional(nbt); + writenbt(nbt); + } + + @Override + public void setRemoved() { + super.setRemoved(); + energy_handler_.invalidate(); + fluid_handler_.invalidate(); + item_handler_.invalidate(); + } + + // Capability export ---------------------------------------------------------------------------- + + @Override + public LazyOptional getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable Direction facing) { + if (capability == ForgeCapabilities.ITEM_HANDLER) return item_handler_.cast(); + if (capability == ForgeCapabilities.FLUID_HANDLER) return fluid_handler_.cast(); + if (capability == ForgeCapabilities.ENERGY) return energy_handler_.cast(); + return super.getCapability(capability, facing); + } + + // ITickable ------------------------------------------------------------------------------------ + + @Override + public void tick() { + if (--tick_timer_ > 0) return; + tick_timer_ = TICK_INTERVAL; + BlockState state = level.getBlockState(worldPosition); + if (!(state.getBlock() instanceof MineralSmelterBlock)) return; + boolean dirty = false; + final int last_phase = phase(); + final ItemStack istack = main_inventory_.getItem(0); + if (istack.isEmpty() && (tank_.getFluidAmount() < MAX_BUCKET_EXTRACT_FLUID_LEVEL) && (phase() != PHASE_LAVA)) { + progress_ = 0; + tank_.clear(); + main_inventory_.clearContent(); + } else if ((battery_.isEmpty()) || (level.hasNeighborSignal(worldPosition))) { + progress_ = Mth.clamp(progress_ - cooldown_rate, 0, 100); + } else if (progress_ >= 100) { + progress_ = 100; + if (!battery_.draw(energy_consumption * TICK_INTERVAL / 20)) battery_.clear(); + } else if ((phase() >= PHASE_LAVA) || (!istack.isEmpty())) { + if (!battery_.draw(energy_consumption * TICK_INTERVAL)) battery_.clear(); + progress_ = Mth.clamp(progress_ + heatup_rate, 0, 100); + } + final int new_phase = phase(); + if (accepts_lava_container(istack)) { + // That stays in the slot until its extracted or somone takes it out. + if (istack.is(BUCKET_STACK.getItem())) { + if (!main_inventory_.getItem(1).is(LAVA_BUCKET_STACK.getItem())) { + if (bucket_extraction_possible()) { + reset_process(); + main_inventory_.setItem(1, LAVA_BUCKET_STACK); + level.playSound(null, worldPosition, SoundEvents.BUCKET_FILL_LAVA, SoundSource.BLOCKS, 0.2f, 1.3f); + } else { + main_inventory_.setItem(1, istack.copy()); + } + dirty = true; + } + } else { + main_inventory_.setItem(1, istack.copy()); + // Out stack -> Somehow the filled container or container with fluid+fluid_level(). + } + } else if (new_phase > last_phase) { + // Heat-up to next phase happened. + switch (new_phase) { + case PHASE_LAVA -> { + tank_.fill(new FluidStack(Fluids.LAVA, 1000), IFluidHandler.FluidAction.EXECUTE); + main_inventory_.setItem(1, ItemStack.EMPTY); + main_inventory_.setItem(0, ItemStack.EMPTY); + level.playSound(null, worldPosition, SoundEvents.LAVA_AMBIENT, SoundSource.BLOCKS, 0.2f, 1.0f); + dirty = true; + } + case PHASE_MAGMABLOCK -> { + main_inventory_.setItem(1, MAGMA_STACK.copy()); + level.playSound(null, worldPosition, SoundEvents.FIRE_AMBIENT, SoundSource.BLOCKS, 0.2f, 0.8f); + dirty = true; + } + case PHASE_HOT -> { + level.playSound(null, worldPosition, SoundEvents.FIRE_AMBIENT, SoundSource.BLOCKS, 0.2f, 0.8f); + } + } + } else if (new_phase < last_phase) { + // Cool-down to prev phase happened. + switch (new_phase) { + case PHASE_MAGMABLOCK -> { + if (tank_.getFluidAmount() < MAX_BUCKET_EXTRACT_FLUID_LEVEL) { + reset_process(); + } else { + main_inventory_.setItem(0, MAGMA_STACK.copy()); + main_inventory_.setItem(1, MAGMA_STACK.copy()); + tank_.drain(1000); + } + level.playSound(null, worldPosition, SoundEvents.LAVA_EXTINGUISH, SoundSource.BLOCKS, 0.5f, 1.1f); + dirty = true; + } + case PHASE_HOT -> { + if (istack.is(MAGMA_STACK.getItem())) { + main_inventory_.setItem(1, new ItemStack(Blocks.OBSIDIAN)); + } else { + main_inventory_.setItem(1, new ItemStack(Blocks.COBBLESTONE)); + } + level.playSound(null, worldPosition, SoundEvents.LAVA_EXTINGUISH, SoundSource.BLOCKS, 0.3f, 0.9f); + dirty = true; + } + case PHASE_WARMUP -> { + level.playSound(null, worldPosition, SoundEvents.LAVA_EXTINGUISH, SoundSource.BLOCKS, 0.3f, 0.7f); + } + } + } else if (phase() >= PHASE_LAVA) { + if (tank_.getFluidAmount() <= 0) { + reset_process(); + level.playSound(null, worldPosition, SoundEvents.LAVA_EXTINGUISH, SoundSource.BLOCKS, 0.3f, 0.7f); + } else { + // Phase unchanged, fluid transfer check. + FluidStack fs = tank_.getFluid().copy(); + if (fs.getAmount() > 100) fs.setAmount(100); + final int n = Fluidics.fill(level, getBlockPos().below(), Direction.UP, fs); + if (n > 0) { + tank_.drain(n); + if (tank_.isEmpty()) { + final ItemStack prev = main_inventory_.getItem(0); + reset_process(); + main_inventory_.setItem(0, prev); + level.playSound(null, worldPosition, SoundEvents.LAVA_EXTINGUISH, SoundSource.BLOCKS, 0.3f, 0.7f); + } + } + } + } + // Block state + if ((force_block_update_ || (state.getValue(MineralSmelterBlock.PHASE) != new_phase))) { + state = state.setValue(MineralSmelterBlock.PHASE, new_phase); + level.setBlock(worldPosition, state, 3 | 16); + level.updateNeighborsAt(getBlockPos(), state.getBlock()); + force_block_update_ = false; + } + if (dirty) setChanged(); } - } - // Block state - if((force_block_update_ || (state.getValue(MineralSmelterBlock.PHASE) != new_phase))) { - state = state.setValue(MineralSmelterBlock.PHASE, new_phase); - level.setBlock(worldPosition, state,3|16); - level.updateNeighborsAt(getBlockPos(), state.getBlock()); - force_block_update_ = false; - } - if(dirty) setChanged(); } - } } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdPipeValve.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdPipeValve.java index 81ac822..4ec8db8 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdPipeValve.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdPipeValve.java @@ -9,6 +9,11 @@ */ package dev.zontreck.engineerdecor.blocks; +import dev.zontreck.engineerdecor.ModConfig; +import dev.zontreck.engineerdecor.ModContent; +import dev.zontreck.libzontreck.edlibmc.RsSignals; +import dev.zontreck.libzontreck.edlibmc.StandardBlocks; +import dev.zontreck.libzontreck.edlibmc.StandardEntityBlocks; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.util.Mth; @@ -18,7 +23,7 @@ import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelAccessor; -import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.SignalGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.entity.BlockEntity; @@ -35,246 +40,299 @@ import net.minecraftforge.common.capabilities.ForgeCapabilities; import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.IFluidHandler; -import dev.zontreck.engineerdecor.ModConfig; -import dev.zontreck.engineerdecor.ModContent; -import dev.zontreck.engineerdecor.libmc.StandardBlocks; -import dev.zontreck.engineerdecor.libmc.StandardEntityBlocks; -import dev.zontreck.engineerdecor.libmc.RsSignals; import javax.annotation.Nonnull; import javax.annotation.Nullable; -public class EdPipeValve -{ - public static final int CFG_CHECK_VALVE = 0x1; - public static final int CFG_ANALOG_VALVE = 0x2; - public static final int CFG_REDSTONE_CONTROLLED_VALVE = 0x4; +public class EdPipeValve { + public static final int CFG_CHECK_VALVE = 0x1; + public static final int CFG_ANALOG_VALVE = 0x2; + public static final int CFG_REDSTONE_CONTROLLED_VALVE = 0x4; - public static void on_config(int container_size_decl, int redstone_slope) - { - PipeValveTileEntity.fluid_maxflow_mb = Mth.clamp(container_size_decl, 1, 10000); - PipeValveTileEntity.redstone_flow_slope_mb = Mth.clamp(redstone_slope, 1, 10000); - ModConfig.log("Config pipe valve: maxflow:" + PipeValveTileEntity.fluid_maxflow_mb + "mb, redstone amp:" + PipeValveTileEntity.redstone_flow_slope_mb + "mb/sig."); - } - - //-------------------------------------------------------------------------------------------------------------------- - // Block - //-------------------------------------------------------------------------------------------------------------------- - - public static class PipeValveBlock extends StandardBlocks.DirectedWaterLoggable implements StandardEntityBlocks.IStandardEntityBlock - { - public static final BooleanProperty RS_CN_N = BooleanProperty.create("rs_n"); - public static final BooleanProperty RS_CN_S = BooleanProperty.create("rs_s"); - public static final BooleanProperty RS_CN_E = BooleanProperty.create("rs_e"); - public static final BooleanProperty RS_CN_W = BooleanProperty.create("rs_w"); - public static final BooleanProperty RS_CN_U = BooleanProperty.create("rs_u"); - public static final BooleanProperty RS_CN_D = BooleanProperty.create("rs_d"); - public final int valve_config; - - public PipeValveBlock(long config, int valve_config, BlockBehaviour.Properties builder, final AABB[] unrotatedAABB) - { super(config, builder, unrotatedAABB); this.valve_config = valve_config; } - - @Override - @Nullable - public BlockEntity newBlockEntity(BlockPos pos, BlockState state) - { - final BlockEntityType tet = ModContent.getBlockEntityTypeOfBlock("straight_pipe_valve"); - return (tet==null) ? null : tet.create(pos, state); + public static void on_config(int container_size_decl, int redstone_slope) { + PipeValveTileEntity.fluid_maxflow_mb = Mth.clamp(container_size_decl, 1, 10000); + PipeValveTileEntity.redstone_flow_slope_mb = Mth.clamp(redstone_slope, 1, 10000); + ModConfig.log("Config pipe valve: maxflow:" + PipeValveTileEntity.fluid_maxflow_mb + "mb, redstone amp:" + PipeValveTileEntity.redstone_flow_slope_mb + "mb/sig."); } - @Override - public boolean isBlockEntityTicking(Level world, BlockState state) - { return false; } + //-------------------------------------------------------------------------------------------------------------------- + // Block + //-------------------------------------------------------------------------------------------------------------------- - @Override - public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) - { return Shapes.block(); } + public static class PipeValveBlock extends StandardBlocks.DirectedWaterLoggable implements StandardEntityBlocks.IStandardEntityBlock { + public static final BooleanProperty RS_CN_N = BooleanProperty.create("rs_n"); + public static final BooleanProperty RS_CN_S = BooleanProperty.create("rs_s"); + public static final BooleanProperty RS_CN_E = BooleanProperty.create("rs_e"); + public static final BooleanProperty RS_CN_W = BooleanProperty.create("rs_w"); + public static final BooleanProperty RS_CN_U = BooleanProperty.create("rs_u"); + public static final BooleanProperty RS_CN_D = BooleanProperty.create("rs_d"); + public final int valve_config; - @Override - protected void createBlockStateDefinition(StateDefinition.Builder builder) - { super.createBlockStateDefinition(builder); builder.add(RS_CN_N, RS_CN_S, RS_CN_E, RS_CN_W, RS_CN_U, RS_CN_D); } - - @Override - @Nullable - public BlockState getStateForPlacement(BlockPlaceContext context) - { - return super.getStateForPlacement(context).setValue(RS_CN_N, false).setValue(RS_CN_S, false).setValue(RS_CN_E, false) - .setValue(RS_CN_W, false).setValue(RS_CN_U, false).setValue(RS_CN_D, false); - } - - @Override - @SuppressWarnings("deprecation") - public BlockState updateShape(BlockState state, Direction facing, BlockState facingState, LevelAccessor world, BlockPos pos, BlockPos facingPos) - { return get_rsconnector_state(state, world, pos, null); } - - @Override - public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) - { world.updateNeighborsAt(pos,this); } - - @Override - public BlockState rotate(BlockState state, LevelAccessor world, BlockPos pos, Rotation direction) - { return get_rsconnector_state(state, world, pos, null); } // don't rotate at all - - @Override - public boolean hasSignalConnector(BlockState state, BlockGetter world, BlockPos pos, @Nullable Direction side) - { return (side!=null) && (side!=state.getValue(FACING)) && (side!=state.getValue(FACING).getOpposite()); } - - @Override - public boolean shouldCheckWeakPower(BlockState state, LevelReader world, BlockPos pos, Direction side) - { return false; } - - @Override - @SuppressWarnings("deprecation") // public boolean canConnectRedstone(BlockState state, BlockGetter world, BlockPos pos, @Nullable Direction side) { return true; } - public boolean isSignalSource(BlockState p_60571_) - { return true; } - - @Override - @SuppressWarnings("deprecation") - public int getSignal(BlockState blockState, BlockGetter blockAccess, BlockPos pos, Direction side) - { return 0; } - - @Override - @SuppressWarnings("deprecation") - public int getDirectSignal(BlockState blockState, BlockGetter blockAccess, BlockPos pos, Direction side) - { return 0; } - - private BlockState get_rsconnector_state(BlockState state, LevelAccessor world, BlockPos pos, @Nullable BlockPos fromPos) - { - if((valve_config & (CFG_REDSTONE_CONTROLLED_VALVE))==0) return state; - Direction.Axis bfa = state.getValue(FACING).getAxis(); - for(Direction f:Direction.values()) { - boolean cn = (f.getAxis() != bfa); - if(cn) { - BlockPos nbp = pos.relative(f); - if((fromPos != null) && (!nbp.equals(fromPos))) continue; // do not change connectors except form the frompos. - BlockState nbs = world.getBlockState(nbp); - if((nbs.getBlock() instanceof PipeValveBlock) || (!nbs.isSignalSource()) || (!RsSignals.hasSignalConnector(nbs, world, nbp, f.getOpposite()))) cn = false; + public PipeValveBlock(long config, int valve_config, BlockBehaviour.Properties builder, final AABB[] unrotatedAABB) { + super(config, builder, unrotatedAABB); + this.valve_config = valve_config; } - switch (f) { - case NORTH -> state = state.setValue(RS_CN_N, cn); - case SOUTH -> state = state.setValue(RS_CN_S, cn); - case EAST -> state = state.setValue(RS_CN_E, cn); - case WEST -> state = state.setValue(RS_CN_W, cn); - case UP -> state = state.setValue(RS_CN_U, cn); - case DOWN -> state = state.setValue(RS_CN_D, cn); + + @Override + @Nullable + public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { + final BlockEntityType tet = ModContent.getBlockEntityTypeOfBlock("straight_pipe_valve"); + return (tet == null) ? null : tet.create(pos, state); } - } - return state; - } - } - - //-------------------------------------------------------------------------------------------------------------------- - // Tile entity - //-------------------------------------------------------------------------------------------------------------------- - - public static class PipeValveTileEntity extends StandardEntityBlocks.StandardBlockEntity - { - protected static int fluid_maxflow_mb = 1000; - protected static int redstone_flow_slope_mb = 1000/15; - private final Direction block_facing_ = null; - private boolean filling_ = false; - private int valve_config_; - - public PipeValveTileEntity(BlockPos pos, BlockState state) - { super(ModContent.getBlockEntityTypeOfBlock("straight_pipe_valve"), pos, state); } - - private Direction block_facing() - { - BlockState st = getLevel().getBlockState(getBlockPos()); - return (st.getBlock() instanceof PipeValveBlock) ? st.getValue(PipeValveBlock.FACING) : Direction.NORTH; - } - - private long valve_config() - { - if(valve_config_ <= 0) { - final Block block = getLevel().getBlockState(getBlockPos()).getBlock(); - if(block instanceof PipeValveBlock) valve_config_ = ((PipeValveBlock)block).valve_config; - } - return valve_config_; - } - - // BlockEntity ----------------------------------------------------------------------------- - - @Override - public void setRemoved() - { - super.setRemoved(); - back_flow_handler_.invalidate(); - fluid_handler_.invalidate(); - } - - // ICapabilityProvider -------------------------------------------------------------------- - - private final LazyOptional back_flow_handler_ = LazyOptional.of(BackFlowHandler::new); - private final LazyOptional fluid_handler_ = LazyOptional.of(() -> new MainFlowHandler(this)); - - @Override - public LazyOptional getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable Direction facing) - { - if(capability == ForgeCapabilities.FLUID_HANDLER) { - Direction bf = block_facing(); - if(facing == bf) return back_flow_handler_.cast(); - if(facing == bf.getOpposite()) return fluid_handler_.cast(); - return LazyOptional.empty(); - } - return super.getCapability(capability, facing); - } - - // IFluidHandlers - - @Nullable - private IFluidHandler forward_fluid_handler() - { - final BlockEntity te = level.getBlockEntity(worldPosition.relative(block_facing())); - if(te == null) return null; - return te.getCapability(ForgeCapabilities.FLUID_HANDLER, block_facing().getOpposite()).orElse(null); - } - - // Forward flow handler -- - - private static class MainFlowHandler implements IFluidHandler - { - private final PipeValveTileEntity te; - public MainFlowHandler(PipeValveTileEntity te) { this.te = te; } - @Override public int getTanks() { return 1; } - @Override public FluidStack getFluidInTank(int tank) { return FluidStack.EMPTY; } - @Override public int getTankCapacity(int tank) { return fluid_maxflow_mb; } - @Override public boolean isFluidValid(int tank, @Nonnull FluidStack stack) { return true; } - @Override public FluidStack drain(FluidStack resource, FluidAction action) { return FluidStack.EMPTY; } - @Override public FluidStack drain(int maxDrain, FluidAction action) { return FluidStack.EMPTY; } - - @Override public int fill(FluidStack resource, FluidAction action) - { - if(te.filling_) return 0; - final IFluidHandler fh = te.forward_fluid_handler(); - if(fh==null) return 0; - FluidStack res = resource.copy(); - if((te.valve_config() & CFG_REDSTONE_CONTROLLED_VALVE) != 0) { - int rs = te.level.getBestNeighborSignal(te.worldPosition); - if(rs <= 0) return 0; - if(((te.valve_config() & CFG_ANALOG_VALVE) != 0) && (rs < 15)) res.setAmount(Mth.clamp(rs * redstone_flow_slope_mb, 1, res.getAmount())); + @Override + public boolean isBlockEntityTicking(Level world, BlockState state) { + return false; } - if(res.getAmount() > fluid_maxflow_mb) res.setAmount(fluid_maxflow_mb); - te.filling_ = true; - int n_filled = fh.fill(res, action); - te.filling_ = false; - return n_filled; - } + + @Override + public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) { + return Shapes.block(); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + super.createBlockStateDefinition(builder); + builder.add(RS_CN_N, RS_CN_S, RS_CN_E, RS_CN_W, RS_CN_U, RS_CN_D); + } + + @Override + @Nullable + public BlockState getStateForPlacement(BlockPlaceContext context) { + return super.getStateForPlacement(context).setValue(RS_CN_N, false).setValue(RS_CN_S, false).setValue(RS_CN_E, false) + .setValue(RS_CN_W, false).setValue(RS_CN_U, false).setValue(RS_CN_D, false); + } + + @Override + @SuppressWarnings("deprecation") + public BlockState updateShape(BlockState state, Direction facing, BlockState facingState, LevelAccessor world, BlockPos pos, BlockPos facingPos) { + return get_rsconnector_state(state, world, pos, null); + } + + @Override + public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) { + world.updateNeighborsAt(pos, this); + } + + @Override + public BlockState rotate(BlockState state, LevelAccessor world, BlockPos pos, Rotation direction) { + return get_rsconnector_state(state, world, pos, null); + } // don't rotate at all + + @Override + public boolean hasSignalConnector(BlockState state, BlockGetter world, BlockPos pos, @Nullable Direction side) { + return (side != null) && (side != state.getValue(FACING)) && (side != state.getValue(FACING).getOpposite()); + } + + @Override + public boolean shouldCheckWeakPower(BlockState state, SignalGetter level, BlockPos pos, Direction side) { + return false; + } + + @Override + @SuppressWarnings("deprecation") + // public boolean canConnectRedstone(BlockState state, BlockGetter world, BlockPos pos, @Nullable Direction side) { return true; } + public boolean isSignalSource(BlockState p_60571_) { + return true; + } + + @Override + @SuppressWarnings("deprecation") + public int getSignal(BlockState blockState, BlockGetter blockAccess, BlockPos pos, Direction side) { + return 0; + } + + @Override + @SuppressWarnings("deprecation") + public int getDirectSignal(BlockState blockState, BlockGetter blockAccess, BlockPos pos, Direction side) { + return 0; + } + + private BlockState get_rsconnector_state(BlockState state, LevelAccessor world, BlockPos pos, @Nullable BlockPos fromPos) { + if ((valve_config & (CFG_REDSTONE_CONTROLLED_VALVE)) == 0) return state; + Direction.Axis bfa = state.getValue(FACING).getAxis(); + for (Direction f : Direction.values()) { + boolean cn = (f.getAxis() != bfa); + if (cn) { + BlockPos nbp = pos.relative(f); + if ((fromPos != null) && (!nbp.equals(fromPos))) + continue; // do not change connectors except form the frompos. + BlockState nbs = world.getBlockState(nbp); + if ((nbs.getBlock() instanceof PipeValveBlock) || (!nbs.isSignalSource()) || (!RsSignals.hasSignalConnector(nbs, world, nbp, f.getOpposite()))) + cn = false; + } + switch (f) { + case NORTH -> state = state.setValue(RS_CN_N, cn); + case SOUTH -> state = state.setValue(RS_CN_S, cn); + case EAST -> state = state.setValue(RS_CN_E, cn); + case WEST -> state = state.setValue(RS_CN_W, cn); + case UP -> state = state.setValue(RS_CN_U, cn); + case DOWN -> state = state.setValue(RS_CN_D, cn); + } + } + return state; + } + } - // Back flow prevention handler -- + //-------------------------------------------------------------------------------------------------------------------- + // Tile entity + //-------------------------------------------------------------------------------------------------------------------- - private static class BackFlowHandler implements IFluidHandler - { - @Override public int getTanks() { return 1; } - @Override public FluidStack getFluidInTank(int tank) { return FluidStack.EMPTY; } - @Override public int getTankCapacity(int tank) { return 0; } - @Override public boolean isFluidValid(int tank, @Nonnull FluidStack stack) { return false; } - @Override public int fill(FluidStack resource, FluidAction action) { return 0; } - @Override public FluidStack drain(FluidStack resource, FluidAction action) { return FluidStack.EMPTY; } - @Override public FluidStack drain(int maxDrain, FluidAction action) { return FluidStack.EMPTY; } + public static class PipeValveTileEntity extends StandardEntityBlocks.StandardBlockEntity { + protected static int fluid_maxflow_mb = 1000; + protected static int redstone_flow_slope_mb = 1000 / 15; + private final Direction block_facing_ = null; + private final LazyOptional back_flow_handler_ = LazyOptional.of(BackFlowHandler::new); + private final LazyOptional fluid_handler_ = LazyOptional.of(() -> new MainFlowHandler(this)); + private boolean filling_ = false; + private int valve_config_; + + public PipeValveTileEntity(BlockPos pos, BlockState state) { + super(ModContent.getBlockEntityTypeOfBlock("straight_pipe_valve"), pos, state); + } + + // BlockEntity ----------------------------------------------------------------------------- + + private Direction block_facing() { + BlockState st = getLevel().getBlockState(getBlockPos()); + return (st.getBlock() instanceof PipeValveBlock) ? st.getValue(PipeValveBlock.FACING) : Direction.NORTH; + } + + // ICapabilityProvider -------------------------------------------------------------------- + + private long valve_config() { + if (valve_config_ <= 0) { + final Block block = getLevel().getBlockState(getBlockPos()).getBlock(); + if (block instanceof PipeValveBlock) valve_config_ = ((PipeValveBlock) block).valve_config; + } + return valve_config_; + } + + @Override + public void setRemoved() { + super.setRemoved(); + back_flow_handler_.invalidate(); + fluid_handler_.invalidate(); + } + + @Override + public LazyOptional getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable Direction facing) { + if (capability == ForgeCapabilities.FLUID_HANDLER) { + Direction bf = block_facing(); + if (facing == bf) return back_flow_handler_.cast(); + if (facing == bf.getOpposite()) return fluid_handler_.cast(); + return LazyOptional.empty(); + } + return super.getCapability(capability, facing); + } + + // IFluidHandlers + + @Nullable + private IFluidHandler forward_fluid_handler() { + final BlockEntity te = level.getBlockEntity(worldPosition.relative(block_facing())); + if (te == null) return null; + return te.getCapability(ForgeCapabilities.FLUID_HANDLER, block_facing().getOpposite()).orElse(null); + } + + // Forward flow handler -- + + private static class MainFlowHandler implements IFluidHandler { + private final PipeValveTileEntity te; + + public MainFlowHandler(PipeValveTileEntity te) { + this.te = te; + } + + @Override + public int getTanks() { + return 1; + } + + @Override + public FluidStack getFluidInTank(int tank) { + return FluidStack.EMPTY; + } + + @Override + public int getTankCapacity(int tank) { + return fluid_maxflow_mb; + } + + @Override + public boolean isFluidValid(int tank, @Nonnull FluidStack stack) { + return true; + } + + @Override + public FluidStack drain(FluidStack resource, FluidAction action) { + return FluidStack.EMPTY; + } + + @Override + public FluidStack drain(int maxDrain, FluidAction action) { + return FluidStack.EMPTY; + } + + @Override + public int fill(FluidStack resource, FluidAction action) { + if (te.filling_) return 0; + final IFluidHandler fh = te.forward_fluid_handler(); + if (fh == null) return 0; + FluidStack res = resource.copy(); + if ((te.valve_config() & CFG_REDSTONE_CONTROLLED_VALVE) != 0) { + int rs = te.level.getBestNeighborSignal(te.worldPosition); + if (rs <= 0) return 0; + if (((te.valve_config() & CFG_ANALOG_VALVE) != 0) && (rs < 15)) + res.setAmount(Mth.clamp(rs * redstone_flow_slope_mb, 1, res.getAmount())); + } + if (res.getAmount() > fluid_maxflow_mb) res.setAmount(fluid_maxflow_mb); + te.filling_ = true; + int n_filled = fh.fill(res, action); + te.filling_ = false; + return n_filled; + } + } + + // Back flow prevention handler -- + + private static class BackFlowHandler implements IFluidHandler { + @Override + public int getTanks() { + return 1; + } + + @Override + public FluidStack getFluidInTank(int tank) { + return FluidStack.EMPTY; + } + + @Override + public int getTankCapacity(int tank) { + return 0; + } + + @Override + public boolean isFluidValid(int tank, @Nonnull FluidStack stack) { + return false; + } + + @Override + public int fill(FluidStack resource, FluidAction action) { + return 0; + } + + @Override + public FluidStack drain(FluidStack resource, FluidAction action) { + return FluidStack.EMPTY; + } + + @Override + public FluidStack drain(int maxDrain, FluidAction action) { + return FluidStack.EMPTY; + } + } } - } } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdPlacer.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdPlacer.java index c00293f..a5257bb 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdPlacer.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdPlacer.java @@ -8,8 +8,9 @@ */ package dev.zontreck.engineerdecor.blocks; -import com.mojang.blaze3d.vertex.PoseStack; -import dev.zontreck.engineerdecor.libmc.*; +import dev.zontreck.engineerdecor.ModContent; +import dev.zontreck.libzontreck.edlibmc.*; +import net.minecraft.client.gui.GuiGraphics; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; @@ -32,7 +33,7 @@ import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.SignalGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.SoundType; @@ -54,8 +55,6 @@ import net.minecraftforge.common.util.FakePlayer; import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.fluids.IFluidBlock; import net.minecraftforge.items.IItemHandler; -import dev.zontreck.engineerdecor.ModContent; -import wile.engineersdecor.libmc.*; import javax.annotation.Nullable; import java.util.ArrayList; @@ -63,693 +62,709 @@ import java.util.Arrays; import java.util.List; -public class EdPlacer -{ - public static void on_config() - {} - - //-------------------------------------------------------------------------------------------------------------------- - // Block - //-------------------------------------------------------------------------------------------------------------------- - - public static class PlacerBlock extends StandardBlocks.Directed implements StandardEntityBlocks.IStandardEntityBlock - { - public PlacerBlock(long config, BlockBehaviour.Properties builder, final AABB[] unrotatedAABB) - { super(config, builder, unrotatedAABB); } - - @Override - public boolean isBlockEntityTicking(Level world, BlockState state) - { return true; } - - @Override - public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) - { return Shapes.block(); } - - @Override - @SuppressWarnings("deprecation") - public boolean hasAnalogOutputSignal(BlockState state) - { return true; } - - @Override - @SuppressWarnings("deprecation") - public int getAnalogOutputSignal(BlockState blockState, Level world, BlockPos pos) - { return (world.getBlockEntity(pos) instanceof EdPlacer.PlacerTileEntity te) ? RsSignals.fromContainer(te.inventory_) : 0; } - - @Override - public boolean shouldCheckWeakPower(BlockState state, LevelReader world, BlockPos pos, Direction side) - { return false; } - - @Override - public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) - { - if(world.isClientSide) return; - if((!stack.hasTag()) || (!stack.getTag().contains("tedata"))) return; - CompoundTag te_nbt = stack.getTag().getCompound("tedata"); - if(te_nbt.isEmpty()) return; - if(!(world.getBlockEntity(pos) instanceof final PlacerTileEntity te)) return; - te.readnbt(te_nbt, false); - te.reset_rtstate(); - te.setChanged(); +public class EdPlacer { + public static void on_config() { } - @Override - public boolean hasDynamicDropList() - { return true; } + //-------------------------------------------------------------------------------------------------------------------- + // Block + //-------------------------------------------------------------------------------------------------------------------- - @Override - public List dropList(BlockState state, Level world, final BlockEntity te, boolean explosion) - { - final List stacks = new ArrayList<>(); - if(world.isClientSide) return stacks; - if(!(te instanceof PlacerTileEntity)) return stacks; - if(!explosion) { - ItemStack stack = new ItemStack(this, 1); - CompoundTag te_nbt = ((PlacerTileEntity) te).clear_getnbt(); - if(!te_nbt.isEmpty()) { - CompoundTag nbt = new CompoundTag(); - nbt.put("tedata", te_nbt); - stack.setTag(nbt); + public static class PlacerBlock extends StandardBlocks.Directed implements StandardEntityBlocks.IStandardEntityBlock { + public PlacerBlock(long config, BlockBehaviour.Properties builder, final AABB[] unrotatedAABB) { + super(config, builder, unrotatedAABB); } - stacks.add(stack); - } else { - for(ItemStack stack: ((PlacerTileEntity)te).inventory_) { - if(!stack.isEmpty()) stacks.add(stack); + + @Override + public boolean isBlockEntityTicking(Level world, BlockState state) { + return true; } - ((PlacerTileEntity)te).reset_rtstate(); - } - return stacks; - } - @Override - @SuppressWarnings("deprecation") - public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult) - { return useOpenGui(state, world, pos, player); } - - @Override - @SuppressWarnings("deprecation") - public void neighborChanged(BlockState state, Level world, BlockPos pos, Block block, BlockPos fromPos, boolean unused) - { - if(!(world instanceof Level) || (world.isClientSide)) return; - BlockEntity te = world.getBlockEntity(pos); - if(!(te instanceof PlacerTileEntity)) return; - ((PlacerTileEntity)te).block_updated(); - } - - @Override - @SuppressWarnings("deprecation") - public boolean isSignalSource(BlockState state) - { return true; } - - @Override - @SuppressWarnings("deprecation") - public int getSignal(BlockState blockState, BlockGetter blockAccess, BlockPos pos, Direction side) - { return 0; } - - @Override - @SuppressWarnings("deprecation") - public int getDirectSignal(BlockState blockState, BlockGetter blockAccess, BlockPos pos, Direction side) - { return 0; } - } - - //-------------------------------------------------------------------------------------------------------------------- - // Tile entity - //-------------------------------------------------------------------------------------------------------------------- - - public static class PlacerTileEntity extends StandardEntityBlocks.StandardBlockEntity implements MenuProvider, Nameable - { - public static final int TICK_INTERVAL = 40; - public static final int NUM_OF_SLOTS = 18; - public static final int NUM_OF_FIELDS = 3; - public static final int LOGIC_NOT_INVERTED = 0x00; - public static final int LOGIC_INVERTED = 0x01; - public static final int LOGIC_CONTINUOUS = 0x02; - public static final int LOGIC_IGNORE_EXT = 0x04; - /// - private boolean block_power_signal_ = false; - private boolean block_power_updated_ = false; - private int logic_ = LOGIC_IGNORE_EXT|LOGIC_CONTINUOUS; - private int current_slot_index_ = 0; - private int tick_timer_ = 0; - private final boolean debug_ = false; // @todo debug stick in `self::use()` toggling. - private final Inventories.StorageInventory inventory_ = new Inventories.StorageInventory(this, NUM_OF_SLOTS, 1); - private final LazyOptional item_handler_; - - public PlacerTileEntity(BlockPos pos, BlockState state) - { - super(ModContent.getBlockEntityTypeOfBlock(state.getBlock()), pos, state); - item_handler_ = Inventories.MappedItemHandler.createGenericHandler(inventory_, - (stack, slot) -> true, - (stack, slot) -> true - ); - } - - public CompoundTag clear_getnbt() - { - CompoundTag nbt = new CompoundTag(); - writenbt(nbt, false); - inventory_.clearContent(); - reset_rtstate(); - block_power_updated_ = false; - return nbt; - } - - public void reset_rtstate() - { - block_power_signal_ = false; - block_power_updated_ = false; - } - - public void readnbt(CompoundTag nbt, boolean update_packet) - { - inventory_.load(nbt); - block_power_signal_ = nbt.getBoolean("powered"); - current_slot_index_ = nbt.getInt("act_slot_index"); - logic_ = nbt.getInt("logic"); - } - - protected void writenbt(CompoundTag nbt, boolean update_packet) - { - inventory_.save(nbt); - nbt.putBoolean("powered", block_power_signal_); - nbt.putInt("act_slot_index", current_slot_index_); - nbt.putInt("logic", logic_); - } - - public void block_updated() - { - boolean powered = level.hasNeighborSignal(worldPosition); - if(block_power_signal_ != powered) block_power_updated_ = true; - block_power_signal_ = powered; - if(block_power_updated_) { - tick_timer_ = 1; - } else if(tick_timer_ > 4) { - tick_timer_ = 4; - } - } - - // BlockEntity ------------------------------------------------------------------------------ - - @Override - public void load(CompoundTag nbt) - { super.load(nbt); readnbt(nbt, false); } - - @Override - protected void saveAdditional(CompoundTag nbt) - { super.saveAdditional(nbt); writenbt(nbt, false); } - - @Override - public void setRemoved() - { - super.setRemoved(); - item_handler_.invalidate(); - } - - // Namable ----------------------------------------------------------------------------------------------- - - @Override - public Component getName() - { return Auxiliaries.localizable(getBlockState().getBlock().getDescriptionId()); } - - @Override - public boolean hasCustomName() - { return false; } - - @Override - public Component getCustomName() - { return getName(); } - - // INamedContainerProvider ------------------------------------------------------------------------------ - - @Override - public Component getDisplayName() - { return Nameable.super.getDisplayName(); } - - @Override - public AbstractContainerMenu createMenu(int id, Inventory inventory, Player player ) - { return new PlacerContainer(id, inventory, inventory_, ContainerLevelAccess.create(level, worldPosition), fields); } - - // Fields ----------------------------------------------------------------------------------------------- - - protected final ContainerData fields = new ContainerData() - { - @Override - public int getCount() - { return PlacerTileEntity.NUM_OF_FIELDS; } - - @Override - public int get(int id) - { - return switch (id) { - case 0 -> logic_; - case 1 -> block_power_signal_ ? 1 : 0; - case 2 -> Mth.clamp(current_slot_index_, 0, NUM_OF_SLOTS - 1); - default -> 0; - }; - } - @Override - public void set(int id, int value) - { - switch (id) { - case 0 -> logic_ = value; - case 1 -> block_power_signal_ = (value != 0); - case 2 -> current_slot_index_ = Mth.clamp(value, 0, NUM_OF_SLOTS - 1); + @Override + public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) { + return Shapes.block(); } - } - }; - // Capability export ------------------------------------------------------------------------------------ - - @Override - public LazyOptional getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable Direction facing) - { - if(capability == ForgeCapabilities.ITEM_HANDLER) return item_handler_.cast(); - return super.getCapability(capability, facing); - } - - // ITickable and aux methods ---------------------------------------------------------------------------- - - private static int next_slot(int i) - { return (i= NUM_OF_SLOTS) current_slot_index_ = 0; - current_stack = inventory_.getItem(current_slot_index_); - if(!current_stack.isEmpty()) break; - current_slot_index_ = next_slot(current_slot_index_); - } - if(current_stack.isEmpty()) { current_slot_index_ = 0; return false; } - boolean no_space = false; - final Item item = current_stack.getItem(); - Block block = Block.byItem(item); - if(block == Blocks.AIR) { - if(item != null) { - if(debug_) Auxiliaries.logInfo("Placer spit: No block for item " + Auxiliaries.getResourceLocation(item)); - return spit_out(facing); // Item not accepted - } - } else if(block instanceof IPlantable) { - if(level.isEmptyBlock(placement_pos)) { - // plant here, block below has to be valid soil. - BlockState soilstate = level.getBlockState(placement_pos.below()); - if(!soilstate.getBlock().canSustainPlant(soilstate, level, worldPosition, Direction.UP, (IPlantable)block)) { - block = Blocks.AIR; - } - } else { - // adjacent block is the soil, plant above if the soil is valid. - BlockState soilstate = level.getBlockState(placement_pos); - if(soilstate.getBlock() == block) { - // The plant is already planted from the case above. - block = Blocks.AIR; - no_space = true; - } else if(!level.isEmptyBlock(placement_pos.above())) { - // If this is the soil an air block is needed above, if that is blocked we can't plant. - block = Blocks.AIR; - no_space = true; - } else if(!soilstate.getBlock().canSustainPlant(soilstate, level, worldPosition, Direction.UP, (IPlantable)block)) { - // Would be space above, but it's not the right soil for the plant. - block = Blocks.AIR; - } else { - // Ok, plant above. - placement_pos = placement_pos.above(); - } - } - } else { - final BlockState current_placement_pos_state = level.getBlockState(placement_pos); + @Override @SuppressWarnings("deprecation") - final boolean replacable = (current_placement_pos_state.getBlock().canBeReplaced(current_placement_pos_state, Fluids.EMPTY)) && ( - level.isEmptyBlock(placement_pos) || - (current_placement_pos_state.getBlock() instanceof IFluidBlock) || - (current_placement_pos_state.getMaterial().isReplaceable() && (!current_placement_pos_state.getMaterial().isSolid())) - ); - if((!replacable) || ( - (!level.getEntitiesOfClass(Entity.class, new AABB(placement_pos), (Entity e)->{ - if(e.isPickable()) return true; - if(triggered) return false; - if((e instanceof ItemEntity)) { - if((e.getDeltaMovement().y() > 0) || (e.getDeltaMovement().y() < -0.5)) return true; // not falling or falling by - if(Math.abs(e.getDeltaMovement().x())+Math.abs(e.getDeltaMovement().z()) > 0) return true; // not straight + public boolean hasAnalogOutputSignal(BlockState state) { + return true; + } + + @Override + @SuppressWarnings("deprecation") + public int getAnalogOutputSignal(BlockState blockState, Level world, BlockPos pos) { + return (world.getBlockEntity(pos) instanceof EdPlacer.PlacerTileEntity te) ? RsSignals.fromContainer(te.inventory_) : 0; + } + + @Override + public boolean shouldCheckWeakPower(BlockState state, SignalGetter level, BlockPos pos, Direction side) { + return false; + } + + @Override + public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) { + if (world.isClientSide) return; + if ((!stack.hasTag()) || (!stack.getTag().contains("tedata"))) return; + CompoundTag te_nbt = stack.getTag().getCompound("tedata"); + if (te_nbt.isEmpty()) return; + if (!(world.getBlockEntity(pos) instanceof final PlacerTileEntity te)) return; + te.readnbt(te_nbt, false); + te.reset_rtstate(); + te.setChanged(); + } + + @Override + public boolean hasDynamicDropList() { + return true; + } + + @Override + public List dropList(BlockState state, Level world, final BlockEntity te, boolean explosion) { + final List stacks = new ArrayList<>(); + if (world.isClientSide) return stacks; + if (!(te instanceof PlacerTileEntity)) return stacks; + if (!explosion) { + ItemStack stack = new ItemStack(this, 1); + CompoundTag te_nbt = ((PlacerTileEntity) te).clear_getnbt(); + if (!te_nbt.isEmpty()) { + CompoundTag nbt = new CompoundTag(); + nbt.put("tedata", te_nbt); + stack.setTag(nbt); + } + stacks.add(stack); + } else { + for (ItemStack stack : ((PlacerTileEntity) te).inventory_) { + if (!stack.isEmpty()) stacks.add(stack); + } + ((PlacerTileEntity) te).reset_rtstate(); + } + return stacks; + } + + @Override + @SuppressWarnings("deprecation") + public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult) { + return useOpenGui(state, world, pos, player); + } + + @Override + @SuppressWarnings("deprecation") + public void neighborChanged(BlockState state, Level world, BlockPos pos, Block block, BlockPos fromPos, boolean unused) { + if (!(world instanceof Level) || (world.isClientSide)) return; + BlockEntity te = world.getBlockEntity(pos); + if (!(te instanceof PlacerTileEntity)) return; + ((PlacerTileEntity) te).block_updated(); + } + + @Override + @SuppressWarnings("deprecation") + public boolean isSignalSource(BlockState state) { + return true; + } + + @Override + @SuppressWarnings("deprecation") + public int getSignal(BlockState blockState, BlockGetter blockAccess, BlockPos pos, Direction side) { + return 0; + } + + @Override + @SuppressWarnings("deprecation") + public int getDirectSignal(BlockState blockState, BlockGetter blockAccess, BlockPos pos, Direction side) { + return 0; + } + } + + //-------------------------------------------------------------------------------------------------------------------- + // Tile entity + //-------------------------------------------------------------------------------------------------------------------- + + public static class PlacerTileEntity extends StandardEntityBlocks.StandardBlockEntity implements MenuProvider, Nameable { + public static final int TICK_INTERVAL = 40; + public static final int NUM_OF_SLOTS = 18; + public static final int NUM_OF_FIELDS = 3; + public static final int LOGIC_NOT_INVERTED = 0x00; + public static final int LOGIC_INVERTED = 0x01; + public static final int LOGIC_CONTINUOUS = 0x02; + public static final int LOGIC_IGNORE_EXT = 0x04; + private final boolean debug_ = false; // @todo debug stick in `self::use()` toggling. + private final Inventories.StorageInventory inventory_ = new Inventories.StorageInventory(this, NUM_OF_SLOTS, 1); + private final LazyOptional item_handler_; + /// + private boolean block_power_signal_ = false; + private boolean block_power_updated_ = false; + private int logic_ = LOGIC_IGNORE_EXT | LOGIC_CONTINUOUS; + private int current_slot_index_ = 0; + protected final ContainerData fields = new ContainerData() { + @Override + public int getCount() { + return PlacerTileEntity.NUM_OF_FIELDS; + } + + @Override + public int get(int id) { + return switch (id) { + case 0 -> logic_; + case 1 -> block_power_signal_ ? 1 : 0; + case 2 -> Mth.clamp(current_slot_index_, 0, NUM_OF_SLOTS - 1); + default -> 0; + }; + } + + @Override + public void set(int id, int value) { + switch (id) { + case 0 -> logic_ = value; + case 1 -> block_power_signal_ = (value != 0); + case 2 -> current_slot_index_ = Mth.clamp(value, 0, NUM_OF_SLOTS - 1); + } + } + }; + private int tick_timer_ = 0; + + public PlacerTileEntity(BlockPos pos, BlockState state) { + super(ModContent.getBlockEntityTypeOfBlock(state.getBlock()), pos, state); + item_handler_ = Inventories.MappedItemHandler.createGenericHandler(inventory_, + (stack, slot) -> true, + (stack, slot) -> true + ); + } + + private static int next_slot(int i) { + return (i < NUM_OF_SLOTS - 1) ? (i + 1) : 0; + } + + public CompoundTag clear_getnbt() { + CompoundTag nbt = new CompoundTag(); + writenbt(nbt, false); + inventory_.clearContent(); + reset_rtstate(); + block_power_updated_ = false; + return nbt; + } + + public void reset_rtstate() { + block_power_signal_ = false; + block_power_updated_ = false; + } + + public void readnbt(CompoundTag nbt, boolean update_packet) { + inventory_.load(nbt); + block_power_signal_ = nbt.getBoolean("powered"); + current_slot_index_ = nbt.getInt("act_slot_index"); + logic_ = nbt.getInt("logic"); + } + + // BlockEntity ------------------------------------------------------------------------------ + + protected void writenbt(CompoundTag nbt, boolean update_packet) { + inventory_.save(nbt); + nbt.putBoolean("powered", block_power_signal_); + nbt.putInt("act_slot_index", current_slot_index_); + nbt.putInt("logic", logic_); + } + + public void block_updated() { + boolean powered = level.hasNeighborSignal(worldPosition); + if (block_power_signal_ != powered) block_power_updated_ = true; + block_power_signal_ = powered; + if (block_power_updated_) { + tick_timer_ = 1; + } else if (tick_timer_ > 4) { + tick_timer_ = 4; + } + } + + @Override + public void load(CompoundTag nbt) { + super.load(nbt); + readnbt(nbt, false); + } + + // Namable ----------------------------------------------------------------------------------------------- + + @Override + protected void saveAdditional(CompoundTag nbt) { + super.saveAdditional(nbt); + writenbt(nbt, false); + } + + @Override + public void setRemoved() { + super.setRemoved(); + item_handler_.invalidate(); + } + + @Override + public Component getName() { + return Auxiliaries.localizable(getBlockState().getBlock().getDescriptionId()); + } + + // INamedContainerProvider ------------------------------------------------------------------------------ + + @Override + public boolean hasCustomName() { + return false; + } + + @Override + public Component getCustomName() { + return getName(); + } + + // Fields ----------------------------------------------------------------------------------------------- + + @Override + public Component getDisplayName() { + return Nameable.super.getDisplayName(); + } + + // Capability export ------------------------------------------------------------------------------------ + + @Override + public AbstractContainerMenu createMenu(int id, Inventory inventory, Player player) { + return new PlacerContainer(id, inventory, inventory_, ContainerLevelAccess.create(level, worldPosition), fields); + } + + // ITickable and aux methods ---------------------------------------------------------------------------- + + @Override + public LazyOptional getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable Direction facing) { + if (capability == ForgeCapabilities.ITEM_HANDLER) return item_handler_.cast(); + return super.getCapability(capability, facing); + } + + private boolean spit_out(Direction facing) { + return spit_out(facing, false); + } + + private boolean spit_out(Direction facing, boolean all) { + ItemStack stack = inventory_.getItem(current_slot_index_); + ItemStack drop = stack.copy(); + if (!all) { + stack.shrink(1); + inventory_.setItem(current_slot_index_, stack); + drop.setCount(1); + } else { + inventory_.setItem(current_slot_index_, ItemStack.EMPTY); + } + for (int i = 0; i < 8; ++i) { + BlockPos p = worldPosition.relative(facing, i); + if (!level.isEmptyBlock(p)) continue; + level.addFreshEntity(new ItemEntity(level, (p.getX() + 0.5), (p.getY() + 0.5), (p.getZ() + 0.5), drop)); + level.playSound(null, p, SoundEvents.ITEM_PICKUP, SoundSource.BLOCKS, 0.7f, 0.8f); + break; + } + return true; + } + + private boolean try_place(Direction facing, boolean triggered) { + if (level.isClientSide()) return false; + BlockPos placement_pos = worldPosition.relative(facing); + if (level.getBlockEntity(placement_pos) != null) return false; + ItemStack current_stack = ItemStack.EMPTY; + for (int i = 0; i < NUM_OF_SLOTS; ++i) { + if (current_slot_index_ >= NUM_OF_SLOTS) current_slot_index_ = 0; + current_stack = inventory_.getItem(current_slot_index_); + if (!current_stack.isEmpty()) break; + current_slot_index_ = next_slot(current_slot_index_); + } + if (current_stack.isEmpty()) { + current_slot_index_ = 0; + return false; + } + boolean no_space = false; + final Item item = current_stack.getItem(); + Block block = Block.byItem(item); + if (block == Blocks.AIR) { + if (item != null) { + if (debug_) + Auxiliaries.logInfo("Placer spit: No block for item " + Auxiliaries.getResourceLocation(item)); + return spit_out(facing); // Item not accepted + } + } else if (block instanceof IPlantable) { + if (level.isEmptyBlock(placement_pos)) { + // plant here, block below has to be valid soil. + BlockState soilstate = level.getBlockState(placement_pos.below()); + if (!soilstate.getBlock().canSustainPlant(soilstate, level, worldPosition, Direction.UP, (IPlantable) block)) { + block = Blocks.AIR; + } + } else { + // adjacent block is the soil, plant above if the soil is valid. + BlockState soilstate = level.getBlockState(placement_pos); + if (soilstate.getBlock() == block) { + // The plant is already planted from the case above. + block = Blocks.AIR; + no_space = true; + } else if (!level.isEmptyBlock(placement_pos.above())) { + // If this is the soil an air block is needed above, if that is blocked we can't plant. + block = Blocks.AIR; + no_space = true; + } else if (!soilstate.getBlock().canSustainPlant(soilstate, level, worldPosition, Direction.UP, (IPlantable) block)) { + // Would be space above, but it's not the right soil for the plant. + block = Blocks.AIR; + } else { + // Ok, plant above. + placement_pos = placement_pos.above(); + } + } + } else { + final BlockState current_placement_pos_state = level.getBlockState(placement_pos); + @SuppressWarnings("deprecation") final boolean replacable = (current_placement_pos_state.getBlock().canBeReplaced(current_placement_pos_state, Fluids.EMPTY)) && ( + level.isEmptyBlock(placement_pos) || + (current_placement_pos_state.getBlock() instanceof IFluidBlock) || + (current_placement_pos_state.canBeReplaced() && (!current_placement_pos_state.isAir())) + ); + if ((!replacable) || ( + (!level.getEntitiesOfClass(Entity.class, new AABB(placement_pos), (Entity e) -> { + if (e.isPickable()) return true; + if (triggered) return false; + if ((e instanceof ItemEntity)) { + if ((e.getDeltaMovement().y() > 0) || (e.getDeltaMovement().y() < -0.5)) + return true; // not falling or falling by + if (Math.abs(e.getDeltaMovement().x()) + Math.abs(e.getDeltaMovement().z()) > 0) + return true; // not straight + } + return false; + }).isEmpty()) + )) { + block = Blocks.AIR; + no_space = true; + } + } + // println("PLACE " + current_stack + " --> " + block + " at " + placement_pos.subtract(pos) + "( item=" + item + ")"); + if (block != Blocks.AIR) { + try { + BlockPlaceContext use_context = null; + { + final FakePlayer placer = net.minecraftforge.common.util.FakePlayerFactory.getMinecraft((ServerLevel) level); + if (placer != null) { + ItemStack placement_stack = current_stack.copy(); + placement_stack.setCount(1); + ItemStack held = placer.getItemInHand(InteractionHand.MAIN_HAND); + placer.setItemInHand(InteractionHand.MAIN_HAND, placement_stack); + List directions = new ArrayList<>(Arrays.asList(Direction.UP, facing.getOpposite())); + for (Direction d : Direction.values()) if (!directions.contains(d)) directions.add(d); + for (Direction d : directions) { + Vec3 v = Vec3.atCenterOf(placement_pos).subtract(Vec3.atLowerCornerOf(d.getNormal())); + use_context = new BlockPlaceContext(new UseOnContext(placer, InteractionHand.MAIN_HAND, new BlockHitResult(v, d, placement_pos, false))); + if (block.getStateForPlacement(use_context) == null) use_context = null; + if (use_context != null) break; + } + placer.setItemInHand(InteractionHand.MAIN_HAND, held); + } + } + BlockState placement_state = (use_context == null) ? (block.defaultBlockState()) : (block.getStateForPlacement(use_context)); + if (placement_state == null) { + if (debug_) + Auxiliaries.logInfo("Placer spit: No valid placement state for item " + Auxiliaries.getResourceLocation(item)); + return spit_out(facing); + } else if ((use_context != null) && (item instanceof BlockItem)) { + if (((BlockItem) item).place(use_context) != InteractionResult.FAIL) { + SoundType stype = block.getSoundType(placement_state, level, worldPosition, null); + if (stype != null) + level.playSound(null, placement_pos, stype.getPlaceSound(), SoundSource.BLOCKS, stype.getVolume() * 0.6f, stype.getPitch()); + } else { + if (level.setBlock(placement_pos, placement_state, 1 | 2 | 8)) { + SoundType stype = block.getSoundType(placement_state, level, worldPosition, null); + if (stype != null) + level.playSound(null, placement_pos, stype.getPlaceSound(), SoundSource.BLOCKS, stype.getVolume() * 0.6f, stype.getPitch()); + } + } + } else { + if (level.setBlock(placement_pos, placement_state, 1 | 2 | 8)) { + SoundType stype = block.getSoundType(placement_state, level, worldPosition, null); + if (stype != null) + level.playSound(null, placement_pos, stype.getPlaceSound(), SoundSource.BLOCKS, stype.getVolume() * 0.6f, stype.getPitch()); + } + } + current_stack.shrink(1); + inventory_.setItem(current_slot_index_, current_stack); + return true; + } catch (Throwable e) { + // The block really needs a player or other issues happened during placement. + // A hard crash should not be fired here, instead spit out the item to indicated that this + // block is not compatible. + Auxiliaries.logger().error("Exception while trying to place " + ((block == null) ? ("") : ("" + block)) + ", spitting out. Exception is: " + e); + try { + level.removeBlock(placement_pos, false); + } catch (Throwable e1) { + Auxiliaries.logger().error("Exception while removing failed block placement " + ((block == null) ? ("") : ("" + block)) + ", spitting out. Exception is: " + e1); + } + return spit_out(facing, true); + } + } + if ((!no_space) && (!current_stack.isEmpty())) { + // There is space, but the current plant cannot be planted there, so try next. + for (int i = 0; i < NUM_OF_SLOTS; ++i) { + current_slot_index_ = next_slot(current_slot_index_); + if (!inventory_.getItem(current_slot_index_).isEmpty()) break; + } } return false; - }).isEmpty()) - )) { - block = Blocks.AIR; - no_space = true; } - } - // println("PLACE " + current_stack + " --> " + block + " at " + placement_pos.subtract(pos) + "( item=" + item + ")"); - if(block != Blocks.AIR) { - try { - BlockPlaceContext use_context = null; - { - final FakePlayer placer = net.minecraftforge.common.util.FakePlayerFactory.getMinecraft((ServerLevel)level); - if(placer != null) { - ItemStack placement_stack = current_stack.copy(); - placement_stack.setCount(1); - ItemStack held = placer.getItemInHand(InteractionHand.MAIN_HAND); - placer.setItemInHand(InteractionHand.MAIN_HAND, placement_stack); - List directions = new ArrayList<>(Arrays.asList(Direction.UP, facing.getOpposite())); - for(Direction d:Direction.values()) if(!directions.contains(d)) directions.add(d); - for(Direction d:directions) { - Vec3 v = Vec3.atCenterOf(placement_pos).subtract(Vec3.atLowerCornerOf(d.getNormal())); - use_context = new BlockPlaceContext(new UseOnContext(placer, InteractionHand.MAIN_HAND, new BlockHitResult(v, d, placement_pos, false))); - if(block.getStateForPlacement(use_context) == null) use_context = null; - if(use_context!=null) break; - } - placer.setItemInHand(InteractionHand.MAIN_HAND, held); + + @Override + public void tick() { + // Tick cycle pre-conditions + if (level.isClientSide) return; + if (--tick_timer_ > 0) return; + tick_timer_ = TICK_INTERVAL; + // Cycle init + final BlockState state = level.getBlockState(worldPosition); + if (!(state.getBlock() instanceof PlacerBlock)) { + block_power_signal_ = false; + return; } - } - BlockState placement_state = (use_context==null) ? (block.defaultBlockState()) : (block.getStateForPlacement(use_context)); - if(placement_state == null) { - if(debug_) Auxiliaries.logInfo("Placer spit: No valid placement state for item " + Auxiliaries.getResourceLocation(item)); - return spit_out(facing); - } else if((use_context!=null) && (item instanceof BlockItem)) { - if(((BlockItem)item).place(use_context) != InteractionResult.FAIL) { - SoundType stype = block.getSoundType(placement_state, level, worldPosition, null); - if(stype != null) level.playSound(null, placement_pos, stype.getPlaceSound(), SoundSource.BLOCKS, stype.getVolume()*0.6f, stype.getPitch()); + final boolean updated = block_power_updated_; + final boolean rssignal = ((logic_ & LOGIC_IGNORE_EXT) != 0) || ((logic_ & LOGIC_INVERTED) != 0) == (!block_power_signal_); + final boolean trigger = ((logic_ & LOGIC_IGNORE_EXT) != 0) || (rssignal && ((updated) || ((logic_ & LOGIC_CONTINUOUS) != 0))); + final Direction placer_facing = state.getValue(PlacerBlock.FACING); + boolean dirty = updated; + // Trigger edge detection for next cycle + { + boolean tr = level.hasNeighborSignal(worldPosition); + block_power_updated_ = (block_power_signal_ != tr); + block_power_signal_ = tr; + if (block_power_updated_) dirty = true; + } + // Placing + if (trigger && try_place(placer_facing, rssignal && updated)) dirty = true; + if (dirty) setChanged(); + if (trigger && (tick_timer_ > TICK_INTERVAL)) tick_timer_ = TICK_INTERVAL; + } + } + + //-------------------------------------------------------------------------------------------------------------------- + // Container + //-------------------------------------------------------------------------------------------------------------------- + + public static class PlacerContainer extends AbstractContainerMenu implements Networking.INetworkSynchronisableContainer { + protected static final String QUICK_MOVE_ALL = "quick-move-all"; + private static final int PLAYER_INV_START_SLOTNO = PlacerTileEntity.NUM_OF_SLOTS; + private final Player player_; + private final Container inventory_; + private final ContainerLevelAccess wpc_; + private final ContainerData fields_; + private final Inventories.InventoryRange player_inventory_range_; + private final Inventories.InventoryRange block_storage_range_; + + public PlacerContainer(int cid, Inventory player_inventory) { + this(cid, player_inventory, new SimpleContainer(PlacerTileEntity.NUM_OF_SLOTS), ContainerLevelAccess.NULL, new SimpleContainerData(PlacerTileEntity.NUM_OF_FIELDS)); + } + + private PlacerContainer(int cid, Inventory player_inventory, Container block_inventory, ContainerLevelAccess wpc, ContainerData fields) { + super(ModContent.getMenuType("factory_placer"), cid); // @todo: class mapping + fields_ = fields; + wpc_ = wpc; + player_ = player_inventory.player; + inventory_ = block_inventory; + block_storage_range_ = new Inventories.InventoryRange(inventory_, 0, PlacerTileEntity.NUM_OF_SLOTS); + player_inventory_range_ = Inventories.InventoryRange.fromPlayerInventory(player_); + int i = -1; + // device slots (stacks 0 to 17) + for (int y = 0; y < 3; ++y) { + for (int x = 0; x < 6; ++x) { + int xpos = 11 + x * 18, ypos = 9 + y * 17; + addSlot(new Slot(inventory_, ++i, xpos, ypos)); + } + } + // player slots + for (int x = 0; x < 9; ++x) { + addSlot(new Slot(player_inventory, x, 9 + x * 18, 129)); // player slots: 0..8 + } + for (int y = 0; y < 3; ++y) { + for (int x = 0; x < 9; ++x) { + addSlot(new Slot(player_inventory, x + y * 9 + 9, 9 + x * 18, 71 + y * 18)); // player slots: 9..35 + } + } + this.addDataSlots(fields_); // === Add reference holders + } + + public final int field(int index) { + return fields_.get(index); + } + + @Override + public boolean stillValid(Player player) { + return inventory_.stillValid(player); + } + + @Override + public ItemStack quickMoveStack(Player player, int index) { + Slot slot = getSlot(index); + if ((slot == null) || (!slot.hasItem())) return ItemStack.EMPTY; + ItemStack slot_stack = slot.getItem(); + ItemStack transferred = slot_stack.copy(); + if ((index >= 0) && (index < PLAYER_INV_START_SLOTNO)) { + // Device slots + if (!moveItemStackTo(slot_stack, PLAYER_INV_START_SLOTNO, PLAYER_INV_START_SLOTNO + 36, false)) + return ItemStack.EMPTY; + } else if ((index >= PLAYER_INV_START_SLOTNO) && (index <= PLAYER_INV_START_SLOTNO + 36)) { + // Player slot + if (!moveItemStackTo(slot_stack, 0, PlacerTileEntity.NUM_OF_SLOTS, false)) return ItemStack.EMPTY; } else { - if(level.setBlock(placement_pos, placement_state, 1|2|8)) { - SoundType stype = block.getSoundType(placement_state, level, worldPosition, null); - if(stype != null) level.playSound(null, placement_pos, stype.getPlaceSound(), SoundSource.BLOCKS, stype.getVolume()*0.6f, stype.getPitch()); - } + // invalid slot + return ItemStack.EMPTY; } - } else { - if(level.setBlock(placement_pos, placement_state, 1|2|8)) { - SoundType stype = block.getSoundType(placement_state, level, worldPosition, null); - if(stype != null) level.playSound(null, placement_pos, stype.getPlaceSound(), SoundSource.BLOCKS, stype.getVolume()*0.6f, stype.getPitch()); + if (slot_stack.isEmpty()) { + slot.set(ItemStack.EMPTY); + } else { + slot.setChanged(); } - } - current_stack.shrink(1); - inventory_.setItem(current_slot_index_, current_stack); - return true; - } catch(Throwable e) { - // The block really needs a player or other issues happened during placement. - // A hard crash should not be fired here, instead spit out the item to indicated that this - // block is not compatible. - Auxiliaries.logger().error("Exception while trying to place " + ((block==null)?(""):(""+block)) + ", spitting out. Exception is: " + e); - try { - level.removeBlock(placement_pos, false); - } catch(Throwable e1) { - Auxiliaries.logger().error("Exception while removing failed block placement " + ((block==null)?(""):(""+block)) + ", spitting out. Exception is: " + e1); - } - return spit_out(facing, true); + if (slot_stack.getCount() == transferred.getCount()) return ItemStack.EMPTY; + slot.onTake(player, slot_stack); + return transferred; } - } - if((!no_space) && (!current_stack.isEmpty())) { - // There is space, but the current plant cannot be planted there, so try next. - for(int i=0; i { + if ((slotId >= 0) && (slotId < PLAYER_INV_START_SLOTNO) && (getSlot(slotId).hasItem())) { + changed = block_storage_range_.move(getSlot(slotId).getSlotIndex(), player_inventory_range_, true, false, true, true); + } else if ((slotId >= PLAYER_INV_START_SLOTNO) && (slotId < PLAYER_INV_START_SLOTNO + 36) && (getSlot(slotId).hasItem())) { + changed = player_inventory_range_.move(getSlot(slotId).getSlotIndex(), block_storage_range_, true, false, false, true); + } + } + } + if (changed) { + inventory_.setChanged(); + player.getInventory().setChanged(); + broadcastChanges(); + } + } else { + if (nbt.contains("logic")) te.logic_ = nbt.getInt("logic"); + if (nbt.contains("manual_trigger") && (nbt.getInt("manual_trigger") != 0)) { + te.block_power_signal_ = true; + te.block_power_updated_ = true; + te.tick_timer_ = 1; + } + te.setChanged(); + } + } - } - return false; } - @Override - public void tick() - { - // Tick cycle pre-conditions - if(level.isClientSide) return; - if(--tick_timer_ > 0) return; - tick_timer_ = TICK_INTERVAL; - // Cycle init - final BlockState state = level.getBlockState(worldPosition); - if(!(state.getBlock() instanceof PlacerBlock)) { block_power_signal_= false; return; } - final boolean updated = block_power_updated_; - final boolean rssignal = ((logic_ & LOGIC_IGNORE_EXT)!=0) || ((logic_ & LOGIC_INVERTED)!=0)==(!block_power_signal_); - final boolean trigger = ((logic_ & LOGIC_IGNORE_EXT)!=0) || (rssignal && ((updated) || ((logic_ & LOGIC_CONTINUOUS)!=0))); - final Direction placer_facing = state.getValue(PlacerBlock.FACING); - boolean dirty = updated; - // Trigger edge detection for next cycle - { - boolean tr = level.hasNeighborSignal(worldPosition); - block_power_updated_ = (block_power_signal_ != tr); - block_power_signal_ = tr; - if(block_power_updated_) dirty = true; - } - // Placing - if(trigger && try_place(placer_facing, rssignal && updated)) dirty = true; - if(dirty) setChanged(); - if(trigger && (tick_timer_ > TICK_INTERVAL)) tick_timer_ = TICK_INTERVAL; - } - } - - //-------------------------------------------------------------------------------------------------------------------- - // Container - //-------------------------------------------------------------------------------------------------------------------- - - public static class PlacerContainer extends AbstractContainerMenu implements Networking.INetworkSynchronisableContainer - { - protected static final String QUICK_MOVE_ALL = "quick-move-all"; - private static final int PLAYER_INV_START_SLOTNO = PlacerTileEntity.NUM_OF_SLOTS; - private final Player player_; - private final Container inventory_; - private final ContainerLevelAccess wpc_; - private final ContainerData fields_; - private final Inventories.InventoryRange player_inventory_range_; - private final Inventories.InventoryRange block_storage_range_; - - public final int field(int index) { return fields_.get(index); } - - public PlacerContainer(int cid, Inventory player_inventory) - { this(cid, player_inventory, new SimpleContainer(PlacerTileEntity.NUM_OF_SLOTS), ContainerLevelAccess.NULL, new SimpleContainerData(PlacerTileEntity.NUM_OF_FIELDS)); } - - private PlacerContainer(int cid, Inventory player_inventory, Container block_inventory, ContainerLevelAccess wpc, ContainerData fields) - { - super(ModContent.getMenuType("factory_placer"), cid); // @todo: class mapping - fields_ = fields; - wpc_ = wpc; - player_ = player_inventory.player; - inventory_ = block_inventory; - block_storage_range_ = new Inventories.InventoryRange(inventory_, 0, PlacerTileEntity.NUM_OF_SLOTS); - player_inventory_range_ = Inventories.InventoryRange.fromPlayerInventory(player_); - int i=-1; - // device slots (stacks 0 to 17) - for(int y=0; y<3; ++y) { - for(int x=0; x<6; ++x) { - int xpos = 11+x*18, ypos = 9+y*17; - addSlot(new Slot(inventory_, ++i, xpos, ypos)); - } - } - // player slots - for(int x=0; x<9; ++x) { - addSlot(new Slot(player_inventory, x, 9+x*18, 129)); // player slots: 0..8 - } - for(int y=0; y<3; ++y) { - for(int x=0; x<9; ++x) { - addSlot(new Slot(player_inventory, x+y*9+9, 9+x*18, 71+y*18)); // player slots: 9..35 - } - } - this.addDataSlots(fields_); // === Add reference holders - } - - @Override - public boolean stillValid(Player player) - { return inventory_.stillValid(player); } - - @Override - public ItemStack quickMoveStack(Player player, int index) - { - Slot slot = getSlot(index); - if((slot==null) || (!slot.hasItem())) return ItemStack.EMPTY; - ItemStack slot_stack = slot.getItem(); - ItemStack transferred = slot_stack.copy(); - if((index>=0) && (index= PLAYER_INV_START_SLOTNO) && (index <= PLAYER_INV_START_SLOTNO+36)) { - // Player slot - if(!moveItemStackTo(slot_stack, 0, PlacerTileEntity.NUM_OF_SLOTS, false)) return ItemStack.EMPTY; - } else { - // invalid slot - return ItemStack.EMPTY; - } - if(slot_stack.isEmpty()) { - slot.set(ItemStack.EMPTY); - } else { - slot.setChanged(); - } - if(slot_stack.getCount() == transferred.getCount()) return ItemStack.EMPTY; - slot.onTake(player, slot_stack); - return transferred; - } - - // INetworkSynchronisableContainer --------------------------------------------------------- + //-------------------------------------------------------------------------------------------------------------------- + // GUI + //-------------------------------------------------------------------------------------------------------------------- @OnlyIn(Dist.CLIENT) - public void onGuiAction(CompoundTag nbt) - { Networking.PacketContainerSyncClientToServer.sendToServer(containerId, nbt); } + public static class PlacerGui extends Guis.ContainerGui { + public PlacerGui(PlacerContainer container, Inventory player_inventory, Component title) { + super(container, player_inventory, title, "textures/gui/factory_placer_gui.png"); + } - @OnlyIn(Dist.CLIENT) - public void onGuiAction(String key, int value) - { - CompoundTag nbt = new CompoundTag(); - nbt.putInt(key, value); - Networking.PacketContainerSyncClientToServer.sendToServer(containerId, nbt); - } - - @OnlyIn(Dist.CLIENT) - public void onGuiAction(String message, CompoundTag nbt) - { - nbt.putString("action", message); - Networking.PacketContainerSyncClientToServer.sendToServer(containerId, nbt); - } - - @Override - public void onServerPacketReceived(int windowId, CompoundTag nbt) - {} - - @Override - public void onClientPacketReceived(int windowId, Player player, CompoundTag nbt) - { - if(!(inventory_ instanceof Inventories.StorageInventory)) return; - if(!((((Inventories.StorageInventory)inventory_).getBlockEntity()) instanceof PlacerTileEntity te)) return; - if(nbt.contains("action")) { - final int slotId = nbt.contains("slot") ? nbt.getInt("slot") : -1; - boolean changed = false; - switch(nbt.getString("action")) { - case QUICK_MOVE_ALL -> { - if ((slotId >= 0) && (slotId < PLAYER_INV_START_SLOTNO) && (getSlot(slotId).hasItem())) { - changed = block_storage_range_.move(getSlot(slotId).getSlotIndex(), player_inventory_range_, true, false, true, true); - } else if ((slotId >= PLAYER_INV_START_SLOTNO) && (slotId < PLAYER_INV_START_SLOTNO + 36) && (getSlot(slotId).hasItem())) { - changed = player_inventory_range_.move(getSlot(slotId).getSlotIndex(), block_storage_range_, true, false, false, true); + @Override + public void init() { + super.init(); + { + final Block block = ModContent.getBlock(Auxiliaries.getResourceLocation(getMenu().getType()).getPath().replaceAll("^ct_", "")); + final String prefix = block.getDescriptionId() + ".tooltips."; + final int x0 = getGuiLeft(), y0 = getGuiTop(); + tooltip_.init( + new TooltipDisplay.TipRange(x0 + 133, y0 + 49, 9, 9, Component.translatable(prefix + "rssignal")), + new TooltipDisplay.TipRange(x0 + 145, y0 + 49, 9, 9, Component.translatable(prefix + "inversion")), + new TooltipDisplay.TipRange(x0 + 159, y0 + 49, 9, 9, Component.translatable(prefix + "triggermode")) + ); } - } } - if(changed) { - inventory_.setChanged(); - player.getInventory().setChanged(); - broadcastChanges(); + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int mouseButton) { + tooltip_.resetTimer(); + PlacerContainer container = getMenu(); + int mx = (int) (mouseX - getGuiLeft() + .5), my = (int) (mouseY - getGuiTop() + .5); + if ((!isHovering(126, 1, 49, 60, mouseX, mouseY))) { + return super.mouseClicked(mouseX, mouseY, mouseButton); + } else if (isHovering(133, 49, 9, 9, mouseX, mouseY)) { + container.onGuiAction("manual_trigger", 1); + } else if (isHovering(145, 49, 9, 9, mouseX, mouseY)) { + final int mask = (PlacerTileEntity.LOGIC_INVERTED | PlacerTileEntity.LOGIC_IGNORE_EXT | PlacerTileEntity.LOGIC_NOT_INVERTED); + final int logic = switch ((container.field(0) & mask)) { + case PlacerTileEntity.LOGIC_NOT_INVERTED -> PlacerTileEntity.LOGIC_INVERTED; + case PlacerTileEntity.LOGIC_INVERTED -> PlacerTileEntity.LOGIC_IGNORE_EXT; + case PlacerTileEntity.LOGIC_IGNORE_EXT -> PlacerTileEntity.LOGIC_NOT_INVERTED; + default -> PlacerTileEntity.LOGIC_IGNORE_EXT; + }; + container.onGuiAction("logic", (container.field(0) & (~mask)) | logic); + } else if (isHovering(159, 49, 7, 9, mouseX, mouseY)) { + container.onGuiAction("logic", container.field(0) ^ PlacerTileEntity.LOGIC_CONTINUOUS); + } + return true; } - } else { - if(nbt.contains("logic")) te.logic_ = nbt.getInt("logic"); - if(nbt.contains("manual_trigger") && (nbt.getInt("manual_trigger")!=0)) { te.block_power_signal_=true; te.block_power_updated_=true; te.tick_timer_=1; } - te.setChanged(); - } - } - } - - //-------------------------------------------------------------------------------------------------------------------- - // GUI - //-------------------------------------------------------------------------------------------------------------------- - - @OnlyIn(Dist.CLIENT) - public static class PlacerGui extends Guis.ContainerGui - { - public PlacerGui(PlacerContainer container, Inventory player_inventory, Component title) - { super(container, player_inventory, title,"textures/gui/factory_placer_gui.png"); } - - @Override - public void init() - { - super.init(); - { - final Block block = ModContent.getBlock(Auxiliaries.getResourceLocation(getMenu().getType()).getPath().replaceAll("^ct_","")); - final String prefix = block.getDescriptionId() + ".tooltips."; - final int x0 = getGuiLeft(), y0 = getGuiTop(); - tooltip_.init( - new TooltipDisplay.TipRange(x0+133, y0+49, 9, 9, Component.translatable(prefix + "rssignal")), - new TooltipDisplay.TipRange(x0+145, y0+49, 9, 9, Component.translatable(prefix + "inversion")), - new TooltipDisplay.TipRange(x0+159, y0+49, 9, 9, Component.translatable(prefix + "triggermode")) - ); - } - } - - @Override - public boolean mouseClicked(double mouseX, double mouseY, int mouseButton) - { - tooltip_.resetTimer(); - PlacerContainer container = getMenu(); - int mx = (int)(mouseX - getGuiLeft() + .5), my = (int)(mouseY - getGuiTop() + .5); - if((!isHovering(126, 1, 49, 60, mouseX, mouseY))) { - return super.mouseClicked(mouseX, mouseY, mouseButton); - } else if(isHovering(133, 49, 9, 9, mouseX, mouseY)) { - container.onGuiAction("manual_trigger", 1); - } else if(isHovering(145, 49, 9, 9, mouseX, mouseY)) { - final int mask = (PlacerTileEntity.LOGIC_INVERTED|PlacerTileEntity.LOGIC_IGNORE_EXT|PlacerTileEntity.LOGIC_NOT_INVERTED); - final int logic = switch((container.field(0) & mask)) { - case PlacerTileEntity.LOGIC_NOT_INVERTED -> PlacerTileEntity.LOGIC_INVERTED; - case PlacerTileEntity.LOGIC_INVERTED -> PlacerTileEntity.LOGIC_IGNORE_EXT; - case PlacerTileEntity.LOGIC_IGNORE_EXT -> PlacerTileEntity.LOGIC_NOT_INVERTED; - default -> PlacerTileEntity.LOGIC_IGNORE_EXT; - }; - container.onGuiAction("logic", (container.field(0) & (~mask)) | logic); - } else if(isHovering(159, 49, 7, 9, mouseX, mouseY)) { - container.onGuiAction("logic", container.field(0) ^ PlacerTileEntity.LOGIC_CONTINUOUS); - } - return true; - } - - @Override - protected void slotClicked(Slot slot, int slotId, int button, ClickType type) - { - tooltip_.resetTimer(); - if((type == ClickType.QUICK_MOVE) && (slot!=null) && slot.hasItem() && Auxiliaries.isShiftDown() && Auxiliaries.isCtrlDown()) { - CompoundTag nbt = new CompoundTag(); - nbt.putInt("slot", slotId); - menu.onGuiAction(PlacerContainer.QUICK_MOVE_ALL, nbt); - } else { - super.slotClicked(slot, slotId, button, type); - } - } - - @Override - protected void renderBgWidgets(PoseStack mx, float partialTicks, int mouseX, int mouseY) - { - final int x0=getGuiLeft(), y0=getGuiTop(), w=getXSize(), h=getYSize(); - PlacerContainer container = getMenu(); - // active slot - { - int slot_index = container.field(2); - if((slot_index < 0) || (slot_index >= PlacerTileEntity.NUM_OF_SLOTS)) slot_index = 0; - int x = (x0+10+((slot_index % 6) * 18)); - int y = (y0+8+((slot_index / 6) * 17)); - blit(mx, x, y, 200, 8, 18, 18); - } - // redstone input - { - if(container.field(1) != 0) { - blit(mx, x0+133, y0+49, 217, 49, 9, 9); + @Override + protected void slotClicked(Slot slot, int slotId, int button, ClickType type) { + tooltip_.resetTimer(); + if ((type == ClickType.QUICK_MOVE) && (slot != null) && slot.hasItem() && Auxiliaries.isShiftDown() && Auxiliaries.isCtrlDown()) { + CompoundTag nbt = new CompoundTag(); + nbt.putInt("slot", slotId); + menu.onGuiAction(PlacerContainer.QUICK_MOVE_ALL, nbt); + } else { + super.slotClicked(slot, slotId, button, type); + } + } + + @Override + protected void renderBgWidgets(GuiGraphics mx, float partialTicks, int mouseX, int mouseY) { + final int x0 = getGuiLeft(), y0 = getGuiTop(), w = getXSize(), h = getYSize(); + PlacerContainer container = getMenu(); + // active slot + { + int slot_index = container.field(2); + if ((slot_index < 0) || (slot_index >= PlacerTileEntity.NUM_OF_SLOTS)) slot_index = 0; + int x = (x0 + 10 + ((slot_index % 6) * 18)); + int y = (y0 + 8 + ((slot_index / 6) * 17)); + mx.blit(getBackgroundImage(), x, y, 200, 8, 18, 18); + } + // redstone input + { + if (container.field(1) != 0) { + mx.blit(getBackgroundImage(), x0 + 133, y0 + 49, 217, 49, 9, 9); + } + } + // trigger logic + { + int inverter_offset_x = ((container.field(0) & PlacerTileEntity.LOGIC_INVERTED) != 0) ? 11 : 0; + int inverter_offset_y = ((container.field(0) & PlacerTileEntity.LOGIC_IGNORE_EXT) != 0) ? 10 : 0; + mx.blit(getBackgroundImage(), x0 + 145, y0 + 49, 177 + inverter_offset_x, 49 + inverter_offset_y, 9, 9); + int pulse_mode_offset = ((container.field(0) & PlacerTileEntity.LOGIC_CONTINUOUS) != 0) ? 9 : 0; + mx.blit(getBackgroundImage(), x0 + 159, y0 + 49, 199 + pulse_mode_offset, 49, 9, 9); + } } - } - // trigger logic - { - int inverter_offset_x = ((container.field(0) & PlacerTileEntity.LOGIC_INVERTED) != 0) ? 11 : 0; - int inverter_offset_y = ((container.field(0) & PlacerTileEntity.LOGIC_IGNORE_EXT) != 0) ? 10 : 0; - blit(mx, x0+145, y0+49, 177+inverter_offset_x, 49+inverter_offset_y, 9, 9); - int pulse_mode_offset = ((container.field(0) & PlacerTileEntity.LOGIC_CONTINUOUS ) != 0) ? 9 : 0; - blit(mx, x0+159, y0+49, 199+pulse_mode_offset, 49, 9, 9); - } } - } } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdRoofBlock.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdRoofBlock.java index 330f40d..8cb14a6 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdRoofBlock.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdRoofBlock.java @@ -8,6 +8,8 @@ */ package dev.zontreck.engineerdecor.blocks; +import dev.zontreck.libzontreck.edlibmc.Auxiliaries; +import dev.zontreck.libzontreck.edlibmc.StandardBlocks; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.item.context.BlockPlaceContext; @@ -33,198 +35,199 @@ import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -import dev.zontreck.engineerdecor.libmc.StandardBlocks; -import dev.zontreck.engineerdecor.libmc.Auxiliaries; -public class EdRoofBlock extends StandardBlocks.HorizontalWaterLoggable -{ - public static final EnumProperty SHAPE = BlockStateProperties.STAIRS_SHAPE; - public static final EnumProperty HALF = BlockStateProperties.HALF; - private final VoxelShape[][][] shape_cache_; +public class EdRoofBlock extends StandardBlocks.HorizontalWaterLoggable { + public static final EnumProperty SHAPE = BlockStateProperties.STAIRS_SHAPE; + public static final EnumProperty HALF = BlockStateProperties.HALF; + private final VoxelShape[][][] shape_cache_; - public EdRoofBlock(long config, BlockBehaviour.Properties properties) - { this(config, properties.dynamicShape(), Shapes.empty(), Shapes.empty()); } - - public EdRoofBlock(long config, BlockBehaviour.Properties properties, VoxelShape add, VoxelShape cut) - { - super(config, properties, Auxiliaries.getPixeledAABB(0, 0,0,16, 8, 16)); - registerDefaultState(super.defaultBlockState().setValue(HORIZONTAL_FACING, Direction.NORTH).setValue(SHAPE, StairsShape.STRAIGHT)); - shape_cache_ = makeShapes(add, cut); - } - - @Override - @SuppressWarnings("deprecation") - public boolean useShapeForLightOcclusion(BlockState state) - { return false; } - - @OnlyIn(Dist.CLIENT) - @SuppressWarnings("deprecation") - public float getShadeBrightness(BlockState state, BlockGetter world, BlockPos pos) - { return 0.98f; } - - @Override - @SuppressWarnings("deprecation") - public int getLightBlock(BlockState state, BlockGetter world, BlockPos pos) - { return 1; } - - @Override - public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) - { return shape_cache_[state.getValue(HALF).ordinal()][state.getValue(HORIZONTAL_FACING).get3DDataValue()][state.getValue(SHAPE).ordinal()]; } - - @Override - public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) - { return getShape(state, world, pos, selectionContext); } - - @Override - protected void createBlockStateDefinition(StateDefinition.Builder builder) - { super.createBlockStateDefinition(builder); builder.add(SHAPE, HALF); } - - @Override - public FluidState getFluidState(BlockState state) - { return state.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : super.getFluidState(state); } - - @Override - public boolean isPathfindable(BlockState state, BlockGetter world, BlockPos pos, PathComputationType type) - { return false; } - - @Override - public BlockState getStateForPlacement(BlockPlaceContext context) - { - BlockPos pos = context.getClickedPos(); - Direction face = context.getClickedFace(); - BlockState state = defaultBlockState() - .setValue(HORIZONTAL_FACING, context.getHorizontalDirection()) - .setValue(HALF, (face == Direction.DOWN) ? Half.TOP : Half.BOTTOM) - .setValue(WATERLOGGED, context.getLevel().getFluidState(pos).getType()==Fluids.WATER); - return state.setValue(SHAPE, getStairsShapeProperty(state, context.getLevel(), pos)); - } - - @Override - public BlockState updateShape(BlockState state, Direction facing, BlockState facingState, LevelAccessor world, BlockPos pos, BlockPos facingPos) - { - if(state.getValue(WATERLOGGED)) world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world)); - return (facing.getAxis().isHorizontal()) ? (state.setValue(SHAPE, getStairsShapeProperty(state, world, pos))) : (super.updateShape(state, facing, facingState, world, pos, facingPos)); - } - - @Override - public BlockState rotate(BlockState state, Rotation rot) - { return state.setValue(HORIZONTAL_FACING, rot.rotate(state.getValue(HORIZONTAL_FACING))); } - - @Override - @SuppressWarnings("deprecation") - public BlockState mirror(BlockState state, Mirror where) - { - if((where==Mirror.LEFT_RIGHT) && (state.getValue(HORIZONTAL_FACING).getAxis()==Direction.Axis.Z)) { - return switch (state.getValue(SHAPE)) { - case INNER_LEFT -> state.rotate(Rotation.CLOCKWISE_180).setValue(SHAPE, StairsShape.INNER_RIGHT); - case INNER_RIGHT -> state.rotate(Rotation.CLOCKWISE_180).setValue(SHAPE, StairsShape.INNER_LEFT); - case OUTER_LEFT -> state.rotate(Rotation.CLOCKWISE_180).setValue(SHAPE, StairsShape.OUTER_RIGHT); - case OUTER_RIGHT -> state.rotate(Rotation.CLOCKWISE_180).setValue(SHAPE, StairsShape.OUTER_LEFT); - default -> state.rotate(Rotation.CLOCKWISE_180); - }; - } else if((where==Mirror.FRONT_BACK) && (state.getValue(HORIZONTAL_FACING).getAxis() == Direction.Axis.X)) { - return switch (state.getValue(SHAPE)) { - case INNER_LEFT -> state.rotate(Rotation.CLOCKWISE_180).setValue(SHAPE, StairsShape.INNER_LEFT); - case INNER_RIGHT -> state.rotate(Rotation.CLOCKWISE_180).setValue(SHAPE, StairsShape.INNER_RIGHT); - case OUTER_LEFT -> state.rotate(Rotation.CLOCKWISE_180).setValue(SHAPE, StairsShape.OUTER_RIGHT); - case OUTER_RIGHT -> state.rotate(Rotation.CLOCKWISE_180).setValue(SHAPE, StairsShape.OUTER_LEFT); - case STRAIGHT -> state.rotate(Rotation.CLOCKWISE_180); - }; + public EdRoofBlock(long config, BlockBehaviour.Properties properties) { + this(config, properties.dynamicShape(), Shapes.empty(), Shapes.empty()); } - return super.mirror(state, where); - } - private static boolean isRoofBlock(BlockState state) - { return (state.getBlock() instanceof EdRoofBlock); } + public EdRoofBlock(long config, BlockBehaviour.Properties properties, VoxelShape add, VoxelShape cut) { + super(config, properties, Auxiliaries.getPixeledAABB(0, 0, 0, 16, 8, 16)); + registerDefaultState(super.defaultBlockState().setValue(HORIZONTAL_FACING, Direction.NORTH).setValue(SHAPE, StairsShape.STRAIGHT)); + shape_cache_ = makeShapes(add, cut); + } - private static boolean isOtherRoofState(BlockState state, BlockGetter world, BlockPos pos, Direction facing) - { - BlockState st = world.getBlockState(pos.relative(facing)); - return (!isRoofBlock(st)) || (st.getValue(HORIZONTAL_FACING) != state.getValue(HORIZONTAL_FACING)); - } + private static boolean isRoofBlock(BlockState state) { + return (state.getBlock() instanceof EdRoofBlock); + } - private static VoxelShape[][][] makeShapes(VoxelShape add, VoxelShape cut) - { - VoxelShape[][][] shapes = new VoxelShape[2][6][5]; - for(int half_index=0; half_index EAST for stairs compliancy. + final StairsShape stairs = StairsShape.values()[stairs_shape_index]; + return switch (stairs) { + case STRAIGHT -> Auxiliaries.getUnionShape(Auxiliaries.getYRotatedAABB(straight, direction_index)); + case OUTER_LEFT -> Auxiliaries.getUnionShape(Auxiliaries.getYRotatedAABB(pyramid, direction_index - 1)); + case OUTER_RIGHT -> Auxiliaries.getUnionShape(Auxiliaries.getYRotatedAABB(pyramid, direction_index)); + case INNER_LEFT -> Auxiliaries.getUnionShape( + Auxiliaries.getYRotatedAABB(straight, direction_index), + Auxiliaries.getYRotatedAABB(straight, direction_index - 1) + ); + case INNER_RIGHT -> Auxiliaries.getUnionShape( + Auxiliaries.getYRotatedAABB(straight, direction_index), + Auxiliaries.getYRotatedAABB(straight, direction_index + 1) + ); + }; } - Direction direction = Direction.from3DDataValue(direction_index); - if((direction==Direction.UP) || (direction==Direction.DOWN)) return Shapes.block(); - direction_index = (direction.get2DDataValue()+1) & 0x03; // ref NORTH -> EAST for stairs compliancy. - final StairsShape stairs = StairsShape.values()[stairs_shape_index]; - return switch (stairs) { - case STRAIGHT -> Auxiliaries.getUnionShape(Auxiliaries.getYRotatedAABB(straight, direction_index)); - case OUTER_LEFT -> Auxiliaries.getUnionShape(Auxiliaries.getYRotatedAABB(pyramid, direction_index - 1)); - case OUTER_RIGHT -> Auxiliaries.getUnionShape(Auxiliaries.getYRotatedAABB(pyramid, direction_index)); - case INNER_LEFT -> Auxiliaries.getUnionShape( - Auxiliaries.getYRotatedAABB(straight, direction_index), - Auxiliaries.getYRotatedAABB(straight, direction_index - 1) - ); - case INNER_RIGHT -> Auxiliaries.getUnionShape( - Auxiliaries.getYRotatedAABB(straight, direction_index), - Auxiliaries.getYRotatedAABB(straight, direction_index + 1) - ); - }; - } - private static StairsShape getStairsShapeProperty(BlockState state, BlockGetter world, BlockPos pos) - { - Direction direction = state.getValue(HORIZONTAL_FACING); - { - BlockState ns = world.getBlockState(pos.relative(direction)); - if(isRoofBlock(ns) && (state.getValue(HALF) == ns.getValue(HALF))) { - Direction nf = ns.getValue(HORIZONTAL_FACING); - if(nf.getAxis() != state.getValue(HORIZONTAL_FACING).getAxis() && isOtherRoofState(state, world, pos, nf.getOpposite())) { - return (nf == direction.getCounterClockWise()) ? StairsShape.OUTER_LEFT : StairsShape.OUTER_RIGHT; + private static StairsShape getStairsShapeProperty(BlockState state, BlockGetter world, BlockPos pos) { + Direction direction = state.getValue(HORIZONTAL_FACING); + { + BlockState ns = world.getBlockState(pos.relative(direction)); + if (isRoofBlock(ns) && (state.getValue(HALF) == ns.getValue(HALF))) { + Direction nf = ns.getValue(HORIZONTAL_FACING); + if (nf.getAxis() != state.getValue(HORIZONTAL_FACING).getAxis() && isOtherRoofState(state, world, pos, nf.getOpposite())) { + return (nf == direction.getCounterClockWise()) ? StairsShape.OUTER_LEFT : StairsShape.OUTER_RIGHT; + } + } } - } - } - { - BlockState ns = world.getBlockState(pos.relative(direction.getOpposite())); - if(isRoofBlock(ns) && (state.getValue(HALF) == ns.getValue(HALF))) { - Direction nf = ns.getValue(HORIZONTAL_FACING); - if(nf.getAxis() != state.getValue(HORIZONTAL_FACING).getAxis() && isOtherRoofState(state, world, pos, nf)) { - return (nf == direction.getCounterClockWise()) ? StairsShape.INNER_LEFT : StairsShape.INNER_RIGHT; + { + BlockState ns = world.getBlockState(pos.relative(direction.getOpposite())); + if (isRoofBlock(ns) && (state.getValue(HALF) == ns.getValue(HALF))) { + Direction nf = ns.getValue(HORIZONTAL_FACING); + if (nf.getAxis() != state.getValue(HORIZONTAL_FACING).getAxis() && isOtherRoofState(state, world, pos, nf)) { + return (nf == direction.getCounterClockWise()) ? StairsShape.INNER_LEFT : StairsShape.INNER_RIGHT; + } + } } - } + return StairsShape.STRAIGHT; + } + + @Override + @SuppressWarnings("deprecation") + public boolean useShapeForLightOcclusion(BlockState state) { + return false; + } + + @OnlyIn(Dist.CLIENT) + @SuppressWarnings("deprecation") + public float getShadeBrightness(BlockState state, BlockGetter world, BlockPos pos) { + return 0.98f; + } + + @Override + @SuppressWarnings("deprecation") + public int getLightBlock(BlockState state, BlockGetter world, BlockPos pos) { + return 1; + } + + @Override + public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) { + return shape_cache_[state.getValue(HALF).ordinal()][state.getValue(HORIZONTAL_FACING).get3DDataValue()][state.getValue(SHAPE).ordinal()]; + } + + @Override + public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) { + return getShape(state, world, pos, selectionContext); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + super.createBlockStateDefinition(builder); + builder.add(SHAPE, HALF); + } + + @Override + public FluidState getFluidState(BlockState state) { + return state.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : super.getFluidState(state); + } + + @Override + public boolean isPathfindable(BlockState state, BlockGetter world, BlockPos pos, PathComputationType type) { + return false; + } + + @Override + public BlockState getStateForPlacement(BlockPlaceContext context) { + BlockPos pos = context.getClickedPos(); + Direction face = context.getClickedFace(); + BlockState state = defaultBlockState() + .setValue(HORIZONTAL_FACING, context.getHorizontalDirection()) + .setValue(HALF, (face == Direction.DOWN) ? Half.TOP : Half.BOTTOM) + .setValue(WATERLOGGED, context.getLevel().getFluidState(pos).getType() == Fluids.WATER); + return state.setValue(SHAPE, getStairsShapeProperty(state, context.getLevel(), pos)); + } + + @Override + public BlockState updateShape(BlockState state, Direction facing, BlockState facingState, LevelAccessor world, BlockPos pos, BlockPos facingPos) { + if (state.getValue(WATERLOGGED)) world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world)); + return (facing.getAxis().isHorizontal()) ? (state.setValue(SHAPE, getStairsShapeProperty(state, world, pos))) : (super.updateShape(state, facing, facingState, world, pos, facingPos)); + } + + @Override + public BlockState rotate(BlockState state, Rotation rot) { + return state.setValue(HORIZONTAL_FACING, rot.rotate(state.getValue(HORIZONTAL_FACING))); + } + + @Override + @SuppressWarnings("deprecation") + public BlockState mirror(BlockState state, Mirror where) { + if ((where == Mirror.LEFT_RIGHT) && (state.getValue(HORIZONTAL_FACING).getAxis() == Direction.Axis.Z)) { + return switch (state.getValue(SHAPE)) { + case INNER_LEFT -> state.rotate(Rotation.CLOCKWISE_180).setValue(SHAPE, StairsShape.INNER_RIGHT); + case INNER_RIGHT -> state.rotate(Rotation.CLOCKWISE_180).setValue(SHAPE, StairsShape.INNER_LEFT); + case OUTER_LEFT -> state.rotate(Rotation.CLOCKWISE_180).setValue(SHAPE, StairsShape.OUTER_RIGHT); + case OUTER_RIGHT -> state.rotate(Rotation.CLOCKWISE_180).setValue(SHAPE, StairsShape.OUTER_LEFT); + default -> state.rotate(Rotation.CLOCKWISE_180); + }; + } else if ((where == Mirror.FRONT_BACK) && (state.getValue(HORIZONTAL_FACING).getAxis() == Direction.Axis.X)) { + return switch (state.getValue(SHAPE)) { + case INNER_LEFT -> state.rotate(Rotation.CLOCKWISE_180).setValue(SHAPE, StairsShape.INNER_LEFT); + case INNER_RIGHT -> state.rotate(Rotation.CLOCKWISE_180).setValue(SHAPE, StairsShape.INNER_RIGHT); + case OUTER_LEFT -> state.rotate(Rotation.CLOCKWISE_180).setValue(SHAPE, StairsShape.OUTER_RIGHT); + case OUTER_RIGHT -> state.rotate(Rotation.CLOCKWISE_180).setValue(SHAPE, StairsShape.OUTER_LEFT); + case STRAIGHT -> state.rotate(Rotation.CLOCKWISE_180); + }; + } + return super.mirror(state, where); } - return StairsShape.STRAIGHT; - } } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdSolarPanel.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdSolarPanel.java index bbc3f52..a5e10cd 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdSolarPanel.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdSolarPanel.java @@ -8,6 +8,9 @@ */ package dev.zontreck.engineerdecor.blocks; +import dev.zontreck.engineerdecor.ModConfig; +import dev.zontreck.engineerdecor.ModContent; +import dev.zontreck.libzontreck.edlibmc.*; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; @@ -16,8 +19,8 @@ import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.LightLayer; +import net.minecraft.world.level.SignalGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockBehaviour; @@ -29,199 +32,193 @@ import net.minecraft.world.phys.BlockHitResult; import net.minecraftforge.common.capabilities.ForgeCapabilities; import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.energy.IEnergyStorage; -import dev.zontreck.engineerdecor.ModConfig; -import dev.zontreck.engineerdecor.ModContent; -import dev.zontreck.engineerdecor.libmc.StandardBlocks; -import dev.zontreck.engineerdecor.libmc.StandardEntityBlocks; -import dev.zontreck.engineerdecor.libmc.Auxiliaries; -import dev.zontreck.engineerdecor.libmc.Overlay; -import dev.zontreck.engineerdecor.libmc.RfEnergy; import javax.annotation.Nullable; import java.util.ArrayList; import java.util.List; -public class EdSolarPanel -{ - public static final int DEFAULT_PEAK_POWER = 40; - private static int peak_power_per_tick_ = DEFAULT_PEAK_POWER; - private static int max_power_storage_ = 64000; - private static int max_feed_power = 4096; - private static int feeding_threshold = max_power_storage_/5; - private static int balancing_threshold = max_power_storage_/10; +public class EdSolarPanel { + public static final int DEFAULT_PEAK_POWER = 40; + private static int peak_power_per_tick_ = DEFAULT_PEAK_POWER; + private static int max_power_storage_ = 64000; + private static int max_feed_power = 4096; + private static int feeding_threshold = max_power_storage_ / 5; + private static int balancing_threshold = max_power_storage_ / 10; - public static void on_config(int peak_power_per_tick, int battery_capacity, int max_feed_in_power) - { - final int t = SolarPanelTileEntity.TICK_INTERVAL; - peak_power_per_tick_ = Mth.clamp(peak_power_per_tick, 12, 8192); - feeding_threshold = Math.max(max_power_storage_/5, 1000); - balancing_threshold = Math.max(max_power_storage_/10, 1000); - max_power_storage_ = battery_capacity; - max_feed_power = max_feed_in_power * t; - ModConfig.log("Config small solar panel: Peak production:" + peak_power_per_tick_ + "/t, capacity:" + max_power_storage_ + "rf, max-feed:" + (max_feed_power/t) + "rf/t"); - } - - //-------------------------------------------------------------------------------------------------------------------- - // Block - //-------------------------------------------------------------------------------------------------------------------- - - public static class SolarPanelBlock extends StandardBlocks.Cutout implements StandardEntityBlocks.IStandardEntityBlock - { - public static final IntegerProperty EXPOSITION = IntegerProperty.create("exposition", 0, 4); - - public SolarPanelBlock(long config, BlockBehaviour.Properties builder, final AABB[] unrotatedAABB) - { - super(config, builder, unrotatedAABB); - registerDefaultState(super.defaultBlockState().setValue(EXPOSITION, 1)); + public static void on_config(int peak_power_per_tick, int battery_capacity, int max_feed_in_power) { + final int t = SolarPanelTileEntity.TICK_INTERVAL; + peak_power_per_tick_ = Mth.clamp(peak_power_per_tick, 12, 8192); + feeding_threshold = Math.max(max_power_storage_ / 5, 1000); + balancing_threshold = Math.max(max_power_storage_ / 10, 1000); + max_power_storage_ = battery_capacity; + max_feed_power = max_feed_in_power * t; + ModConfig.log("Config small solar panel: Peak production:" + peak_power_per_tick_ + "/t, capacity:" + max_power_storage_ + "rf, max-feed:" + (max_feed_power / t) + "rf/t"); } - @Override - public boolean isBlockEntityTicking(Level world, BlockState state) - { return true; } + //-------------------------------------------------------------------------------------------------------------------- + // Block + //-------------------------------------------------------------------------------------------------------------------- - @Override - protected void createBlockStateDefinition(StateDefinition.Builder builder) - { super.createBlockStateDefinition(builder); builder.add(EXPOSITION); } + public static class SolarPanelBlock extends StandardBlocks.Cutout implements StandardEntityBlocks.IStandardEntityBlock { + public static final IntegerProperty EXPOSITION = IntegerProperty.create("exposition", 0, 4); - @Override - @SuppressWarnings("deprecation") - public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) - { - if(world.isClientSide()) return InteractionResult.SUCCESS; - BlockEntity te = world.getBlockEntity(pos); - if(te instanceof SolarPanelTileEntity) ((SolarPanelTileEntity)te).state_message(player); - return InteractionResult.CONSUME; - } - - @Override - public boolean shouldCheckWeakPower(BlockState state, LevelReader world, BlockPos pos, Direction side) - { return false; } - } - - //-------------------------------------------------------------------------------------------------------------------- - // Tile entity - //-------------------------------------------------------------------------------------------------------------------- - - public static class SolarPanelTileEntity extends StandardEntityBlocks.StandardBlockEntity - { - public static final int TICK_INTERVAL = 4; - public static final int ACCUMULATION_INTERVAL = 8; - private static final Direction[] transfer_directions_ = {Direction.DOWN, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.NORTH }; - - private int tick_timer_ = 0; - private int recalc_timer_ = 0; - private int current_production_ = 0; - private int current_feedin_ = 0; - private boolean output_enabled_ = false; - - private final RfEnergy.Battery battery_ = new RfEnergy.Battery(max_power_storage_, 0, 1024); - private final LazyOptional energy_handler_ = battery_.createEnergyHandler(); - - //------------------------------------------------------------------------------------------------------------------ - - public SolarPanelTileEntity(BlockPos pos, BlockState state) - { super(ModContent.getBlockEntityTypeOfBlock(state.getBlock()), pos, state); } - - public void readnbt(CompoundTag nbt, boolean update_packet) - { battery_.load(nbt); } - - protected void writenbt(CompoundTag nbt, boolean update_packet) - { battery_.save(nbt); } - - public void state_message(Player player) - { - String soc = Integer.toString(Mth.clamp((battery_.getEnergyStored()*100/max_power_storage_),0,100)); - Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.small_solar_panel.status", soc, max_power_storage_, current_production_, current_feedin_)); - } - - // ICapabilityProvider --------------------------------------------------------------------- - - @Override - public LazyOptional getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable Direction facing) - { - if(capability == ForgeCapabilities.ENERGY) return energy_handler_.cast(); - return super.getCapability(capability, facing); - } - - // BlockEntity ------------------------------------------------------------------------------ - - @Override - public void load(CompoundTag nbt) - { super.load(nbt); readnbt(nbt, false); } - - @Override - protected void saveAdditional(CompoundTag nbt) - { super.saveAdditional(nbt); writenbt(nbt, false); } - - @Override - public void setRemoved() - { - super.setRemoved(); - energy_handler_.invalidate(); - } - - @Override - public void tick() - { - if((level.isClientSide) || (--tick_timer_ > 0)) return; - tick_timer_ = TICK_INTERVAL; - BlockState state = level.getBlockState(worldPosition); - if(!(state.getBlock() instanceof SolarPanelBlock)) return; - current_feedin_ = 0; - final List adjacent_panels = new ArrayList<>(); - if(output_enabled_) { - for(int i=0; (i (max_power_storage_/10)) ? max_feed_power : Math.max(current_production_*2, (peak_power_per_tick_/4)); - final int fed = es.receiveEnergy(Math.min(battery_.getEnergyStored(), feed_power * TICK_INTERVAL), false); - battery_.draw(fed); - current_feedin_ += fed; + public SolarPanelBlock(long config, BlockBehaviour.Properties builder, final AABB[] unrotatedAABB) { + super(config, builder, unrotatedAABB); + registerDefaultState(super.defaultBlockState().setValue(EXPOSITION, 1)); } - } - current_feedin_ /= TICK_INTERVAL; - if((current_feedin_ <= 0) && ((battery_.getEnergyStored() >= balancing_threshold) || (current_production_ <= 0))) { - for(SolarPanelTileEntity panel: adjacent_panels) { - if(panel.battery_.getEnergyStored() >= (battery_.getEnergyStored()-balancing_threshold)) continue; - panel.battery_.setEnergyStored(panel.battery_.getEnergyStored() + balancing_threshold); - battery_.setEnergyStored(battery_.getEnergyStored() - balancing_threshold); - if(battery_.getEnergyStored() < balancing_threshold) break; + + @Override + public boolean isBlockEntityTicking(Level world, BlockState state) { + return true; + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + super.createBlockStateDefinition(builder); + builder.add(EXPOSITION); + } + + @Override + @SuppressWarnings("deprecation") + public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { + if (world.isClientSide()) return InteractionResult.SUCCESS; + BlockEntity te = world.getBlockEntity(pos); + if (te instanceof SolarPanelTileEntity) ((SolarPanelTileEntity) te).state_message(player); + return InteractionResult.CONSUME; + } + + @Override + public boolean shouldCheckWeakPower(BlockState state, SignalGetter level, BlockPos pos, Direction side) { + return false; + } + } + + //-------------------------------------------------------------------------------------------------------------------- + // Tile entity + //-------------------------------------------------------------------------------------------------------------------- + + public static class SolarPanelTileEntity extends StandardEntityBlocks.StandardBlockEntity { + public static final int TICK_INTERVAL = 4; + public static final int ACCUMULATION_INTERVAL = 8; + private static final Direction[] transfer_directions_ = {Direction.DOWN, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.NORTH}; + private final RfEnergy.Battery battery_ = new RfEnergy.Battery(max_power_storage_, 0, 1024); + private final LazyOptional energy_handler_ = battery_.createEnergyHandler(); + private int tick_timer_ = 0; + private int recalc_timer_ = 0; + private int current_production_ = 0; + private int current_feedin_ = 0; + private boolean output_enabled_ = false; + + //------------------------------------------------------------------------------------------------------------------ + + public SolarPanelTileEntity(BlockPos pos, BlockState state) { + super(ModContent.getBlockEntityTypeOfBlock(state.getBlock()), pos, state); + } + + public void readnbt(CompoundTag nbt, boolean update_packet) { + battery_.load(nbt); + } + + protected void writenbt(CompoundTag nbt, boolean update_packet) { + battery_.save(nbt); + } + + public void state_message(Player player) { + String soc = Integer.toString(Mth.clamp((battery_.getEnergyStored() * 100 / max_power_storage_), 0, 100)); + Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.small_solar_panel.status", soc, max_power_storage_, current_production_, current_feedin_)); + } + + // ICapabilityProvider --------------------------------------------------------------------- + + @Override + public LazyOptional getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable Direction facing) { + if (capability == ForgeCapabilities.ENERGY) return energy_handler_.cast(); + return super.getCapability(capability, facing); + } + + // BlockEntity ------------------------------------------------------------------------------ + + @Override + public void load(CompoundTag nbt) { + super.load(nbt); + readnbt(nbt, false); + } + + @Override + protected void saveAdditional(CompoundTag nbt) { + super.saveAdditional(nbt); + writenbt(nbt, false); + } + + @Override + public void setRemoved() { + super.setRemoved(); + energy_handler_.invalidate(); + } + + @Override + public void tick() { + if ((level.isClientSide) || (--tick_timer_ > 0)) return; + tick_timer_ = TICK_INTERVAL; + BlockState state = level.getBlockState(worldPosition); + if (!(state.getBlock() instanceof SolarPanelBlock)) return; + current_feedin_ = 0; + final List adjacent_panels = new ArrayList<>(); + if (output_enabled_) { + for (int i = 0; (i < transfer_directions_.length) && (!battery_.isEmpty()); ++i) { + final Direction f = transfer_directions_[i]; + BlockEntity te = level.getBlockEntity(worldPosition.relative(f)); + if (te == null) continue; + IEnergyStorage es = te.getCapability(ForgeCapabilities.ENERGY, f.getOpposite()).orElse(null); + if (es == null) continue; + if (!es.canReceive()) { + if (!(te instanceof SolarPanelTileEntity)) continue; + adjacent_panels.add((SolarPanelTileEntity) te); + continue; + } + final int feed_power = (battery_.getEnergyStored() > (max_power_storage_ / 10)) ? max_feed_power : Math.max(current_production_ * 2, (peak_power_per_tick_ / 4)); + final int fed = es.receiveEnergy(Math.min(battery_.getEnergyStored(), feed_power * TICK_INTERVAL), false); + battery_.draw(fed); + current_feedin_ += fed; + } + } + current_feedin_ /= TICK_INTERVAL; + if ((current_feedin_ <= 0) && ((battery_.getEnergyStored() >= balancing_threshold) || (current_production_ <= 0))) { + for (SolarPanelTileEntity panel : adjacent_panels) { + if (panel.battery_.getEnergyStored() >= (battery_.getEnergyStored() - balancing_threshold)) + continue; + panel.battery_.setEnergyStored(panel.battery_.getEnergyStored() + balancing_threshold); + battery_.setEnergyStored(battery_.getEnergyStored() - balancing_threshold); + if (battery_.getEnergyStored() < balancing_threshold) break; + } + } + if (!level.canSeeSkyFromBelowWater(worldPosition)) { + tick_timer_ = TICK_INTERVAL * 10; + current_production_ = 0; + if ((!battery_.isEmpty())) output_enabled_ = true; + if (state.getValue((SolarPanelBlock.EXPOSITION)) != 2) + level.setBlockAndUpdate(worldPosition, state.setValue(SolarPanelBlock.EXPOSITION, 2)); + return; + } + if (battery_.isEmpty()) output_enabled_ = false; + if (--recalc_timer_ > 0) return; + recalc_timer_ = ACCUMULATION_INTERVAL + ((int) (Math.random() + .5)); + int theta = ((((int) (level.getSunAngle(1f) * (180.0 / Math.PI))) + 90) % 360); + int e = 2; + if (theta > 340) e = 2; + else if (theta < 45) e = 0; + else if (theta < 80) e = 1; + else if (theta < 100) e = 2; + else if (theta < 135) e = 3; + else if (theta < 190) e = 4; + BlockState nstate = state.setValue(SolarPanelBlock.EXPOSITION, e); + if (nstate != state) level.setBlock(worldPosition, nstate, 1 | 2); + final double eff = (1.0 - ((level.getRainLevel(1f) * 0.6) + (level.getThunderLevel(1f) * 0.3))); + final double ll = ((double) (level.getLightEngine().getLayerListener(LightLayer.SKY).getLightValue(getBlockPos()))) / 15; + final double rf = Math.sin((Math.PI / 2) * Math.sqrt(((double) (((theta < 0) || (theta > 180)) ? (0) : ((theta > 90) ? (180 - theta) : (theta)))) / 90)); + current_production_ = (int) (Math.min(rf * rf * eff * ll, 1) * peak_power_per_tick_); + battery_.setEnergyStored(Math.min(battery_.getEnergyStored() + (current_production_ * (TICK_INTERVAL * ACCUMULATION_INTERVAL)), max_power_storage_)); + if (battery_.getEnergyStored() >= (feeding_threshold)) output_enabled_ = true; } - } - if(!level.canSeeSkyFromBelowWater(worldPosition)) { - tick_timer_ = TICK_INTERVAL * 10; - current_production_ = 0; - if((!battery_.isEmpty())) output_enabled_ = true; - if(state.getValue((SolarPanelBlock.EXPOSITION))!=2) level.setBlockAndUpdate(worldPosition, state.setValue(SolarPanelBlock.EXPOSITION, 2)); - return; - } - if(battery_.isEmpty()) output_enabled_ = false; - if(--recalc_timer_ > 0) return; - recalc_timer_ = ACCUMULATION_INTERVAL + ((int)(Math.random()+.5)); - int theta = ((((int)(level.getSunAngle(1f) * (180.0/Math.PI)))+90) % 360); - int e = 2; - if(theta > 340) e = 2; - else if(theta < 45) e = 0; - else if(theta < 80) e = 1; - else if(theta < 100) e = 2; - else if(theta < 135) e = 3; - else if(theta < 190) e = 4; - BlockState nstate = state.setValue(SolarPanelBlock.EXPOSITION, e); - if(nstate != state) level.setBlock(worldPosition, nstate, 1|2); - final double eff = (1.0-((level.getRainLevel(1f)*0.6)+(level.getThunderLevel(1f)*0.3))); - final double ll = ((double)(level.getLightEngine().getLayerListener(LightLayer.SKY).getLightValue(getBlockPos())))/15; - final double rf = Math.sin((Math.PI/2) * Math.sqrt(((double)(((theta<0)||(theta>180))?(0):((theta>90)?(180-theta):(theta))))/90)); - current_production_ = (int)(Math.min(rf*rf*eff*ll, 1) * peak_power_per_tick_); - battery_.setEnergyStored(Math.min(battery_.getEnergyStored() + (current_production_*(TICK_INTERVAL*ACCUMULATION_INTERVAL)), max_power_storage_)); - if(battery_.getEnergyStored() >= (feeding_threshold)) output_enabled_ = true; } - } } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdStraightPoleBlock.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdStraightPoleBlock.java index 1a902b6..ea75544 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdStraightPoleBlock.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdStraightPoleBlock.java @@ -8,6 +8,8 @@ */ package dev.zontreck.engineerdecor.blocks; +import dev.zontreck.libzontreck.edlibmc.Inventories; +import dev.zontreck.libzontreck.edlibmc.StandardBlocks; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.sounds.SoundEvents; @@ -26,63 +28,61 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.Vec3; -import dev.zontreck.engineerdecor.libmc.StandardBlocks; -import dev.zontreck.engineerdecor.libmc.Inventories; import javax.annotation.Nullable; import java.util.Arrays; -public class EdStraightPoleBlock extends StandardBlocks.DirectedWaterLoggable -{ - private final EdStraightPoleBlock default_pole; +public class EdStraightPoleBlock extends StandardBlocks.DirectedWaterLoggable { + private final EdStraightPoleBlock default_pole; - public EdStraightPoleBlock(long config, BlockBehaviour.Properties builder, final AABB unrotatedAABB, @Nullable EdStraightPoleBlock defaultPole) - { super(config, builder, unrotatedAABB); default_pole=(defaultPole==null) ? (this) : (defaultPole); } - - @Override - @Nullable - public BlockState getStateForPlacement(BlockPlaceContext context) - { - Direction facing = context.getClickedFace(); - BlockState state = super.getStateForPlacement(context).setValue(FACING, facing); - if((config & StandardBlocks.CFG_FLIP_PLACEMENT_IF_SAME) != 0) { - Level world = context.getLevel(); - BlockPos pos = context.getClickedPos(); - if(world.getBlockState(pos.relative(facing.getOpposite())).getBlock() instanceof EdStraightPoleBlock) { - state = state.setValue(FACING, state.getValue(FACING).getOpposite()); - } + public EdStraightPoleBlock(long config, BlockBehaviour.Properties builder, final AABB unrotatedAABB, @Nullable EdStraightPoleBlock defaultPole) { + super(config, builder, unrotatedAABB); + default_pole = (defaultPole == null) ? (this) : (defaultPole); } - return state; - } - @Override - @SuppressWarnings("deprecation") - public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) - { - if((hit.getDirection().getAxis() == state.getValue(FACING).getAxis())) return InteractionResult.PASS; - final ItemStack held_stack = player.getItemInHand(hand); - if((held_stack.isEmpty()) || (!(held_stack.getItem() instanceof BlockItem))) return InteractionResult.PASS; - if(!(((BlockItem)(held_stack.getItem())).getBlock() instanceof EdStraightPoleBlock)) return InteractionResult.PASS; - if(held_stack.getItem() != default_pole.asItem()) return InteractionResult.sidedSuccess(world.isClientSide()); - final Block held_block = ((BlockItem)(held_stack.getItem())).getBlock(); - final Direction block_direction = state.getValue(FACING); - final Vec3 block_vec = Vec3.atLowerCornerOf(state.getValue(FACING).getNormal()); - final double colinearity = 1.0-block_vec.cross(player.getLookAngle()).length(); - final Direction placement_direction = Arrays.stream(Direction.orderedByNearest(player)).filter(d->d.getAxis()==block_direction.getAxis()).findFirst().orElse(Direction.NORTH); - final BlockPos adjacent_pos = pos.relative(placement_direction); - final BlockState adjacent = world.getBlockState(adjacent_pos); - final BlockPlaceContext ctx = new DirectionalPlaceContext(world, adjacent_pos, placement_direction, player.getItemInHand(hand), placement_direction.getOpposite()); - if(!adjacent.canBeReplaced(ctx)) return InteractionResult.sidedSuccess(world.isClientSide()); - final BlockState new_state = held_block.getStateForPlacement(ctx); - if(new_state == null) return InteractionResult.FAIL; - if(!world.setBlock(adjacent_pos, new_state, 1|2)) return InteractionResult.FAIL; - world.playSound(player, pos, SoundEvents.METAL_PLACE, SoundSource.BLOCKS, 1f, 1f); - if(!player.isCreative()) { - held_stack.shrink(1); - Inventories.setItemInPlayerHand(player, hand, held_stack); + @Override + @Nullable + public BlockState getStateForPlacement(BlockPlaceContext context) { + Direction facing = context.getClickedFace(); + BlockState state = super.getStateForPlacement(context).setValue(FACING, facing); + if ((config & StandardBlocks.CFG_FLIP_PLACEMENT_IF_SAME) != 0) { + Level world = context.getLevel(); + BlockPos pos = context.getClickedPos(); + if (world.getBlockState(pos.relative(facing.getOpposite())).getBlock() instanceof EdStraightPoleBlock) { + state = state.setValue(FACING, state.getValue(FACING).getOpposite()); + } + } + return state; + } + + @Override + @SuppressWarnings("deprecation") + public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { + if ((hit.getDirection().getAxis() == state.getValue(FACING).getAxis())) return InteractionResult.PASS; + final ItemStack held_stack = player.getItemInHand(hand); + if ((held_stack.isEmpty()) || (!(held_stack.getItem() instanceof BlockItem))) return InteractionResult.PASS; + if (!(((BlockItem) (held_stack.getItem())).getBlock() instanceof EdStraightPoleBlock)) + return InteractionResult.PASS; + if (held_stack.getItem() != default_pole.asItem()) return InteractionResult.sidedSuccess(world.isClientSide()); + final Block held_block = ((BlockItem) (held_stack.getItem())).getBlock(); + final Direction block_direction = state.getValue(FACING); + final Vec3 block_vec = Vec3.atLowerCornerOf(state.getValue(FACING).getNormal()); + final double colinearity = 1.0 - block_vec.cross(player.getLookAngle()).length(); + final Direction placement_direction = Arrays.stream(Direction.orderedByNearest(player)).filter(d -> d.getAxis() == block_direction.getAxis()).findFirst().orElse(Direction.NORTH); + final BlockPos adjacent_pos = pos.relative(placement_direction); + final BlockState adjacent = world.getBlockState(adjacent_pos); + final BlockPlaceContext ctx = new DirectionalPlaceContext(world, adjacent_pos, placement_direction, player.getItemInHand(hand), placement_direction.getOpposite()); + if (!adjacent.canBeReplaced(ctx)) return InteractionResult.sidedSuccess(world.isClientSide()); + final BlockState new_state = held_block.getStateForPlacement(ctx); + if (new_state == null) return InteractionResult.FAIL; + if (!world.setBlock(adjacent_pos, new_state, 1 | 2)) return InteractionResult.FAIL; + world.playSound(player, pos, SoundEvents.METAL_PLACE, SoundSource.BLOCKS, 1f, 1f); + if (!player.isCreative()) { + held_stack.shrink(1); + Inventories.setItemInPlayerHand(player, hand, held_stack); + } + return InteractionResult.sidedSuccess(world.isClientSide()); } - return InteractionResult.sidedSuccess(world.isClientSide()); - } } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdTestBlock.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdTestBlock.java index 05cf6cb..870654e 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdTestBlock.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdTestBlock.java @@ -8,7 +8,8 @@ */ package dev.zontreck.engineerdecor.blocks; -import dev.zontreck.engineerdecor.libmc.*; +import dev.zontreck.engineerdecor.ModContent; +import dev.zontreck.libzontreck.edlibmc.*; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; @@ -20,7 +21,7 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.SignalGetter; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockBehaviour; @@ -38,9 +39,6 @@ import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.IFluidHandler; import net.minecraftforge.items.IItemHandler; import net.minecraftforge.registries.ForgeRegistries; -import dev.zontreck.engineerdecor.ModContent; -import wile.engineersdecor.libmc.*; - import javax.annotation.Nullable; import java.util.ArrayList; @@ -48,283 +46,279 @@ import java.util.Collections; import java.util.List; -public class EdTestBlock -{ - //-------------------------------------------------------------------------------------------------------------------- - // Block - //-------------------------------------------------------------------------------------------------------------------- +public class EdTestBlock { + //-------------------------------------------------------------------------------------------------------------------- + // Block + //-------------------------------------------------------------------------------------------------------------------- - public static class TestBlock extends StandardBlocks.Directed implements StandardEntityBlocks.IStandardEntityBlock, Auxiliaries.IExperimentalFeature - { - public TestBlock(long config, BlockBehaviour.Properties builder, final AABB unrotatedAABB) - { super(config, builder, unrotatedAABB); } - - @Override - public boolean isBlockEntityTicking(Level world, BlockState state) - { return true; } - - @Override - public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) - { return Shapes.block(); } - - @Override - @SuppressWarnings("deprecation") // public boolean canConnectRedstone(BlockState state, BlockGetter world, BlockPos pos, @Nullable Direction side) { return true; } - public boolean isSignalSource(BlockState p_60571_) - { return true; } - - @Override - public boolean shouldCheckWeakPower(BlockState state, LevelReader world, BlockPos pos, Direction side) - { return false; } - - @Override - public boolean hasDynamicDropList() - { return true; } - - @Override - public List dropList(BlockState state, Level world, BlockEntity te, boolean explosion) - { return Collections.singletonList(new ItemStack(this)); } - - @Override - @SuppressWarnings("deprecation") - public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) - { - if(world.isClientSide()) return InteractionResult.SUCCESS; - BlockEntity te = world.getBlockEntity(pos); - if(!(te instanceof TestTileEntity)) return InteractionResult.FAIL; - return ((TestTileEntity)te).activated(player, hand, hit) ? InteractionResult.CONSUME : InteractionResult.PASS; - } - } - - //-------------------------------------------------------------------------------------------------------------------- - // Tile entity - //-------------------------------------------------------------------------------------------------------------------- - - public static class TestTileEntity extends StandardEntityBlocks.StandardBlockEntity - { - private final RfEnergy.Battery battery_; - private final LazyOptional energy_handler_; - private final Fluidics.Tank tank_; - private final LazyOptional fluid_handler_; - private final Inventories.StorageInventory inventory_; - private final LazyOptional item_handler_; - private int tick_timer = 0; - private int rf_fed_avg = 0; - private int rf_fed_total = 0; - private int rf_fed_acc = 0; - private int rf_received_avg = 0; - private int rf_received_total = 0; - private int liq_filled_avg = 0; - private int liq_filled_total = 0; - private int liq_filled_acc = 0; - private int liq_received_avg = 0; - private int liq_received_total = 0; - private int items_inserted_total = 0; - private int items_received_total = 0; - private int rf_feed_setting = 4096; - private FluidStack liq_fill_stack = new FluidStack(Fluids.WATER, 128); - private ItemStack insertion_item = ItemStack.EMPTY; - private Direction block_facing = Direction.NORTH; - private boolean paused = false; - - - public TestTileEntity(BlockPos pos, BlockState state) - { - super(ModContent.getBlockEntityTypeOfBlock(state.getBlock()), pos, state); - battery_ = new RfEnergy.Battery((int)1e9, (int)1e9, 0, 0); - energy_handler_ = battery_.createEnergyHandler(); - tank_ = new Fluidics.Tank((int)1e9); - fluid_handler_ = tank_.createFluidHandler(); - inventory_ = new Inventories.StorageInventory(this, 1); - item_handler_ = Inventories.MappedItemHandler.createInsertionHandler(inventory_); - } - - @Override - public void load(CompoundTag nbt) - { - super.load(nbt); - tank_.load(nbt); - battery_.load(nbt); - rf_fed_avg = nbt.getInt("rf_fed_avg"); - rf_fed_total = nbt.getInt("rf_fed_total"); - rf_fed_acc = nbt.getInt("rf_fed_acc"); - rf_received_avg = nbt.getInt("rf_received_avg"); - rf_received_total = nbt.getInt("rf_received_total"); - liq_filled_avg = nbt.getInt("liq_filled_avg"); - liq_filled_total = nbt.getInt("liq_filled_total"); - liq_filled_acc = nbt.getInt("liq_filled_acc"); - liq_received_avg = nbt.getInt("liq_received_avg"); - liq_received_total = nbt.getInt("liq_received_total"); - rf_feed_setting = nbt.getInt("rf_feed_setting"); - items_received_total = nbt.getInt("items_received_total"); - items_inserted_total = nbt.getInt("items_inserted_total"); - if(nbt.contains("liq_fill_stack")) liq_fill_stack = FluidStack.loadFluidStackFromNBT(nbt.getCompound("liq_fill_stack")); - if(nbt.contains("insertion_item")) insertion_item = ItemStack.of(nbt.getCompound("insertion_item")); - } - - @Override - protected void saveAdditional(CompoundTag nbt) - { - super.saveAdditional(nbt); - tank_.save(nbt); - battery_.save(nbt); - nbt.putInt("rf_fed_avg", rf_fed_avg); - nbt.putInt("rf_fed_total", rf_fed_total); - nbt.putInt("rf_fed_acc", rf_fed_acc); - nbt.putInt("rf_received_avg", rf_received_avg); - nbt.putInt("rf_received_total", rf_received_total); - nbt.putInt("liq_filled_avg", liq_filled_avg); - nbt.putInt("liq_filled_total", liq_filled_total); - nbt.putInt("liq_filled_acc", liq_filled_acc); - nbt.putInt("liq_received_avg", liq_received_avg); - nbt.putInt("liq_received_total", liq_received_total); - nbt.putInt("rf_feed_setting", rf_feed_setting); - nbt.putInt("items_received_total", items_received_total); - nbt.putInt("items_inserted_total", items_inserted_total); - if(!liq_fill_stack.isEmpty()) nbt.put("liq_fill_stack", liq_fill_stack.writeToNBT(new CompoundTag())); - if(!insertion_item.isEmpty()) nbt.put("insertion_item", insertion_item.save(new CompoundTag())); - } - - private FluidStack getFillFluid(ItemStack stack) - { - // intentionally not item fluid handler, only specific items. - if(stack.getItem() == Items.WATER_BUCKET) return new FluidStack(Fluids.WATER, 1000); - if(stack.getItem() == Items.LAVA_BUCKET) return new FluidStack(Fluids.LAVA, 1000); - return FluidStack.EMPTY; - } - - private ItemStack getRandomItemstack() - { - final int n = (int)Math.floor(Math.random() * ForgeRegistries.ITEMS.getValues().size()); - ItemStack stack = new ItemStack(ForgeRegistries.ITEMS.getValues().stream().skip(n).findAny().orElse(Items.COBBLESTONE)); - stack.setCount((int)Math.floor(Math.random() * stack.getMaxStackSize())); - return stack; - } - - public boolean activated(Player player, InteractionHand hand, BlockHitResult hit) - { - final ItemStack held = player.getItemInHand(hand); - if(held.isEmpty()) { - ArrayList msgs = new ArrayList<>(); - if(rf_fed_avg > 0) msgs.add("-" + rf_fed_avg + "rf/t"); - if(rf_fed_total > 0) msgs.add("-" + rf_fed_total + "rf"); - if(rf_received_avg > 0) msgs.add("+" + rf_received_avg + "rf/t"); - if(rf_received_total > 0) msgs.add("+" + rf_received_total + "rf"); - if(liq_filled_avg > 0) msgs.add("-" + liq_filled_avg + "mb/t"); - if(liq_filled_total > 0) msgs.add("-" + liq_filled_total + "mb"); - if(liq_received_avg > 0) msgs.add("+" + liq_received_avg + "mb/t"); - if(liq_received_total > 0) msgs.add("+" + liq_received_total + "mb"); - if(items_received_total > 0) msgs.add("+" + items_received_total + "items"); - if(items_inserted_total > 0) msgs.add("-" + items_inserted_total + "items"); - if(msgs.isEmpty()) msgs.add("Nothing transferred yet."); - Overlay.show(player, Component.literal(String.join(" | ", msgs)), 1000); - return true; - } else if(paused) { - if(!getFillFluid(held).isEmpty()) { - FluidStack fs = getFillFluid(held); - if(liq_fill_stack.isEmpty() || !liq_fill_stack.isFluidEqual(fs)) { - fs.setAmount(128); - liq_fill_stack = fs; - } else { - int amount = liq_fill_stack.getAmount() * 2; - if(amount > 4096) amount = 16; - liq_fill_stack.setAmount(amount); - } - if(liq_fill_stack.isEmpty()) { - Overlay.show(player, Component.literal("Fluid fill: none"), 1000); - } else { - Overlay.show(player, Component.literal("Fluid fill: " + liq_fill_stack.getAmount() + "mb/t of " + Auxiliaries.getResourceLocation(liq_fill_stack.getFluid())), 1000); - } - } else if(held.getItem() == Items.REDSTONE) { - rf_feed_setting = (rf_feed_setting<<1) & 0x00fffff0; - if(rf_feed_setting == 0) rf_feed_setting = 0x10; - Overlay.show(player, Component.literal("RF feed rate: " + rf_feed_setting + "rf/t"), 1000); - } else { - BlockState adjacent_state = level.getBlockState(worldPosition.relative(block_facing)); - if(adjacent_state.getBlock()==Blocks.HOPPER || adjacent_state.getBlock()==ModContent.getBlock("factory_hopper")) { - insertion_item = held.copy(); - Overlay.show(player, Component.literal("Insertion item: " + (insertion_item.getItem()==Items.LEVER ? "random" : insertion_item.toString()) + "/s"), 1000); - } + public static class TestBlock extends StandardBlocks.Directed implements StandardEntityBlocks.IStandardEntityBlock, Auxiliaries.IExperimentalFeature { + public TestBlock(long config, BlockBehaviour.Properties builder, final AABB unrotatedAABB) { + super(config, builder, unrotatedAABB); + } + + @Override + public boolean isBlockEntityTicking(Level world, BlockState state) { + return true; + } + + @Override + public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) { + return Shapes.block(); + } + + @Override + @SuppressWarnings("deprecation") + // public boolean canConnectRedstone(BlockState state, BlockGetter world, BlockPos pos, @Nullable Direction side) { return true; } + public boolean isSignalSource(BlockState p_60571_) { + return true; + } + + @Override + public boolean shouldCheckWeakPower(BlockState state, SignalGetter level, BlockPos pos, Direction side) { + return false; + } + + @Override + public boolean hasDynamicDropList() { + return true; + } + + @Override + public List dropList(BlockState state, Level world, BlockEntity te, boolean explosion) { + return Collections.singletonList(new ItemStack(this)); + } + + @Override + @SuppressWarnings("deprecation") + public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { + if (world.isClientSide()) return InteractionResult.SUCCESS; + BlockEntity te = world.getBlockEntity(pos); + if (!(te instanceof TestTileEntity)) return InteractionResult.FAIL; + return ((TestTileEntity) te).activated(player, hand, hit) ? InteractionResult.CONSUME : InteractionResult.PASS; } - return true; - } else { - return false; - } } - @Override - public void setRemoved() - { - super.setRemoved(); - energy_handler_.invalidate(); - fluid_handler_.invalidate(); - item_handler_.invalidate(); - } + //-------------------------------------------------------------------------------------------------------------------- + // Tile entity + //-------------------------------------------------------------------------------------------------------------------- - @Override - public LazyOptional getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable Direction facing) - { - if((!paused) && (facing != block_facing)) { - if(capability == ForgeCapabilities.FLUID_HANDLER) return fluid_handler_.cast(); - if(capability == ForgeCapabilities.ENERGY) return energy_handler_.cast(); - if(capability == ForgeCapabilities.ITEM_HANDLER) return item_handler_.cast(); - } - return super.getCapability(capability, facing); - } + public static class TestTileEntity extends StandardEntityBlocks.StandardBlockEntity { + private final RfEnergy.Battery battery_; + private final LazyOptional energy_handler_; + private final Fluidics.Tank tank_; + private final LazyOptional fluid_handler_; + private final Inventories.StorageInventory inventory_; + private final LazyOptional item_handler_; + private int tick_timer = 0; + private int rf_fed_avg = 0; + private int rf_fed_total = 0; + private int rf_fed_acc = 0; + private int rf_received_avg = 0; + private int rf_received_total = 0; + private int liq_filled_avg = 0; + private int liq_filled_total = 0; + private int liq_filled_acc = 0; + private int liq_received_avg = 0; + private int liq_received_total = 0; + private int items_inserted_total = 0; + private int items_received_total = 0; + private int rf_feed_setting = 4096; + private FluidStack liq_fill_stack = new FluidStack(Fluids.WATER, 128); + private ItemStack insertion_item = ItemStack.EMPTY; + private Direction block_facing = Direction.NORTH; + private boolean paused = false; - @Override - public void tick() - { - if(level.isClientSide()) return; - block_facing = getBlockState().getValue(TestBlock.FACING); - paused = level.hasNeighborSignal(getBlockPos()); - if(!paused) { - boolean dirty = false; - { - int p = RfEnergy.feed(getLevel(), getBlockPos().relative(block_facing), block_facing.getOpposite(), rf_feed_setting); - rf_fed_acc += p; - dirty |= p>0; - } - if(!liq_fill_stack.isEmpty()) { - int f = Fluidics.fill(getLevel(), getBlockPos().relative(block_facing), block_facing.getOpposite(), liq_fill_stack); - liq_filled_acc += f; - dirty |= f>0; - } - if(!inventory_.isEmpty()) { - int i = inventory_.getItem(0).getCount(); - items_received_total += i; - inventory_.clearContent(); - dirty |= i>0; - } - if((tick_timer == 1) && (!insertion_item.isEmpty())) { - BlockState adjacent_state = level.getBlockState(worldPosition.relative(block_facing)); - ItemStack stack = (insertion_item.getItem()==Items.LEVER) ? getRandomItemstack() : insertion_item.copy(); - if(adjacent_state.getBlock()==Blocks.HOPPER || adjacent_state.getBlock()==ModContent.getBlock("factory_hopper")) { - ItemStack remaining = Inventories.insert(getLevel(), getBlockPos().relative(block_facing), block_facing.getOpposite(), stack, false); - int n = stack.getCount() - remaining.getCount(); - items_inserted_total += n; - dirty |= n>0; - } - } - if(dirty) { - setChanged(); - } - } - if(--tick_timer <= 0) { - tick_timer = 20; - rf_fed_avg = rf_fed_acc/20; - rf_fed_total += rf_fed_acc; - rf_fed_acc = 0; - rf_received_avg = battery_.getEnergyStored()/20; - rf_received_total += battery_.getEnergyStored(); - battery_.clear(); - liq_received_avg = tank_.getFluidAmount(); - liq_received_total += tank_.getFluidAmount(); - tank_.clear(); - liq_filled_avg = (liq_fill_stack.isEmpty()) ? 0 : (liq_filled_acc/20); - liq_filled_total = (liq_fill_stack.isEmpty()) ? 0 : (liq_filled_total+liq_filled_acc); - liq_filled_acc = 0; - } - } - } + public TestTileEntity(BlockPos pos, BlockState state) { + super(ModContent.getBlockEntityTypeOfBlock(state.getBlock()), pos, state); + battery_ = new RfEnergy.Battery((int) 1e9, (int) 1e9, 0, 0); + energy_handler_ = battery_.createEnergyHandler(); + tank_ = new Fluidics.Tank((int) 1e9); + fluid_handler_ = tank_.createFluidHandler(); + inventory_ = new Inventories.StorageInventory(this, 1); + item_handler_ = Inventories.MappedItemHandler.createInsertionHandler(inventory_); + } + + @Override + public void load(CompoundTag nbt) { + super.load(nbt); + tank_.load(nbt); + battery_.load(nbt); + rf_fed_avg = nbt.getInt("rf_fed_avg"); + rf_fed_total = nbt.getInt("rf_fed_total"); + rf_fed_acc = nbt.getInt("rf_fed_acc"); + rf_received_avg = nbt.getInt("rf_received_avg"); + rf_received_total = nbt.getInt("rf_received_total"); + liq_filled_avg = nbt.getInt("liq_filled_avg"); + liq_filled_total = nbt.getInt("liq_filled_total"); + liq_filled_acc = nbt.getInt("liq_filled_acc"); + liq_received_avg = nbt.getInt("liq_received_avg"); + liq_received_total = nbt.getInt("liq_received_total"); + rf_feed_setting = nbt.getInt("rf_feed_setting"); + items_received_total = nbt.getInt("items_received_total"); + items_inserted_total = nbt.getInt("items_inserted_total"); + if (nbt.contains("liq_fill_stack")) + liq_fill_stack = FluidStack.loadFluidStackFromNBT(nbt.getCompound("liq_fill_stack")); + if (nbt.contains("insertion_item")) insertion_item = ItemStack.of(nbt.getCompound("insertion_item")); + } + + @Override + protected void saveAdditional(CompoundTag nbt) { + super.saveAdditional(nbt); + tank_.save(nbt); + battery_.save(nbt); + nbt.putInt("rf_fed_avg", rf_fed_avg); + nbt.putInt("rf_fed_total", rf_fed_total); + nbt.putInt("rf_fed_acc", rf_fed_acc); + nbt.putInt("rf_received_avg", rf_received_avg); + nbt.putInt("rf_received_total", rf_received_total); + nbt.putInt("liq_filled_avg", liq_filled_avg); + nbt.putInt("liq_filled_total", liq_filled_total); + nbt.putInt("liq_filled_acc", liq_filled_acc); + nbt.putInt("liq_received_avg", liq_received_avg); + nbt.putInt("liq_received_total", liq_received_total); + nbt.putInt("rf_feed_setting", rf_feed_setting); + nbt.putInt("items_received_total", items_received_total); + nbt.putInt("items_inserted_total", items_inserted_total); + if (!liq_fill_stack.isEmpty()) nbt.put("liq_fill_stack", liq_fill_stack.writeToNBT(new CompoundTag())); + if (!insertion_item.isEmpty()) nbt.put("insertion_item", insertion_item.save(new CompoundTag())); + } + + private FluidStack getFillFluid(ItemStack stack) { + // intentionally not item fluid handler, only specific items. + if (stack.getItem() == Items.WATER_BUCKET) return new FluidStack(Fluids.WATER, 1000); + if (stack.getItem() == Items.LAVA_BUCKET) return new FluidStack(Fluids.LAVA, 1000); + return FluidStack.EMPTY; + } + + private ItemStack getRandomItemstack() { + final int n = (int) Math.floor(Math.random() * ForgeRegistries.ITEMS.getValues().size()); + ItemStack stack = new ItemStack(ForgeRegistries.ITEMS.getValues().stream().skip(n).findAny().orElse(Items.COBBLESTONE)); + stack.setCount((int) Math.floor(Math.random() * stack.getMaxStackSize())); + return stack; + } + + public boolean activated(Player player, InteractionHand hand, BlockHitResult hit) { + final ItemStack held = player.getItemInHand(hand); + if (held.isEmpty()) { + ArrayList msgs = new ArrayList<>(); + if (rf_fed_avg > 0) msgs.add("-" + rf_fed_avg + "rf/t"); + if (rf_fed_total > 0) msgs.add("-" + rf_fed_total + "rf"); + if (rf_received_avg > 0) msgs.add("+" + rf_received_avg + "rf/t"); + if (rf_received_total > 0) msgs.add("+" + rf_received_total + "rf"); + if (liq_filled_avg > 0) msgs.add("-" + liq_filled_avg + "mb/t"); + if (liq_filled_total > 0) msgs.add("-" + liq_filled_total + "mb"); + if (liq_received_avg > 0) msgs.add("+" + liq_received_avg + "mb/t"); + if (liq_received_total > 0) msgs.add("+" + liq_received_total + "mb"); + if (items_received_total > 0) msgs.add("+" + items_received_total + "items"); + if (items_inserted_total > 0) msgs.add("-" + items_inserted_total + "items"); + if (msgs.isEmpty()) msgs.add("Nothing transferred yet."); + Overlay.show(player, Component.literal(String.join(" | ", msgs)), 1000); + return true; + } else if (paused) { + if (!getFillFluid(held).isEmpty()) { + FluidStack fs = getFillFluid(held); + if (liq_fill_stack.isEmpty() || !liq_fill_stack.isFluidEqual(fs)) { + fs.setAmount(128); + liq_fill_stack = fs; + } else { + int amount = liq_fill_stack.getAmount() * 2; + if (amount > 4096) amount = 16; + liq_fill_stack.setAmount(amount); + } + if (liq_fill_stack.isEmpty()) { + Overlay.show(player, Component.literal("Fluid fill: none"), 1000); + } else { + Overlay.show(player, Component.literal("Fluid fill: " + liq_fill_stack.getAmount() + "mb/t of " + Auxiliaries.getResourceLocation(liq_fill_stack.getFluid())), 1000); + } + } else if (held.getItem() == Items.REDSTONE) { + rf_feed_setting = (rf_feed_setting << 1) & 0x00fffff0; + if (rf_feed_setting == 0) rf_feed_setting = 0x10; + Overlay.show(player, Component.literal("RF feed rate: " + rf_feed_setting + "rf/t"), 1000); + } else { + BlockState adjacent_state = level.getBlockState(worldPosition.relative(block_facing)); + if (adjacent_state.getBlock() == Blocks.HOPPER || adjacent_state.getBlock() == ModContent.getBlock("factory_hopper")) { + insertion_item = held.copy(); + Overlay.show(player, Component.literal("Insertion item: " + (insertion_item.getItem() == Items.LEVER ? "random" : insertion_item.toString()) + "/s"), 1000); + } + } + return true; + } else { + return false; + } + } + + @Override + public void setRemoved() { + super.setRemoved(); + energy_handler_.invalidate(); + fluid_handler_.invalidate(); + item_handler_.invalidate(); + } + + @Override + public LazyOptional getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable Direction facing) { + if ((!paused) && (facing != block_facing)) { + if (capability == ForgeCapabilities.FLUID_HANDLER) return fluid_handler_.cast(); + if (capability == ForgeCapabilities.ENERGY) return energy_handler_.cast(); + if (capability == ForgeCapabilities.ITEM_HANDLER) return item_handler_.cast(); + } + return super.getCapability(capability, facing); + } + + @Override + public void tick() { + if (level.isClientSide()) return; + block_facing = getBlockState().getValue(TestBlock.FACING); + paused = level.hasNeighborSignal(getBlockPos()); + if (!paused) { + boolean dirty = false; + { + int p = RfEnergy.feed(getLevel(), getBlockPos().relative(block_facing), block_facing.getOpposite(), rf_feed_setting); + rf_fed_acc += p; + dirty |= p > 0; + } + if (!liq_fill_stack.isEmpty()) { + int f = Fluidics.fill(getLevel(), getBlockPos().relative(block_facing), block_facing.getOpposite(), liq_fill_stack); + liq_filled_acc += f; + dirty |= f > 0; + } + if (!inventory_.isEmpty()) { + int i = inventory_.getItem(0).getCount(); + items_received_total += i; + inventory_.clearContent(); + dirty |= i > 0; + } + if ((tick_timer == 1) && (!insertion_item.isEmpty())) { + BlockState adjacent_state = level.getBlockState(worldPosition.relative(block_facing)); + ItemStack stack = (insertion_item.getItem() == Items.LEVER) ? getRandomItemstack() : insertion_item.copy(); + if (adjacent_state.getBlock() == Blocks.HOPPER || adjacent_state.getBlock() == ModContent.getBlock("factory_hopper")) { + ItemStack remaining = Inventories.insert(getLevel(), getBlockPos().relative(block_facing), block_facing.getOpposite(), stack, false); + int n = stack.getCount() - remaining.getCount(); + items_inserted_total += n; + dirty |= n > 0; + } + } + if (dirty) { + setChanged(); + } + } + if (--tick_timer <= 0) { + tick_timer = 20; + rf_fed_avg = rf_fed_acc / 20; + rf_fed_total += rf_fed_acc; + rf_fed_acc = 0; + rf_received_avg = battery_.getEnergyStored() / 20; + rf_received_total += battery_.getEnergyStored(); + battery_.clear(); + liq_received_avg = tank_.getFluidAmount(); + liq_received_total += tank_.getFluidAmount(); + tank_.clear(); + liq_filled_avg = (liq_fill_stack.isEmpty()) ? 0 : (liq_filled_acc / 20); + liq_filled_total = (liq_fill_stack.isEmpty()) ? 0 : (liq_filled_total + liq_filled_acc); + liq_filled_acc = 0; + } + } + + } } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdTreeCutter.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdTreeCutter.java index 40c5307..ccce973 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdTreeCutter.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdTreeCutter.java @@ -8,7 +8,13 @@ */ package dev.zontreck.engineerdecor.blocks; +import dev.zontreck.engineerdecor.ModConfig; +import dev.zontreck.engineerdecor.ModContent; import dev.zontreck.engineerdecor.detail.TreeCutting; +import dev.zontreck.libzontreck.edlibmc.Auxiliaries; +import dev.zontreck.libzontreck.edlibmc.Overlay; +import dev.zontreck.libzontreck.edlibmc.StandardBlocks; +import dev.zontreck.libzontreck.edlibmc.StandardEntityBlocks; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.particles.ParticleTypes; @@ -21,7 +27,7 @@ import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.SignalGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockBehaviour; @@ -35,234 +41,235 @@ import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.common.capabilities.ForgeCapabilities; import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.energy.IEnergyStorage; -import dev.zontreck.engineerdecor.ModConfig; -import dev.zontreck.engineerdecor.ModContent; -import dev.zontreck.engineerdecor.libmc.StandardBlocks; -import dev.zontreck.engineerdecor.libmc.StandardEntityBlocks; -import dev.zontreck.engineerdecor.libmc.Auxiliaries; -import dev.zontreck.engineerdecor.libmc.Overlay; import javax.annotation.Nullable; import java.util.Random; +public class EdTreeCutter { + public static void on_config(int boost_energy_per_tick, int cutting_time_seconds, boolean power_required) { + TreeCutterTileEntity.on_config(boost_energy_per_tick, cutting_time_seconds, power_required); + } -public class EdTreeCutter -{ - public static void on_config(int boost_energy_per_tick, int cutting_time_seconds, boolean power_required) - { TreeCutterTileEntity.on_config(boost_energy_per_tick, cutting_time_seconds,power_required); } + //-------------------------------------------------------------------------------------------------------------------- + // Block + //-------------------------------------------------------------------------------------------------------------------- - //-------------------------------------------------------------------------------------------------------------------- - // Block - //-------------------------------------------------------------------------------------------------------------------- + public static class TreeCutterBlock extends StandardBlocks.Horizontal implements StandardEntityBlocks.IStandardEntityBlock { + public static final BooleanProperty ACTIVE = BooleanProperty.create("active"); - public static class TreeCutterBlock extends StandardBlocks.Horizontal implements StandardEntityBlocks.IStandardEntityBlock - { - public static final BooleanProperty ACTIVE = BooleanProperty.create("active"); - - public TreeCutterBlock(long config, BlockBehaviour.Properties builder, final AABB[] unrotatedAABB) - { super(config, builder, unrotatedAABB); } - - @Override - public boolean isBlockEntityTicking(Level world, BlockState state) - { return true; } - - @Override - protected void createBlockStateDefinition(StateDefinition.Builder builder) - { super.createBlockStateDefinition(builder); builder.add(ACTIVE); } - - @Override - @Nullable - public BlockState getStateForPlacement(BlockPlaceContext context) - { return super.getStateForPlacement(context).setValue(ACTIVE, false); } - - @OnlyIn(Dist.CLIENT) - public void animateTick(BlockState state, Level world, BlockPos pos, Random rnd) - { - if((state.getBlock()!=this) || (!state.getValue(ACTIVE))) return; - // Sound - /*if((world.getGameTime() & 0x1) == 0)*/ { - world.playLocalSound(pos.getX(), pos.getY(), pos.getZ(), SoundEvents.WOOD_HIT, SoundSource.BLOCKS, 0.1f, 1.0f, false); - } - // Particles - { - final double rv = rnd.nextDouble(); - if(rv < 0.8) { - final double x=0.5+pos.getX(), y=0.5+pos.getY(), z=0.5+pos.getZ(); - final double xc=0.52, xr=rnd.nextDouble()*0.4-0.2, yr=(y-0.3+rnd.nextDouble()*0.2); - switch(state.getValue(HORIZONTAL_FACING)) { - case WEST -> world.addParticle(ParticleTypes.SMOKE, x - xc, yr, z + xr, 0.0, 0.0, 0.0); - case EAST -> world.addParticle(ParticleTypes.SMOKE, x + xc, yr, z + xr, 0.0, 0.0, 0.0); - case NORTH -> world.addParticle(ParticleTypes.SMOKE, x + xr, yr, z - xc, 0.0, 0.0, 0.0); - default -> world.addParticle(ParticleTypes.SMOKE, x + xr, yr, z + xc, 0.0, 0.0, 0.0); - } + public TreeCutterBlock(long config, BlockBehaviour.Properties builder, final AABB[] unrotatedAABB) { + super(config, builder, unrotatedAABB); + } + + @Override + public boolean isBlockEntityTicking(Level world, BlockState state) { + return true; + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + super.createBlockStateDefinition(builder); + builder.add(ACTIVE); + } + + @Override + @Nullable + public BlockState getStateForPlacement(BlockPlaceContext context) { + return super.getStateForPlacement(context).setValue(ACTIVE, false); + } + + @OnlyIn(Dist.CLIENT) + public void animateTick(BlockState state, Level world, BlockPos pos, Random rnd) { + if ((state.getBlock() != this) || (!state.getValue(ACTIVE))) return; + // Sound + /*if((world.getGameTime() & 0x1) == 0)*/ + { + world.playLocalSound(pos.getX(), pos.getY(), pos.getZ(), SoundEvents.WOOD_HIT, SoundSource.BLOCKS, 0.1f, 1.0f, false); + } + // Particles + { + final double rv = rnd.nextDouble(); + if (rv < 0.8) { + final double x = 0.5 + pos.getX(), y = 0.5 + pos.getY(), z = 0.5 + pos.getZ(); + final double xc = 0.52, xr = rnd.nextDouble() * 0.4 - 0.2, yr = (y - 0.3 + rnd.nextDouble() * 0.2); + switch (state.getValue(HORIZONTAL_FACING)) { + case WEST -> world.addParticle(ParticleTypes.SMOKE, x - xc, yr, z + xr, 0.0, 0.0, 0.0); + case EAST -> world.addParticle(ParticleTypes.SMOKE, x + xc, yr, z + xr, 0.0, 0.0, 0.0); + case NORTH -> world.addParticle(ParticleTypes.SMOKE, x + xr, yr, z - xc, 0.0, 0.0, 0.0); + default -> world.addParticle(ParticleTypes.SMOKE, x + xr, yr, z + xc, 0.0, 0.0, 0.0); + } + } + } + } + + @Override + @SuppressWarnings("deprecation") + public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { + if (world.isClientSide()) return InteractionResult.SUCCESS; + BlockEntity te = world.getBlockEntity(pos); + if (te instanceof TreeCutterTileEntity) ((TreeCutterTileEntity) te).state_message(player); + return InteractionResult.CONSUME; + } + + @Override + public boolean shouldCheckWeakPower(BlockState state, SignalGetter level, BlockPos pos, Direction side) { + return false; } - } } - @Override - @SuppressWarnings("deprecation") - public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) - { - if(world.isClientSide()) return InteractionResult.SUCCESS; - BlockEntity te = world.getBlockEntity(pos); - if(te instanceof TreeCutterTileEntity) ((TreeCutterTileEntity)te).state_message(player); - return InteractionResult.CONSUME; + //-------------------------------------------------------------------------------------------------------------------- + // Tile entity + //-------------------------------------------------------------------------------------------------------------------- + + public static class TreeCutterTileEntity extends StandardEntityBlocks.StandardBlockEntity implements IEnergyStorage { + public static final int IDLE_TICK_INTERVAL = 40; + public static final int TICK_INTERVAL = 5; + public static final int BOOST_FACTOR = 6; + public static final int DEFAULT_BOOST_ENERGY = 64; + public static final int DEFAULT_CUTTING_TIME_NEEDED = 60; // 60 secs, so that people don't come to the bright idea to carry one with them. + private static int boost_energy_consumption = DEFAULT_BOOST_ENERGY; + private static int energy_max = DEFAULT_BOOST_ENERGY * 20; + private static int cutting_time_needed = 20 * DEFAULT_CUTTING_TIME_NEEDED; + private static boolean requires_power = false; + protected LazyOptional energy_handler_ = LazyOptional.of(() -> this); + private int tick_timer_; + private int active_timer_; + private int proc_time_elapsed_; + private int energy_; + + public TreeCutterTileEntity(BlockPos pos, BlockState state) { + super(ModContent.getBlockEntityTypeOfBlock(state.getBlock()), pos, state); + } + + public static void on_config(int boost_energy_per_tick, int cutting_time_seconds, boolean power_required) { + boost_energy_consumption = TICK_INTERVAL * Mth.clamp(boost_energy_per_tick, 4, 4096); + energy_max = Math.max(boost_energy_consumption * 10, 10000); + cutting_time_needed = 20 * Mth.clamp(cutting_time_seconds, 10, 240); + requires_power = power_required; + ModConfig.log("Config tree cutter: energy consumption:" + (boost_energy_consumption / TICK_INTERVAL) + "rf/t" + (requires_power ? " (power required for operation) " : "") + ", cutting time:" + cutting_time_needed + "t."); + } + + public void readnbt(CompoundTag nbt) { + energy_ = nbt.getInt("energy"); + } + + private void writenbt(CompoundTag nbt) { + nbt.putInt("energy", energy_); + } + + // BlockEntity ------------------------------------------------------------------------------ + + public void state_message(Player player) { + String progress = "0"; + if ((active_timer_ > 0) && (cutting_time_needed > 0) && (active_timer_ > 0)) { + progress = Integer.toString((int) Mth.clamp((((double) proc_time_elapsed_) / ((double) cutting_time_needed) * 100), 0, 100)); + } + String soc = Integer.toString(Mth.clamp((energy_ * 100 / energy_max), 0, 100)); + Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.small_tree_cutter.status", soc, energy_max, progress, (cutting_time_needed / 20))); + } + + @Override + public void load(CompoundTag nbt) { + super.load(nbt); + readnbt(nbt); + } + + @Override + protected void saveAdditional(CompoundTag nbt) { + super.saveAdditional(nbt); + writenbt(nbt); + } + + // IEnergyStorage ---------------------------------------------------------------------------- + + @Override + public void setRemoved() { + super.setRemoved(); + energy_handler_.invalidate(); + } + + @Override + public boolean canExtract() { + return false; + } + + @Override + public boolean canReceive() { + return true; + } + + @Override + public int getMaxEnergyStored() { + return boost_energy_consumption * 2; + } + + @Override + public int getEnergyStored() { + return energy_; + } + + @Override + public int extractEnergy(int maxExtract, boolean simulate) { + return 0; + } + + @Override + public int receiveEnergy(int maxReceive, boolean simulate) { + maxReceive = Mth.clamp(maxReceive, 0, Math.max((energy_max) - energy_, 0)); + if (!simulate) energy_ += maxReceive; + return maxReceive; + } + + // Capability export ---------------------------------------------------------------------------- + + @Override + public LazyOptional getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable Direction facing) { + if (capability == ForgeCapabilities.ENERGY) return energy_handler_.cast(); + return super.getCapability(capability, facing); + } + + // ITickable ------------------------------------------------------------------------------------ + + @Override + public void tick() { + if (--tick_timer_ > 0) return; + tick_timer_ = TICK_INTERVAL; + final BlockState device_state = level.getBlockState(worldPosition); + if (!(device_state.getBlock() instanceof TreeCutterBlock)) return; + final BlockPos tree_pos = worldPosition.relative(device_state.getValue(TreeCutterBlock.HORIZONTAL_FACING)); + final BlockState tree_state = level.getBlockState(tree_pos); + if (!TreeCutting.canChop(level, tree_state, tree_pos) || (level.hasNeighborSignal(worldPosition))) { + if (device_state.getValue(TreeCutterBlock.ACTIVE)) + level.setBlock(worldPosition, device_state.setValue(TreeCutterBlock.ACTIVE, false), 1 | 2); + proc_time_elapsed_ = 0; + active_timer_ = 0; + tick_timer_ = IDLE_TICK_INTERVAL; + return; + } + proc_time_elapsed_ += TICK_INTERVAL; + if (energy_ >= boost_energy_consumption) { + energy_ -= boost_energy_consumption; + proc_time_elapsed_ += TICK_INTERVAL * BOOST_FACTOR; + active_timer_ = 2; + } else if (!requires_power) { + active_timer_ = 1024; + } else if (active_timer_ > 0) { + --active_timer_; + } + boolean active = (active_timer_ > 0); + if (requires_power && !active) { + proc_time_elapsed_ = Math.max(0, proc_time_elapsed_ - 2 * TICK_INTERVAL); + } + if (proc_time_elapsed_ >= cutting_time_needed) { + proc_time_elapsed_ = 0; + TreeCutting.chopTree(level, tree_state, tree_pos, 512, false); + level.playSound(null, worldPosition.getX(), worldPosition.getY(), worldPosition.getZ(), SoundEvents.WOOD_BREAK, SoundSource.BLOCKS, 1.0f, 1.0f); + active = false; + } + if (device_state.getValue(TreeCutterBlock.ACTIVE) != active) { + level.setBlock(worldPosition, device_state.setValue(TreeCutterBlock.ACTIVE, active), 1 | 2); + } + } } - - @Override - public boolean shouldCheckWeakPower(BlockState state, LevelReader world, BlockPos pos, Direction side) - { return false; } - } - - //-------------------------------------------------------------------------------------------------------------------- - // Tile entity - //-------------------------------------------------------------------------------------------------------------------- - - public static class TreeCutterTileEntity extends StandardEntityBlocks.StandardBlockEntity implements IEnergyStorage - { - public static final int IDLE_TICK_INTERVAL = 40; - public static final int TICK_INTERVAL = 5; - public static final int BOOST_FACTOR = 6; - public static final int DEFAULT_BOOST_ENERGY = 64; - public static final int DEFAULT_CUTTING_TIME_NEEDED = 60; // 60 secs, so that people don't come to the bright idea to carry one with them. - private static int boost_energy_consumption = DEFAULT_BOOST_ENERGY; - private static int energy_max = DEFAULT_BOOST_ENERGY * 20; - private static int cutting_time_needed = 20 * DEFAULT_CUTTING_TIME_NEEDED; - private static boolean requires_power = false; - - private int tick_timer_; - private int active_timer_; - private int proc_time_elapsed_; - private int energy_; - - public static void on_config(int boost_energy_per_tick, int cutting_time_seconds, boolean power_required) - { - boost_energy_consumption = TICK_INTERVAL * Mth.clamp(boost_energy_per_tick, 4, 4096); - energy_max = Math.max(boost_energy_consumption * 10, 10000); - cutting_time_needed = 20 * Mth.clamp(cutting_time_seconds, 10, 240); - requires_power = power_required; - ModConfig.log("Config tree cutter: energy consumption:" + (boost_energy_consumption/TICK_INTERVAL) + "rf/t" + (requires_power?" (power required for operation) ":"") + ", cutting time:" + cutting_time_needed + "t." ); - } - - public TreeCutterTileEntity(BlockPos pos, BlockState state) - { super(ModContent.getBlockEntityTypeOfBlock(state.getBlock()), pos, state); } - - public void readnbt(CompoundTag nbt) - { energy_ = nbt.getInt("energy"); } - - private void writenbt(CompoundTag nbt) - { nbt.putInt("energy", energy_); } - - public void state_message(Player player) - { - String progress = "0"; - if((active_timer_ > 0) && (cutting_time_needed > 0) && (active_timer_ > 0)) { - progress = Integer.toString((int)Mth.clamp((((double)proc_time_elapsed_) / ((double)cutting_time_needed) * 100), 0, 100)); - } - String soc = Integer.toString(Mth.clamp((energy_*100/energy_max),0,100)); - Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.small_tree_cutter.status", soc, energy_max, progress, (cutting_time_needed/20))); - } - - // BlockEntity ------------------------------------------------------------------------------ - - @Override - public void load(CompoundTag nbt) - { super.load(nbt); readnbt(nbt); } - - @Override - protected void saveAdditional(CompoundTag nbt) - { super.saveAdditional(nbt); writenbt(nbt); } - - @Override - public void setRemoved() - { - super.setRemoved(); - energy_handler_.invalidate(); - } - - // IEnergyStorage ---------------------------------------------------------------------------- - - protected LazyOptional energy_handler_ = LazyOptional.of(() -> this); - - @Override - public boolean canExtract() - { return false; } - - @Override - public boolean canReceive() - { return true; } - - @Override - public int getMaxEnergyStored() - { return boost_energy_consumption*2; } - - @Override - public int getEnergyStored() - { return energy_; } - - @Override - public int extractEnergy(int maxExtract, boolean simulate) - { return 0; } - - @Override - public int receiveEnergy(int maxReceive, boolean simulate) - { - maxReceive = Mth.clamp(maxReceive, 0, Math.max((energy_max) - energy_, 0)); - if(!simulate) energy_ += maxReceive; - return maxReceive; - } - - // Capability export ---------------------------------------------------------------------------- - - @Override - public LazyOptional getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable Direction facing) - { - if(capability == ForgeCapabilities.ENERGY) return energy_handler_.cast(); - return super.getCapability(capability, facing); - } - - // ITickable ------------------------------------------------------------------------------------ - - @Override - public void tick() - { - if(--tick_timer_ > 0) return; - tick_timer_ = TICK_INTERVAL; - final BlockState device_state = level.getBlockState(worldPosition); - if(!(device_state.getBlock() instanceof TreeCutterBlock)) return; - final BlockPos tree_pos = worldPosition.relative(device_state.getValue(TreeCutterBlock.HORIZONTAL_FACING)); - final BlockState tree_state = level.getBlockState(tree_pos); - if(!TreeCutting.canChop(level, tree_state, tree_pos) || (level.hasNeighborSignal(worldPosition))) { - if(device_state.getValue(TreeCutterBlock.ACTIVE)) level.setBlock(worldPosition, device_state.setValue(TreeCutterBlock.ACTIVE, false), 1|2); - proc_time_elapsed_ = 0; - active_timer_ = 0; - tick_timer_ = IDLE_TICK_INTERVAL; - return; - } - proc_time_elapsed_ += TICK_INTERVAL; - if(energy_ >= boost_energy_consumption) { - energy_ -= boost_energy_consumption; - proc_time_elapsed_ += TICK_INTERVAL*BOOST_FACTOR; - active_timer_ = 2; - } else if(!requires_power) { - active_timer_ = 1024; - } else if(active_timer_ > 0) { - --active_timer_; - } - boolean active = (active_timer_ > 0); - if(requires_power && !active) { - proc_time_elapsed_ = Math.max(0, proc_time_elapsed_ - 2*TICK_INTERVAL); - } - if(proc_time_elapsed_ >= cutting_time_needed) { - proc_time_elapsed_ = 0; - TreeCutting.chopTree(level, tree_state, tree_pos, 512, false); - level.playSound(null, worldPosition.getX(), worldPosition.getY(), worldPosition.getZ(), SoundEvents.WOOD_BREAK, SoundSource.BLOCKS, 1.0f, 1.0f); - active = false; - } - if(device_state.getValue(TreeCutterBlock.ACTIVE) != active) { - level.setBlock(worldPosition, device_state.setValue(TreeCutterBlock.ACTIVE, active), 1|2); - } - } - } } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdWallBlock.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdWallBlock.java index e267eef..ce0b545 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdWallBlock.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdWallBlock.java @@ -8,6 +8,7 @@ */ package dev.zontreck.engineerdecor.blocks; +import dev.zontreck.libzontreck.edlibmc.VariantWallBlock; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.level.LevelReader; @@ -15,21 +16,19 @@ import net.minecraft.world.level.block.IronBarsBlock; import net.minecraft.world.level.block.StainedGlassPaneBlock; import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; -import dev.zontreck.engineerdecor.libmc.VariantWallBlock; -public class EdWallBlock extends VariantWallBlock -{ - public EdWallBlock(long config, BlockBehaviour.Properties builder) - { super(config, builder); } +public class EdWallBlock extends VariantWallBlock { + public EdWallBlock(long config, BlockBehaviour.Properties builder) { + super(config, builder); + } - protected boolean attachesTo(BlockState facingState, LevelReader world, BlockPos facingPos, Direction side) - { - if(facingState==null) return false; - if(super.attachesTo(facingState, world, facingPos, side)) return true; - if(facingState.getBlock() instanceof EdWindowBlock) return true; - if(facingState.getBlock() instanceof IronBarsBlock) return true; - if(facingState.getBlock() instanceof StainedGlassPaneBlock) return true; - return false; - } + protected boolean attachesTo(BlockState facingState, LevelReader world, BlockPos facingPos, Direction side) { + if (facingState == null) return false; + if (super.attachesTo(facingState, world, facingPos, side)) return true; + if (facingState.getBlock() instanceof EdWindowBlock) return true; + if (facingState.getBlock() instanceof IronBarsBlock) return true; + if (facingState.getBlock() instanceof StainedGlassPaneBlock) return true; + return false; + } } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdWasteIncinerator.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdWasteIncinerator.java index 632272c..023e838 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdWasteIncinerator.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdWasteIncinerator.java @@ -8,7 +8,9 @@ */ package dev.zontreck.engineerdecor.blocks; -import dev.zontreck.engineerdecor.libmc.RfEnergy; +import dev.zontreck.engineerdecor.ModConfig; +import dev.zontreck.engineerdecor.ModContent; +import dev.zontreck.libzontreck.edlibmc.*; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.particles.ParticleTypes; @@ -26,7 +28,7 @@ import net.minecraft.world.inventory.*; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.SignalGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockBehaviour; @@ -41,440 +43,450 @@ import net.minecraftforge.common.capabilities.ForgeCapabilities; import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.energy.IEnergyStorage; import net.minecraftforge.items.IItemHandler; -import dev.zontreck.engineerdecor.ModConfig; -import dev.zontreck.engineerdecor.ModContent; -import dev.zontreck.engineerdecor.libmc.StandardBlocks; -import dev.zontreck.engineerdecor.libmc.StandardEntityBlocks; -import dev.zontreck.engineerdecor.libmc.Auxiliaries; -import dev.zontreck.engineerdecor.libmc.Inventories; -import dev.zontreck.engineerdecor.libmc.RsSignals; -import dev.zontreck.engineerdecor.libmc.Guis; import javax.annotation.Nullable; import java.util.ArrayList; import java.util.List; -public class EdWasteIncinerator -{ - public static final int MAX_ENERGY_BUFFER = 16000; - public static final int MAX_ENERGY_TRANSFER = 256; - public static final int DEFAULT_ENERGY_CONSUMPTION = 16; - private static int energy_consumption = DEFAULT_ENERGY_CONSUMPTION; +public class EdWasteIncinerator { + public static final int MAX_ENERGY_BUFFER = 16000; + public static final int MAX_ENERGY_TRANSFER = 256; + public static final int DEFAULT_ENERGY_CONSUMPTION = 16; + private static int energy_consumption = DEFAULT_ENERGY_CONSUMPTION; - public static void on_config(int boost_energy_per_tick) - { - energy_consumption = Mth.clamp(boost_energy_per_tick, 4, 4096); - ModConfig.log("Config waste incinerator: boost energy consumption:" + energy_consumption + "."); - } - - //-------------------------------------------------------------------------------------------------------------------- - // Block - //-------------------------------------------------------------------------------------------------------------------- - - public static class WasteIncineratorBlock extends StandardBlocks.Cutout implements StandardEntityBlocks.IStandardEntityBlock - { - public static final BooleanProperty LIT = EdFurnace.FurnaceBlock.LIT; - - public WasteIncineratorBlock(long config, BlockBehaviour.Properties builder, final AABB unrotatedAABB) - { super(config, builder, unrotatedAABB); } - - @Override - public boolean isBlockEntityTicking(Level world, BlockState state) - { return true; } - - @Override - protected void createBlockStateDefinition(StateDefinition.Builder builder) - { super.createBlockStateDefinition(builder); builder.add(LIT); } - - @Override - @Nullable - public BlockState getStateForPlacement(BlockPlaceContext context) - { return super.getStateForPlacement(context).setValue(LIT, false); } - - @Override - @SuppressWarnings("deprecation") - public boolean hasAnalogOutputSignal(BlockState state) - { return true; } - - @Override - @SuppressWarnings("deprecation") - public int getAnalogOutputSignal(BlockState blockState, Level world, BlockPos pos) - { return (!(world.getBlockEntity(pos) instanceof WasteIncineratorTileEntity te)) ? 0 : RsSignals.fromContainer(te.main_inventory_); } - - @Override - public boolean shouldCheckWeakPower(BlockState state, LevelReader world, BlockPos pos, Direction side) - { return false; } - - @Override - public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) - { - if(world.isClientSide) return; - if((!stack.hasTag()) || (!stack.getTag().contains("tedata"))) return; - CompoundTag te_nbt = stack.getTag().getCompound("tedata"); - if(te_nbt.isEmpty()) return; - final BlockEntity te = world.getBlockEntity(pos); - if(!(te instanceof EdWasteIncinerator.WasteIncineratorTileEntity)) return; - ((EdWasteIncinerator.WasteIncineratorTileEntity)te).readnbt(te_nbt); - te.setChanged(); + public static void on_config(int boost_energy_per_tick) { + energy_consumption = Mth.clamp(boost_energy_per_tick, 4, 4096); + ModConfig.log("Config waste incinerator: boost energy consumption:" + energy_consumption + "."); } - @Override - public boolean hasDynamicDropList() - { return true; } + //-------------------------------------------------------------------------------------------------------------------- + // Block + //-------------------------------------------------------------------------------------------------------------------- - @Override - public List dropList(BlockState state, Level world, final BlockEntity te, boolean explosion) - { - final List stacks = new ArrayList<>(); - if(world.isClientSide) return stacks; - if(!(te instanceof WasteIncineratorTileEntity)) return stacks; - if(!explosion) { - ItemStack stack = new ItemStack(this, 1); - CompoundTag te_nbt = ((WasteIncineratorTileEntity) te).getnbt(); - if(!te_nbt.isEmpty()) { - CompoundTag nbt = new CompoundTag(); - nbt.put("tedata", te_nbt); - stack.setTag(nbt); + public static class WasteIncineratorBlock extends StandardBlocks.Cutout implements StandardEntityBlocks.IStandardEntityBlock { + public static final BooleanProperty LIT = EdFurnace.FurnaceBlock.LIT; + + public WasteIncineratorBlock(long config, BlockBehaviour.Properties builder, final AABB unrotatedAABB) { + super(config, builder, unrotatedAABB); + } + + @Override + public boolean isBlockEntityTicking(Level world, BlockState state) { + return true; + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + super.createBlockStateDefinition(builder); + builder.add(LIT); + } + + @Override + @Nullable + public BlockState getStateForPlacement(BlockPlaceContext context) { + return super.getStateForPlacement(context).setValue(LIT, false); + } + + @Override + @SuppressWarnings("deprecation") + public boolean hasAnalogOutputSignal(BlockState state) { + return true; + } + + @Override + @SuppressWarnings("deprecation") + public int getAnalogOutputSignal(BlockState blockState, Level world, BlockPos pos) { + return (!(world.getBlockEntity(pos) instanceof WasteIncineratorTileEntity te)) ? 0 : RsSignals.fromContainer(te.main_inventory_); + } + + @Override + public boolean shouldCheckWeakPower(BlockState state, SignalGetter level, BlockPos pos, Direction side) { + return false; + } + + @Override + public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) { + if (world.isClientSide) return; + if ((!stack.hasTag()) || (!stack.getTag().contains("tedata"))) return; + CompoundTag te_nbt = stack.getTag().getCompound("tedata"); + if (te_nbt.isEmpty()) return; + final BlockEntity te = world.getBlockEntity(pos); + if (!(te instanceof EdWasteIncinerator.WasteIncineratorTileEntity)) return; + ((EdWasteIncinerator.WasteIncineratorTileEntity) te).readnbt(te_nbt); + te.setChanged(); + } + + @Override + public boolean hasDynamicDropList() { + return true; + } + + @Override + public List dropList(BlockState state, Level world, final BlockEntity te, boolean explosion) { + final List stacks = new ArrayList<>(); + if (world.isClientSide) return stacks; + if (!(te instanceof WasteIncineratorTileEntity)) return stacks; + if (!explosion) { + ItemStack stack = new ItemStack(this, 1); + CompoundTag te_nbt = ((WasteIncineratorTileEntity) te).getnbt(); + if (!te_nbt.isEmpty()) { + CompoundTag nbt = new CompoundTag(); + nbt.put("tedata", te_nbt); + stack.setTag(nbt); + } + stacks.add(stack); + } else { + for (ItemStack stack : ((WasteIncineratorTileEntity) te).main_inventory_) stacks.add(stack); + ((WasteIncineratorTileEntity) te).getnbt(); + } + return stacks; + } + + @Override + @SuppressWarnings("deprecation") + public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult) { + return useOpenGui(state, world, pos, player); + } + + @Override + @OnlyIn(Dist.CLIENT) + public void animateTick(BlockState state, Level world, BlockPos pos, RandomSource rnd) { + if ((state.getBlock() != this) || (!state.getValue(LIT))) return; + final double rv = rnd.nextDouble(); + if (rv > 0.5) return; + final double x = 0.5 + pos.getX(), y = 0.5 + pos.getY(), z = 0.5 + pos.getZ(); + final double xr = rnd.nextDouble() * 0.4 - 0.2, yr = rnd.nextDouble() * 0.5, zr = rnd.nextDouble() * 0.4 - 0.2; + world.addParticle(ParticleTypes.SMOKE, x + xr, y + yr, z + zr, 0.0, 0.0, 0.0); } - stacks.add(stack); - } else { - for(ItemStack stack: ((WasteIncineratorTileEntity)te).main_inventory_) stacks.add(stack); - ((WasteIncineratorTileEntity)te).getnbt(); - } - return stacks; } - @Override - @SuppressWarnings("deprecation") - public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult) - { return useOpenGui(state, world, pos, player); } + //-------------------------------------------------------------------------------------------------------------------- + // Tile entity + //-------------------------------------------------------------------------------------------------------------------- - @Override - @OnlyIn(Dist.CLIENT) - public void animateTick(BlockState state, Level world, BlockPos pos, RandomSource rnd) - { - if((state.getBlock()!=this) || (!state.getValue(LIT))) return; - final double rv = rnd.nextDouble(); - if(rv > 0.5) return; - final double x=0.5+pos.getX(), y=0.5+pos.getY(), z=0.5+pos.getZ(); - final double xr=rnd.nextDouble()*0.4-0.2, yr=rnd.nextDouble()*0.5, zr=rnd.nextDouble()*0.4-0.2; - world.addParticle(ParticleTypes.SMOKE, x+xr, y+yr, z+zr, 0.0, 0.0, 0.0); - } - } + public static class WasteIncineratorTileEntity extends StandardEntityBlocks.StandardBlockEntity implements MenuProvider, Nameable { + public static final int NUM_OF_FIELDS = 1; + public static final int TICK_INTERVAL = 20; + public static final int ENERGIZED_TICK_INTERVAL = 5; + public static final int INCINERATION_STACK_DECREMENT = 4; + public static final int NUM_OF_SLOTS = 16; + public static final int INPUT_SLOT_NO = 0; + public static final int BURN_SLOT_NO = NUM_OF_SLOTS - 1; - //-------------------------------------------------------------------------------------------------------------------- - // Tile entity - //-------------------------------------------------------------------------------------------------------------------- + // WasteIncineratorTileEntity ----------------------------------------------------------------------------- + protected final ContainerData fields = new ContainerData() { + @Override + public int getCount() { + return WasteIncineratorTileEntity.NUM_OF_FIELDS; + } - public static class WasteIncineratorTileEntity extends StandardEntityBlocks.StandardBlockEntity implements MenuProvider, Nameable - { - public static final int NUM_OF_FIELDS = 1; - public static final int TICK_INTERVAL = 20; - public static final int ENERGIZED_TICK_INTERVAL = 5; - public static final int INCINERATION_STACK_DECREMENT = 4; - public static final int NUM_OF_SLOTS = 16; - public static final int INPUT_SLOT_NO = 0; - public static final int BURN_SLOT_NO = NUM_OF_SLOTS-1; + @Override + public int get(int id) { + return switch (id) { + default -> 0; + }; + } - // WasteIncineratorTileEntity ----------------------------------------------------------------------------- - - private int tick_timer_; - private int check_timer_; - private final Inventories.StorageInventory main_inventory_ = new Inventories.StorageInventory(this, NUM_OF_SLOTS, 1); - private final LazyOptional item_handler_ = Inventories.MappedItemHandler.createInsertionHandler(main_inventory_, INPUT_SLOT_NO); - private final RfEnergy.Battery battery_ = new RfEnergy.Battery(MAX_ENERGY_BUFFER, MAX_ENERGY_TRANSFER, 0); - private final LazyOptional energy_handler_ = battery_.createEnergyHandler(); - - public WasteIncineratorTileEntity(BlockPos pos, BlockState state) - { super(ModContent.getBlockEntityTypeOfBlock(state.getBlock()), pos, state); reset(); } - - public CompoundTag getnbt() - { return writenbt(new CompoundTag()); } - - protected void reset() - { - main_inventory_.clearContent(); - check_timer_ = 0; - tick_timer_ = 0; - } - - public void readnbt(CompoundTag nbt) - { - main_inventory_.load(nbt); - battery_.load(nbt); - } - - protected CompoundTag writenbt(CompoundTag nbt) - { - main_inventory_.save(nbt); - battery_.save(nbt); - return nbt; - } - - // BlockEntity ------------------------------------------------------------------------------ - - @Override - public void load(CompoundTag nbt) - { super.load(nbt); readnbt(nbt); } - - @Override - protected void saveAdditional(CompoundTag nbt) - { super.saveAdditional(nbt); writenbt(nbt); } - - @Override - public void setRemoved() - { - super.setRemoved(); - energy_handler_.invalidate(); - item_handler_.invalidate(); - } - - // INameable --------------------------------------------------------------------------- - - @Override - public Component getName() - { return Auxiliaries.localizable(getBlockState().getBlock().getDescriptionId()); } - - @Override - public boolean hasCustomName() - { return false; } - - @Override - public Component getCustomName() - { return getName(); } - - // IContainerProvider ---------------------------------------------------------------------- - - @Override - public Component getDisplayName() - { return Nameable.super.getDisplayName(); } - - @Override - public AbstractContainerMenu createMenu(int id, Inventory inventory, Player player ) - { return new EdWasteIncinerator.WasteIncineratorContainer(id, inventory, main_inventory_, ContainerLevelAccess.create(level, worldPosition), fields); } - - // Fields ----------------------------------------------------------------------------------------------- - - protected final ContainerData fields = new ContainerData() - { - @Override - public int getCount() - { return WasteIncineratorTileEntity.NUM_OF_FIELDS; } - - @Override - public int get(int id) - { - return switch (id) { - default -> 0; + @Override + public void set(int id, int value) { + } }; - } - @Override - public void set(int id, int value) - {} - }; + private final Inventories.StorageInventory main_inventory_ = new Inventories.StorageInventory(this, NUM_OF_SLOTS, 1); + private final LazyOptional item_handler_ = Inventories.MappedItemHandler.createInsertionHandler(main_inventory_, INPUT_SLOT_NO); + private final RfEnergy.Battery battery_ = new RfEnergy.Battery(MAX_ENERGY_BUFFER, MAX_ENERGY_TRANSFER, 0); + private final LazyOptional energy_handler_ = battery_.createEnergyHandler(); + private int tick_timer_; + private int check_timer_; - // Capability export ---------------------------------------------------------------------------- + public WasteIncineratorTileEntity(BlockPos pos, BlockState state) { + super(ModContent.getBlockEntityTypeOfBlock(state.getBlock()), pos, state); + reset(); + } - @Override - public LazyOptional getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable Direction facing) - { - if(capability == ForgeCapabilities.ITEM_HANDLER) return item_handler_.cast(); - if(capability == ForgeCapabilities.ENERGY) return energy_handler_.cast(); - return super.getCapability(capability, facing); + public CompoundTag getnbt() { + return writenbt(new CompoundTag()); + } + + protected void reset() { + main_inventory_.clearContent(); + check_timer_ = 0; + tick_timer_ = 0; + } + + public void readnbt(CompoundTag nbt) { + main_inventory_.load(nbt); + battery_.load(nbt); + } + + // BlockEntity ------------------------------------------------------------------------------ + + protected CompoundTag writenbt(CompoundTag nbt) { + main_inventory_.save(nbt); + battery_.save(nbt); + return nbt; + } + + @Override + public void load(CompoundTag nbt) { + super.load(nbt); + readnbt(nbt); + } + + @Override + protected void saveAdditional(CompoundTag nbt) { + super.saveAdditional(nbt); + writenbt(nbt); + } + + // INameable --------------------------------------------------------------------------- + + @Override + public void setRemoved() { + super.setRemoved(); + energy_handler_.invalidate(); + item_handler_.invalidate(); + } + + @Override + public Component getName() { + return Auxiliaries.localizable(getBlockState().getBlock().getDescriptionId()); + } + + @Override + public boolean hasCustomName() { + return false; + } + + // IContainerProvider ---------------------------------------------------------------------- + + @Override + public Component getCustomName() { + return getName(); + } + + @Override + public Component getDisplayName() { + return Nameable.super.getDisplayName(); + } + + // Fields ----------------------------------------------------------------------------------------------- + + @Override + public AbstractContainerMenu createMenu(int id, Inventory inventory, Player player) { + return new EdWasteIncinerator.WasteIncineratorContainer(id, inventory, main_inventory_, ContainerLevelAccess.create(level, worldPosition), fields); + } + + // Capability export ---------------------------------------------------------------------------- + + @Override + public LazyOptional getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable Direction facing) { + if (capability == ForgeCapabilities.ITEM_HANDLER) return item_handler_.cast(); + if (capability == ForgeCapabilities.ENERGY) return energy_handler_.cast(); + return super.getCapability(capability, facing); + } + + // ----------------------------------------------------------------------------------------------------------------- + + @Override + public void tick() { + if (--tick_timer_ > 0) return; + tick_timer_ = TICK_INTERVAL; + if (level.isClientSide) return; + boolean dirty = false; + ItemStack processing_stack = main_inventory_.getItem(BURN_SLOT_NO); + final boolean was_processing = !processing_stack.isEmpty(); + boolean is_processing = was_processing; + boolean new_stack_processing = false; + if ((!main_inventory_.getItem(0).isEmpty()) && transferItems(0, 1, main_inventory_.getMaxStackSize())) + dirty = true; + ItemStack first_stack = main_inventory_.getItem(0); + boolean shift = !first_stack.isEmpty(); + if (is_processing) { + processing_stack.shrink(INCINERATION_STACK_DECREMENT); + if (processing_stack.getCount() <= 0) { + processing_stack = ItemStack.EMPTY; + is_processing = false; + } + main_inventory_.setItem(BURN_SLOT_NO, processing_stack); + if (battery_.draw(energy_consumption * TICK_INTERVAL)) { + tick_timer_ = ENERGIZED_TICK_INTERVAL; + } + dirty = true; + } + if (shift) { + boolean transferred = false; + for (int i = BURN_SLOT_NO - 1; i > 0; --i) { + transferred |= transferItems(i - 1, i, main_inventory_.getMaxStackSize()); + } + if ((!is_processing) && (!transferred)) { + shiftStacks(0, BURN_SLOT_NO); + dirty = true; + } + } + if ((was_processing != is_processing) || (new_stack_processing)) { + if (new_stack_processing) + level.playSound(null, worldPosition, SoundEvents.LAVA_AMBIENT, SoundSource.BLOCKS, 0.05f, 2.4f); + final BlockState state = level.getBlockState(worldPosition); + if (state.getBlock() instanceof WasteIncineratorBlock) { + level.setBlock(worldPosition, state.setValue(WasteIncineratorBlock.LIT, is_processing), 2 | 16); + } + } + if (dirty) setChanged(); + } + + // Aux methods ---------------------------------------------------------------------------------- + + private ItemStack shiftStacks(final int index_from, final int index_to) { + if (index_from >= index_to) return ItemStack.EMPTY; + ItemStack out_stack = ItemStack.EMPTY; + ItemStack stack = main_inventory_.getItem(index_from); + for (int i = index_from + 1; i <= index_to; ++i) { + out_stack = main_inventory_.getItem(i); + main_inventory_.setItem(i, stack); + stack = out_stack; + } + main_inventory_.setItem(index_from, ItemStack.EMPTY); + return out_stack; + } + + private boolean transferItems(final int index_from, final int index_to, int count) { + ItemStack from = main_inventory_.getItem(index_from); + if (from.isEmpty()) return false; + ItemStack to = main_inventory_.getItem(index_to); + if (from.getCount() < count) count = from.getCount(); + if (count <= 0) return false; + boolean changed = true; + if (to.isEmpty()) { + main_inventory_.setItem(index_to, from.split(count)); + } else if (to.getCount() >= to.getMaxStackSize()) { + changed = false; + } else if (Inventories.areItemStacksDifferent(from, to)) { + changed = false; + } else { + if ((to.getCount() + count) >= to.getMaxStackSize()) { + from.shrink(to.getMaxStackSize() - to.getCount()); + to.setCount(to.getMaxStackSize()); + } else { + from.shrink(count); + to.grow(count); + } + } + if (from.isEmpty() && from != ItemStack.EMPTY) { + main_inventory_.setItem(index_from, ItemStack.EMPTY); + changed = true; + } + return changed; + } } - // ----------------------------------------------------------------------------------------------------------------- + //-------------------------------------------------------------------------------------------------------------------- + // Container + //-------------------------------------------------------------------------------------------------------------------- - @Override - public void tick() - { - if(--tick_timer_ > 0) return; - tick_timer_ = TICK_INTERVAL; - if(level.isClientSide) return; - boolean dirty = false; - ItemStack processing_stack = main_inventory_.getItem(BURN_SLOT_NO); - final boolean was_processing = !processing_stack.isEmpty(); - boolean is_processing = was_processing; - boolean new_stack_processing = false; - if((!main_inventory_.getItem(0).isEmpty()) && transferItems(0, 1, main_inventory_.getMaxStackSize())) dirty = true; - ItemStack first_stack = main_inventory_.getItem(0); - boolean shift = !first_stack.isEmpty(); - if(is_processing) { - processing_stack.shrink(INCINERATION_STACK_DECREMENT); - if(processing_stack.getCount() <= 0) { - processing_stack = ItemStack.EMPTY; - is_processing = false; + public static class WasteIncineratorContainer extends AbstractContainerMenu { + private static final int PLAYER_INV_START_SLOTNO = WasteIncineratorTileEntity.NUM_OF_SLOTS; + protected final Player player_; + protected final Container inventory_; + protected final ContainerLevelAccess wpc_; + private final ContainerData fields_; + private int proc_time_needed_; + + public WasteIncineratorContainer(int cid, Inventory player_inventory) { + this(cid, player_inventory, new SimpleContainer(WasteIncineratorTileEntity.NUM_OF_SLOTS), ContainerLevelAccess.NULL, new SimpleContainerData(WasteIncineratorTileEntity.NUM_OF_FIELDS)); } - main_inventory_.setItem(BURN_SLOT_NO, processing_stack); - if(battery_.draw(energy_consumption * TICK_INTERVAL)) { - tick_timer_ = ENERGIZED_TICK_INTERVAL; + + private WasteIncineratorContainer(int cid, Inventory player_inventory, Container block_inventory, ContainerLevelAccess wpc, ContainerData fields) { + super(ModContent.getMenuType("small_waste_incinerator"), cid); // @todo: class mapping + player_ = player_inventory.player; + inventory_ = block_inventory; + wpc_ = wpc; + fields_ = fields; + int i = -1; + addSlot(new Slot(inventory_, ++i, 13, 9)); + addSlot(new Slot(inventory_, ++i, 37, 12)); + addSlot(new Slot(inventory_, ++i, 54, 13)); + addSlot(new Slot(inventory_, ++i, 71, 14)); + addSlot(new Slot(inventory_, ++i, 88, 15)); + addSlot(new Slot(inventory_, ++i, 105, 16)); + addSlot(new Slot(inventory_, ++i, 122, 17)); + addSlot(new Slot(inventory_, ++i, 139, 18)); + addSlot(new Slot(inventory_, ++i, 144, 38)); + addSlot(new Slot(inventory_, ++i, 127, 39)); + addSlot(new Slot(inventory_, ++i, 110, 40)); + addSlot(new Slot(inventory_, ++i, 93, 41)); + addSlot(new Slot(inventory_, ++i, 76, 42)); + addSlot(new Slot(inventory_, ++i, 59, 43)); + addSlot(new Slot(inventory_, ++i, 42, 44)); + addSlot(new Slot(inventory_, ++i, 17, 58)); + for (int x = 0; x < 9; ++x) { + addSlot(new Slot(player_inventory, x, 8 + x * 18, 144)); // player slots: 0..8 + } + for (int y = 0; y < 3; ++y) { + for (int x = 0; x < 9; ++x) { + addSlot(new Slot(player_inventory, x + y * 9 + 9, 8 + x * 18, 86 + y * 18)); // player slots: 9..35 + } + } } - dirty = true; - } - if(shift) { - boolean transferred = false; - for(int i=BURN_SLOT_NO-1; i>0; --i) { - transferred |= transferItems(i-1, i, main_inventory_.getMaxStackSize()); + + public int field(int index) { + return fields_.get(index); } - if((!is_processing) && (!transferred)) { - shiftStacks(0, BURN_SLOT_NO); - dirty = true; + + public Player player() { + return player_; } - } - if((was_processing != is_processing) || (new_stack_processing)) { - if(new_stack_processing) level.playSound(null, worldPosition, SoundEvents.LAVA_AMBIENT, SoundSource.BLOCKS, 0.05f, 2.4f); - final BlockState state = level.getBlockState(worldPosition); - if(state.getBlock() instanceof WasteIncineratorBlock) { - level.setBlock(worldPosition, state.setValue(WasteIncineratorBlock.LIT, is_processing), 2|16); + + public Container inventory() { + return inventory_; + } + + public Level world() { + return player_.level(); + } + + @Override + public boolean stillValid(Player player) { + return inventory_.stillValid(player); + } + + @Override + public ItemStack quickMoveStack(Player player, int index) { + Slot slot = getSlot(index); + if ((slot == null) || (!slot.hasItem())) return ItemStack.EMPTY; + ItemStack slot_stack = slot.getItem(); + ItemStack transferred = slot_stack.copy(); + if ((index >= 0) && (index < PLAYER_INV_START_SLOTNO)) { + // Device slots + if (!moveItemStackTo(slot_stack, PLAYER_INV_START_SLOTNO, PLAYER_INV_START_SLOTNO + 36, true)) + return ItemStack.EMPTY; + } else if ((index >= PLAYER_INV_START_SLOTNO) && (index <= PLAYER_INV_START_SLOTNO + 36)) { + // Player slot + if (!moveItemStackTo(slot_stack, 0, PLAYER_INV_START_SLOTNO - 1, true)) return ItemStack.EMPTY; + } else { + // invalid slot + return ItemStack.EMPTY; + } + if (slot_stack.isEmpty()) { + slot.set(ItemStack.EMPTY); + } else { + slot.setChanged(); + } + if (slot_stack.getCount() == transferred.getCount()) return ItemStack.EMPTY; + slot.onTake(player, slot_stack); + return transferred; } - } - if(dirty) setChanged(); } - // Aux methods ---------------------------------------------------------------------------------- + //-------------------------------------------------------------------------------------------------------------------- + // GUI + //-------------------------------------------------------------------------------------------------------------------- - private ItemStack shiftStacks(final int index_from, final int index_to) - { - if(index_from >= index_to) return ItemStack.EMPTY; - ItemStack out_stack = ItemStack.EMPTY; - ItemStack stack = main_inventory_.getItem(index_from); - for(int i=index_from+1; i<=index_to; ++i) { - out_stack = main_inventory_.getItem(i); - main_inventory_.setItem(i, stack); - stack = out_stack; - } - main_inventory_.setItem(index_from, ItemStack.EMPTY); - return out_stack; - } - - private boolean transferItems(final int index_from, final int index_to, int count) - { - ItemStack from = main_inventory_.getItem(index_from); - if(from.isEmpty()) return false; - ItemStack to = main_inventory_.getItem(index_to); - if(from.getCount() < count) count = from.getCount(); - if(count <= 0) return false; - boolean changed = true; - if(to.isEmpty()) { - main_inventory_.setItem(index_to, from.split(count)); - } else if(to.getCount() >= to.getMaxStackSize()) { - changed = false; - } else if(Inventories.areItemStacksDifferent(from, to)) { - changed = false; - } else { - if((to.getCount()+count) >= to.getMaxStackSize()) { - from.shrink(to.getMaxStackSize()-to.getCount()); - to.setCount(to.getMaxStackSize()); - } else { - from.shrink(count); - to.grow(count); + @OnlyIn(Dist.CLIENT) + public static class WasteIncineratorGui extends Guis.ContainerGui { + public WasteIncineratorGui(WasteIncineratorContainer container, Inventory player_inventory, Component title) { + super(container, player_inventory, title, "textures/gui/small_waste_incinerator_gui.png"); } - } - if(from.isEmpty() && from!=ItemStack.EMPTY) { - main_inventory_.setItem(index_from, ItemStack.EMPTY); - changed = true; - } - return changed; } - } - - //-------------------------------------------------------------------------------------------------------------------- - // Container - //-------------------------------------------------------------------------------------------------------------------- - - public static class WasteIncineratorContainer extends AbstractContainerMenu - { - private static final int PLAYER_INV_START_SLOTNO = WasteIncineratorTileEntity.NUM_OF_SLOTS; - protected final Player player_; - protected final Container inventory_; - protected final ContainerLevelAccess wpc_; - private final ContainerData fields_; - private int proc_time_needed_; - - public int field(int index) { return fields_.get(index); } - public Player player() { return player_ ; } - public Container inventory() { return inventory_ ; } - public Level world() { return player_.level; } - - public WasteIncineratorContainer(int cid, Inventory player_inventory) - { this(cid, player_inventory, new SimpleContainer(WasteIncineratorTileEntity.NUM_OF_SLOTS), ContainerLevelAccess.NULL, new SimpleContainerData(WasteIncineratorTileEntity.NUM_OF_FIELDS)); } - - private WasteIncineratorContainer(int cid, Inventory player_inventory, Container block_inventory, ContainerLevelAccess wpc, ContainerData fields) - { - super(ModContent.getMenuType("small_waste_incinerator"), cid); // @todo: class mapping - player_ = player_inventory.player; - inventory_ = block_inventory; - wpc_ = wpc; - fields_ = fields; - int i=-1; - addSlot(new Slot(inventory_, ++i, 13, 9)); - addSlot(new Slot(inventory_, ++i, 37, 12)); - addSlot(new Slot(inventory_, ++i, 54, 13)); - addSlot(new Slot(inventory_, ++i, 71, 14)); - addSlot(new Slot(inventory_, ++i, 88, 15)); - addSlot(new Slot(inventory_, ++i, 105, 16)); - addSlot(new Slot(inventory_, ++i, 122, 17)); - addSlot(new Slot(inventory_, ++i, 139, 18)); - addSlot(new Slot(inventory_, ++i, 144, 38)); - addSlot(new Slot(inventory_, ++i, 127, 39)); - addSlot(new Slot(inventory_, ++i, 110, 40)); - addSlot(new Slot(inventory_, ++i, 93, 41)); - addSlot(new Slot(inventory_, ++i, 76, 42)); - addSlot(new Slot(inventory_, ++i, 59, 43)); - addSlot(new Slot(inventory_, ++i, 42, 44)); - addSlot(new Slot(inventory_, ++i, 17, 58)); - for(int x=0; x<9; ++x) { - addSlot(new Slot(player_inventory, x, 8+x*18, 144)); // player slots: 0..8 - } - for(int y=0; y<3; ++y) { - for(int x=0; x<9; ++x) { - addSlot(new Slot(player_inventory, x+y*9+9, 8+x*18, 86+y*18)); // player slots: 9..35 - } - } - } - - @Override - public boolean stillValid(Player player) - { return inventory_.stillValid(player); } - - @Override - public ItemStack quickMoveStack(Player player, int index) - { - Slot slot = getSlot(index); - if((slot==null) || (!slot.hasItem())) return ItemStack.EMPTY; - ItemStack slot_stack = slot.getItem(); - ItemStack transferred = slot_stack.copy(); - if((index>=0) && (index= PLAYER_INV_START_SLOTNO) && (index <= PLAYER_INV_START_SLOTNO+36)) { - // Player slot - if(!moveItemStackTo(slot_stack, 0, PLAYER_INV_START_SLOTNO-1, true)) return ItemStack.EMPTY; - } else { - // invalid slot - return ItemStack.EMPTY; - } - if(slot_stack.isEmpty()) { - slot.set(ItemStack.EMPTY); - } else { - slot.setChanged(); - } - if(slot_stack.getCount() == transferred.getCount()) return ItemStack.EMPTY; - slot.onTake(player, slot_stack); - return transferred; - } - } - - //-------------------------------------------------------------------------------------------------------------------- - // GUI - //-------------------------------------------------------------------------------------------------------------------- - - @OnlyIn(Dist.CLIENT) - public static class WasteIncineratorGui extends Guis.ContainerGui - { - public WasteIncineratorGui(WasteIncineratorContainer container, Inventory player_inventory, Component title) - { super(container, player_inventory, title, "textures/gui/small_waste_incinerator_gui.png"); } - } } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdWindowBlock.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdWindowBlock.java index 7d5ec06..898e2af 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdWindowBlock.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdWindowBlock.java @@ -8,6 +8,7 @@ */ package dev.zontreck.engineerdecor.blocks; +import dev.zontreck.libzontreck.edlibmc.StandardBlocks; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.sounds.SoundEvents; @@ -24,70 +25,69 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.Fluids; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.BlockHitResult; -import dev.zontreck.engineerdecor.libmc.StandardBlocks; import javax.annotation.Nullable; import java.util.Arrays; - -public class EdWindowBlock extends StandardBlocks.DirectedWaterLoggable -{ - public EdWindowBlock(long config, BlockBehaviour.Properties builder, final AABB unrotatedAABB) - { super(config, builder, unrotatedAABB); } - - @Override - public RenderTypeHint getRenderTypeHint() - { return RenderTypeHint.TRANSLUCENT; } - - @Override - @Nullable - public BlockState getStateForPlacement(BlockPlaceContext context) - { - Direction facing = context.getHorizontalDirection(); - if(Math.abs(context.getPlayer().getLookAngle().y) > 0.9) { - facing = context.getNearestLookingDirection(); - } else { - for(Direction f: Direction.values()) { - BlockState st = context.getLevel().getBlockState(context.getClickedPos().relative(f)); - if(st.getBlock() == this) { - facing = st.getValue(FACING); - break; - } - } +public class EdWindowBlock extends StandardBlocks.DirectedWaterLoggable { + public EdWindowBlock(long config, BlockBehaviour.Properties builder, final AABB unrotatedAABB) { + super(config, builder, unrotatedAABB); } - return super.getStateForPlacement(context).setValue(FACING, facing); - } - @Override - @SuppressWarnings("deprecation") - public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) - { - if(player.getItemInHand(hand).getItem() != asItem()) return InteractionResult.PASS; - final Direction facing = state.getValue(FACING); - if(facing.getAxis() != hit.getDirection().getAxis()) return InteractionResult.PASS; - Arrays.stream(Direction.orderedByNearest(player)) - .filter(d->d.getAxis() != facing.getAxis()) - .filter(d->world.getBlockState(pos.relative(d)).canBeReplaced((new DirectionalPlaceContext(world, pos.relative(d), facing.getOpposite(), player.getItemInHand(hand), facing)))) - .findFirst().ifPresent((d)->{ - BlockState st = defaultBlockState() - .setValue(FACING, facing) - .setValue(WATERLOGGED,world.getBlockState(pos.relative(d)).getFluidState().getType()==Fluids.WATER); - world.setBlock(pos.relative(d), st, 1|2); - world.playSound(player, pos, SoundEvents.METAL_PLACE, SoundSource.BLOCKS, 1f, 1f); - player.getItemInHand(hand).shrink(1); - } - ); - return InteractionResult.sidedSuccess(world.isClientSide()); - } + @Override + public RenderTypeHint getRenderTypeHint() { + return RenderTypeHint.TRANSLUCENT; + } - @Override - public boolean propagatesSkylightDown(BlockState state, BlockGetter reader, BlockPos pos) - { return true; } + @Override + @Nullable + public BlockState getStateForPlacement(BlockPlaceContext context) { + Direction facing = context.getHorizontalDirection(); + if (Math.abs(context.getPlayer().getLookAngle().y) > 0.9) { + facing = context.getNearestLookingDirection(); + } else { + for (Direction f : Direction.values()) { + BlockState st = context.getLevel().getBlockState(context.getClickedPos().relative(f)); + if (st.getBlock() == this) { + facing = st.getValue(FACING); + break; + } + } + } + return super.getStateForPlacement(context).setValue(FACING, facing); + } - @Override - @SuppressWarnings("deprecation") - public boolean useShapeForLightOcclusion(BlockState state) - { return true; } + @Override + @SuppressWarnings("deprecation") + public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { + if (player.getItemInHand(hand).getItem() != asItem()) return InteractionResult.PASS; + final Direction facing = state.getValue(FACING); + if (facing.getAxis() != hit.getDirection().getAxis()) return InteractionResult.PASS; + Arrays.stream(Direction.orderedByNearest(player)) + .filter(d -> d.getAxis() != facing.getAxis()) + .filter(d -> world.getBlockState(pos.relative(d)).canBeReplaced((new DirectionalPlaceContext(world, pos.relative(d), facing.getOpposite(), player.getItemInHand(hand), facing)))) + .findFirst().ifPresent((d) -> { + BlockState st = defaultBlockState() + .setValue(FACING, facing) + .setValue(WATERLOGGED, world.getBlockState(pos.relative(d)).getFluidState().getType() == Fluids.WATER); + world.setBlock(pos.relative(d), st, 1 | 2); + world.playSound(player, pos, SoundEvents.METAL_PLACE, SoundSource.BLOCKS, 1f, 1f); + player.getItemInHand(hand).shrink(1); + } + ); + return InteractionResult.sidedSuccess(world.isClientSide()); + } + + @Override + public boolean propagatesSkylightDown(BlockState state, BlockGetter reader, BlockPos pos) { + return true; + } + + @Override + @SuppressWarnings("deprecation") + public boolean useShapeForLightOcclusion(BlockState state) { + return true; + } } diff --git a/src/main/java/dev/zontreck/engineerdecor/blocks/EdgeAlignedRailingBlock.java b/src/main/java/dev/zontreck/engineerdecor/blocks/EdgeAlignedRailingBlock.java index 7067ef8..63935a2 100644 --- a/src/main/java/dev/zontreck/engineerdecor/blocks/EdgeAlignedRailingBlock.java +++ b/src/main/java/dev/zontreck/engineerdecor/blocks/EdgeAlignedRailingBlock.java @@ -8,6 +8,7 @@ */ package dev.zontreck.engineerdecor.blocks; +import dev.zontreck.libzontreck.edlibmc.StandardBlocks; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.InteractionHand; @@ -26,7 +27,6 @@ import net.minecraft.world.level.material.Fluids; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.Vec3; -import dev.zontreck.engineerdecor.libmc.StandardBlocks; import javax.annotation.Nullable; import java.util.ArrayList; @@ -34,67 +34,68 @@ import java.util.Collections; import java.util.List; -public class EdgeAlignedRailingBlock extends StandardBlocks.HorizontalFourWayWaterLoggable -{ - public EdgeAlignedRailingBlock(long config, BlockBehaviour.Properties properties, final AABB base_aabb, final AABB railing_aabb) - { super(config, properties, base_aabb, railing_aabb, 0); } - - @Override - public boolean propagatesSkylightDown(BlockState state, BlockGetter reader, BlockPos pos) - { return true; } - - @Override - @SuppressWarnings("deprecation") - public boolean canBeReplaced(BlockState state, BlockPlaceContext useContext) - { return (useContext.getItemInHand().getItem() == asItem()) || super.canBeReplaced(state, useContext); } - - @Override - @Nullable - public BlockState getStateForPlacement(BlockPlaceContext context) - { - if(context.getClickedFace() != Direction.UP) return null; - BlockState state = context.getLevel().getBlockState(context.getClickedPos()); - if(state.getBlock() != this) state = super.getStateForPlacement(context); - final Vec3 rhv = context.getClickLocation().subtract(Vec3.atCenterOf(context.getClickedPos())); - BooleanProperty side = getDirectionProperty(Direction.getNearest(rhv.x, 0, rhv.z)); - return state.setValue(side, true); - } - - @Override - @SuppressWarnings("deprecation") - public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) - { - if(player.getItemInHand(hand).getItem() != asItem()) return InteractionResult.PASS; - Direction face = hit.getDirection(); - if(!face.getAxis().isHorizontal()) return InteractionResult.sidedSuccess(world.isClientSide()); - final Vec3 rhv = hit.getLocation().subtract(Vec3.atCenterOf(hit.getBlockPos())); - if(rhv.multiply(Vec3.atLowerCornerOf(face.getNormal())).scale(2).lengthSqr() < 0.99) face = face.getOpposite(); // click on railing, not the outer side. - BooleanProperty railing = getDirectionProperty(face); - boolean add = (!state.getValue(railing)); - state = state.setValue(railing, add); - if((!state.getValue(NORTH)) && (!state.getValue(EAST)) && (!state.getValue(SOUTH)) && (!state.getValue(WEST))) { - state = (world.getFluidState(pos).getType() == Fluids.WATER) ? Blocks.WATER.defaultBlockState() : (Blocks.AIR.defaultBlockState()); - EdCatwalkBlock.place_consume(state, world, pos, player, hand, add ? 1 : -1); - } else { - EdCatwalkBlock.place_consume(state, world, pos, player, hand, add ? 1 : -1); +public class EdgeAlignedRailingBlock extends StandardBlocks.HorizontalFourWayWaterLoggable { + public EdgeAlignedRailingBlock(long config, BlockBehaviour.Properties properties, final AABB base_aabb, final AABB railing_aabb) { + super(config, properties, base_aabb, railing_aabb, 0); } - return InteractionResult.sidedSuccess(world.isClientSide()); - } - // -- IDecorBlock + @Override + public boolean propagatesSkylightDown(BlockState state, BlockGetter reader, BlockPos pos) { + return true; + } - @Override - public boolean hasDynamicDropList() - { return true; } + @Override + @SuppressWarnings("deprecation") + public boolean canBeReplaced(BlockState state, BlockPlaceContext useContext) { + return (useContext.getItemInHand().getItem() == asItem()) || super.canBeReplaced(state, useContext); + } - @Override - public List dropList(BlockState state, Level world, @Nullable BlockEntity te, boolean explosion) - { - if(world.isClientSide()) return Collections.singletonList(ItemStack.EMPTY); - List drops = new ArrayList<>(); - int n = (state.getValue(NORTH)?1:0)+(state.getValue(EAST)?1:0)+(state.getValue(SOUTH)?1:0)+(state.getValue(WEST)?1:0); - drops.add(new ItemStack(state.getBlock().asItem(), Math.max(n, 1))); - return drops; - } + @Override + @Nullable + public BlockState getStateForPlacement(BlockPlaceContext context) { + if (context.getClickedFace() != Direction.UP) return null; + BlockState state = context.getLevel().getBlockState(context.getClickedPos()); + if (state.getBlock() != this) state = super.getStateForPlacement(context); + final Vec3 rhv = context.getClickLocation().subtract(Vec3.atCenterOf(context.getClickedPos())); + BooleanProperty side = getDirectionProperty(Direction.getNearest(rhv.x, 0, rhv.z)); + return state.setValue(side, true); + } + + @Override + @SuppressWarnings("deprecation") + public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { + if (player.getItemInHand(hand).getItem() != asItem()) return InteractionResult.PASS; + Direction face = hit.getDirection(); + if (!face.getAxis().isHorizontal()) return InteractionResult.sidedSuccess(world.isClientSide()); + final Vec3 rhv = hit.getLocation().subtract(Vec3.atCenterOf(hit.getBlockPos())); + if (rhv.multiply(Vec3.atLowerCornerOf(face.getNormal())).scale(2).lengthSqr() < 0.99) + face = face.getOpposite(); // click on railing, not the outer side. + BooleanProperty railing = getDirectionProperty(face); + boolean add = (!state.getValue(railing)); + state = state.setValue(railing, add); + if ((!state.getValue(NORTH)) && (!state.getValue(EAST)) && (!state.getValue(SOUTH)) && (!state.getValue(WEST))) { + state = (world.getFluidState(pos).getType() == Fluids.WATER) ? Blocks.WATER.defaultBlockState() : (Blocks.AIR.defaultBlockState()); + EdCatwalkBlock.place_consume(state, world, pos, player, hand, add ? 1 : -1); + } else { + EdCatwalkBlock.place_consume(state, world, pos, player, hand, add ? 1 : -1); + } + return InteractionResult.sidedSuccess(world.isClientSide()); + } + + // -- IDecorBlock + + @Override + public boolean hasDynamicDropList() { + return true; + } + + @Override + public List dropList(BlockState state, Level world, @Nullable BlockEntity te, boolean explosion) { + if (world.isClientSide()) return Collections.singletonList(ItemStack.EMPTY); + List drops = new ArrayList<>(); + int n = (state.getValue(NORTH) ? 1 : 0) + (state.getValue(EAST) ? 1 : 0) + (state.getValue(SOUTH) ? 1 : 0) + (state.getValue(WEST) ? 1 : 0); + drops.add(new ItemStack(state.getBlock().asItem(), Math.max(n, 1))); + return drops; + } } diff --git a/src/main/java/dev/zontreck/engineerdecor/datagen/ModDatagen.java b/src/main/java/dev/zontreck/engineerdecor/datagen/ModDatagen.java new file mode 100644 index 0000000..3fb99ab --- /dev/null +++ b/src/main/java/dev/zontreck/engineerdecor/datagen/ModDatagen.java @@ -0,0 +1,16 @@ +package dev.zontreck.engineerdecor.datagen; + +import dev.zontreck.engineerdecor.ModEngineersDecor; +import net.minecraftforge.data.event.GatherDataEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; + +@Mod.EventBusSubscriber(modid = ModEngineersDecor.MODID, bus = Mod.EventBusSubscriber.Bus.MOD) +public class ModDatagen +{ + @SubscribeEvent + public static void onDatagen(GatherDataEvent ev) + { + + } +} diff --git a/src/main/java/dev/zontreck/engineerdecor/detail/ModRenderers.java b/src/main/java/dev/zontreck/engineerdecor/detail/ModRenderers.java index 63cdef6..173f0fa 100644 --- a/src/main/java/dev/zontreck/engineerdecor/detail/ModRenderers.java +++ b/src/main/java/dev/zontreck/engineerdecor/detail/ModRenderers.java @@ -23,35 +23,37 @@ import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -public class ModRenderers -{ - //-------------------------------------------------------------------------------------------------------------------- - // InvisibleEntityRenderer - //-------------------------------------------------------------------------------------------------------------------- +public class ModRenderers { + //-------------------------------------------------------------------------------------------------------------------- + // InvisibleEntityRenderer + //-------------------------------------------------------------------------------------------------------------------- - @OnlyIn(Dist.CLIENT) - public static class InvisibleEntityRenderer extends EntityRenderer - { - private final Minecraft mc = Minecraft.getInstance(); + @OnlyIn(Dist.CLIENT) + public static class InvisibleEntityRenderer extends EntityRenderer { + private final Minecraft mc = Minecraft.getInstance(); - public InvisibleEntityRenderer(EntityRendererProvider.Context context) - { super(context); } + public InvisibleEntityRenderer(EntityRendererProvider.Context context) { + super(context); + } - public void render(T entity, float entityYaw, float partialTicks, PoseStack matrixStack, MultiBufferSource buffer, int packedLight) - {} + public void render(T entity, float entityYaw, float partialTicks, PoseStack matrixStack, MultiBufferSource buffer, int packedLight) { + } - public Vec3 getRenderOffset(T entity, float partialTicks) - { return Vec3.ZERO; } + public Vec3 getRenderOffset(T entity, float partialTicks) { + return Vec3.ZERO; + } - @SuppressWarnings("deprecation") - public ResourceLocation getTextureLocation(T entity) - { return TextureAtlas.LOCATION_BLOCKS; } + @SuppressWarnings("deprecation") + public ResourceLocation getTextureLocation(T entity) { + return TextureAtlas.LOCATION_BLOCKS; + } - protected boolean shouldShowName(T entity) - { return false; } + protected boolean shouldShowName(T entity) { + return false; + } - protected void renderNameTag(T entity, Component displayName, PoseStack matrixStack, MultiBufferSource buffer, int packedLight) - {} - } + protected void renderNameTag(T entity, Component displayName, PoseStack matrixStack, MultiBufferSource buffer, int packedLight) { + } + } } diff --git a/src/main/java/dev/zontreck/engineerdecor/detail/TreeCutting.java b/src/main/java/dev/zontreck/engineerdecor/detail/TreeCutting.java index 952b014..94c12b3 100644 --- a/src/main/java/dev/zontreck/engineerdecor/detail/TreeCutting.java +++ b/src/main/java/dev/zontreck/engineerdecor/detail/TreeCutting.java @@ -9,6 +9,7 @@ package dev.zontreck.engineerdecor.detail; import com.google.common.collect.ImmutableList; +import dev.zontreck.libzontreck.edlibmc.Auxiliaries; import net.minecraft.core.BlockPos; import net.minecraft.core.Vec3i; import net.minecraft.resources.ResourceLocation; @@ -20,189 +21,184 @@ import net.minecraft.world.level.block.LeavesBlock; import net.minecraft.world.level.block.VineBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraftforge.registries.ForgeRegistries; -import dev.zontreck.engineerdecor.libmc.Auxiliaries; import java.util.*; import java.util.stream.Collectors; -public class TreeCutting -{ - private static final Set universal_logs_ = new HashSet<>(); +public class TreeCutting { + private static final Set universal_logs_ = new HashSet<>(); + private static final List hoffsets = ImmutableList.of( + new Vec3i(1, 0, 0), new Vec3i(1, 0, 1), new Vec3i(0, 0, 1), + new Vec3i(-1, 0, 1), new Vec3i(-1, 0, 0), new Vec3i(-1, 0, -1), + new Vec3i(0, 0, -1), new Vec3i(1, 0, -1) + ); - public static void on_config(List universal_logs) - { - universal_logs_.clear(); - if(universal_logs.isEmpty()) return; - try { - universal_logs.forEach(rls->{ - final ResourceLocation rl = ResourceLocation.tryParse(rls); - if((rl == null) || (!ForgeRegistries.BLOCKS.containsKey(rl))) return; - universal_logs_.add(ForgeRegistries.BLOCKS.getValue(rl)); - }); - } catch(Throwable ex) { - Auxiliaries.logError("Unexpected exception parsing universal log blocks: " + ex.getMessage()); - } - if(!universal_logs_.isEmpty()) { - Auxiliaries.logger().info("Tree cutting: Universal logs:" + universal_logs_.stream().map(Block::toString).collect(Collectors.joining()) + "."); - } - } - - public static boolean canChop(Level world, BlockState state, BlockPos pos) - { return isLog(state) || (universal_logs_.contains(state.getBlock()) && isLog(world.getBlockState(pos.above()))); } - - // ------------------------------------------------------------------------------------------------------------------- - - private static final List hoffsets = ImmutableList.of( - new Vec3i( 1,0, 0), new Vec3i( 1,0, 1), new Vec3i( 0,0, 1), - new Vec3i(-1,0, 1), new Vec3i(-1,0, 0), new Vec3i(-1,0,-1), - new Vec3i( 0,0,-1), new Vec3i( 1,0,-1) - ); - - private static boolean isLog(BlockState state) - { return (state.is(BlockTags.LOGS)); } - - private static boolean isSameLog(BlockState a, BlockState b) - { - final Block ba = a.getBlock(); - final Block bb = b.getBlock(); - return (ba==bb) || (universal_logs_.contains(ba) && isLog(b)) || (universal_logs_.contains(bb) && isLog(a)) || (universal_logs_.contains(ba) && universal_logs_.contains(bb)); - } - - private static boolean isLeaves(BlockState state) - { - if(state.getBlock() instanceof LeavesBlock) return true; - if(state.is(BlockTags.LEAVES)) return true; - return false; - } - - private static List findBlocksAround(final Level world, final BlockPos centerPos, final BlockState leaf_type_state, final Set checked, int recursion_left) - { - ArrayList to_decay = new ArrayList<>(); - for(int y=-1; y<=1; ++y) { - final BlockPos layer = centerPos.offset(0,y,0); - for(Vec3i v:hoffsets) { - BlockPos pos = layer.offset(v); - if((!checked.contains(pos)) && (world.getBlockState(pos).getBlock()==leaf_type_state.getBlock())) { - checked.add(pos); - to_decay.add(pos); - if(recursion_left > 0) { - to_decay.addAll(findBlocksAround(world, pos, leaf_type_state, checked, recursion_left-1)); - } + public static void on_config(List universal_logs) { + universal_logs_.clear(); + if (universal_logs.isEmpty()) return; + try { + universal_logs.forEach(rls -> { + final ResourceLocation rl = ResourceLocation.tryParse(rls); + if ((rl == null) || (!ForgeRegistries.BLOCKS.containsKey(rl))) return; + universal_logs_.add(ForgeRegistries.BLOCKS.getValue(rl)); + }); + } catch (Throwable ex) { + Auxiliaries.logError("Unexpected exception parsing universal log blocks: " + ex.getMessage()); + } + if (!universal_logs_.isEmpty()) { + Auxiliaries.logger().info("Tree cutting: Universal logs:" + universal_logs_.stream().map(Block::toString).collect(Collectors.joining()) + "."); } - } } - return to_decay; - } - private static void breakBlock(Level world, BlockPos pos) - { - Block.dropResources(world.getBlockState(pos), world, pos); - world.setBlock(pos, world.getFluidState(pos).createLegacyBlock(), 1|2|8); - } + // ------------------------------------------------------------------------------------------------------------------- - public static int chopTree(Level world, BlockState broken_state, BlockPos startPos, int max_blocks_to_break, boolean without_target_block) - { - if(world.isClientSide) return 0; - if(universal_logs_.contains(broken_state.getBlock())) broken_state = world.getBlockState(startPos.above()); // For safe detection, at least the block above must be a normal log block. - if(!isLog(broken_state)) return 0; - final long ymin = startPos.getY(); - final long max_leaf_distance = 8; - Set checked = new HashSet<>(); - ArrayList to_break = new ArrayList<>(); - ArrayList to_decay = new ArrayList<>(); - checked.add(startPos); - // Initial simple layer-up search of same logs. This forms the base corpus, and only leaves and - // leaf-enclosed logs attached to this corpus may be broken/decayed. - { - LinkedList queue = new LinkedList<>(); - LinkedList upqueue = new LinkedList<>(); - queue.add(startPos); - int cutlevel = 0; - int steps_left = 128; - while(!queue.isEmpty() && (--steps_left >= 0)) { - final BlockPos pos = queue.removeFirst(); - // Vertical search - final BlockPos uppos = pos.above(); - final BlockState upstate = world.getBlockState(uppos); - if(!checked.contains(uppos)) { - checked.add(uppos); - if(isSameLog(upstate, broken_state)) { - // Up is log - upqueue.add(uppos); - to_break.add(uppos); - steps_left = 128; - } else { - boolean isleaf = isLeaves(upstate); - if(isleaf || world.isEmptyBlock(uppos) || (upstate.getBlock() instanceof VineBlock)) { - if(isleaf) to_decay.add(uppos); - // Up is air, check adjacent for diagonal up (e.g. Accacia) - for(Vec3i v:hoffsets) { - final BlockPos p = uppos.offset(v); - if(checked.contains(p)) continue; - checked.add(p); - final BlockState st = world.getBlockState(p); - final Block bl = st.getBlock(); - if(isSameLog(st, broken_state)) { - queue.add(p); - to_break.add(p); - } else if(isLeaves(st)) { - to_decay.add(p); + public static boolean canChop(Level world, BlockState state, BlockPos pos) { + return isLog(state) || (universal_logs_.contains(state.getBlock()) && isLog(world.getBlockState(pos.above()))); + } + + private static boolean isLog(BlockState state) { + return (state.is(BlockTags.LOGS)); + } + + private static boolean isSameLog(BlockState a, BlockState b) { + final Block ba = a.getBlock(); + final Block bb = b.getBlock(); + return (ba == bb) || (universal_logs_.contains(ba) && isLog(b)) || (universal_logs_.contains(bb) && isLog(a)) || (universal_logs_.contains(ba) && universal_logs_.contains(bb)); + } + + private static boolean isLeaves(BlockState state) { + if (state.getBlock() instanceof LeavesBlock) return true; + if (state.is(BlockTags.LEAVES)) return true; + return false; + } + + private static List findBlocksAround(final Level world, final BlockPos centerPos, final BlockState leaf_type_state, final Set checked, int recursion_left) { + ArrayList to_decay = new ArrayList<>(); + for (int y = -1; y <= 1; ++y) { + final BlockPos layer = centerPos.offset(0, y, 0); + for (Vec3i v : hoffsets) { + BlockPos pos = layer.offset(v); + if ((!checked.contains(pos)) && (world.getBlockState(pos).getBlock() == leaf_type_state.getBlock())) { + checked.add(pos); + to_decay.add(pos); + if (recursion_left > 0) { + to_decay.addAll(findBlocksAround(world, pos, leaf_type_state, checked, recursion_left - 1)); + } } - } } - } } - // Lateral search - for(Vec3i v:hoffsets) { - final BlockPos p = pos.offset(v); - if(checked.contains(p)) continue; - checked.add(p); - if(p.distSqr(new BlockPos(startPos.getX(), p.getY(), startPos.getZ())) > (cutlevel > 2 ? 256 : 9)) continue; - final BlockState st = world.getBlockState(p); - final Block bl = st.getBlock(); - if(isSameLog(st, broken_state)) { - queue.add(p); - to_break.add(p); - } else if(isLeaves(st)) { - queue.add(p); - to_decay.add(p); - } + return to_decay; + } + + private static void breakBlock(Level world, BlockPos pos) { + Block.dropResources(world.getBlockState(pos), world, pos); + world.setBlock(pos, world.getFluidState(pos).createLegacyBlock(), 1 | 2 | 8); + } + + public static int chopTree(Level world, BlockState broken_state, BlockPos startPos, int max_blocks_to_break, boolean without_target_block) { + if (world.isClientSide) return 0; + if (universal_logs_.contains(broken_state.getBlock())) + broken_state = world.getBlockState(startPos.above()); // For safe detection, at least the block above must be a normal log block. + if (!isLog(broken_state)) return 0; + final long ymin = startPos.getY(); + final long max_leaf_distance = 8; + Set checked = new HashSet<>(); + ArrayList to_break = new ArrayList<>(); + ArrayList to_decay = new ArrayList<>(); + checked.add(startPos); + // Initial simple layer-up search of same logs. This forms the base corpus, and only leaves and + // leaf-enclosed logs attached to this corpus may be broken/decayed. + { + LinkedList queue = new LinkedList<>(); + LinkedList upqueue = new LinkedList<>(); + queue.add(startPos); + int cutlevel = 0; + int steps_left = 128; + while (!queue.isEmpty() && (--steps_left >= 0)) { + final BlockPos pos = queue.removeFirst(); + // Vertical search + final BlockPos uppos = pos.above(); + final BlockState upstate = world.getBlockState(uppos); + if (!checked.contains(uppos)) { + checked.add(uppos); + if (isSameLog(upstate, broken_state)) { + // Up is log + upqueue.add(uppos); + to_break.add(uppos); + steps_left = 128; + } else { + boolean isleaf = isLeaves(upstate); + if (isleaf || world.isEmptyBlock(uppos) || (upstate.getBlock() instanceof VineBlock)) { + if (isleaf) to_decay.add(uppos); + // Up is air, check adjacent for diagonal up (e.g. Accacia) + for (Vec3i v : hoffsets) { + final BlockPos p = uppos.offset(v); + if (checked.contains(p)) continue; + checked.add(p); + final BlockState st = world.getBlockState(p); + final Block bl = st.getBlock(); + if (isSameLog(st, broken_state)) { + queue.add(p); + to_break.add(p); + } else if (isLeaves(st)) { + to_decay.add(p); + } + } + } + } + } + // Lateral search + for (Vec3i v : hoffsets) { + final BlockPos p = pos.offset(v); + if (checked.contains(p)) continue; + checked.add(p); + if (p.distSqr(new BlockPos(startPos.getX(), p.getY(), startPos.getZ())) > (cutlevel > 2 ? 256 : 9)) + continue; + final BlockState st = world.getBlockState(p); + final Block bl = st.getBlock(); + if (isSameLog(st, broken_state)) { + queue.add(p); + to_break.add(p); + } else if (isLeaves(st)) { + queue.add(p); + to_decay.add(p); + } + } + if (queue.isEmpty() && (!upqueue.isEmpty())) { + queue = upqueue; + upqueue = new LinkedList<>(); + ++cutlevel; + } + } } - if(queue.isEmpty() && (!upqueue.isEmpty())) { - queue = upqueue; - upqueue = new LinkedList<>(); - ++cutlevel; + { + // Determine lose logs between the leafs + for (BlockPos pos : to_decay) { + int dist = 1; + to_break.addAll(findBlocksAround(world, pos, broken_state, checked, dist)); + } + } + if (!to_decay.isEmpty()) { + final BlockState leaf_type_state = world.getBlockState(to_decay.get(0)); + final ArrayList leafs = to_decay; + to_decay = new ArrayList<>(); + for (BlockPos pos : leafs) { + int dist = 3; + to_decay.add(pos); + to_decay.addAll(findBlocksAround(world, pos, leaf_type_state, checked, dist)); + } + } + if (without_target_block) { + checked.remove(startPos); + } else { + to_break.add(startPos); + } + for (BlockPos pos : to_break) breakBlock(world, pos); + for (BlockPos pos : to_decay) breakBlock(world, pos); + { + // And now the bill. + return Mth.clamp(((to_break.size() * 6 / 5) + (to_decay.size() / 10) - 1), 1, 65535); } - } } - { - // Determine lose logs between the leafs - for(BlockPos pos:to_decay) { - int dist = 1; - to_break.addAll(findBlocksAround(world, pos, broken_state, checked, dist)); - } - } - if(!to_decay.isEmpty()) { - final BlockState leaf_type_state = world.getBlockState(to_decay.get(0)); - final ArrayList leafs = to_decay; - to_decay = new ArrayList<>(); - for(BlockPos pos:leafs) { - int dist = 3; - to_decay.add(pos); - to_decay.addAll(findBlocksAround(world, pos, leaf_type_state, checked, dist)); - } - } - if(without_target_block) { - checked.remove(startPos); - } else { - to_break.add(startPos); - } - for(BlockPos pos:to_break) breakBlock(world, pos); - for(BlockPos pos:to_decay) breakBlock(world, pos); - { - // And now the bill. - return Mth.clamp(((to_break.size()*6/5)+(to_decay.size()/10)-1), 1, 65535); - } - } } diff --git a/src/main/java/dev/zontreck/engineerdecor/eapi/jei/JEIPlugin.java b/src/main/java/dev/zontreck/engineerdecor/eapi/jei/JEIPlugin.java index a704262..6d688cc 100644 --- a/src/main/java/dev/zontreck/engineerdecor/eapi/jei/JEIPlugin.java +++ b/src/main/java/dev/zontreck/engineerdecor/eapi/jei/JEIPlugin.java @@ -8,7 +8,8 @@ */ package dev.zontreck.engineerdecor.eapi.jei; -public class JEIPlugin {} +public class JEIPlugin { +} /* import mezz.jei.api.constants.RecipeTypes; diff --git a/src/main/java/dev/zontreck/engineerdecor/items/EdItem.java b/src/main/java/dev/zontreck/engineerdecor/items/EdItem.java index 3cbb714..cd17be9 100644 --- a/src/main/java/dev/zontreck/engineerdecor/items/EdItem.java +++ b/src/main/java/dev/zontreck/engineerdecor/items/EdItem.java @@ -8,35 +8,26 @@ */ package dev.zontreck.engineerdecor.items; +import dev.zontreck.libzontreck.edlibmc.Auxiliaries; import net.minecraft.network.chat.Component; -import net.minecraft.world.item.CreativeModeTab; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.level.Level; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -import dev.zontreck.engineerdecor.ModConfig; -import dev.zontreck.engineerdecor.libmc.Auxiliaries; -import dev.zontreck.engineerdecor.libmc.Registries; import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; import java.util.List; -public class EdItem extends Item -{ - public EdItem(Item.Properties properties) - { super(properties.tab(Registries.getCreativeModeTab())); } +public class EdItem extends Item { + public EdItem(Item.Properties properties) { + super(properties); + } - @Override - @OnlyIn(Dist.CLIENT) - public void appendHoverText(ItemStack stack, @Nullable Level world, List tooltip, TooltipFlag flag) - { Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true); } - - @Override - public Collection getCreativeTabs() - { return ModConfig.isOptedOut(this) ? (new ArrayList<>()) : (Collections.singletonList(Registries.getCreativeModeTab())); } + @Override + @OnlyIn(Dist.CLIENT) + public void appendHoverText(ItemStack stack, @Nullable Level world, List tooltip, TooltipFlag flag) { + Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true); + } }