Update to 1.20.x

This commit is contained in:
Zontreck 2024-02-28 15:59:48 -07:00
parent 27a3496310
commit 10af6a66f9
43 changed files with 10836 additions and 10572 deletions

View file

@ -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<CommonConfig, ForgeConfigSpec> common_ = (new ForgeConfigSpec.Builder()).configure(CommonConfig::new);
COMMON_CONFIG_SPEC = common_.getRight();
COMMON = common_.getLeft();
final Pair<ServerConfig, ForgeConfigSpec> 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<String> pattern_excludes;
public final ForgeConfigSpec.ConfigValue<String> 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<String> 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<CommonConfig, ForgeConfigSpec> common_ = (new ForgeConfigSpec.Builder()).configure(CommonConfig::new);
COMMON_CONFIG_SPEC = common_.getRight();
COMMON = common_.getLeft();
final Pair<ServerConfig, ForgeConfigSpec> 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<String> 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<List<String>> 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<String> 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<String> includes = new ArrayList<>();
final ArrayList<String> 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<String> 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<String> 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<String> includes = new ArrayList<>();
final ArrayList<String> 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<String> 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<String> 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<String> pattern_excludes;
public final ForgeConfigSpec.ConfigValue<String> 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<String> 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<List<String>> 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();
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -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());
}
}
}
}
}

View file

@ -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<BreakerTileEntity>
{
public static final BooleanProperty ACTIVE = BooleanProperty.create("active");
public static class BreakerBlock extends StandardBlocks.HorizontalWaterLoggable implements StandardEntityBlocks.IStandardEntityBlock<BreakerTileEntity> {
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<Block, BlockState> 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<IEnergyStorage> 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 <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing)
{
if(capability == ForgeCapabilities.ENERGY) return energy_handler_.cast();
return super.getCapability(capability, facing);
}
// ITickable ------------------------------------------------------------------------------------
private static final HashSet<Block> 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<ItemStack> 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<Block, BlockState> 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<Block> 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<IEnergyStorage> 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 <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> 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<ItemStack> 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);
}
}
}
}

View file

@ -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<Direction> 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<ItemStack> dropList(BlockState state, Level world, @Nullable BlockEntity te, boolean explosion)
{
if(world.isClientSide()) return Collections.singletonList(ItemStack.EMPTY);
List<ItemStack> 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<Direction> 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<ItemStack> dropList(BlockState state, Level world, @Nullable BlockEntity te, boolean explosion) {
if (world.isClientSide()) return Collections.singletonList(ItemStack.EMPTY);
List<ItemStack> 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;
}
}

View file

@ -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<BlockState, VoxelShape> shapes;
protected final Map<BlockState, VoxelShape> collision_shapes;
protected final Map<Direction, Integer> 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<BlockState, VoxelShape> shapes;
protected final Map<BlockState, VoxelShape> collision_shapes;
protected final Map<Direction, Integer> y_rotations;
public EdCatwalkStairsBlock(long config, BlockBehaviour.Properties properties, final AABB[] base_aabb, final AABB[] railing_aabbs)
{
super(config, properties, base_aabb);
Map<BlockState, VoxelShape> sh = new HashMap<>();
Map<BlockState, VoxelShape> 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<Block, BlockState> 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<BlockState, VoxelShape> sh = new HashMap<>();
Map<BlockState, VoxelShape> 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<ItemStack> dropList(BlockState state, Level world, @Nullable BlockEntity te, boolean explosion)
{
if(world.isClientSide()) return Collections.singletonList(ItemStack.EMPTY);
List<ItemStack> 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<Block, BlockState> 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<ItemStack> dropList(BlockState state, Level world, @Nullable BlockEntity te, boolean explosion) {
if (world.isClientSide()) return Collections.singletonList(ItemStack.EMPTY);
List<ItemStack> 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;
}
}

View file

@ -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<VoxelShape> 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<VoxelShape> 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<variant_shapes.length) ? (variant_shapes[i]) : (Shapes.block())).collect(Collectors.toList());
this.inset_light_block = inset_light_block;
}
@Override
public boolean propagatesSkylightDown(BlockState state, BlockGetter reader, BlockPos pos)
{ return true; }
@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); }
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> 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<Block, BlockState> 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<ItemStack> dropList(BlockState state, Level world, @Nullable BlockEntity te, boolean explosion)
{
if(world.isClientSide()) return Collections.singletonList(ItemStack.EMPTY);
List<ItemStack> 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<ItemStack> dropList(BlockState state, Level world, @Nullable BlockEntity te, boolean explosion) {
if (world.isClientSide()) return Collections.singletonList(ItemStack.EMPTY);
List<ItemStack> 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;
}
}

