1.12: Release commit. Backported right-click stats for Solar Panel, Block Breaker, Tree Cutter.

This commit is contained in:
stfwi 2020-02-29 10:01:13 +01:00
parent adc0df9d47
commit cb9932ca1b
31 changed files with 842 additions and 110 deletions

View file

@ -4,4 +4,4 @@ org.gradle.jvmargs=-Xmx8G
version_minecraft=1.12.2
version_forge=14.23.5.2768
version_jei=4.10.0.198
version_engineersdecor=1.0.19-b4
version_engineersdecor=1.0.19

View file

@ -1,6 +1,7 @@
{
"homepage": "https://www.curseforge.com/minecraft/mc-mods/engineers-decor/",
"1.12.2": {
"1.0.19": "[R] Release based on v1.0.19-b4. Release-to-release changes: * Transfer fixes for Tree Cutter / Block Braker, and Factory hopper. * Cleanups, feature backports * Visual fixes and improvements\n[A] Backport of status display for Tree Cutter, Block Breaker and Solar Panel.",
"1.0.19-b4": "[A] Creative tab opt-out visibility handling added (issue #90, thx pimalel233).",
"1.0.19-b3": "[A] Factory Hopper: Added bottom item handler (CR#227).",
"1.0.19-b2": "[F] Fixed Floor Grating item pass-through jitters (thx Cid).\n[M] Removed obsolete recipe collision testing recipes.",
@ -80,7 +81,7 @@
"1.0.0-b1": "[A] Initial structure.\n[A] Added clinker bricks and clinker brick stairs.\n[A] Added slag bricks and slag brick stairs.\n[A] Added metal rung ladder.\n[A] Added staggered metal steps ladder.\n[A] Added treated wood ladder.\n[A] Added treated wood pole.\n[A] Added treated wood table."
},
"promos": {
"1.12.2-recommended": "1.0.18",
"1.12.2-latest": "1.0.19-b4"
"1.12.2-recommended": "1.0.19",
"1.12.2-latest": "1.0.19"
}
}

View file

@ -10,6 +10,14 @@ Mod sources for Minecraft version 1.12.2.
----
## Version history
-------------------------------------------------------------------
- v1.0.19 [R] Release based on v1.0.19-b4. Release-to-release changes:
* Transfer fixes for Tree Cutter / Block Braker, and Factory hopper.
* Cleanups, feature backports
* Visual fixes and improvements
-------------------------------------------------------------------
[A] Backport of status display for Tree Cutter, Block Breaker and Solar Panel.
- v1.0.19-b4 [A] Creative tab opt-out visibility handling added (issue #90, thx pimalel233).
- v1.0.19-b3 [A] Factory Hopper: Added bottom item handler (CR#227).

View file

@ -9,6 +9,7 @@
package wile.engineersdecor.blocks;
import wile.engineersdecor.ModEngineersDecor;
import wile.engineersdecor.detail.ModAuxiliaries;
import net.minecraft.world.World;
import net.minecraft.block.Block;
import net.minecraft.block.properties.PropertyBool;
@ -17,7 +18,9 @@ import net.minecraft.block.SoundType;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.init.Blocks;
import net.minecraft.init.SoundEvents;
import net.minecraft.util.math.AxisAlignedBB;
@ -96,6 +99,16 @@ public class BlockDecorBreaker extends BlockDecorDirectedHorizontal
((BTileEntity)te).block_updated();
}
@Override
public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ)
{
if(world.isRemote) return true;
TileEntity te = world.getTileEntity(pos);
if(!(te instanceof BTileEntity)) return true;
((BTileEntity)te).state_message(player);
return true;
}
//--------------------------------------------------------------------------------------------------------------------
// Tile entity
//--------------------------------------------------------------------------------------------------------------------
@ -110,17 +123,20 @@ public class BlockDecorBreaker extends BlockDecorDirectedHorizontal
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 = 10000;
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 int tick_timer_;
private int active_timer_;
private int proc_time_elapsed_;
private int time_needed_;
private int energy_;
public static void on_config(int boost_energy_per_tick, int breaking_time_per_hardness, int min_breaking_time_ticks, boolean power_required)
{
boost_energy_consumption = TICK_INTERVAL * MathHelper.clamp(boost_energy_per_tick, 16, 512);
energy_max = Math.max(boost_energy_consumption * 10, 10000);
breaking_reluctance = MathHelper.clamp(breaking_time_per_hardness, 5, 50);
min_breaking_time = MathHelper.clamp(min_breaking_time_ticks, 10, 100);
requires_power = power_required;
@ -133,12 +149,36 @@ public class BlockDecorBreaker extends BlockDecorDirectedHorizontal
public void block_updated()
{ if(tick_timer_ > 2) tick_timer_ = 2; }
public void state_message(EntityPlayer player)
{
String soc = Integer.toString(MathHelper.clamp((energy_*100/energy_max),0,100));
String progress = "";
if((proc_time_elapsed_ > 0) && (time_needed_ > 0) && (active_timer_ > 0)) {
progress = " | " + Integer.toString((int)MathHelper.clamp((((double)proc_time_elapsed_) / ((double)time_needed_) * 100), 0, 100)) + "%%";
}
ModAuxiliaries.playerChatMessage(player, soc + "%%/" + energy_max + "RF" + progress);
}
public void readnbt(NBTTagCompound nbt)
{ energy_ = nbt.getInteger("energy"); }
private void writenbt(NBTTagCompound nbt)
{ nbt.setInteger("energy", energy_); }
// TileEntity ------------------------------------------------------------------------------
@Override
public boolean shouldRefresh(World world, BlockPos pos, IBlockState os, IBlockState ns)
{ return (os.getBlock() != ns.getBlock()) || (!(ns.getBlock() instanceof BlockDecorBreaker)); }
@Override
public void readFromNBT(NBTTagCompound nbt)
{ super.readFromNBT(nbt); readnbt(nbt); }
@Override
public NBTTagCompound writeToNBT(NBTTagCompound nbt)
{ super.writeToNBT(nbt); writenbt(nbt); return nbt; }
// IEnergyStorage ----------------------------------------------------------------------------
@Override
@ -164,7 +204,7 @@ public class BlockDecorBreaker extends BlockDecorDirectedHorizontal
@Override
public int receiveEnergy(int maxReceive, boolean simulate)
{
maxReceive = MathHelper.clamp(maxReceive, 0, Math.max((boost_energy_consumption*2) - energy_, 0));
maxReceive = MathHelper.clamp(maxReceive, 0, Math.max(energy_max-energy_, 0));
if(!simulate) energy_ += maxReceive;
return maxReceive;
}
@ -253,11 +293,11 @@ public class BlockDecorBreaker extends BlockDecorDirectedHorizontal
tick_timer_ = IDLE_TICK_INTERVAL;
return;
}
int time_needed = MathHelper.clamp((int)(target_state.getBlockHardness(world, pos) * breaking_reluctance) + min_breaking_time, min_breaking_time, MAX_BREAKING_TIME);
time_needed_ = MathHelper.clamp((int)(target_state.getBlockHardness(world, pos) * breaking_reluctance) + min_breaking_time, min_breaking_time, MAX_BREAKING_TIME);
if(energy_ >= boost_energy_consumption) {
energy_ -= boost_energy_consumption;
proc_time_elapsed_ += TICK_INTERVAL * (1+BOOST_FACTOR);
time_needed += min_breaking_time * (3*BOOST_FACTOR/5);
time_needed_ += min_breaking_time * (3*BOOST_FACTOR/5);
active_timer_ = 2;
} else if(!requires_power) {
proc_time_elapsed_ += TICK_INTERVAL;
@ -269,7 +309,7 @@ public class BlockDecorBreaker extends BlockDecorDirectedHorizontal
if(requires_power && !active) {
proc_time_elapsed_ = Math.max(0, proc_time_elapsed_ - 2*TICK_INTERVAL);
}
if(proc_time_elapsed_ >= time_needed) {
if(proc_time_elapsed_ >= time_needed_) {
proc_time_elapsed_ = 0;
breakBlock(target_state, target_pos, world);
active = false;

View file

@ -8,10 +8,8 @@
*/
package wile.engineersdecor.blocks;
import net.minecraft.util.math.MathHelper;
import net.minecraftforge.energy.CapabilityEnergy;
import net.minecraftforge.energy.IEnergyStorage;
import wile.engineersdecor.ModEngineersDecor;
import wile.engineersdecor.detail.ModAuxiliaries;
import net.minecraft.block.properties.PropertyInteger;
import net.minecraft.block.state.BlockStateContainer;
import net.minecraft.block.material.Material;
@ -19,6 +17,7 @@ import net.minecraft.block.state.BlockFaceShape;
import net.minecraft.block.state.IBlockState;
import net.minecraft.block.SoundType;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ITickable;
@ -28,6 +27,10 @@ import net.minecraft.util.EnumHand;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraftforge.energy.CapabilityEnergy;
import net.minecraftforge.energy.IEnergyStorage;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -93,6 +96,16 @@ public class BlockDecorSolarPanel extends BlockDecor
public TileEntity createTileEntity(World world, IBlockState state)
{ return new BlockDecorSolarPanel.BTileEntity(); }
@Override
public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ)
{
if(world.isRemote) return true;
TileEntity te = world.getTileEntity(pos);
if(!(te instanceof BTileEntity)) return true;
((BTileEntity)te).state_message(player);
return true;
}
//--------------------------------------------------------------------------------------------------------------------
// Tile entity
//--------------------------------------------------------------------------------------------------------------------
@ -105,9 +118,12 @@ public class BlockDecorSolarPanel extends BlockDecor
private static final EnumFacing transfer_directions_[] = {EnumFacing.DOWN, EnumFacing.EAST, EnumFacing.SOUTH, EnumFacing.WEST, EnumFacing.NORTH };
private static int peak_power_per_tick_ = DEFAULT_PEAK_POWER;
private static int max_power_storage_ = 10000;
private static int max_feed_power = 128;
private int current_production_ = 0;
private int tick_timer_ = 0;
private int recalc_timer_ = 0;
private int accumulated_power_ = 0;
private int current_feedin_ = 0;
public static void on_config(int peak_power_per_tick)
{
@ -126,6 +142,12 @@ public class BlockDecorSolarPanel extends BlockDecor
protected void writenbt(NBTTagCompound nbt, boolean update_packet)
{ nbt.setInteger("energy", accumulated_power_); }
public void state_message(EntityPlayer player)
{
String soc = Integer.toString(MathHelper.clamp((accumulated_power_*100/max_power_storage_),0,100));
ModAuxiliaries.playerChatMessage(player, soc + "%%/" + max_power_storage_ + "RF | +" + + current_production_ + "RF/t | -" + current_feedin_ + "RF/t");
}
// TileEntity ------------------------------------------------------------------------------
@Override
@ -145,7 +167,7 @@ public class BlockDecorSolarPanel extends BlockDecor
{
if((world.isRemote) || (--tick_timer_ > 0)) return;
tick_timer_ = TICK_INTERVAL;
if(!world.canSeeSky(pos)) { tick_timer_ = TICK_INTERVAL * 5; return; }
current_feedin_ = 0;
if(accumulated_power_ > 0) {
for(int i=0; (i<transfer_directions_.length) && (accumulated_power_>0); ++i) {
final EnumFacing f = transfer_directions_[i];
@ -153,9 +175,19 @@ public class BlockDecorSolarPanel extends BlockDecor
if((te==null) || (!(te.hasCapability(CapabilityEnergy.ENERGY, f.getOpposite())))) continue;
IEnergyStorage es = te.getCapability(CapabilityEnergy.ENERGY, f.getOpposite());
if(!es.canReceive()) continue;
accumulated_power_ = MathHelper.clamp(accumulated_power_-es.receiveEnergy(accumulated_power_, false),0, accumulated_power_);
int fed = es.receiveEnergy(max_feed_power * TICK_INTERVAL, false);
accumulated_power_ = MathHelper.clamp(accumulated_power_-fed,0, accumulated_power_);
current_feedin_ += fed;
}
}
current_feedin_ /= TICK_INTERVAL;
if(!world.canSeeSky(pos)) {
tick_timer_ = TICK_INTERVAL * 5;
current_production_ = 0;
IBlockState state = world.getBlockState(pos);
if(state.getValue((EXPOSITION))!=2) world.setBlockState(pos, state.withProperty(EXPOSITION, 2));
return;
}
if(--recalc_timer_ > 0) return;
recalc_timer_ = ACCUMULATION_INTERVAL + ((int)(Math.random()+.5));
IBlockState state = world.getBlockState(pos);
@ -169,9 +201,10 @@ public class BlockDecorSolarPanel extends BlockDecor
else if(theta < 190) e = 4;
IBlockState nstate = state.withProperty(EXPOSITION, e);
if(nstate != state) world.setBlockState(pos, nstate, 1|2);
double rf = Math.abs(1.0-(((double)Math.abs(MathHelper.clamp(theta, 0, 180)-90))/90));
rf = Math.sqrt(rf) * world.getSunBrightnessFactor(1f) * ((TICK_INTERVAL*ACCUMULATION_INTERVAL)+2) * peak_power_per_tick_;
accumulated_power_ = Math.min(accumulated_power_+(int)rf, max_power_storage_);
final double eff = (1.0-((world.getRainStrength(1f)*0.6)+(world.getThunderStrength(1f)*0.3)));
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, 1) * peak_power_per_tick_));
accumulated_power_ = Math.min(accumulated_power_ + (current_production_*(TICK_INTERVAL*ACCUMULATION_INTERVAL)), max_power_storage_);
}
}
}