View file

@ -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<Mob> 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<? extends Entity> 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<Mob> 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<? extends Entity> 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<ClientGamePacketListener> 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);
}
}
}
}

View file

@ -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<Block, BlockState> 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<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
);
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<Block, BlockState> 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
);
}
}
}
}

View file

@ -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));
}
}

View file

@ -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<Block> compatible_blocks;
public class EdCornerOrnamentedBlock extends StandardBlocks.Directed {
protected final HashSet<Block> 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);
}
}
}

View file

@ -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<VoxelShape> 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<VoxelShape> 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<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
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<Block, BlockState> 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<Block, BlockState> 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);
}
}

File diff suppressed because it is too large Load diff

View file

@ -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);
}
}

View file

@ -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);
}
}
}
}

View file

@ -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<FluidBarrelTileEntity>
{
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<FluidBarrelTileEntity> {
public static final int FILL_LEVEL_MAX = 4;
public static final IntegerProperty FILL_LEVEL = IntegerProperty.create("level", 0, FILL_LEVEL_MAX);
@Override
public List<ItemStack> dropList(BlockState state, Level world, final BlockEntity te, boolean explosion)
{
final List<ItemStack> 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<Component> 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<Block, BlockState> 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<IFluidHandler> 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<Fluid,Integer> 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<ItemStack> dropList(BlockState state, Level world, final BlockEntity te, boolean explosion) {
final List<ItemStack> 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<Component> 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<Block, BlockState> 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<IFluidHandler> 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<Fluid, Integer> 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 <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> 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 <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> 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);
}
}
}

View file

@ -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<FluidFunnelTileEntity>
{
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<Block, BlockState> 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<ItemStack> dropList(BlockState state, Level world, final BlockEntity te, boolean explosion)
{
final List<ItemStack> 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<FluidFunnelTileEntity> {
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<Vec3i> search_offsets_ = null;
private final Fluidics.Tank tank_ = new Fluidics.Tank(TANK_CAPACITY, 0, TANK_CAPACITY);
private final LazyOptional<IFluidHandler> 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 <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> 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<Vec3i> 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<Vec3i> 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<BlockPos> checked = new HashSet<>();
Stack<BlockPos> 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);
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> 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<ItemStack> dropList(BlockState state, Level world, final BlockEntity te, boolean explosion) {
final List<ItemStack> 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<IFluidHandler> 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<Vec3i> 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 <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> 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<Vec3i> 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<Vec3i> 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<BlockPos> checked = new HashSet<>();
Stack<BlockPos> 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();
}
}
}

View file

@ -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<FreezerTileEntity>
{
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<Block, BlockState> 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<ItemStack> dropList(BlockState state, Level world, BlockEntity te, boolean explosion)
{
final List<ItemStack> 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<FreezerTileEntity> {
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<Block, BlockState> 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<ItemStack> dropList(BlockState state, Level world, BlockEntity te, boolean explosion) {
final List<ItemStack> 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<IItemHandler> item_handler_ = LazyOptional.of(() -> new FreezerItemHandler(this));
private final LazyOptional<IFluidHandler> fluid_handler_ = LazyOptional.of(() -> new Fluidics.SingleTankFluidHandler(tank_));
protected LazyOptional<IEnergyStorage> 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 <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> 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<IItemHandler> 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<IFluidHandler> fluid_handler_ = LazyOptional.of(() -> new Fluidics.SingleTankFluidHandler(tank_));
// IEnergyStorage ----------------------------------------------------------------------------
protected LazyOptional<IEnergyStorage> 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 <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> 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();
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -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<Component> tooltip, TooltipFlag flag)
{ Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true); }
@Override
@OnlyIn(Dist.CLIENT)
public void appendHoverText(ItemStack stack, @Nullable BlockGetter world, List<Component> 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;
}
}

View file

@ -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<VoxelShape> 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<VoxelShape> 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<VoxelShape> 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<VoxelShape> 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<Block, BlockState> 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<Block, BlockState> 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);
}
}

File diff suppressed because it is too large Load diff

View file

@ -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)

View file

@ -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<Component> 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<Component> 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;
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -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<MineralSmelterTileEntity>
{
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<Block, BlockState> 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<ItemStack> dropList(BlockState state, Level world, BlockEntity te, boolean explosion)
{
final List<ItemStack> 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<player.getInventory().getContainerSize(); ++i) {
if(player.getInventory().getItem(i).isEmpty()) {
target_stack_index = i;
break;
//--------------------------------------------------------------------------------------------------------------------
// Block
//--------------------------------------------------------------------------------------------------------------------
public static class MineralSmelterBlock extends StandardBlocks.Horizontal implements StandardEntityBlocks.IStandardEntityBlock<MineralSmelterTileEntity> {
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<Block, BlockState> 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<ItemStack> dropList(BlockState state, Level world, BlockEntity te, boolean explosion) {
final List<ItemStack> 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<Item> accepted_minerals = new HashSet<>();
private static final Set<Item> 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<IEnergyStorage> energy_handler_ = battery_.createEnergyHandler();
private final Fluidics.Tank tank_ = new Fluidics.Tank(MAX_FLUID_LEVEL, 0, 100);
private final LazyOptional<? extends IFluidHandler> fluid_handler_ = tank_.createOutputFluidHandler();
private final Inventories.StorageInventory main_inventory_;
private final LazyOptional<? extends IItemHandler> 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 <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> 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.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<Item> accepted_minerals = new HashSet<>();
private static final Set<Item> 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<IEnergyStorage> energy_handler_ = battery_.createEnergyHandler();
private final Fluidics.Tank tank_ = new Fluidics.Tank(MAX_FLUID_LEVEL, 0, 100);
private final LazyOptional<? extends IFluidHandler> fluid_handler_ = tank_.createOutputFluidHandler();
private final Inventories.StorageInventory main_inventory_;
private final LazyOptional<? extends IItemHandler> 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 <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> 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();
}
}
}

View file

@ -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<PipeValveTileEntity>
{
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<PipeValveTileEntity> {
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<Block, BlockState> 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<IFluidHandler> back_flow_handler_ = LazyOptional.of(BackFlowHandler::new);
private final LazyOptional<IFluidHandler> fluid_handler_ = LazyOptional.of(() -> new MainFlowHandler(this));
@Override
public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> 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<Block, BlockState> 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<IFluidHandler> back_flow_handler_ = LazyOptional.of(BackFlowHandler::new);
private final LazyOptional<IFluidHandler> 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 <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> 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;
}
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -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<StairsShape> SHAPE = BlockStateProperties.STAIRS_SHAPE;
public static final EnumProperty<Half> HALF = BlockStateProperties.HALF;
private final VoxelShape[][][] shape_cache_;
public class EdRoofBlock extends StandardBlocks.HorizontalWaterLoggable {
public static final EnumProperty<StairsShape> SHAPE = BlockStateProperties.STAIRS_SHAPE;
public static final EnumProperty<Half> 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<Block, BlockState> 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<Half.values().length; ++half_index) {
for(int direction_index=0; direction_index<Direction.values().length; ++direction_index) {
for(int stairs_shape_index=0; stairs_shape_index<StairsShape.values().length; ++stairs_shape_index) {
VoxelShape shape = makeShape(half_index, direction_index, stairs_shape_index);
try {
// Only in case something changes and this fails, log but do not prevent the game from starting.
// Roof shapes are not the most important thing in the world.
if(!add.isEmpty()) shape = Shapes.joinUnoptimized(shape, add, BooleanOp.OR);
if(!cut.isEmpty()) shape = Shapes.joinUnoptimized(shape, cut, BooleanOp.ONLY_FIRST);
} catch(Throwable ex) {
Auxiliaries.logError("Failed to cut shape using Boolean function. This is bug.");
}
shapes[half_index][direction_index][stairs_shape_index] = shape;
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 VoxelShape[][][] makeShapes(VoxelShape add, VoxelShape cut) {
VoxelShape[][][] shapes = new VoxelShape[2][6][5];
for (int half_index = 0; half_index < Half.values().length; ++half_index) {
for (int direction_index = 0; direction_index < Direction.values().length; ++direction_index) {
for (int stairs_shape_index = 0; stairs_shape_index < StairsShape.values().length; ++stairs_shape_index) {
VoxelShape shape = makeShape(half_index, direction_index, stairs_shape_index);
try {
// Only in case something changes and this fails, log but do not prevent the game from starting.
// Roof shapes are not the most important thing in the world.
if (!add.isEmpty()) shape = Shapes.joinUnoptimized(shape, add, BooleanOp.OR);
if (!cut.isEmpty()) shape = Shapes.joinUnoptimized(shape, cut, BooleanOp.ONLY_FIRST);
} catch (Throwable ex) {
Auxiliaries.logError("Failed to cut shape using Boolean function. This is bug.");
}
shapes[half_index][direction_index][stairs_shape_index] = shape;
}
}
}
}
return shapes;
}
return shapes;
}
private static VoxelShape makeShape(int half_index, int direction_index, int stairs_shape_index)
{
AABB[] straight = new AABB[]{
Auxiliaries.getPixeledAABB( 0, 0, 0, 16, 4, 16),
Auxiliaries.getPixeledAABB( 4, 4, 0, 16, 8, 16),
Auxiliaries.getPixeledAABB( 8, 8, 0, 16, 12, 16),
Auxiliaries.getPixeledAABB(12, 12, 0, 16, 16, 16)
};
AABB[] pyramid = new AABB[]{
Auxiliaries.getPixeledAABB( 0, 0, 0, 16, 4, 16),
Auxiliaries.getPixeledAABB( 4, 4, 4, 16, 8, 16),
Auxiliaries.getPixeledAABB( 8, 8, 8, 16, 12, 16),
Auxiliaries.getPixeledAABB(12, 12, 12, 16, 16, 16)
};
final Half half = Half.values()[half_index];
if(half==Half.TOP) {
straight = Auxiliaries.getMirroredAABB(straight, Direction.Axis.Y);
pyramid = Auxiliaries.getMirroredAABB(pyramid, Direction.Axis.Y);
private static VoxelShape makeShape(int half_index, int direction_index, int stairs_shape_index) {
AABB[] straight = new AABB[]{
Auxiliaries.getPixeledAABB(0, 0, 0, 16, 4, 16),
Auxiliaries.getPixeledAABB(4, 4, 0, 16, 8, 16),
Auxiliaries.getPixeledAABB(8, 8, 0, 16, 12, 16),
Auxiliaries.getPixeledAABB(12, 12, 0, 16, 16, 16)
};
AABB[] pyramid = new AABB[]{
Auxiliaries.getPixeledAABB(0, 0, 0, 16, 4, 16),
Auxiliaries.getPixeledAABB(4, 4, 4, 16, 8, 16),
Auxiliaries.getPixeledAABB(8, 8, 8, 16, 12, 16),
Auxiliaries.getPixeledAABB(12, 12, 12, 16, 16, 16)
};
final Half half = Half.values()[half_index];
if (half == Half.TOP) {
straight = Auxiliaries.getMirroredAABB(straight, Direction.Axis.Y);
pyramid = Auxiliaries.getMirroredAABB(pyramid, Direction.Axis.Y);
}
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)
);
};
}
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<Block, BlockState> 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;
}
}

View file

@ -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<SolarPanelTileEntity>
{
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<Block, BlockState> builder)
{ super.createBlockStateDefinition(builder); builder.add(EXPOSITION); }
public static class SolarPanelBlock extends StandardBlocks.Cutout implements StandardEntityBlocks.IStandardEntityBlock<SolarPanelTileEntity> {
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<IEnergyStorage> 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 <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> 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<SolarPanelTileEntity> 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;
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<Block, BlockState> 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<IEnergyStorage> 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 <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> 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<SolarPanelTileEntity> 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;
}
}
}