View file

@ -9,6 +9,7 @@
package wile.engineersdecor.blocks;
import wile.engineersdecor.ModEngineersDecor;
import wile.engineersdecor.detail.ModAuxiliaries;
import wile.engineersdecor.detail.TreeCutting;
import net.minecraft.block.properties.PropertyBool;
import net.minecraft.block.state.BlockStateContainer;
@ -17,18 +18,19 @@ import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.world.World;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.init.SoundEvents;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.*;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.energy.CapabilityEnergy;
import net.minecraftforge.energy.IEnergyStorage;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Random;
@ -86,6 +88,16 @@ public class BlockDecorTreeCutter extends BlockDecorDirectedHorizontal
}
}
@Override
public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ)
{
if(world.isRemote) return true;
TileEntity te = world.getTileEntity(pos);
if(!(te instanceof BTileEntity)) return true;
((BTileEntity)te).state_message(player);
return true;
}
//--------------------------------------------------------------------------------------------------------------------
// Tile entity
//--------------------------------------------------------------------------------------------------------------------
@ -97,6 +109,7 @@ public class BlockDecorTreeCutter extends BlockDecorDirectedHorizontal
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 energy_max = DEFAULT_BOOST_ENERGY * 20;
private static int boost_energy_consumption = DEFAULT_BOOST_ENERGY;
private static int cutting_time_needed = 20 * DEFAULT_CUTTING_TIME_NEEDED;
private static boolean requires_power = false;
@ -109,6 +122,7 @@ public class BlockDecorTreeCutter extends BlockDecorDirectedHorizontal
public static void on_config(int boost_energy_per_tick, int cutting_time_seconds, boolean power_required)
{
boost_energy_consumption = TICK_INTERVAL * MathHelper.clamp(boost_energy_per_tick, 16, 512);
energy_max = Math.max(boost_energy_consumption * 10, 10000);
cutting_time_needed = 20 * MathHelper.clamp(cutting_time_seconds, 10, 240);
requires_power = power_required;
ModEngineersDecor.logger.info("Config tree cutter: Boost energy consumption:" + boost_energy_consumption + "rf/t" + (requires_power?" (power required for operation) ":"") + ", cutting time " + cutting_time_needed + "t." );
@ -117,12 +131,36 @@ public class BlockDecorTreeCutter extends BlockDecorDirectedHorizontal
public BTileEntity()
{}
public void state_message(EntityPlayer player)
{
String soc = Integer.toString(MathHelper.clamp((energy_*100/energy_max),0,100));
String progress = "";
if((active_timer_ > 0) && (cutting_time_needed > 0)) {
progress = " | " + Integer.toString((int)MathHelper.clamp((((double)proc_time_elapsed_) / ((double)cutting_time_needed) * 100), 0, 100)) + "%%";
}
ModAuxiliaries.playerChatMessage(player, soc + "%%/" + energy_max + "RF" + progress);
}
public void readnbt(NBTTagCompound nbt)
{ energy_ = nbt.getInteger("energy"); }
private void writenbt(NBTTagCompound nbt)
{ nbt.setInteger("energy", energy_); }
// TileEntity ------------------------------------------------------------------------------
@Override
public boolean shouldRefresh(World world, BlockPos pos, IBlockState os, IBlockState ns)
{ return (os.getBlock() != ns.getBlock()) || (!(ns.getBlock() instanceof BlockDecorTreeCutter)); }
@Override
public void readFromNBT(NBTTagCompound nbt)
{ super.readFromNBT(nbt); readnbt(nbt); }
@Override
public NBTTagCompound writeToNBT(NBTTagCompound nbt)
{ super.writeToNBT(nbt); writenbt(nbt); return nbt; }
// IEnergyStorage ----------------------------------------------------------------------------
@Override
@ -148,7 +186,7 @@ public class BlockDecorTreeCutter extends BlockDecorDirectedHorizontal
@Override
public int receiveEnergy(int maxReceive, boolean simulate)
{
maxReceive = MathHelper.clamp(maxReceive, 0, Math.max((boost_energy_consumption*2) - energy_, 0));
maxReceive = MathHelper.clamp(maxReceive, 0, Math.max((energy_max) - energy_, 0));
if(!simulate) energy_ += maxReceive;
return maxReceive;
}

View file

@ -2,7 +2,7 @@
org.gradle.daemon=false
org.gradle.jvmargs=-Xmx8G
version_minecraft=1.14.4
version_forge_minecraft=1.14.4-28.2.0
version_forge_minecraft=1.14.4-28.2.2
version_fml_mappings=20190719-1.14.3
version_jei=1.14.4:6.0.0.10
version_engineersdecor=1.0.19-b5

View file

@ -11,9 +11,10 @@ Mod sources for Minecraft version 1.14.4.
## Version history
~ v1.0.19-b5 [A] Factory Hopper: Resetting NBT when breaking with empty inventory (for stacking), enabled
item cap for all sides.
~ v1.0.19-b5 [A] Added right-click display of power and progress information for Block Breaker, Solar Panel, and Tree Cutter.
[A] Solar Panel power curve tuned.
[A] Mod manual 1st edition release recipe added.
[A] Factory Hopper: Resetting NBT when breaking with empty inventory (for stacking), enabled item cap for all sides.
- v1.0.19-b4 [A] Ported primary Immersive Engineering dependent recipes (alternative recipes
will still work if IE is not installed).

View file

@ -1,10 +1,9 @@
package wile.engineersdecor;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import wile.engineersdecor.detail.ModConfig;
import wile.engineersdecor.blocks.*;
import wile.engineersdecor.detail.ModConfig;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import wile.engineersdecor.libmc.detail.OptionalRecipeCondition;
import wile.engineersdecor.libmc.detail.Networking;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.container.ContainerType;
@ -20,7 +19,6 @@ import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.InterModComms;
import net.minecraftforge.fml.event.lifecycle.*;
import net.minecraftforge.fml.event.server.FMLServerStartingEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
@ -62,7 +60,7 @@ public class ModEngineersDecor
{
LOGGER.info("Registering recipe condition processor ...");
CraftingHelper.register(OptionalRecipeCondition.Serializer.INSTANCE);
Networking.init(MODID);
wile.engineersdecor.libmc.detail.Networking.init(MODID);
if(config_loaded) {
try {
logger().info("Applying loaded config file.");
@ -76,7 +74,11 @@ public class ModEngineersDecor
}
private void onClientSetup(final FMLClientSetupEvent event)
{ ModContent.registerContainerGuis(event); ModContent.registerTileEntityRenderers(event); }
{
ModContent.registerContainerGuis(event);
ModContent.registerTileEntityRenderers(event);
wile.engineersdecor.libmc.detail.Overlay.register();
}
private void onSendImc(final InterModEnqueueEvent event)
{

View file

@ -11,22 +11,27 @@ package wile.engineersdecor.blocks;
import wile.engineersdecor.libmc.blocks.StandardBlocks;
import wile.engineersdecor.ModContent;
import wile.engineersdecor.ModEngineersDecor;
import net.minecraft.block.Blocks;
import net.minecraft.block.SoundType;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.item.ItemStack;
import net.minecraft.world.server.ServerWorld;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.state.BooleanProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntityType;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import wile.engineersdecor.libmc.detail.Overlay;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;
import net.minecraft.state.BooleanProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.block.Blocks;
import net.minecraft.block.SoundType;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.item.ItemStack;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.*;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
@ -35,6 +40,7 @@ import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.energy.CapabilityEnergy;
import net.minecraftforge.energy.IEnergyStorage;
import javax.annotation.Nullable;
import java.util.HashSet;
import java.util.List;
@ -107,6 +113,15 @@ public class BlockDecorBreaker extends StandardBlocks.HorizontalWaterLoggable im
public int getStrongPower(BlockState blockState, IBlockReader blockAccess, BlockPos pos, Direction side)
{ return 0; }
@Override
@SuppressWarnings("deprecation")
public boolean onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit)
{
TileEntity te = world.getTileEntity(pos);
if(te instanceof BTileEntity) ((BTileEntity)te).state_message(player);
return true;
}
//--------------------------------------------------------------------------------------------------------------------
// Tile entity
//--------------------------------------------------------------------------------------------------------------------
@ -121,17 +136,20 @@ public class BlockDecorBreaker extends StandardBlocks.HorizontalWaterLoggable im
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 int tick_timer_;
private int active_timer_;
private int proc_time_elapsed_;
private int time_needed_;
private int energy_;
public static void on_config(int boost_energy_per_tick, int breaking_time_per_hardness, int min_breaking_time_ticks, boolean power_required)
{
boost_energy_consumption = TICK_INTERVAL * MathHelper.clamp(boost_energy_per_tick, 4, 4096);
energy_max = Math.max(boost_energy_consumption * 10, 10000);
breaking_reluctance = MathHelper.clamp(breaking_time_per_hardness, 5, 50);
min_breaking_time = MathHelper.clamp(min_breaking_time_ticks, 10, 100);
requires_power = power_required;
@ -147,6 +165,31 @@ public class BlockDecorBreaker extends StandardBlocks.HorizontalWaterLoggable im
public void block_updated()
{ if(tick_timer_ > 2) tick_timer_ = 2; }
public void readnbt(CompoundNBT nbt)
{ energy_ = nbt.getInt("energy"); }
private void writenbt(CompoundNBT nbt)
{ nbt.putInt("energy", energy_); }
public void state_message(PlayerEntity player)
{
String progress = "0";
if((proc_time_elapsed_ > 0) && (time_needed_ > 0) && (active_timer_ > 0)) {
progress = Integer.toString((int)MathHelper.clamp((((double)proc_time_elapsed_) / ((double)time_needed_) * 100), 0, 100));
}
String soc = Integer.toString(MathHelper.clamp((energy_*100/energy_max),0,100));
Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.small_block_breaker.status", null, new Object[]{soc, energy_max, progress }));
}
// TileEntity ------------------------------------------------------------------------------
@Override
public void read(CompoundNBT nbt)
{ super.read(nbt); readnbt(nbt); }
@Override
public CompoundNBT write(CompoundNBT nbt)
{ super.write(nbt); writenbt(nbt); return nbt; }
// IEnergyStorage ----------------------------------------------------------------------------
@ -175,7 +218,7 @@ public class BlockDecorBreaker extends StandardBlocks.HorizontalWaterLoggable im
@Override
public int receiveEnergy(int maxReceive, boolean simulate)
{
maxReceive = MathHelper.clamp(maxReceive, 0, Math.max((boost_energy_consumption*2) - energy_, 0));
maxReceive = MathHelper.clamp(maxReceive, 0, Math.max(energy_max-energy_, 0));
if(!simulate) energy_ += maxReceive;
return maxReceive;
}
@ -195,7 +238,6 @@ public class BlockDecorBreaker extends StandardBlocks.HorizontalWaterLoggable im
// ITickable ------------------------------------------------------------------------------------
private static HashSet<Block> blacklist = new HashSet<>();
static {
blacklist.add(Blocks.AIR);
@ -269,11 +311,11 @@ public class BlockDecorBreaker extends StandardBlocks.HorizontalWaterLoggable im
tick_timer_ = IDLE_TICK_INTERVAL;
return;
}
int time_needed = MathHelper.clamp((int)(target_state.getBlockHardness(world, pos) * breaking_reluctance) + min_breaking_time, min_breaking_time, MAX_BREAKING_TIME);
time_needed_ = MathHelper.clamp((int)(target_state.getBlockHardness(world, pos) * breaking_reluctance) + min_breaking_time, min_breaking_time, MAX_BREAKING_TIME);
if(energy_ >= boost_energy_consumption) {
energy_ -= boost_energy_consumption;
proc_time_elapsed_ += TICK_INTERVAL * (1+BOOST_FACTOR);
time_needed += min_breaking_time * (3*BOOST_FACTOR/5);
time_needed_ += min_breaking_time * (3*BOOST_FACTOR/5);
active_timer_ = 2;
} else if(!requires_power) {
proc_time_elapsed_ += TICK_INTERVAL;
@ -285,7 +327,7 @@ public class BlockDecorBreaker extends StandardBlocks.HorizontalWaterLoggable im
if(requires_power && !active) {
proc_time_elapsed_ = Math.max(0, proc_time_elapsed_ - 2*TICK_INTERVAL);
}
if(proc_time_elapsed_ >= time_needed) {
if(proc_time_elapsed_ >= time_needed_) {
proc_time_elapsed_ = 0;
breakBlock(target_state, target_pos, world);
active = false;

View file

@ -11,19 +11,27 @@ package wile.engineersdecor.blocks;
import wile.engineersdecor.libmc.blocks.StandardBlocks;
import wile.engineersdecor.ModContent;
import wile.engineersdecor.ModEngineersDecor;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import wile.engineersdecor.libmc.detail.Overlay;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.Direction;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.world.World;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.LightType;
import net.minecraft.state.IntegerProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.IBlockReader;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraftforge.energy.CapabilityEnergy;
import net.minecraftforge.energy.IEnergyStorage;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
@ -60,6 +68,16 @@ public class BlockDecorSolarPanel extends StandardBlocks.BaseBlock implements ID
public TileEntity createTileEntity(BlockState state, IBlockReader world)
{ return new BlockDecorSolarPanel.BTileEntity(); }
@Override
@SuppressWarnings("deprecation")
public boolean onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit)
{
TileEntity te = world.getTileEntity(pos);
if(te instanceof BTileEntity) ((BTileEntity)te).state_message(player);
return true;
}
//--------------------------------------------------------------------------------------------------------------------
// Tile entity
//--------------------------------------------------------------------------------------------------------------------
@ -71,10 +89,13 @@ public class BlockDecorSolarPanel extends StandardBlocks.BaseBlock implements ID
public static final int ACCUMULATION_INTERVAL = 4;
private static final Direction transfer_directions_[] = {Direction.DOWN, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.NORTH };
private static int peak_power_per_tick_ = DEFAULT_PEAK_POWER;
private static int max_power_storage_ = 100000;
private static int max_power_storage_ = 64000;
private static int max_feed_power = 8192;
private int tick_timer_ = 0;
private int recalc_timer_ = 0;
private int accumulated_power_ = 0;
private int current_production_ = 0;
private int current_feedin_ = 0;
private boolean output_enabled_ = false;
public static void on_config(int peak_power_per_tick)
@ -97,6 +118,12 @@ public class BlockDecorSolarPanel extends StandardBlocks.BaseBlock implements ID
protected void writenbt(CompoundNBT nbt, boolean update_packet)
{ nbt.putInt("energy", accumulated_power_); }
public void state_message(PlayerEntity player)
{
String soc = Integer.toString(MathHelper.clamp((accumulated_power_*100/max_power_storage_),0,100));
Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.small_solar_panel.status", null, new Object[]{soc, max_power_storage_, current_production_, current_feedin_ }));
}
// IEnergyStorage --------------------------------------------------------------------------
@Override
@ -157,7 +184,7 @@ public class BlockDecorSolarPanel extends StandardBlocks.BaseBlock implements ID
{
if((world.isRemote) || (--tick_timer_ > 0)) return;
tick_timer_ = TICK_INTERVAL;
if(!world.canBlockSeeSky(pos)) { tick_timer_ = TICK_INTERVAL * 5; return; }
current_feedin_ = 0;
if(output_enabled_) {
for(int i=0; (i<transfer_directions_.length) && (accumulated_power_>0); ++i) {
final Direction f = transfer_directions_[i];
@ -165,10 +192,20 @@ public class BlockDecorSolarPanel extends StandardBlocks.BaseBlock implements ID
if(te==null) continue;
IEnergyStorage es = te.getCapability(CapabilityEnergy.ENERGY, f.getOpposite()).orElse(null);
if((es==null) || (!es.canReceive())) continue;
accumulated_power_ = MathHelper.clamp(accumulated_power_-es.receiveEnergy(accumulated_power_, false),0, accumulated_power_);
int fed = es.receiveEnergy(Math.min(accumulated_power_, max_feed_power * TICK_INTERVAL), false);
accumulated_power_ = MathHelper.clamp(accumulated_power_-fed,0, accumulated_power_);
current_feedin_ += fed;
}
}
if(accumulated_power_ <= 0) output_enabled_ = false;
current_feedin_ /= TICK_INTERVAL;
if((accumulated_power_ <= 0) || (current_feedin_ <= 0)) output_enabled_ = false; // feed-in power: no need to waste CPU if noone needs power.
if(!world.canBlockSeeSky(pos)) {
tick_timer_ = TICK_INTERVAL * 5;
current_production_ = 0;
BlockState state = world.getBlockState(pos);
if(state.get((EXPOSITION))!=2) world.setBlockState(pos, state.with(EXPOSITION, 2));
return;
}
if(--recalc_timer_ > 0) return;
recalc_timer_ = ACCUMULATION_INTERVAL + ((int)(Math.random()+.5));
BlockState state = world.getBlockState(pos);
@ -182,10 +219,11 @@ public class BlockDecorSolarPanel extends StandardBlocks.BaseBlock implements ID
else if(theta < 190) e = 4;
BlockState nstate = state.with(EXPOSITION, e);
if(nstate != state) world.setBlockState(pos, nstate, 1|2);
final double sb = world.getSunBrightness(1f);
double rf = Math.abs(1.0-(((double)Math.abs(MathHelper.clamp(theta, 0, 180)-90))/90));
rf = Math.sqrt(rf) * sb * ((TICK_INTERVAL*ACCUMULATION_INTERVAL)+2) * peak_power_per_tick_;
accumulated_power_ = Math.min(accumulated_power_+(int)rf, max_power_storage_);
final double eff = (1.0-((world.getRainStrength(1f)*0.6)+(world.getThunderStrength(1f)*0.3)));
final double ll = ((double)(world.getLightFor(LightType.SKY, getPos())))/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_);
accumulated_power_ = Math.min(accumulated_power_ + (current_production_*(TICK_INTERVAL*ACCUMULATION_INTERVAL)), max_power_storage_);
if(accumulated_power_ >= (max_power_storage_/5)) output_enabled_ = true;
}
}

View file

@ -12,18 +12,23 @@ import wile.engineersdecor.ModContent;
import wile.engineersdecor.ModEngineersDecor;
import wile.engineersdecor.detail.TreeCutting;
import wile.engineersdecor.libmc.blocks.StandardBlocks;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.state.BooleanProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntityType;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import wile.engineersdecor.libmc.detail.Overlay;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
import net.minecraft.state.BooleanProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.*;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
@ -78,6 +83,15 @@ public class BlockDecorTreeCutter extends StandardBlocks.Horizontal implements I
}
}
@Override
@SuppressWarnings("deprecation")
public boolean onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit)
{
TileEntity te = world.getTileEntity(pos);
if(te instanceof BTileEntity) ((BTileEntity)te).state_message(player);
return true;
}
//--------------------------------------------------------------------------------------------------------------------
// Tile entity
//--------------------------------------------------------------------------------------------------------------------
@ -90,9 +104,9 @@ public class BlockDecorTreeCutter extends StandardBlocks.Horizontal implements I
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_;
@ -101,6 +115,7 @@ public class BlockDecorTreeCutter extends StandardBlocks.Horizontal implements I
public static void on_config(int boost_energy_per_tick, int cutting_time_seconds, boolean power_required)
{
boost_energy_consumption = TICK_INTERVAL * MathHelper.clamp(boost_energy_per_tick, 4, 4096);
energy_max = Math.max(boost_energy_consumption * 10, 10000);
cutting_time_needed = 20 * MathHelper.clamp(cutting_time_seconds, 10, 240);
requires_power = power_required;
ModEngineersDecor.logger().info("Config tree cutter: Boost energy consumption:" + boost_energy_consumption + "rf/t" + (requires_power?" (power required for operation) ":"") + ", cutting time " + cutting_time_needed + "t." );
@ -112,6 +127,32 @@ public class BlockDecorTreeCutter extends StandardBlocks.Horizontal implements I
public BTileEntity(TileEntityType<?> te_type)
{ super(te_type); }
public void readnbt(CompoundNBT nbt)
{ energy_ = nbt.getInt("energy"); }
private void writenbt(CompoundNBT nbt)
{ nbt.putInt("energy", energy_); }
public void state_message(PlayerEntity player)
{
String progress = "0";
if((active_timer_ > 0) && (cutting_time_needed > 0) && (active_timer_ > 0)) {
progress = Integer.toString((int)MathHelper.clamp((((double)proc_time_elapsed_) / ((double)cutting_time_needed) * 100), 0, 100));
}
String soc = Integer.toString(MathHelper.clamp((energy_*100/energy_max),0,100));
Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.small_tree_cutter.status", null, new Object[]{soc, energy_max, progress, (cutting_time_needed/20) }));
}
// TileEntity ------------------------------------------------------------------------------
@Override
public void read(CompoundNBT nbt)
{ super.read(nbt); readnbt(nbt); }
@Override
public CompoundNBT write(CompoundNBT nbt)
{ super.write(nbt); writenbt(nbt); return nbt; }
// IEnergyStorage ----------------------------------------------------------------------------
protected LazyOptional<IEnergyStorage> energy_handler_ = LazyOptional.of(() -> (IEnergyStorage)this);
@ -139,7 +180,7 @@ public class BlockDecorTreeCutter extends StandardBlocks.Horizontal implements I
@Override
public int receiveEnergy(int maxReceive, boolean simulate)
{
maxReceive = MathHelper.clamp(maxReceive, 0, Math.max((boost_energy_consumption*2) - energy_, 0));
maxReceive = MathHelper.clamp(maxReceive, 0, Math.max((energy_max) - energy_, 0));
if(!simulate) energy_ += maxReceive;
return maxReceive;
}

View file

@ -113,7 +113,7 @@ public class Auxiliaries
*/
public static TranslationTextComponent localizable(String modtrkey, @Nullable TextFormatting color, Object... args)
{
TranslationTextComponent tr = new TranslationTextComponent(modid+"."+modtrkey, args);
TranslationTextComponent tr = new TranslationTextComponent((modtrkey.startsWith("block.")) ? (modtrkey) : (modid+"."+modtrkey), args);
if(color!=null) tr.getStyle().setColor(color);
return tr;
}

View file

@ -9,6 +9,8 @@
package wile.engineersdecor.libmc.detail;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TranslationTextComponent;
import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.fml.network.NetworkEvent;
import net.minecraft.nbt.CompoundNBT;
@ -22,6 +24,7 @@ import net.minecraft.network.PacketBuffer;
import net.minecraftforge.fml.network.NetworkRegistry;
import net.minecraftforge.fml.network.simple.SimpleChannel;
import net.minecraftforge.fml.network.NetworkDirection;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
@ -41,6 +44,7 @@ public class Networking
DEFAULT_CHANNEL.registerMessage(++discr, PacketTileNotifyServerToClient.class, PacketTileNotifyServerToClient::compose, PacketTileNotifyServerToClient::parse, PacketTileNotifyServerToClient.Handler::handle);
DEFAULT_CHANNEL.registerMessage(++discr, PacketContainerSyncClientToServer.class, PacketContainerSyncClientToServer::compose, PacketContainerSyncClientToServer::parse, PacketContainerSyncClientToServer.Handler::handle);
DEFAULT_CHANNEL.registerMessage(++discr, PacketContainerSyncServerToClient.class, PacketContainerSyncServerToClient::compose, PacketContainerSyncServerToClient::parse, PacketContainerSyncServerToClient.Handler::handle);
DEFAULT_CHANNEL.registerMessage(++discr, OverlayTextMessage.class, OverlayTextMessage::compose, OverlayTextMessage::parse, OverlayTextMessage.Handler::handle);
}
//--------------------------------------------------------------------------------------------------------------------
@ -239,4 +243,61 @@ public class Networking
}
}
//--------------------------------------------------------------------------------------------------------------------
// Main window GUI text message
//--------------------------------------------------------------------------------------------------------------------
public static class OverlayTextMessage
{
public static final int DISPLAY_TIME_MS = 3000;
private static BiConsumer<ITextComponent, Integer> handler_ = null;
private ITextComponent data_;
private int delay_ = DISPLAY_TIME_MS;
private ITextComponent data() { return data_; }
private int delay() { return delay_; }
public static void setHandler(BiConsumer<ITextComponent, Integer> handler)
{ if(handler_==null) handler_ = handler; }
public static void sendToPlayer(PlayerEntity player, ITextComponent message, int delay)
{
if((!(player instanceof ServerPlayerEntity)) || (player instanceof FakePlayer)) return;
DEFAULT_CHANNEL.sendTo(new OverlayTextMessage(message, delay), ((ServerPlayerEntity)player).connection.netManager, NetworkDirection.PLAY_TO_CLIENT);
}
public OverlayTextMessage()
{ data_ = new TranslationTextComponent("[unset]"); }
public OverlayTextMessage(final ITextComponent tct, int delay)
{ data_ = (ITextComponent)tct.deepCopy(); delay_ = delay; }
public static OverlayTextMessage parse(final PacketBuffer buf)
{
try {
return new OverlayTextMessage((ITextComponent)buf.readTextComponent(), DISPLAY_TIME_MS);
} catch(Throwable e) {
return new OverlayTextMessage(new TranslationTextComponent("[incorrect translation]"), DISPLAY_TIME_MS);
}
}
public static void compose(final OverlayTextMessage pkt, final PacketBuffer buf)
{
try {
buf.writeTextComponent(pkt.data());
} catch(Throwable e) {
Auxiliaries.logger().error("OverlayTextMessage.toBytes() failed: " + e.toString());
}
}
public static class Handler
{
public static void handle(final OverlayTextMessage pkt, final Supplier<NetworkEvent.Context> ctx)
{
if(handler_ != null) ctx.get().enqueueWork(() -> handler_.accept(pkt.data(), pkt.delay()));
ctx.get().setPacketHandled(true);
}
}
}
}

View file

@ -0,0 +1,114 @@
/*
* @file OverlayEventHandler.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2018 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Renders status messages in one line.
*/
package wile.engineersdecor.libmc.detail;
import net.minecraft.client.MainWindow;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.AbstractGui;
import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.Mod;
public class Overlay
{
public static void register()
{
if(SidedProxy.mc() != null) {
MinecraftForge.EVENT_BUS.register(new TextOverlayGui());
Networking.OverlayTextMessage.setHandler(TextOverlayGui::show);
}
}
public static void show(PlayerEntity player, final ITextComponent message)
{ Networking.OverlayTextMessage.sendToPlayer(player, message, 3000); }
public static void show(PlayerEntity player, final ITextComponent message, int delay)
{ Networking.OverlayTextMessage.sendToPlayer(player, message, delay); }
// -----------------------------------------------------------------------------
// Client side handler
// -----------------------------------------------------------------------------
@Mod.EventBusSubscriber(Dist.CLIENT)
@OnlyIn(Dist.CLIENT)
public static class TextOverlayGui extends AbstractGui
{
private static double overlay_y_ = 0.75;
private static int text_color_ = 0x00ffaa00;
private static int border_color_ = 0xaa333333;
private static int background_color1_ = 0xaa333333;
private static int background_color2_ = 0xaa444444;
private final Minecraft mc;
private static long deadline_;
private static String text_;
public static void on_config(double overlay_y)
{
overlay_y_ = overlay_y;
// currently const, just to circumvent "useless variable" warnings
text_color_ = 0x00ffaa00;
border_color_ = 0xaa333333;
background_color1_ = 0xaa333333;
background_color2_ = 0xaa444444;
}
public static synchronized String text()
{ return text_; }
public static synchronized long deadline()
{ return deadline_; }
public static synchronized void hide()
{ deadline_ = 0; text_ = ""; }
public static synchronized void show(ITextComponent s, int displayTimeoutMs)
{ text_ = (s==null)?(""):(s.getFormattedText()); deadline_ = System.currentTimeMillis() + displayTimeoutMs; }
public static synchronized void show(String s, int displayTimeoutMs)
{ text_ = (s == null) ? ("") : (s); deadline_ = System.currentTimeMillis() + displayTimeoutMs; }
TextOverlayGui()
{ super(); mc = SidedProxy.mc(); }
@SubscribeEvent
public void onRenderGui(RenderGameOverlayEvent.Post event)
{
if(event.getType() != RenderGameOverlayEvent.ElementType.CHAT) return;
if(deadline() < System.currentTimeMillis()) return;
String txt = text();
if(txt.isEmpty()) return;
final MainWindow win = mc.mainWindow;
final FontRenderer fr = mc.fontRenderer;
final boolean was_unicode = fr.getBidiFlag();
try {
final int cx = win.getScaledWidth() / 2;
final int cy = (int)(win.getScaledHeight() * overlay_y_);
final int w = fr.getStringWidth(txt);
final int h = fr.FONT_HEIGHT;
fillGradient(cx-(w/2)-3, cy-2, cx+(w/2)+2, cy+h+2, background_color1_, background_color2_);
hLine(cx-(w/2)-3, cx+(w/2)+2, cy-2, border_color_);
hLine(cx-(w/2)-3, cx+(w/2)+2, cy+h+2, border_color_);
vLine(cx-(w/2)-3, cy-2, cy+h+2, border_color_);
vLine(cx+(w/2)+2, cy-2, cy+h+2, border_color_);
drawCenteredString(fr, txt, cx , cy+1, text_color_);
} finally {
fr.setBidiFlag(was_unicode);
}
}
}
}