View file

@ -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());
}
}

View file

@ -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<TestTileEntity>, 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<ItemStack> 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<IEnergyStorage> energy_handler_;
private final Fluidics.Tank tank_;
private final LazyOptional<IFluidHandler> fluid_handler_;
private final Inventories.StorageInventory inventory_;
private final LazyOptional<IItemHandler> 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<String> 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<TestTileEntity>, 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<ItemStack> 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 <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> 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<IEnergyStorage> energy_handler_;
private final Fluidics.Tank tank_;
private final LazyOptional<IFluidHandler> fluid_handler_;
private final Inventories.StorageInventory inventory_;
private final LazyOptional<IItemHandler> 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<String> 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 <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> 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;
}
}
}
}

View file

@ -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<TreeCutterTileEntity> {
public static final BooleanProperty ACTIVE = BooleanProperty.create("active");
public static class TreeCutterBlock extends StandardBlocks.Horizontal implements StandardEntityBlocks.IStandardEntityBlock<TreeCutterTileEntity>
{
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<Block, BlockState> 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<Block, BlockState> 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<IEnergyStorage> 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 <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> 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<IEnergyStorage> 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 <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> 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);
}
}
}
}

View file

@ -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;
}
}

View file

@ -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<WasteIncineratorTileEntity>
{
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<Block, BlockState> 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<ItemStack> dropList(BlockState state, Level world, final BlockEntity te, boolean explosion)
{
final List<ItemStack> 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<WasteIncineratorTileEntity> {
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<Block, BlockState> 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<ItemStack> dropList(BlockState state, Level world, final BlockEntity te, boolean explosion) {
final List<ItemStack> 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<? extends IItemHandler> 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<IEnergyStorage> 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<? extends IItemHandler> 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<IEnergyStorage> 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 <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> 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 <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> 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<WasteIncineratorContainer> {
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)) {
// 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;
}
}
//--------------------------------------------------------------------------------------------------------------------
// GUI
//--------------------------------------------------------------------------------------------------------------------
@OnlyIn(Dist.CLIENT)
public static class WasteIncineratorGui extends Guis.ContainerGui<WasteIncineratorContainer>
{
public WasteIncineratorGui(WasteIncineratorContainer container, Inventory player_inventory, Component title)
{ super(container, player_inventory, title, "textures/gui/small_waste_incinerator_gui.png"); }
}
}

View file

@ -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;
}
}