View file

@ -175,12 +175,15 @@
"block.engineersdecor.factory_placer.help": "§6Allows placing blocks and planting crops or trees.§r\n GUI Redstone controls: Not inverted / inverted (default), pulse mode / continuous mode (default).\n Also supports spike planing from underneath the soil. Spits out items that it cannot place or plant.",
"block.engineersdecor.small_block_breaker": "Small Block Breaker",
"block.engineersdecor.small_block_breaker.help": "§6Breaks blocks in front of it.§r\n Can be disabled by applying a redstone signal. The time needed to destroy a block depends on the hardness of that block. ${!block_breaker_requires_power?engineersdecor.tooltip.massive_speed_boost_with_rf_power} ${block_breaker_requires_power?engineersdecor.tooltip.requires_rf_power}",
"block.engineersdecor.small_block_breaker.status": "SOC: %1$s%% of %2$sRF§r | progress: %3$s%%",
"block.engineersdecor.small_mineral_smelter": "Small Mineral Melting Furnace",
"block.engineersdecor.small_mineral_smelter.help": "§6High temperature, high insulation electrical stone melting furnace.§r\n Heats up mineral blocks to magma blocks, and finally to lava. Click with a mineral block (stone, cobble, etc) or use item insertion to place a block in the melter. Use bucket or fluid extraction to retrieve the lava. When cooling down lava, obsidian is generated. Remove the RF power or apply a redstone signal to disable the furnace. For automation, use a redstone comparator to see in which melting phase the mineral is a moment.",
"block.engineersdecor.small_solar_panel": "Small Solar Panel",
"block.engineersdecor.small_solar_panel.help": "§6Produces a small amount of power when exposed to sunlight.§r\n Useful for charging LF capacitors in remote systems with low consumption. The internal charge pump circuit accumulates and frequently transfers RF. Production depends on day time and the weather.",
"block.engineersdecor.small_solar_panel.status": "SOC: %1$s%% of %2$sRF§r | producing %3$sRF/t | feeding %4$sRF/t",
"block.engineersdecor.small_tree_cutter": "Small Tree Cutter",
"block.engineersdecor.small_tree_cutter.help": "§6Chops grown trees in front of it.§r\n Does not collect the lumbers. Deactivate with a redstone signal. ${!tree_cuttter_requires_power?engineersdecor.tooltip.massive_speed_boost_with_rf_power} ${tree_cuttter_requires_power?engineersdecor.tooltip.requires_rf_power}",
"block.engineersdecor.small_tree_cutter.status": "SOC: %1$s%% of %2$sRF§r | progress: %3$s%% (%4$ss)",
"block.engineersdecor.small_milking_machine": "Small Milking Machine",
"block.engineersdecor.small_milking_machine.help": "§6Occasionally grooms and milks cows.§r\n Has an internal fluid tank. Does not feed the animals. Use buckets to retrieve the milk. Pulls/stores milk container items from/to inventories at the back or bottom (preferrs extracting from the back and inserting below, but can also put filled vessels back into the same inventory). Supports fluid output to tanks or pipes below (only if milk exists as fluid). Care that it's not too crowdy in the cow pen, only happy animals stroll by voluntarily.",
"block.engineersdecor.sign_decor": "Sign Plate (Engineer's decor)",

View file

@ -175,12 +175,15 @@
"block.engineersdecor.factory_placer.help": "§6Позволяет размещать блоки и сажать урожай или деревья.§r\n GUI редстоун: Не инвертирован / инвертирован (по умолчанию), пульс режим / постоянный режим (по умолчанию).\n Also supports spike planing from underneath the soil. Выплевывает предметы, которые он не может поместить или посадить.",
"block.engineersdecor.small_block_breaker": "Фабричный разбиватель блоков",
"block.engineersdecor.small_block_breaker.help": "§6Разбивает блоки перед собой.§r\n Можно отключить, подав сигнал красного камня. Время, необходимое для разрушения блока, зависит от твердости этого блока. ${!block_breaker_requires_power?engineersdecor.tooltip.massive_speed_boost_with_rf_power} ${block_breaker_requires_power?engineersdecor.tooltip.requires_rf_power}",
"block.engineersdecor.small_block_breaker.status": "SOC: %1$s%% of %2$sRF§r | progress: %3$s%%",
"block.engineersdecor.small_mineral_smelter": "Малая плавильная печь минералов",
"block.engineersdecor.small_mineral_smelter.help": "§6Высокотемпературная электрическая каменная плавильная печь с высокой изоляцией.§r\n Нагревает минеральные блоки до магматических блоков и, наконец, до лавы. Нажмите с минеральным блоком (камень, булыжник, и т.п.) или используйте другие способы загрузки. Используйте ведро или жидкостные насосы для извлечения лавы. При охлаждении лавы, генерируется обсидиан. Отключите RF питание или подайте сигнал красного камня для отключения печи. Для автоматизации используйте редстоуновый компаратор, чтобы увидеть, в какой фазе плавления находится минерал.",
"block.engineersdecor.small_solar_panel": "Малая солнечная панель",
"block.engineersdecor.small_solar_panel.help": "§6Вырабатывает небольшое количество энергии при воздействии солнечного света.§r\n Полезно для зарядки низковольтных конденсаторов в удаленных системах с низким потреблением. Внутренний контур насоса накапливает и часто передает RF. Производство зависит от времени суток и погоды.",
"block.engineersdecor.small_solar_panel.status": "SOC: %1$s%% of %2$sRF§r | producing %3$sRF/t | feeding %4$sRF/t",
"block.engineersdecor.small_tree_cutter": "Малый лесоруб",
"block.engineersdecor.small_tree_cutter.help": "§6Вырубает деревья перед ним.§r\n Не собирает срубленное. Выключается с помощью сигнала красного камня. ${!tree_cuttter_requires_power?engineersdecor.tooltip.massive_speed_boost_with_rf_power} ${tree_cuttter_requires_power?engineersdecor.tooltip.requires_rf_power}",
"block.engineersdecor.small_tree_cutter.status": "SOC: %1$s%% of %2$sRF§r | progress: %3$s%% (%4$ss)",
"block.engineersdecor.small_milking_machine": "Малый доильный аппарат",
"block.engineersdecor.small_milking_machine.help": "§6Изредка чистит и доит коров.§r\n Имеет внутренний бак для жидкости. Не кормит животных. Используйте ведра для извлечения молока. Извлекает/кладёт сосуды для молока из/в инвентарь сзади или снизу (предпочитает извлекать сзади и класть снизу, но может также класть заполненные сосуды обратно в тот же инвентарь). Поддерживает выход жидкости в резервуары или трубы снизу (только если молоко существует в виде жидкости). Позаботьтесь, чтобы в загоне не было тесно, добровольно прогуливаются только счастливые животные.",
"block.engineersdecor.sign_decor": "Табличка с надписью (Логотип Engineer's decor)",

View file

@ -175,12 +175,15 @@
"block.engineersdecor.factory_placer.help": "§6能够放置方块和种植作物或树。§r\n GUI红石控制不反相/反相(默认),脉冲模式/持续模式(默认)。\n 也支持从耕地下方穿透方块种植。会吐出不能放置或种植的物品。",
"block.engineersdecor.small_block_breaker": "工厂方块破坏器",
"block.engineersdecor.small_block_breaker.help": "§6破坏其正前方的方块。§r\n 通入红石信号可以停止破坏。 破坏一个方块需要的时间取决于其硬度。 ${!block_breaker_requires_power?engineersdecor.tooltip.massive_speed_boost_with_rf_power} ${block_breaker_requires_power?engineersdecor.tooltip.requires_rf_power}",
"block.engineersdecor.small_block_breaker.status": "SOC: %1$s%% of %2$sRF§r | progress: %3$s%%",
"block.engineersdecor.small_mineral_smelter": "小型矿物熔炼炉",
"block.engineersdecor.small_mineral_smelter.help": "§6高温、高绝缘电熔石炉。§r\n 把矿物块加热成岩浆块,最后变成熔岩。由于 小型化的设备大小,该过程效率不高,需要大量时间和能源 来液化一块石头。",
"block.engineersdecor.small_solar_panel": "小型太阳能板",
"block.engineersdecor.small_solar_panel.help": "§6暴露在阳光下时产生少量能量。§r\n 用于低消耗地在远程系统给低压电容器充电。 内部电荷泵电路积累并频繁输出RF。产出取决于时间 和天气。",
"block.engineersdecor.small_solar_panel.status": "SOC: %1$s%% of %2$sRF§r | producing %3$sRF/t | feeding %4$sRF/t",
"block.engineersdecor.small_tree_cutter": "小型砍树机",
"block.engineersdecor.small_tree_cutter.help": "§6砍倒正前方的树。§r\n 不收集木材。通入红石信号停用。 提供RF来加快砍树速度。没有的话会很慢。",
"block.engineersdecor.small_tree_cutter.status": "SOC: %1$s%% of %2$sRF§r | progress: %3$s%% (%4$ss)",
"block.engineersdecor.small_milking_machine": "小型挤奶机",
"block.engineersdecor.small_milking_machine.help": "§6偶尔给奶牛梳理和挤奶。§r\n 有一个内置流体储罐。不会喂食动物。使用桶获得牛奶。 从后方或下方的方块物品栏拉取或存储牛奶容器物品(偏好 从下方输入,从后方输出,但也能将装满的牛奶容器放回原来 的物品栏)支持将牛奶作为流体输出到下方的储罐和管道(只要牛奶是流体)。 小心牛栏里不要太拥挤,只有快乐的动物才会自觉地在挤奶机前散步。",
"block.engineersdecor.sign_decor": "标志板(工程师的装饰)",

View file

@ -2,7 +2,7 @@
org.gradle.daemon=false
org.gradle.jvmargs=-Xmx8G
version_minecraft=1.15.2
version_forge_minecraft=1.15.2-31.1.1
version_forge_minecraft=1.15.2-31.1.18
version_fml_mappings=20191105-1.14.3
version_jei=1.15.2:6.0.0.2
version_engineersdecor=1.0.19-b5

View file

@ -11,9 +11,10 @@ Mod sources for Minecraft version 1.15.1.
## Version history
~ v1.0.19-b5 [A] Factory Hopper: Resetting NBT when breaking with empty inventory (for stacking), enabled
item cap for all sides.
~ v1.0.19-b5 [A] Added right-click display of power and progress information for Block Breaker, Solar Panel, and Tree Cutter.
[A] Solar Panel power curve tuned.
[A] Mod manual 1st edition release recipe added.
[A] Factory Hopper: Resetting NBT when breaking with empty inventory (for stacking), enabled item cap for all sides.
- v1.0.19-b4 [A] Ported primary Immersive Engineering dependent recipes (alternative recipes
will still work if IE is not installed).

View file