View file

@ -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<ItemStack> dropList(BlockState state, Level world, @Nullable BlockEntity te, boolean explosion)
{
if(world.isClientSide()) return Collections.singletonList(ItemStack.EMPTY);
List<ItemStack> 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<ItemStack> dropList(BlockState state, Level world, @Nullable BlockEntity te, boolean explosion) {
if (world.isClientSide()) return Collections.singletonList(ItemStack.EMPTY);
List<ItemStack> 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;
}
}

View file

@ -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)
{
}
}

View file

@ -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<T extends Entity> extends EntityRenderer<T>
{
private final Minecraft mc = Minecraft.getInstance();
@OnlyIn(Dist.CLIENT)
public static class InvisibleEntityRenderer<T extends Entity> extends EntityRenderer<T> {
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) {
}
}
}

View file

@ -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<Block> universal_logs_ = new HashSet<>();
public class TreeCutting {
private static final Set<Block> universal_logs_ = new HashSet<>();
private static final List<Vec3i> 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<String> 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<Vec3i> 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<BlockPos> findBlocksAround(final Level world, final BlockPos centerPos, final BlockState leaf_type_state, final Set<BlockPos> checked, int recursion_left)
{
ArrayList<BlockPos> 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<String> 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<BlockPos> checked = new HashSet<>();
ArrayList<BlockPos> to_break = new ArrayList<>();
ArrayList<BlockPos> 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<BlockPos> queue = new LinkedList<>();
LinkedList<BlockPos> 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<BlockPos> findBlocksAround(final Level world, final BlockPos centerPos, final BlockState leaf_type_state, final Set<BlockPos> checked, int recursion_left) {
ArrayList<BlockPos> 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<BlockPos> checked = new HashSet<>();
ArrayList<BlockPos> to_break = new ArrayList<>();
ArrayList<BlockPos> 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<BlockPos> queue = new LinkedList<>();
LinkedList<BlockPos> 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<BlockPos> 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<BlockPos> 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);
}
}
}

View file

@ -8,7 +8,8 @@
*/
package dev.zontreck.engineerdecor.eapi.jei;
public class JEIPlugin {}
public class JEIPlugin {
}
/*
import mezz.jei.api.constants.RecipeTypes;

View file

@ -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<Component> tooltip, TooltipFlag flag)
{ Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true); }
@Override
public Collection<CreativeModeTab> getCreativeTabs()
{ return ModConfig.isOptedOut(this) ? (new ArrayList<>()) : (Collections.singletonList(Registries.getCreativeModeTab())); }
@Override
@OnlyIn(Dist.CLIENT)
public void appendHoverText(ItemStack stack, @Nullable Level world, List<Component> tooltip, TooltipFlag flag) {
Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true);
}
}