@ -4,7 +4,6 @@ import wile.engineersdecor.blocks.*;
import wile.engineersdecor.detail.ModConfig;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import wile.engineersdecor.libmc.detail.OptionalRecipeCondition;
import wile.engineersdecor.libmc.detail.Networking;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.container.ContainerType;
@ -63,7 +62,7 @@ public class ModEngineersDecor
{
LOGGER.info("Registering recipe condition processor ...");
CraftingHelper.register(OptionalRecipeCondition.Serializer.INSTANCE);
Networking.init(MODID);
wile.engineersdecor.libmc.detail.Networking.init(MODID);
if(config_loaded) {
try {
logger().info("Applying loaded config file.");
@ -81,6 +80,7 @@ public class ModEngineersDecor
ModContent.registerContainerGuis(event);
ModContent.registerTileEntityRenderers(event);
ModContent.processContentClientSide(event);
wile.engineersdecor.libmc.detail.Overlay.register();
}
private void onSendImc(final InterModEnqueueEvent event)

View file

@ -10,22 +10,27 @@ package wile.engineersdecor.blocks;
import wile.engineersdecor.ModContent;
import wile.engineersdecor.ModEngineersDecor;
import net.minecraft.block.Blocks;
import net.minecraft.block.SoundType;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.item.ItemStack;
import net.minecraft.world.server.ServerWorld;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.state.BooleanProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntityType;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import wile.engineersdecor.libmc.detail.Overlay;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;
import net.minecraft.state.BooleanProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.block.Blocks;
import net.minecraft.block.SoundType;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.item.ItemStack;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.*;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
@ -106,6 +111,15 @@ public class BlockDecorBreaker extends BlockDecor.HorizontalWaterLoggable implem
public int getStrongPower(BlockState blockState, IBlockReader blockAccess, BlockPos pos, Direction side)
{ return 0; }
@Override
@SuppressWarnings("deprecation")
public boolean onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit)
{
TileEntity te = world.getTileEntity(pos);
if(te instanceof BTileEntity) ((BTileEntity)te).state_message(player);
return true;
}
//--------------------------------------------------------------------------------------------------------------------
// Tile entity
//--------------------------------------------------------------------------------------------------------------------
@ -120,17 +134,20 @@ public class BlockDecorBreaker extends BlockDecor.HorizontalWaterLoggable implem
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 int tick_timer_;
private int active_timer_;
private int proc_time_elapsed_;
private int time_needed_;
private int energy_;
public static void on_config(int boost_energy_per_tick, int breaking_time_per_hardness, int min_breaking_time_ticks, boolean power_required)
{
boost_energy_consumption = TICK_INTERVAL * MathHelper.clamp(boost_energy_per_tick, 4, 4096);
energy_max = Math.max(boost_energy_consumption * 10, 10000);
breaking_reluctance = MathHelper.clamp(breaking_time_per_hardness, 5, 50);
min_breaking_time = MathHelper.clamp(min_breaking_time_ticks, 10, 100);
requires_power = power_required;
@ -146,6 +163,31 @@ public class BlockDecorBreaker extends BlockDecor.HorizontalWaterLoggable implem
public void block_updated()
{ if(tick_timer_ > 2) tick_timer_ = 2; }
public void readnbt(CompoundNBT nbt)
{ energy_ = nbt.getInt("energy"); }
private void writenbt(CompoundNBT nbt)
{ nbt.putInt("energy", energy_); }
public void state_message(PlayerEntity player)
{
String progress = "0";
if((proc_time_elapsed_ > 0) && (time_needed_ > 0)) {
progress = Integer.toString((int)MathHelper.clamp((((double)proc_time_elapsed_) / ((double)time_needed_) * 100), 0, 100));
}
String soc = Integer.toString(MathHelper.clamp((energy_*100/energy_max),0,100));
Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.small_block_breaker.status", null, new Object[]{soc, energy_max, progress }));
}
// TileEntity ------------------------------------------------------------------------------
@Override
public void read(CompoundNBT nbt)
{ super.read(nbt); readnbt(nbt); }
@Override
public CompoundNBT write(CompoundNBT nbt)
{ super.write(nbt); writenbt(nbt); return nbt; }
// IEnergyStorage ----------------------------------------------------------------------------
@ -174,7 +216,7 @@ public class BlockDecorBreaker extends BlockDecor.HorizontalWaterLoggable implem
@Override
public int receiveEnergy(int maxReceive, boolean simulate)
{
maxReceive = MathHelper.clamp(maxReceive, 0, Math.max((boost_energy_consumption*2) - energy_, 0));
maxReceive = MathHelper.clamp(maxReceive, 0, Math.max(energy_max-energy_, 0));
if(!simulate) energy_ += maxReceive;
return maxReceive;
}
@ -194,7 +236,6 @@ public class BlockDecorBreaker extends BlockDecor.HorizontalWaterLoggable implem
// ITickable ------------------------------------------------------------------------------------
private static HashSet<Block> blacklist = new HashSet<>();
static {
blacklist.add(Blocks.AIR);
@ -268,11 +309,11 @@ public class BlockDecorBreaker extends BlockDecor.HorizontalWaterLoggable implem
tick_timer_ = IDLE_TICK_INTERVAL;
return;
}
int time_needed = MathHelper.clamp((int)(target_state.getBlockHardness(world, pos) * breaking_reluctance) + min_breaking_time, min_breaking_time, MAX_BREAKING_TIME);
time_needed_ = MathHelper.clamp((int)(target_state.getBlockHardness(world, pos) * breaking_reluctance) + min_breaking_time, min_breaking_time, MAX_BREAKING_TIME);
if(energy_ >= boost_energy_consumption) {
energy_ -= boost_energy_consumption;
proc_time_elapsed_ += TICK_INTERVAL * (1+BOOST_FACTOR);
time_needed += min_breaking_time * (3*BOOST_FACTOR/5);
time_needed_ += min_breaking_time * (3*BOOST_FACTOR/5);
active_timer_ = 2;
} else if(!requires_power) {
proc_time_elapsed_ += TICK_INTERVAL;
@ -284,7 +325,7 @@ public class BlockDecorBreaker extends BlockDecor.HorizontalWaterLoggable implem
if(requires_power && !active) {
proc_time_elapsed_ = Math.max(0, proc_time_elapsed_ - 2*TICK_INTERVAL);
}
if(proc_time_elapsed_ >= time_needed) {
if(proc_time_elapsed_ >= time_needed_) {
proc_time_elapsed_ = 0;
breakBlock(target_state, target_pos, world);
active = false;

View file

@ -10,17 +10,24 @@ package wile.engineersdecor.blocks;
import wile.engineersdecor.ModContent;
import wile.engineersdecor.ModEngineersDecor;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.state.IntegerProperty;
import net.minecraft.state.StateContainer;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import wile.engineersdecor.libmc.detail.Overlay;
import net.minecraft.world.World;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.LightType;
import net.minecraft.state.IntegerProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.Direction;
import net.minecraft.util.math.MathHelper;
import net.minecraftforge.energy.CapabilityEnergy;
@ -53,6 +60,15 @@ public class BlockDecorSolarPanel extends BlockDecor implements IDecorBlock
public TileEntity createTileEntity(BlockState state, IBlockReader world)
{ return new BlockDecorSolarPanel.BTileEntity(); }
@Override
@SuppressWarnings("deprecation")
public boolean onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit)
{
TileEntity te = world.getTileEntity(pos);
if(te instanceof BTileEntity) ((BTileEntity)te).state_message(player);
return true;
}
//--------------------------------------------------------------------------------------------------------------------
// Tile entity
//--------------------------------------------------------------------------------------------------------------------
@ -64,10 +80,13 @@ public class BlockDecorSolarPanel extends BlockDecor implements IDecorBlock
public static final int ACCUMULATION_INTERVAL = 4;
private static final Direction transfer_directions_[] = {Direction.DOWN, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.NORTH };
private static int peak_power_per_tick_ = DEFAULT_PEAK_POWER;
private static int max_power_storage_ = 100000;
private static int max_power_storage_ = 64000;
private static int max_feed_power = 8192;
private int tick_timer_ = 0;
private int recalc_timer_ = 0;
private int accumulated_power_ = 0;
private int current_production_ = 0;
private int current_feedin_ = 0;
private boolean output_enabled_ = false;
public static void on_config(int peak_power_per_tick)
@ -90,6 +109,12 @@ public class BlockDecorSolarPanel extends BlockDecor implements IDecorBlock
protected void writenbt(CompoundNBT nbt, boolean update_packet)
{ nbt.putInt("energy", accumulated_power_); }
public void state_message(PlayerEntity player)
{
String soc = Integer.toString(MathHelper.clamp((accumulated_power_*100/max_power_storage_),0,100));
Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.small_solar_panel.status", null, new Object[]{soc, max_power_storage_, current_production_, current_feedin_ }));
}
// IEnergyStorage --------------------------------------------------------------------------
@Override
@ -150,7 +175,7 @@ public class BlockDecorSolarPanel extends BlockDecor implements IDecorBlock
{
if((world.isRemote) || (--tick_timer_ > 0)) return;
tick_timer_ = TICK_INTERVAL;
if(!world.canBlockSeeSky(pos)) { tick_timer_ = TICK_INTERVAL * 5; return; }
current_feedin_ = 0;
if(output_enabled_) {
for(int i=0; (i<transfer_directions_.length) && (accumulated_power_>0); ++i) {
final Direction f = transfer_directions_[i];
@ -158,10 +183,20 @@ public class BlockDecorSolarPanel extends BlockDecor implements IDecorBlock
if(te==null) continue;
IEnergyStorage es = te.getCapability(CapabilityEnergy.ENERGY, f.getOpposite()).orElse(null);
if((es==null) || (!es.canReceive())) continue;
accumulated_power_ = MathHelper.clamp(accumulated_power_-es.receiveEnergy(accumulated_power_, false),0, accumulated_power_);
int fed = es.receiveEnergy(Math.min(accumulated_power_, max_feed_power * TICK_INTERVAL), false);
accumulated_power_ = MathHelper.clamp(accumulated_power_-fed,0, accumulated_power_);
current_feedin_ += fed;
}
}
if(accumulated_power_ <= 0) output_enabled_ = false;
current_feedin_ /= TICK_INTERVAL;
if((accumulated_power_ <= 0) || (current_feedin_ <= 0)) output_enabled_ = false; // feed-in power: no need to waste CPU if noone needs power.
if(!world.canBlockSeeSky(pos)) {
tick_timer_ = TICK_INTERVAL * 5;
current_production_ = 0;
BlockState state = world.getBlockState(pos);
if(state.get((EXPOSITION))!=2) world.setBlockState(pos, state.with(EXPOSITION, 2));
return;
}
if(--recalc_timer_ > 0) return;
recalc_timer_ = ACCUMULATION_INTERVAL + ((int)(Math.random()+.5));
BlockState state = world.getBlockState(pos);
@ -175,10 +210,11 @@ public class BlockDecorSolarPanel extends BlockDecor implements IDecorBlock
else if(theta < 190) e = 4;
BlockState nstate = state.with(EXPOSITION, e);
if(nstate != state) world.setBlockState(pos, nstate, 1|2);
final double sb = world.func_226658_a_/*getLight()*/(LightType.SKY, getPos()); //world.getSunBrightness(1f);
double rf = Math.abs(1.0-(((double)Math.abs(MathHelper.clamp(theta, 0, 180)-90))/90));
rf = Math.sqrt(rf) * sb * ((TICK_INTERVAL*ACCUMULATION_INTERVAL)+2) * peak_power_per_tick_;
accumulated_power_ = Math.min(accumulated_power_+(int)rf, max_power_storage_);
final double eff = (1.0-((world.getRainStrength(1f)*0.6)+(world.getThunderStrength(1f)*0.3)));
final double ll = ((double)(world.func_225524_e_().getLightEngine(LightType.SKY).getLightFor(getPos())))/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_);
accumulated_power_ = Math.min(accumulated_power_ + (current_production_*(TICK_INTERVAL*ACCUMULATION_INTERVAL)), max_power_storage_);
if(accumulated_power_ >= (max_power_storage_/5)) output_enabled_ = true;
}
}

View file

@ -10,6 +10,9 @@ package wile.engineersdecor.blocks;
import wile.engineersdecor.ModContent;
import wile.engineersdecor.ModEngineersDecor;
import wile.engineersdecor.detail.TreeCutting;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import wile.engineersdecor.libmc.detail.Overlay;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
import net.minecraft.block.Block;
@ -19,18 +22,20 @@ import net.minecraft.state.StateContainer;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.*;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.energy.CapabilityEnergy;
import net.minecraftforge.energy.IEnergyStorage;
import wile.engineersdecor.detail.TreeCutting;
import javax.annotation.Nullable;
import java.util.Random;
@ -76,6 +81,15 @@ public class BlockDecorTreeCutter extends BlockDecor.Horizontal implements IDeco
}
}
@Override
@SuppressWarnings("deprecation")
public boolean onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit)
{
TileEntity te = world.getTileEntity(pos);
if(te instanceof BTileEntity) ((BTileEntity)te).state_message(player);
return true;
}
//--------------------------------------------------------------------------------------------------------------------
// Tile entity
//--------------------------------------------------------------------------------------------------------------------
@ -88,6 +102,7 @@ public class BlockDecorTreeCutter extends BlockDecor.Horizontal implements IDeco
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;
@ -99,6 +114,7 @@ public class BlockDecorTreeCutter extends BlockDecor.Horizontal implements IDeco
public static void on_config(int boost_energy_per_tick, int cutting_time_seconds, boolean power_required)
{
boost_energy_consumption = TICK_INTERVAL * MathHelper.clamp(boost_energy_per_tick, 4, 4096);
energy_max = Math.max(boost_energy_consumption * 10, 10000);
cutting_time_needed = 20 * MathHelper.clamp(cutting_time_seconds, 10, 240);
requires_power = power_required;
ModEngineersDecor.logger().info("Config tree cutter: Boost energy consumption:" + boost_energy_consumption + "rf/t" + (requires_power?" (power required for operation) ":"") + ", cutting time " + cutting_time_needed + "t." );
@ -110,6 +126,32 @@ public class BlockDecorTreeCutter extends BlockDecor.Horizontal implements IDeco
public BTileEntity(TileEntityType<?> te_type)
{ super(te_type); }
public void readnbt(CompoundNBT nbt)
{ energy_ = nbt.getInt("energy"); }
private void writenbt(CompoundNBT nbt)
{ nbt.putInt("energy", energy_); }
public void state_message(PlayerEntity player)
{
String progress = "0";
if((active_timer_ > 0) && (cutting_time_needed > 0) && (active_timer_ > 0)) {
progress = Integer.toString((int)MathHelper.clamp((((double)proc_time_elapsed_) / ((double)cutting_time_needed) * 100), 0, 100));
}
String soc = Integer.toString(MathHelper.clamp((energy_*100/energy_max),0,100));
Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.small_tree_cutter.status", null, new Object[]{soc, energy_max, progress, (cutting_time_needed/20) }));
}
// TileEntity ------------------------------------------------------------------------------
@Override
public void read(CompoundNBT nbt)
{ super.read(nbt); readnbt(nbt); }
@Override
public CompoundNBT write(CompoundNBT nbt)
{ super.write(nbt); writenbt(nbt); return nbt; }
// IEnergyStorage ----------------------------------------------------------------------------
protected LazyOptional<IEnergyStorage> energy_handler_ = LazyOptional.of(() -> (IEnergyStorage)this);
@ -137,7 +179,7 @@ public class BlockDecorTreeCutter extends BlockDecor.Horizontal implements IDeco
@Override
public int receiveEnergy(int maxReceive, boolean simulate)
{
maxReceive = MathHelper.clamp(maxReceive, 0, Math.max((boost_energy_consumption*2) - energy_, 0));
maxReceive = MathHelper.clamp(maxReceive, 0, Math.max((energy_max) - energy_, 0));
if(!simulate) energy_ += maxReceive;
return maxReceive;
}

View file

@ -113,7 +113,7 @@ public class Auxiliaries
*/
public static TranslationTextComponent localizable(String modtrkey, @Nullable TextFormatting color, Object... args)
{
TranslationTextComponent tr = new TranslationTextComponent(modid+"."+modtrkey, args);
TranslationTextComponent tr = new TranslationTextComponent((modtrkey.startsWith("block.")) ? (modtrkey) : (modid+"."+modtrkey), args);
if(color!=null) tr.getStyle().setColor(color);
return tr;
}

View file

@ -9,6 +9,8 @@
package wile.engineersdecor.libmc.detail;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TranslationTextComponent;
import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.fml.network.NetworkEvent;
import net.minecraft.nbt.CompoundNBT;
@ -22,6 +24,7 @@ import net.minecraft.network.PacketBuffer;
import net.minecraftforge.fml.network.NetworkRegistry;
import net.minecraftforge.fml.network.simple.SimpleChannel;
import net.minecraftforge.fml.network.NetworkDirection;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
@ -41,6 +44,7 @@ public class Networking
DEFAULT_CHANNEL.registerMessage(++discr, PacketTileNotifyServerToClient.class, PacketTileNotifyServerToClient::compose, PacketTileNotifyServerToClient::parse, PacketTileNotifyServerToClient.Handler::handle);
DEFAULT_CHANNEL.registerMessage(++discr, PacketContainerSyncClientToServer.class, PacketContainerSyncClientToServer::compose, PacketContainerSyncClientToServer::parse, PacketContainerSyncClientToServer.Handler::handle);
DEFAULT_CHANNEL.registerMessage(++discr, PacketContainerSyncServerToClient.class, PacketContainerSyncServerToClient::compose, PacketContainerSyncServerToClient::parse, PacketContainerSyncServerToClient.Handler::handle);
DEFAULT_CHANNEL.registerMessage(++discr, OverlayTextMessage.class, OverlayTextMessage::compose, OverlayTextMessage::parse, OverlayTextMessage.Handler::handle);
}
//--------------------------------------------------------------------------------------------------------------------
@ -239,4 +243,61 @@ public class Networking
}
}
//--------------------------------------------------------------------------------------------------------------------
// Main window GUI text message
//--------------------------------------------------------------------------------------------------------------------
public static class OverlayTextMessage
{
public static final int DISPLAY_TIME_MS = 3000;
private static BiConsumer<ITextComponent, Integer> handler_ = null;
private ITextComponent data_;
private int delay_ = DISPLAY_TIME_MS;
private ITextComponent data() { return data_; }
private int delay() { return delay_; }
public static void setHandler(BiConsumer<ITextComponent, Integer> handler)
{ if(handler_==null) handler_ = handler; }
public static void sendToPlayer(PlayerEntity player, ITextComponent message, int delay)
{
if((!(player instanceof ServerPlayerEntity)) || (player instanceof FakePlayer)) return;
DEFAULT_CHANNEL.sendTo(new OverlayTextMessage(message, delay), ((ServerPlayerEntity)player).connection.netManager, NetworkDirection.PLAY_TO_CLIENT);
}
public OverlayTextMessage()
{ data_ = new TranslationTextComponent("[unset]"); }
public OverlayTextMessage(final ITextComponent tct, int delay)
{ data_ = (ITextComponent)tct.deepCopy(); delay_ = delay; }
public static OverlayTextMessage parse(final PacketBuffer buf)
{
try {
return new OverlayTextMessage((ITextComponent)buf.readTextComponent(), DISPLAY_TIME_MS);
} catch(Throwable e) {
return new OverlayTextMessage(new TranslationTextComponent("[incorrect translation]"), DISPLAY_TIME_MS);
}
}
public static void compose(final OverlayTextMessage pkt, final PacketBuffer buf)
{
try {
buf.writeTextComponent(pkt.data());
} catch(Throwable e) {
Auxiliaries.logger().error("OverlayTextMessage.toBytes() failed: " + e.toString());
}
}
public static class Handler
{
public static void handle(final OverlayTextMessage pkt, final Supplier<NetworkEvent.Context> ctx)
{
if(handler_ != null) ctx.get().enqueueWork(() -> handler_.accept(pkt.data(), pkt.delay()));
ctx.get().setPacketHandled(true);
}
}
}
}

View file

@ -0,0 +1,113 @@
/*
* @file OverlayEventHandler.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2018 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Renders status messages in one line.
*/
package wile.engineersdecor.libmc.detail;
import net.minecraft.client.MainWindow;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.AbstractGui;
import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.Mod;
public class Overlay
{
public static void register()
{
if(SidedProxy.mc() != null) {
MinecraftForge.EVENT_BUS.register(new TextOverlayGui());
Networking.OverlayTextMessage.setHandler(TextOverlayGui::show);
}
}
public static void show(PlayerEntity player, final ITextComponent message)
{ Networking.OverlayTextMessage.sendToPlayer(player, message, 3000); }
public static void show(PlayerEntity player, final ITextComponent message, int delay)
{ Networking.OverlayTextMessage.sendToPlayer(player, message, delay); }
// -----------------------------------------------------------------------------
// Client side handler
// -----------------------------------------------------------------------------
@Mod.EventBusSubscriber(Dist.CLIENT)
@OnlyIn(Dist.CLIENT)
public static class TextOverlayGui extends AbstractGui
{
private static double overlay_y_ = 0.75;
private static int text_color_ = 0x00ffaa00;
private static int border_color_ = 0xaa333333;
private static int background_color1_ = 0xaa333333;
private static int background_color2_ = 0xaa444444;
private final Minecraft mc;
private static long deadline_;
private static String text_;
public static void on_config(double overlay_y)
{
overlay_y_ = overlay_y;
// currently const, just to circumvent "useless variable" warnings
text_color_ = 0x00ffaa00;
border_color_ = 0xaa333333;
background_color1_ = 0xaa333333;
background_color2_ = 0xaa444444;
}
public static synchronized String text()
{ return text_; }
public static synchronized long deadline()
{ return deadline_; }
public static synchronized void hide()
{ deadline_ = 0; text_ = ""; }
public static synchronized void show(ITextComponent s, int displayTimeoutMs)
{ text_ = (s==null)?(""):(s.getFormattedText()); deadline_ = System.currentTimeMillis() + displayTimeoutMs; }
public static synchronized void show(String s, int displayTimeoutMs)
{ text_ = (s == null) ? ("") : (s); deadline_ = System.currentTimeMillis() + displayTimeoutMs; }
TextOverlayGui()
{ super(); mc = SidedProxy.mc(); }
@SubscribeEvent
public void onRenderGui(RenderGameOverlayEvent.Post event)
{
if(event.getType() != RenderGameOverlayEvent.ElementType.CHAT) return;
if(deadline() < System.currentTimeMillis()) return;
String txt = text();
if(txt.isEmpty()) return;
final MainWindow win = mc.func_228018_at_()/*getMainWindow()*/;
final FontRenderer fr = mc.fontRenderer;
final boolean was_unicode = fr.getBidiFlag();
try {
final int cx = win.getScaledWidth() / 2;
final int cy = (int)(win.getScaledHeight() * overlay_y_);
final int w = fr.getStringWidth(txt);
final int h = fr.FONT_HEIGHT;
fillGradient(cx-(w/2)-3, cy-2, cx+(w/2)+2, cy+h+2, background_color1_, background_color2_);
hLine(cx-(w/2)-3, cx+(w/2)+2, cy-2, border_color_);
hLine(cx-(w/2)-3, cx+(w/2)+2, cy+h+2, border_color_);
vLine(cx-(w/2)-3, cy-2, cy+h+2, border_color_);
vLine(cx+(w/2)+2, cy-2, cy+h+2, border_color_);
drawCenteredString(fr, txt, cx , cy+1, text_color_);
} finally {
fr.setBidiFlag(was_unicode);
}
}
}
}

View file

@ -175,12 +175,15 @@
"block.engineersdecor.factory_placer.help": "§6Allows placing blocks and planting crops or trees.§r\n GUI Redstone controls: Not inverted / inverted (default), pulse mode / continuous mode (default).\n Also supports spike planing from underneath the soil. Spits out items that it cannot place or plant.",
"block.engineersdecor.small_block_breaker": "Small Block Breaker",
"block.engineersdecor.small_block_breaker.help": "§6Breaks blocks in front of it.§r\n Can be disabled by applying a redstone signal. The time needed to destroy a block depends on the hardness of that block. ${!block_breaker_requires_power?engineersdecor.tooltip.massive_speed_boost_with_rf_power} ${block_breaker_requires_power?engineersdecor.tooltip.requires_rf_power}",
"block.engineersdecor.small_block_breaker.status": "SOC: %1$s%% of %2$sRF§r | progress: %3$s%%",
"block.engineersdecor.small_mineral_smelter": "Small Mineral Melting Furnace",
"block.engineersdecor.small_mineral_smelter.help": "§6High temperature, high insulation electrical stone melting furnace.§r\n Heats up mineral blocks to magma blocks, and finally to lava. Click with a mineral block (stone, cobble, etc) or use item insertion to place a block in the melter. Use bucket or fluid extraction to retrieve the lava. When cooling down lava, obsidian is generated. Remove the RF power or apply a redstone signal to disable the furnace. For automation, use a redstone comparator to see in which melting phase the mineral is a moment.",
"block.engineersdecor.small_solar_panel": "Small Solar Panel",
"block.engineersdecor.small_solar_panel.help": "§6Produces a small amount of power when exposed to sunlight.§r\n Useful for charging LF capacitors in remote systems with low consumption. The internal charge pump circuit accumulates and frequently transfers RF. Production depends on day time and the weather.",
"block.engineersdecor.small_solar_panel.status": "SOC: %1$s%% of %2$sRF§r | producing %3$sRF/t | feeding %4$sRF/t",
"block.engineersdecor.small_tree_cutter": "Small Tree Cutter",
"block.engineersdecor.small_tree_cutter.help": "§6Chops grown trees in front of it.§r\n Does not collect the lumbers. Deactivate with a redstone signal. ${!tree_cuttter_requires_power?engineersdecor.tooltip.massive_speed_boost_with_rf_power} ${tree_cuttter_requires_power?engineersdecor.tooltip.requires_rf_power}",
"block.engineersdecor.small_tree_cutter.status": "SOC: %1$s%% of %2$sRF§r | progress: %3$s%% (%4$ss)",
"block.engineersdecor.small_milking_machine": "Small Milking Machine",
"block.engineersdecor.small_milking_machine.help": "§6Occasionally grooms and milks cows.§r\n Has an internal fluid tank. Does not feed the animals. Use buckets to retrieve the milk. Pulls/stores milk container items from/to inventories at the back or bottom (preferrs extracting from the back and inserting below, but can also put filled vessels back into the same inventory). Supports fluid output to tanks or pipes below (only if milk exists as fluid). Care that it's not too crowdy in the cow pen, only happy animals stroll by voluntarily.",
"block.engineersdecor.sign_decor": "Sign Plate (Engineer's decor)",

View file

@ -175,12 +175,15 @@
"block.engineersdecor.factory_placer.help": "§6Позволяет размещать блоки и сажать урожай или деревья.§r\n GUI редстоун: Не инвертирован / инвертирован (по умолчанию), пульс режим / постоянный режим (по умолчанию).\n Also supports spike planing from underneath the soil. Выплевывает предметы, которые он не может поместить или посадить.",
"block.engineersdecor.small_block_breaker": "Фабричный разбиватель блоков",
"block.engineersdecor.small_block_breaker.help": "§6Разбивает блоки перед собой.§r\n Можно отключить, подав сигнал красного камня. Время, необходимое для разрушения блока, зависит от твердости этого блока. ${!block_breaker_requires_power?engineersdecor.tooltip.massive_speed_boost_with_rf_power} ${block_breaker_requires_power?engineersdecor.tooltip.requires_rf_power}",
"block.engineersdecor.small_block_breaker.status": "SOC: %1$s%% of %2$sRF§r | progress: %3$s%%",
"block.engineersdecor.small_mineral_smelter": "Малая плавильная печь минералов",
"block.engineersdecor.small_mineral_smelter.help": "§6Высокотемпературная электрическая каменная плавильная печь с высокой изоляцией.§r\n Нагревает минеральные блоки до магматических блоков и, наконец, до лавы. Нажмите с минеральным блоком (камень, булыжник, и т.п.) или используйте другие способы загрузки. Используйте ведро или жидкостные насосы для извлечения лавы. При охлаждении лавы, генерируется обсидиан. Отключите RF питание или подайте сигнал красного камня для отключения печи. Для автоматизации используйте редстоуновый компаратор, чтобы увидеть, в какой фазе плавления находится минерал.",
"block.engineersdecor.small_solar_panel": "Малая солнечная панель",
"block.engineersdecor.small_solar_panel.help": "§6Вырабатывает небольшое количество энергии при воздействии солнечного света.§r\n Полезно для зарядки низковольтных конденсаторов в удаленных системах с низким потреблением. Внутренний контур насоса накапливает и часто передает RF. Производство зависит от времени суток и погоды.",
"block.engineersdecor.small_solar_panel.status": "SOC: %1$s%% of %2$sRF§r | producing %3$sRF/t | feeding %4$sRF/t",
"block.engineersdecor.small_tree_cutter": "Малый лесоруб",
"block.engineersdecor.small_tree_cutter.help": "§6Вырубает деревья перед ним.§r\n Не собирает срубленное. Выключается с помощью сигнала красного камня. ${!tree_cuttter_requires_power?engineersdecor.tooltip.massive_speed_boost_with_rf_power} ${tree_cuttter_requires_power?engineersdecor.tooltip.requires_rf_power}",
"block.engineersdecor.small_tree_cutter.status": "SOC: %1$s%% of %2$sRF§r | progress: %3$s%% (%4$ss)",
"block.engineersdecor.small_milking_machine": "Малый доильный аппарат",
"block.engineersdecor.small_milking_machine.help": "§6Изредка чистит и доит коров.§r\n Имеет внутренний бак для жидкости. Не кормит животных. Используйте ведра для извлечения молока. Извлекает/кладёт сосуды для молока из/в инвентарь сзади или снизу (предпочитает извлекать сзади и класть снизу, но может также класть заполненные сосуды обратно в тот же инвентарь). Поддерживает выход жидкости в резервуары или трубы снизу (только если молоко существует в виде жидкости). Позаботьтесь, чтобы в загоне не было тесно, добровольно прогуливаются только счастливые животные.",
"block.engineersdecor.sign_decor": "Табличка с надписью (Логотип Engineer's decor)",

View file

@ -175,12 +175,15 @@
"block.engineersdecor.factory_placer.help": "§6能够放置方块和种植作物或树。§r\n GUI红石控制不反相/反相(默认),脉冲模式/持续模式(默认)。\n 也支持从耕地下方穿透方块种植。会吐出不能放置或种植的物品。",
"block.engineersdecor.small_block_breaker": "工厂方块破坏器",
"block.engineersdecor.small_block_breaker.help": "§6破坏其正前方的方块。§r\n 通入红石信号可以停止破坏。 破坏一个方块需要的时间取决于其硬度。 ${!block_breaker_requires_power?engineersdecor.tooltip.massive_speed_boost_with_rf_power} ${block_breaker_requires_power?engineersdecor.tooltip.requires_rf_power}",
"block.engineersdecor.small_block_breaker.status": "SOC: %1$s%% of %2$sRF§r | progress: %3$s%%",
"block.engineersdecor.small_mineral_smelter": "小型矿物熔炼炉",
"block.engineersdecor.small_mineral_smelter.help": "§6高温、高绝缘电熔石炉。§r\n 把矿物块加热成岩浆块,最后变成熔岩。由于 小型化的设备大小,该过程效率不高,需要大量时间和能源 来液化一块石头。",
"block.engineersdecor.small_solar_panel": "小型太阳能板",
"block.engineersdecor.small_solar_panel.help": "§6暴露在阳光下时产生少量能量。§r\n 用于低消耗地在远程系统给低压电容器充电。 内部电荷泵电路积累并频繁输出RF。产出取决于时间 和天气。",
"block.engineersdecor.small_solar_panel.status": "SOC: %1$s%% of %2$sRF§r | producing %3$sRF/t | feeding %4$sRF/t",
"block.engineersdecor.small_tree_cutter": "小型砍树机",
"block.engineersdecor.small_tree_cutter.help": "§6砍倒正前方的树。§r\n 不收集木材。通入红石信号停用。 提供RF来加快砍树速度。没有的话会很慢。",
"block.engineersdecor.small_tree_cutter.status": "SOC: %1$s%% of %2$sRF§r | progress: %3$s%% (%4$ss)",
"block.engineersdecor.small_milking_machine": "小型挤奶机",
"block.engineersdecor.small_milking_machine.help": "§6偶尔给奶牛梳理和挤奶。§r\n 有一个内置流体储罐。不会喂食动物。使用桶获得牛奶。 从后方或下方的方块物品栏拉取或存储牛奶容器物品(偏好 从下方输入,从后方输出,但也能将装满的牛奶容器放回原来 的物品栏)支持将牛奶作为流体输出到下方的储罐和管道(只要牛奶是流体)。 小心牛栏里不要太拥挤,只有快乐的动物才会自觉地在挤奶机前散步。",
"block.engineersdecor.sign_decor": "标志板(工程师的装饰)",

View file

@ -1,14 +1,15 @@
{
"homepage": "https://www.curseforge.com/minecraft/mc-mods/engineers-decor/",
"promos": {
"1.12.2-recommended": "1.0.18",
"1.12.2-latest": "1.0.19-b4",
"1.12.2-recommended": "1.0.19",
"1.12.2-latest": "1.0.19",
"1.14.4-recommended": "",
"1.14.4-latest": "1.0.19-b4",
"1.15.2-recommended": "",
"1.15.2-latest": "1.0.19-b4"
},
"1.12.2": {
"1.0.19": "[R] Release based on v1.0.19-b4. Release-to-release changes: * Transfer fixes for Tree Cutter / Block Braker, and Factory hopper. * Cleanups, feature backports * Visual fixes and improvements\n[A] Backport of status display for Tree Cutter, Block Breaker and Solar Panel.",
"1.0.19-b4": "[A] Creative tab opt-out visibility handling added (issue #90, thx pimalel233).",
"1.0.19-b3": "[A] Factory Hopper: Added bottom item handler (CR#227).",
"1.0.19-b2": "[F] Fixed Floor Grating item pass-through jitters (thx Cid).\n[M] Removed obsolete recipe collision testing recipes.",