1.12: v1.0.18 release commit. 1.14/1.15: Slab placement improved, Milking Machine cow tracking added, AI goals fixed.

This commit is contained in:
stfwi 2020-01-25 10:45:31 +01:00
parent 0f05f8e112
commit 216c1259e2
21 changed files with 404 additions and 156 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.18-b3
version_engineersdecor=1.0.18

View file

@ -1,6 +1,7 @@
{
"homepage": "https://www.curseforge.com/minecraft/mc-mods/engineers-decor/",
"1.12.2": {
"1.0.18": "[R] Release based on v1.0.18-b2. Release-to-release changes: * Tree cutter config fixes. * Treated Wood Crafting Table mouse tweaks. * Lang updates.\n[M] Lang update ru_ru (PR#77, thanks Smollet777).",
"1.0.18-b2": "[A] Added Treated Wood Crafting table tweaks (ctrl-shift moves all same stacks from the inventory, mouse wheel over crafting slot increases/decreases crafting grid stacks).\n[F] EN Lang file fixed (issue #76, thx Riverstar907).\n[F] Fixed Tree Cutter not respecting power-required config (thx federsavo, issue #77).",
"1.0.18-b1": "[M] Lang ru_ru updated (Smollet777).",
"1.0.17": "[R] Release based on v1.0.17-b3. Release-to-release changes: * Milking machine added. * Reverse recipes for slab slices added. * Texture and model improvements. * Lang file updates. * Minor bug fixes. * Config options added.\n[M] Updated zh_cn lang file (scikirbypoke).\n[A] Added opt-out config for the Small Tree Cutter.",
@ -75,7 +76,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.17",
"1.12.2-latest": "1.0.18-b2"
"1.12.2-recommended": "1.0.18",
"1.12.2-latest": "1.0.18"
}
}

View file

@ -10,7 +10,13 @@ Mod sources for Minecraft version 1.12.2.
----
## Version history
~ v1.0.18-b3
-------------------------------------------------------------------
- v1.0.18 [R] Release based on v1.0.18-b2. Release-to-release changes:
* Tree cutter config fixes.
* Treated Wood Crafting Table mouse tweaks.
* Lang updates.
-------------------------------------------------------------------
[M] Lang update ru_ru (PR#77, thanks Smollet777).
- v1.0.18-b2 [A] Added Treated Wood Crafting table tweaks (ctrl-shift moves all same stacks from the
inventory, mouse wheel over crafting slot increases/decreases crafting grid stacks).

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.1.111
version_forge_minecraft=1.14.4-28.1.116
version_fml_mappings=20190719-1.14.3
version_jei=1.14.4:6.0.0.10
version_engineersdecor=1.0.18-b4

View file

@ -1,6 +1,7 @@
{
"homepage": "https://www.curseforge.com/minecraft/mc-mods/engineers-decor/",
"1.14.4": {
"1.0.18-b4": "[M] Lang update ru_ru (PR#77, thanks Smollet777).\n[F] Fixed Milking machine cow path issue, added milking delay cow tracking.\n[F] Slab / Slab Slice placement adapted to vanilla standard.",
"1.0.18-b3": "[A] Added Treated Wood Crafting table tweaks (ctrl-shift moves all same stacks from the inventory, mouse wheel over crafting slot increases/decreases crafting grid stacks).\n[F] EN Lang file fixed (issue #76, thx Riverstar907).\n[F] Fixed Tree Cutter not respecting power-required config (thx federsavo, issue #77).\n[F] Fixed Small Solar Panel not exposing energy capability (thx MatthiasMann, issue #78).",
"1.0.18-b2": "[F] Fixed JEI integration warning if nothing is opt'ed out (thx @SDUBZ for reporting).\n[M] Lang ru_ru updated (Smollet777).",
"1.0.18-b1": "[U] Updated to Forge 1.14.4-28.1.109/20190719-1.14.3.\n[A] Added opt-out config for the Small Tree Cutter.",
@ -43,6 +44,6 @@
},
"promos": {
"1.14.4-recommended": "",
"1.14.4-latest": "1.0.18-b3"
"1.14.4-latest": "1.0.18-b4"
}
}

View file

@ -11,7 +11,9 @@ Mod sources for Minecraft version 1.14.4.
## Version history
~ v1.0.18-b4
- v1.0.18-b4 [M] Lang update ru_ru (PR#77, thanks Smollet777).
[F] Fixed Milking machine cow path issue, added milking delay cow tracking.
[F] Slab / Slab Slice placement adapted to vanilla standard.
- v1.0.18-b3 [A] Added Treated Wood Crafting table tweaks (ctrl-shift moves all same stacks from the
inventory, mouse wheel over crafting slot increases/decreases crafting grid stacks).

View file

@ -9,6 +9,9 @@
*/
package wile.engineersdecor.blocks;
import net.minecraft.fluid.Fluid;
import net.minecraft.fluid.IFluidState;
import net.minecraft.world.IWorld;
import wile.engineersdecor.detail.ModAuxiliaries;
import net.minecraft.block.*;
import net.minecraft.client.util.ITooltipFlag;
@ -40,7 +43,7 @@ import java.util.Collections;
import java.util.List;
public class BlockDecorHalfSlab extends BlockDecor implements IWaterLoggable
public class BlockDecorHalfSlab extends BlockDecor.WaterLoggable
{
public static final IntegerProperty PARTS = IntegerProperty.create("parts", 0, 14);
@ -103,7 +106,7 @@ public class BlockDecorHalfSlab extends BlockDecor implements IWaterLoggable
@Override
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder)
{ super.fillStateContainer(builder); builder.add(PARTS, WATERLOGGED); }
{ super.fillStateContainer(builder); builder.add(PARTS); }
@Override
@Nullable
@ -193,4 +196,13 @@ public class BlockDecorHalfSlab extends BlockDecor implements IWaterLoggable
SoundType st = this.getSoundType(state, world, pos, null);
world.playSound(player, pos, st.getPlaceSound(), SoundCategory.BLOCKS, (st.getVolume()+1f)/2.5f, 0.9f*st.getPitch());
}
@Override
public boolean receiveFluid(IWorld world, BlockPos pos, BlockState state, IFluidState fluidState)
{ return (state.get(PARTS)==14) ? false : super.receiveFluid(world, pos, state, fluidState); }
@Override
public boolean canContainFluid(IBlockReader world, BlockPos pos, BlockState state, Fluid fluid)
{ return (state.get(PARTS)==14) ? false : super.canContainFluid(world, pos, state, fluid); }
}

View file

@ -50,6 +50,7 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Collectors;
public class BlockDecorMilker extends BlockDecorDirectedHorizontal
@ -143,12 +144,14 @@ public class BlockDecorMilker extends BlockDecorDirectedHorizontal
public static final int MAX_ENERGY_BUFFER = 16000;
public static final int MAX_ENERGY_TRANSFER = 512;
public static final int DEFAULT_ENERGY_CONSUMPTION = 0;
public static final int DEFAULT_MILKING_DELAY_PER_COW = 4000;
private static final Direction FLUID_TRANSFER_DIRECTRIONS[] = {Direction.DOWN,Direction.EAST,Direction.SOUTH,Direction.WEST,Direction.NORTH};
private enum MilkingState { IDLE, PICKED, COMING, POSITIONING, MILKING, LEAVING, WAITING }
private static FluidStack milk_fluid_ = new FluidStack(Fluids.WATER, 0);
private static HashMap<ItemStack, ItemStack> milk_containers_ = new HashMap<>();
private static int energy_consumption = DEFAULT_ENERGY_CONSUMPTION;
private static long min_milking_delay_per_cow_ticks = DEFAULT_MILKING_DELAY_PER_COW;
private int tick_timer_;
private int energy_stored_;
private int tank_level_ = 0;
@ -158,9 +161,10 @@ public class BlockDecorMilker extends BlockDecorDirectedHorizontal
private int state_timer_ = 0;
private BlockPos tracked_cow_original_position_ = null;
public static void on_config(int energy_consumption_per_tick)
public static void on_config(int energy_consumption_per_tick, int min_milking_delay_per_cow)
{
energy_consumption = MathHelper.clamp(energy_consumption_per_tick, 0, 128);
min_milking_delay_per_cow_ticks = MathHelper.clamp(min_milking_delay_per_cow, 1000, 24000);
{
Fluid milk = null; // FluidRe.getFluid("milk");
if(milk != null) milk_fluid_ = new FluidStack(milk, BUCKET_SIZE);
@ -299,8 +303,9 @@ public class BlockDecorMilker extends BlockDecorDirectedHorizontal
// ITickable ------------------------------------------------------------------------------------
private void log(String s)
{} // println("Milker|" + s); may be enabled with config, for dev was println
private void log(String s) {} // println("Milker|" + s); may be enabled with config, for dev was println
private static final HashMap<Integer, Long> tracked_cows_ = new HashMap<Integer, Long>();
private static ItemStack milk_filled_container_item(ItemStack stack)
{ return milk_containers_.entrySet().stream().filter(e->e.getKey().isItemEqual(stack)).map(Map.Entry::getValue).findFirst().orElse(ItemStack.EMPTY); }
@ -331,6 +336,10 @@ public class BlockDecorMilker extends BlockDecorDirectedHorizontal
if(cow != null) {
cow.setNoAI(false);
SingleMoveGoal.abortFor(cow);
tracked_cows_.remove(cow.getEntityId());
for(int id:tracked_cows_.keySet().stream().filter(i->cow.getEntityWorld().getEntityByID(i)==null).collect(Collectors.toList())) {
tracked_cows_.remove(id);
}
}
tracked_cow_ = null;
state_ = MilkingState.IDLE;
@ -345,8 +354,15 @@ public class BlockDecorMilker extends BlockDecorDirectedHorizontal
CowEntity cow = null;
{
AxisAlignedBB aabb = new AxisAlignedBB(pos.offset(facing, 3)).grow(4, 2, 4);
final long t = world.getGameTime();
final List<CowEntity> cows = world.getEntitiesWithinAABB(CowEntity.class, aabb,
e->( ((tracked_cow_==null) && ((!e.isChild()) && (!e.isInLove()) && (!e.isBeingRidden()))) || (e.getUniqueID().equals(tracked_cow_)) )
e-> {
if(e.getUniqueID().equals(tracked_cow_)) return true;
if((tracked_cow_!=null) || e.isChild() || e.isInLove() || e.isBeingRidden()) return false;
if(!e.getNavigator().noPath()) return false;
if(Math.abs(tracked_cows_.getOrDefault(e.getEntityId(), 0L)-t) < min_milking_delay_per_cow_ticks) return false;
return true;
}
);
if(cows.size() == 1) {
cow = cows.get(0); // tracked or only one
@ -372,11 +388,12 @@ public class BlockDecorMilker extends BlockDecorDirectedHorizontal
BlockPos p = getPos().offset(facing,2);
log("Idle: Shove off");
blocker.setNoAI(false);
SingleMoveGoal.startFor(blocker, p, 2, 1.0, (goal, world, pos)->(pos.distanceSq(goal.getCreature().getPosition())>49));
SingleMoveGoal.startFor(blocker, p, 2, 1.0, (goal, world, pos)->(pos.distanceSq(goal.getCreature().getPosition())>100));
}
return false;
}
if(cow.getLeashed() || cow.isChild() || cow.isInLove() || (!cow.onGround) || cow.isBeingRidden() || cow.isSprinting()) return false;
tracked_cows_.put(cow.getEntityId(), cow.getEntityWorld().getGameTime());
tracked_cow_ = cow.getUniqueID();
state_ = MilkingState.PICKED;
state_timeout_ = 200;
@ -387,13 +404,7 @@ public class BlockDecorMilker extends BlockDecorDirectedHorizontal
case PICKED: {
SingleMoveGoal.startFor(
cow, target_pos, 2, 1.0,
(goal, world, pos)->{
if(pos.distanceSq(goal.getCreature().getPosition())>100) return true;
for(int i=0; i<4; ++i) {
if(world.getBlockState(pos.offset(Direction.byHorizontalIndex(i))).getBlock() instanceof BlockDecorMilker) return false;
}
return true;
},
(goal, world, pos)->(pos.distanceSq(goal.getCreature().getPosition())>100),
(goal, world, pos)->{
log("move: position reached");
goal.getCreature().setLocationAndAngles(goal.getTargetPosition().getX(), goal.getTargetPosition().getY(), goal.getTargetPosition().getZ(), facing.getHorizontalAngle(), 0);
@ -422,6 +433,7 @@ public class BlockDecorMilker extends BlockDecorDirectedHorizontal
}
case POSITIONING: {
log("Positioning: start milking");
SingleMoveGoal.abortFor(cow);
cow.setNoAI(true);
cow.setLocationAndAngles(target_pos.getX(), target_pos.getY(), target_pos.getZ(), facing.getHorizontalAngle(), 0);
world.playSound(null, pos, SoundEvents.ENTITY_COW_MILK, SoundCategory.BLOCKS, 0.5f, 1f);
@ -447,13 +459,18 @@ public class BlockDecorMilker extends BlockDecorDirectedHorizontal
state_timer_ = 500;
tick_timer_ = TICK_INTERVAL;
state_ = MilkingState.WAITING;
tracked_cows_.put(cow.getEntityId(), cow.getEntityWorld().getGameTime());
log("Leaving: process done");
return true;
}
case WAITING: {
// wait for the timeout to kick in until starting with the next.
tick_timer_ = TICK_INTERVAL;
log("Waiting: ...");
if(state_timer_ < 40) {
tracked_cow_ = null;
release_cow(null);
}
log("Waiting time elapsed");
return true;
}
default: {
@ -510,8 +527,9 @@ public class BlockDecorMilker extends BlockDecorDirectedHorizontal
{
@FunctionalInterface public interface TargetPositionInValidCheck { boolean test(SingleMoveGoal goal, IWorldReader world, BlockPos pos); }
@FunctionalInterface public interface StrollEvent { void apply(SingleMoveGoal goal, IWorldReader world, Vec3d pos); }
private static void log(String s) {} // println(s);
private static void log(String s) {} // println("SingleMoveGoal: "+s);
private static final HashMap<Integer, SingleMoveGoal> tracked_entities_ = new HashMap<Integer, SingleMoveGoal>();
private static final int motion_timeout = 20*20;
private boolean aborted_;
private boolean in_position_;
@ -538,14 +556,40 @@ public class BlockDecorMilker extends BlockDecorDirectedHorizontal
public static void startFor(CreatureEntity entity, BlockPos target_pos, int priority, double speed, TargetPositionInValidCheck abort_condition)
{ startFor(entity, new Vec3d(target_pos.getX(),target_pos.getY(),target_pos.getZ()), priority, speed, abort_condition, null, null); }
public static void startFor(CreatureEntity entity, Vec3d target_pos, int priority, double speed, TargetPositionInValidCheck abort_condition, @Nullable StrollEvent on_position_reached, @Nullable StrollEvent on_aborted)
{ entity.goalSelector.addGoal(priority, new SingleMoveGoal(entity, target_pos, speed, abort_condition, on_position_reached, on_aborted)); }
public static boolean startFor(CreatureEntity entity, Vec3d target_pos, int priority, double speed, TargetPositionInValidCheck abort_condition, @Nullable StrollEvent on_position_reached, @Nullable StrollEvent on_aborted)
{
synchronized(tracked_entities_) {
SingleMoveGoal goal = tracked_entities_.getOrDefault(entity.getEntityId(), null);
if(goal != null) {
if(!goal.aborted()) return false; // that is still running.
entity.goalSelector.removeGoal(goal);
}
log("::start("+entity.getEntityId()+")");
goal = new SingleMoveGoal(entity, target_pos, speed, abort_condition, on_position_reached, on_aborted);
tracked_entities_.put(entity.getEntityId(), goal);
entity.goalSelector.addGoal(priority, goal);
return true;
}
}
public static boolean isActiveFor(CreatureEntity entity)
{ return (entity != null) && (entity.goalSelector.getRunningGoals().anyMatch(g->(g.getGoal()) instanceof SingleMoveGoal)); }
{ return (entity != null) && (entity.goalSelector.getRunningGoals().anyMatch(
g->((g.getGoal()) instanceof SingleMoveGoal) && (!((SingleMoveGoal)(g.getGoal())).aborted())
)); }
public static void abortFor(CreatureEntity entity)
{ entity.goalSelector.getRunningGoals().filter(g->(g.getGoal()) instanceof SingleMoveGoal).forEach(g->((SingleMoveGoal)g.getGoal()).abort()); }
{
log("::abort("+entity.getEntityId()+")");
if(entity.isAlive()) {
entity.goalSelector.getRunningGoals().filter(g->(g.getGoal()) instanceof SingleMoveGoal).forEach(g->((SingleMoveGoal)g.getGoal()).abort());
}
final World world = entity.getEntityWorld();
if(world != null) {
// @todo: check nicer way to filter a map.
List<Integer> to_remove = tracked_entities_.keySet().stream().filter(i->(world.getEntityByID(i) == null)).collect(Collectors.toList());
for(int id:to_remove)tracked_entities_.remove(id);
}
}
public Vec3d getTargetPosition()
{ return target_pos_; }
@ -553,34 +597,48 @@ public class BlockDecorMilker extends BlockDecorDirectedHorizontal
public CreatureEntity getCreature()
{ return creature; }
public void abort()
public synchronized void abort()
{ aborted_ = true; }
public synchronized boolean aborted()
{ return aborted_; }
public synchronized void initialize(Vec3d target_pos, double speed, TargetPositionInValidCheck abort_condition, @Nullable StrollEvent on_position_reached, @Nullable StrollEvent on_aborted)
{
abort_condition_ = abort_condition;
on_target_position_reached_ = on_position_reached;
on_aborted_ = on_aborted;
destinationBlock = new BlockPos(target_pos.getX(), target_pos.getY(), target_pos.getZ());
timeoutCounter = 0;
runDelay = 0;
aborted_ = false;
was_aborted_ = false;
target_pos_ = new Vec3d(target_pos.getX(), target_pos.getY(), target_pos.getZ());
// this.movementSpeed = speed; -> that is final, need to override tick and func_whatever
}
@Override
public void resetTask()
{ runDelay = 0; timeoutCounter = 0; }
@Override
public double getTargetDistanceSq()
{ return 1.2; }
{ return 0.7; }
@Override
public boolean shouldMove()
{ return (timeoutCounter & 0x7) == 0; }
{ return (!aborted()) && (timeoutCounter & 0x7) == 0; }
@Override
public boolean shouldExecute()
{
if(!shouldMoveTo(creature.world, destinationBlock)) {
aborted_ = true;
return false;
} else if(aborted_) {
// shouldExecute is the point where in GoalSelector.tick() the goal is not in flagGoals and can be removed.
creature.goalSelector.getRunningGoals().filter(g->(g.getGoal()) instanceof SingleMoveGoal).forEach(g->creature.goalSelector.removeGoal(g));
creature.goalSelector.removeGoal(this);
if(aborted_) {
if((!was_aborted_) && (on_aborted_!=null)) on_aborted_.apply(this, creature.world, target_pos_);
was_aborted_ = true;
return false;
} else if(!shouldMoveTo(creature.world, destinationBlock)) {
synchronized(this) { aborted_ = true; }
return false;
} else if(--runDelay > 0) {
return false;
} else {
@ -592,32 +650,69 @@ public class BlockDecorMilker extends BlockDecorDirectedHorizontal
@Override
public void startExecuting()
{
log("startExecuting()");
timeoutCounter = 0;
if(!creature.getNavigator().tryMoveToXYZ(target_pos_.getX(), target_pos_.getY(), target_pos_.getZ(), this.movementSpeed)) abort();
if(!creature.getNavigator().tryMoveToXYZ(target_pos_.getX(), target_pos_.getY(), target_pos_.getZ(), this.movementSpeed)) {
abort();
log("startExecuting() -> abort, no path");
} else {
log("startExecuting() -> started");
}
}
public boolean shouldContinueExecuting()
{
if((aborted_) || (creature.getNavigator().noPath()) || (timeoutCounter > motion_timeout) || (!shouldMoveTo(creature.world, destinationBlock))) {
if(aborted()) {
log("shouldContinueExecuting() -> already aborted");
return false;
} else if(creature.getNavigator().noPath()) {
if((!creature.getNavigator().setPath(creature.getNavigator().func_225466_a(target_pos_.getX(), target_pos_.getY(), target_pos_.getZ(), 0), movementSpeed))) {
log("shouldContinueExecuting() -> abort, no path");
abort();
return false;
} else {
return true;
}
} else if(timeoutCounter > motion_timeout) {
log("shouldContinueExecuting() -> abort, timeout");
abort();
return false;
} else if(!shouldMoveTo(creature.world, destinationBlock)) {
log("shouldContinueExecuting() -> abort, !shouldMoveTo()");
abort();
return false;
} else {
log("shouldContinueExecuting() -> yes");
return true;
}
return true;
}
@Override
protected boolean shouldMoveTo(IWorldReader world, BlockPos pos)
{ if(!abort_condition_.test(this, world, pos)) { return true; } abort(); return false; }
{
if(abort_condition_.test(this, world, pos)) {
log("shouldMoveTo() -> abort_condition");
return false;
} else {
return true;
}
}
@Override
public void tick()
{
BlockPos testpos = new BlockPos(target_pos_.getX(), creature.getPositionVec().getY(), target_pos_.getZ());
if(!testpos.withinDistance(creature.getPositionVec(), getTargetDistanceSq())) {
if((++timeoutCounter > motion_timeout)) { abort(); return; }
if(shouldMove() && (!creature.getNavigator().tryMoveToXYZ(target_pos_.getX(), target_pos_.getY(), target_pos_.getZ(), movementSpeed))) abort();
if((++timeoutCounter > motion_timeout)) {
log("tick() -> abort, timeoutCounter");
abort();
return;
}
if(shouldMove() && (!creature.getNavigator().tryMoveToXYZ(target_pos_.getX(), target_pos_.getY(), target_pos_.getZ(), movementSpeed))) {
log("tick() -> abort, !tryMoveToXYZ()");
abort();
}
} else {
log("tick() -> abort, in position)");
in_position_ = true;
abort();
if(on_target_position_reached_ != null) on_target_position_reached_.apply(this, creature.world, target_pos_);

View file

@ -8,7 +8,10 @@
*/
package wile.engineersdecor.blocks;
import net.minecraft.fluid.Fluid;
import net.minecraft.fluid.IFluidState;
import net.minecraft.util.math.*;
import net.minecraft.world.IWorld;
import wile.engineersdecor.detail.ModAuxiliaries;
import net.minecraft.block.*;
import net.minecraft.client.util.ITooltipFlag;
@ -36,7 +39,7 @@ import java.util.Collections;
import java.util.List;
public class BlockDecorSlab extends BlockDecor implements IWaterLoggable
public class BlockDecorSlab extends BlockDecor.WaterLoggable
{
public static final IntegerProperty PARTS = IntegerProperty.create("parts", 0, 2);
public static final IntegerProperty TEXTURE_VARIANT = IntegerProperty.create("tvariant", 0, 3);
@ -93,16 +96,37 @@ public class BlockDecorSlab extends BlockDecor implements IWaterLoggable
@Override
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder)
{ super.fillStateContainer(builder); builder.add(PARTS, TEXTURE_VARIANT, WATERLOGGED); }
{ super.fillStateContainer(builder); builder.add(PARTS, TEXTURE_VARIANT); }
@Override
@Nullable
public BlockState getStateForPlacement(BlockItemUseContext context)
{
final Direction facing = context.getFace();
double y = context.getHitVec().getY();
int rnd = MathHelper.clamp((int)(MathHelper.getPositionRandom(context.getPos()) % 4), 0, 3);
return super.getStateForPlacement(context).with(PARTS, ((facing==Direction.UP) || ((facing!=Direction.DOWN) && (y < 0.6))) ? 0 : 1).with(TEXTURE_VARIANT, rnd);
BlockPos pos = context.getPos();
if(context.getWorld().getBlockState(pos).getBlock() == this) return context.getWorld().getBlockState(pos).with(PARTS, 2).with(WATERLOGGED, false);
final int rnd = MathHelper.clamp((int)(MathHelper.getPositionRandom(context.getPos()) & 0x3), 0, 3);
final Direction face = context.getFace();
final BlockState placement_state = super.getStateForPlacement(context).with(TEXTURE_VARIANT, rnd); // fluid state
if(face == Direction.UP) return placement_state.with(PARTS, 0);
if(face == Direction.DOWN) return placement_state.with(PARTS, 1);
if(!face.getAxis().isHorizontal()) return placement_state;
final boolean isupper = ((context.getHitVec().getY() - context.getPos().getY()) > 0.5);
return placement_state.with(PARTS, isupper ? 1 : 0);
}
@Override
@SuppressWarnings("deprecation")
public boolean isReplaceable(BlockState state, BlockItemUseContext context)
{
if(context.getItem().getItem() != this.asItem()) return false;
if(!context.replacingClickedOnBlock()) return true;
final Direction face = context.getFace();
final int parts = state.get(PARTS);
if((face == Direction.UP) && (parts==0)) return true;
if((face == Direction.DOWN) && (parts==1)) return true;
if(!face.getAxis().isHorizontal()) return false;
final boolean isupper = ((context.getHitVec().getY() - context.getPos().getY()) > 0.5);
return isupper ? (parts==0) : (parts==1);
}
@Override
@ -123,29 +147,6 @@ public class BlockDecorSlab extends BlockDecor implements IWaterLoggable
public List<ItemStack> dropList(BlockState state, World world, BlockPos pos, boolean explosion)
{ return new ArrayList<ItemStack>(Collections.singletonList(new ItemStack(this.asItem(), num_slabs_contained_in_parts_[state.get(PARTS) & 0x3]))); }
@Override
@SuppressWarnings("deprecation")
public boolean onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult rayTraceResult)
{
Direction face = rayTraceResult.getFace();
final ItemStack stack = player.getHeldItem(hand);
if(stack.isEmpty() || (Block.getBlockFromItem(stack.getItem()) != this)) return false;
int parts = state.get(PARTS);
if(((face == Direction.UP) && (parts == 0)) || ((face == Direction.DOWN) && (parts == 1))) {
world.setBlockState(pos, state.with(PARTS, 2), 3);
} else {
return false; // "not handled" -> let parent decide if a new slab has to be placed on top/bottom.
}
if(world.isRemote) return true;
if(!player.isCreative()) {
stack.shrink(1);
if(player.inventory != null) player.inventory.markDirty();
}
SoundType st = this.getSoundType(state, world, pos, null);
world.playSound(null, pos, st.getPlaceSound(), SoundCategory.BLOCKS, (st.getVolume()+1f)/2.5f, 0.9f*st.getPitch());
return true;
}
@Override
@SuppressWarnings("deprecation")
public void onBlockClicked(BlockState state, World world, BlockPos pos, PlayerEntity player)
@ -180,4 +181,11 @@ public class BlockDecorSlab extends BlockDecor implements IWaterLoggable
world.playSound(player, pos, st.getPlaceSound(), SoundCategory.BLOCKS, (st.getVolume()+1f)/2.5f, 0.9f*st.getPitch());
}
@Override
public boolean receiveFluid(IWorld world, BlockPos pos, BlockState state, IFluidState fluidState)
{ return (state.get(PARTS)==2) ? false : super.receiveFluid(world, pos, state, fluidState); }
public boolean canContainFluid(IBlockReader world, BlockPos pos, BlockState state, Fluid fluid)
{ return (state.get(PARTS)==2) ? false : super.canContainFluid(world, pos, state, fluid); }
}

View file

@ -154,6 +154,7 @@ public class ModConfig
public final ForgeConfigSpec.IntValue tree_cuttter_cutting_time_needed;
public final ForgeConfigSpec.BooleanValue tree_cuttter_requires_power;
public final ForgeConfigSpec.IntValue milking_machine_energy_consumption;
public final ForgeConfigSpec.IntValue milking_machine_milking_delay;
CommonConfig(ForgeConfigSpec.Builder builder)
{
@ -476,6 +477,10 @@ public class ModConfig
"Use zero to disable energy dependency and energy handling of the machine. " +
"The config value can be changed on-the-fly for tuning.")
.defineInRange("milking_machine_energy_consumption", BlockDecorMilker.BTileEntity.DEFAULT_ENERGY_CONSUMPTION, 0, 128);
milking_machine_milking_delay = builder
.translation(ModEngineersDecor.MODID + ".config.milking_machine_milking_delay")
.comment("Defines (for each individual cow) the minimum time between milking." )
.defineInRange("milking_machine_milking_delay", BlockDecorMilker.BTileEntity.DEFAULT_MILKING_DELAY_PER_COW, 1000, 24000);
builder.pop();
}
}
@ -609,7 +614,7 @@ public class ModConfig
BlockDecorSolarPanel.BTileEntity.on_config(COMMON.small_solar_panel_peak_production.get());
BlockDecorBreaker.BTileEntity.on_config(COMMON.block_breaker_power_consumption.get(), COMMON.block_breaker_reluctance.get(), COMMON.block_breaker_min_breaking_time.get(), COMMON.block_breaker_requires_power.get());
BlockDecorTreeCutter.BTileEntity.on_config(COMMON.tree_cuttter_energy_consumption.get(), COMMON.tree_cuttter_cutting_time_needed.get(), COMMON.tree_cuttter_requires_power.get());
BlockDecorMilker.BTileEntity.on_config(COMMON.milking_machine_energy_consumption.get());
BlockDecorMilker.BTileEntity.on_config(COMMON.milking_machine_energy_consumption.get(), COMMON.milking_machine_milking_delay.get());
without_crafting_table = isOptedOut(ModContent.TREATED_WOOD_CRAFTING_TABLE);
immersiveengineering_installed = ModAuxiliaries.isModLoaded("immersiveengineering");
with_experimental_features_ = COMMON.with_experimental.get();

View file

@ -101,8 +101,8 @@
"block.engineersdecor.clinker_brick_stairs.help": "§6По цвету выглядят немного темнее и интенсивнее, чем Кирпичный блок",
"block.engineersdecor.clinker_brick_stained_stairs": "Грязные кирпичные ступеньки",
"block.engineersdecor.clinker_brick_stained_stairs.help": "§6Выглядят немного темнее и интенсивнее, чем Кирпичный блок. Имеют более заметные следы грязи или пятен.",
"block.engineersdecor.slag_brick_stairs": "Клинкерные кирпичные ступеньки",
"block.engineersdecor.slag_brick_stairs.help": "§6По цвету выглядят немного темнее и интенсивнее, чем Кирпичный блок.",
"block.engineersdecor.slag_brick_stairs": "Кирпичные ступеньки из шлакоблока",
"block.engineersdecor.slag_brick_stairs.help": "§6Серо-коричневые кирпичные ступеньки.",
"block.engineersdecor.rebar_concrete_stairs": "Железобетонные ступеньки",
"block.engineersdecor.rebar_concrete_stairs.help": "§6Железобетонные ступеньки.§r Дорогие, но взрывоустойчивые, как обсидиан.",
"block.engineersdecor.rebar_concrete_tile_stairs": "Ступеньки из железобетонной плитки",
@ -134,7 +134,7 @@
"block.engineersdecor.treated_wood_stool": "Табурет из обработанного дерева",
"block.engineersdecor.treated_wood_stool.help": "§6Крепкий деревянный табурет.§r Для использования в помещении и на улице.",
"block.engineersdecor.treated_wood_crafting_table": "Верстак из обработанного дерева",
"block.engineersdecor.treated_wood_crafting_table.help": "§6Прочный и устойчивый к погодным условиям. Восемь слотов для хранения. Хранит инвентарь. Нажимайте кнопки со стрелками вверх/вниз для выбора из истории, выходной слот для размещения предметов, X-кнопка очистить сетку крафта и историю. Shift-клик по стеку: передача стека от игрока в хранилище при создании если сетка пуста, в противном случае перенос от игрока в сетку. Автоматически распределяет кликаемый стек.",
"block.engineersdecor.treated_wood_crafting_table.help": "§6Прочный и устойчивый к погодным условиям. Восемь слотов для хранения. Хранит инвентарь. Нажимайте кнопки со стрелками вверх/вниз для выбора из истории, выходной слот для размещения предметов, X-кнопка очистить сетку крафта и историю. Shift-клик по стеку: передача стека от игрока в хранилище при создании если сетка пуста, в противном случае перенос от игрока в сетку. Автоматически распределяет кликаемый стек. Shift-Ctrl-клик по стаку: перемещает одинаковые стаки. Колёсико мыши: добавляет/отнимает предметы в сетке.",
"block.engineersdecor.treated_wood_side_table": "Столик из обработанного дерева",
"block.engineersdecor.treated_wood_side_table.help": "§6Нужен после того, как работа выполнена.",
"block.engineersdecor.iron_inset_light": "Встраиваемый осветитель",

View file

@ -2,7 +2,7 @@
org.gradle.daemon=false
org.gradle.jvmargs=-Xmx8G
version_minecraft=1.15.1
version_forge_minecraft=1.15.1-30.0.36
version_forge_minecraft=1.15.1-30.0.51
version_fml_mappings=20191105-1.14.3
version_jei=1.15.1-6.0.0.1
version_engineersdecor=1.0.18-b4

View file

@ -1,6 +1,7 @@
{
"homepage": "https://www.curseforge.com/minecraft/mc-mods/engineers-decor/",
"1.15.1": {
"1.0.18-b4": "[A] Ported Treated Wood Crafting Table item rendering.\n[F] Fixed Milking machine cow path issue, added milking delay cow tracking.\n[F] Slab / Slab Slice placement adapted to vanilla standard.\n[M] Lang update ru_ru (PR#77, thanks Smollet777).",
"1.0.18-b3": "[A] Added Treated Wood Crafting Table tweaks (ctrl-shift moves all same stacks from the inventory, mouse wheel over crafting slot increases/decreases crafting grid stacks).\n[F] EN Lang file fixed (issue #76, thx Riverstar907).\n[F] Fixed Tree Cutter not respecting power-required config (thx federsavo, issue #77).\n[F] Fixed Small Solar Panel not exposing energy capability (thx MatthiasMann, issue #78).",
"1.0.18-b2": "[M] Lang ru_ru updated (Smollet777).",
"1.0.18-b1": "[U] Updated to Forge 1.15.1-30.0.16/20190719-1.14.3.\n[F] Client setup Dist annotation fixed (issue #73, thx hitsu420).\n[F] Double newline escapes in lang files fixed (\"\\n\" in a tooltip).\n[M] Updated zh_cn lang file (scikirbypoke).\n[A] Added opt-out config for the Small Tree Cutter",
@ -8,6 +9,6 @@
},
"promos": {
"1.15.1-recommended": "",
"1.15.1-latest": "1.0.18-b3"
"1.15.1-latest": "1.0.18-b4"
}
}

View file

@ -11,7 +11,10 @@ Mod sources for Minecraft version 1.15.1.
## Version history
~ v1.0.18-b4 [A] Ported Treated Wood Crafting Table item rendering.
- v1.0.18-b4 [A] Ported Treated Wood Crafting Table item rendering.
[F] Fixed Milking machine cow path issue, added milking delay cow tracking.
[F] Slab / Slab Slice placement adapted to vanilla standard.
[M] Lang update ru_ru (PR#77, thanks Smollet777).
- v1.0.18-b3 [A] Added Treated Wood Crafting Table tweaks (ctrl-shift moves all same stacks from the
inventory, mouse wheel over crafting slot increases/decreases crafting grid stacks).

View file

@ -124,7 +124,7 @@ public class ModEngineersDecor
{ config_loaded = true; }
@SubscribeEvent
public static void onConfigFileChange(net.minecraftforge.fml.config.ModConfig.ConfigReloading configEvent)
public static void onConfigReload(net.minecraftforge.fml.config.ModConfig.Reloading configEvent)
{
try {
ModEngineersDecor.logger().info("Config file changed {}", configEvent.getConfig().getFileName());

View file

@ -11,7 +11,7 @@ package wile.engineersdecor.blocks;
import wile.engineersdecor.detail.ModAuxiliaries;
import net.minecraft.block.*;
import net.minecraft.client.util.ITooltipFlag;
import net.minecraft.world.IWorld;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.state.IntegerProperty;
@ -20,9 +20,12 @@ import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.shapes.VoxelShapes;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.fluid.Fluid;
import net.minecraft.fluid.IFluidState;
import net.minecraft.world.World;
import net.minecraft.entity.EntityType;
import net.minecraft.state.StateContainer;
import net.minecraft.client.util.ITooltipFlag;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.block.BlockState;
@ -40,7 +43,7 @@ import java.util.Collections;
import java.util.List;
public class BlockDecorHalfSlab extends BlockDecor implements IWaterLoggable
public class BlockDecorHalfSlab extends BlockDecor.WaterLoggable
{
public static final IntegerProperty PARTS = IntegerProperty.create("parts", 0, 14);
@ -102,7 +105,7 @@ public class BlockDecorHalfSlab extends BlockDecor implements IWaterLoggable
@Override
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder)
{ super.fillStateContainer(builder); builder.add(PARTS, WATERLOGGED); }
{ super.fillStateContainer(builder); builder.add(PARTS); }
@Override
@Nullable
@ -192,4 +195,13 @@ public class BlockDecorHalfSlab extends BlockDecor implements IWaterLoggable
SoundType st = this.getSoundType(state, world, pos, null);
world.playSound(player, pos, st.getPlaceSound(), SoundCategory.BLOCKS, (st.getVolume()+1f)/2.5f, 0.9f*st.getPitch());
}
@Override
public boolean receiveFluid(IWorld world, BlockPos pos, BlockState state, IFluidState fluidState)
{ return (state.get(PARTS)==14) ? false : super.receiveFluid(world, pos, state, fluidState); }
@Override
public boolean canContainFluid(IBlockReader world, BlockPos pos, BlockState state, Fluid fluid)
{ return (state.get(PARTS)==14) ? false : super.canContainFluid(world, pos, state, fluid); }
}

View file

@ -50,6 +50,7 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Collectors;
public class BlockDecorMilker extends BlockDecorDirectedHorizontal
@ -143,12 +144,14 @@ public class BlockDecorMilker extends BlockDecorDirectedHorizontal
public static final int MAX_ENERGY_BUFFER = 16000;
public static final int MAX_ENERGY_TRANSFER = 512;
public static final int DEFAULT_ENERGY_CONSUMPTION = 0;
public static final int DEFAULT_MILKING_DELAY_PER_COW = 4000;
private static final Direction FLUID_TRANSFER_DIRECTRIONS[] = {Direction.DOWN,Direction.EAST,Direction.SOUTH,Direction.WEST,Direction.NORTH};
private enum MilkingState { IDLE, PICKED, COMING, POSITIONING, MILKING, LEAVING, WAITING }
private static FluidStack milk_fluid_ = new FluidStack(Fluids.WATER, 0);
private static HashMap<ItemStack, ItemStack> milk_containers_ = new HashMap<>();
private static int energy_consumption = DEFAULT_ENERGY_CONSUMPTION;
private static long min_milking_delay_per_cow_ticks = DEFAULT_MILKING_DELAY_PER_COW;
private int tick_timer_;
private int energy_stored_;
private int tank_level_ = 0;
@ -158,9 +161,10 @@ public class BlockDecorMilker extends BlockDecorDirectedHorizontal
private int state_timer_ = 0;
private BlockPos tracked_cow_original_position_ = null;
public static void on_config(int energy_consumption_per_tick)
public static void on_config(int energy_consumption_per_tick, int min_milking_delay_per_cow)
{
energy_consumption = MathHelper.clamp(energy_consumption_per_tick, 0, 128);
min_milking_delay_per_cow_ticks = MathHelper.clamp(min_milking_delay_per_cow, 1000, 24000);
{
Fluid milk = null; // FluidRe.getFluid("milk");
if(milk != null) milk_fluid_ = new FluidStack(milk, BUCKET_SIZE);
@ -299,8 +303,10 @@ public class BlockDecorMilker extends BlockDecorDirectedHorizontal
// ITickable ------------------------------------------------------------------------------------
private static final HashMap<Integer, Long> tracked_cows_ = new HashMap<Integer, Long>();
private void log(String s)
{} // println("Milker|" + s); may be enabled with config, for dev was println
{ System.out.println("Milker|" + s); } // println("Milker|" + s); may be enabled with config, for dev was println
private static ItemStack milk_filled_container_item(ItemStack stack)
{ return milk_containers_.entrySet().stream().filter(e->e.getKey().isItemEqual(stack)).map(Map.Entry::getValue).findFirst().orElse(ItemStack.EMPTY); }
@ -331,6 +337,10 @@ public class BlockDecorMilker extends BlockDecorDirectedHorizontal
if(cow != null) {
cow.setNoAI(false);
SingleMoveGoal.abortFor(cow);
tracked_cows_.remove(cow.getEntityId());
for(int id:tracked_cows_.keySet().stream().filter(i->cow.getEntityWorld().getEntityByID(i)==null).collect(Collectors.toList())) {
tracked_cows_.remove(id);
}
}
tracked_cow_ = null;
state_ = MilkingState.IDLE;
@ -345,8 +355,15 @@ public class BlockDecorMilker extends BlockDecorDirectedHorizontal
CowEntity cow = null;
{
AxisAlignedBB aabb = new AxisAlignedBB(pos.offset(facing, 3)).grow(4, 2, 4);
final long t = world.getGameTime();
final List<CowEntity> cows = world.getEntitiesWithinAABB(CowEntity.class, aabb,
e->( ((tracked_cow_==null) && ((!e.isChild()) && (!e.isInLove()) && (!e.isBeingRidden()))) || (e.getUniqueID().equals(tracked_cow_)) )
e-> {
if(e.getUniqueID().equals(tracked_cow_)) return true;
if((tracked_cow_!=null) || e.isChild() || e.isInLove() || e.isBeingRidden()) return false;
if(!e.getNavigator().noPath()) return false;
if(Math.abs(tracked_cows_.getOrDefault(e.getEntityId(), 0L)-t) < min_milking_delay_per_cow_ticks) return false;
return true;
}
);
if(cows.size() == 1) {
cow = cows.get(0); // tracked or only one
@ -372,11 +389,12 @@ public class BlockDecorMilker extends BlockDecorDirectedHorizontal
BlockPos p = getPos().offset(facing,2);
log("Idle: Shove off");
blocker.setNoAI(false);
SingleMoveGoal.startFor(blocker, p, 2, 1.0, (goal, world, pos)->(pos.distanceSq(goal.getCreature().getPosition())>49));
SingleMoveGoal.startFor(blocker, p, 2, 1.0, (goal, world, pos)->(pos.distanceSq(goal.getCreature().getPosition())>100));
}
return false;
}
if(cow.getLeashed() || cow.isChild() || cow.isInLove() || (!cow.onGround) || cow.isBeingRidden() || cow.isSprinting()) return false;
tracked_cows_.put(cow.getEntityId(), cow.getEntityWorld().getGameTime());
tracked_cow_ = cow.getUniqueID();
state_ = MilkingState.PICKED;
state_timeout_ = 200;
@ -387,13 +405,7 @@ public class BlockDecorMilker extends BlockDecorDirectedHorizontal
case PICKED: {
SingleMoveGoal.startFor(
cow, target_pos, 2, 1.0,
(goal, world, pos)->{
if(pos.distanceSq(goal.getCreature().getPosition())>100) return true;
for(int i=0; i<4; ++i) {
if(world.getBlockState(pos.offset(Direction.byHorizontalIndex(i))).getBlock() instanceof BlockDecorMilker) return false;
}
return true;
},
(goal, world, pos)->(pos.distanceSq(goal.getCreature().getPosition())>100),
(goal, world, pos)->{
log("move: position reached");
goal.getCreature().setLocationAndAngles(goal.getTargetPosition().getX(), goal.getTargetPosition().getY(), goal.getTargetPosition().getZ(), facing.getHorizontalAngle(), 0);
@ -422,6 +434,7 @@ public class BlockDecorMilker extends BlockDecorDirectedHorizontal
}
case POSITIONING: {
log("Positioning: start milking");
SingleMoveGoal.abortFor(cow);
cow.setNoAI(true);
cow.setLocationAndAngles(target_pos.getX(), target_pos.getY(), target_pos.getZ(), facing.getHorizontalAngle(), 0);
world.playSound(null, pos, SoundEvents.ENTITY_COW_MILK, SoundCategory.BLOCKS, 0.5f, 1f);
@ -447,13 +460,18 @@ public class BlockDecorMilker extends BlockDecorDirectedHorizontal
state_timer_ = 500;
tick_timer_ = TICK_INTERVAL;
state_ = MilkingState.WAITING;
tracked_cows_.put(cow.getEntityId(), cow.getEntityWorld().getGameTime());
log("Leaving: process done");
return true;
}
case WAITING: {
// wait for the timeout to kick in until starting with the next.
tick_timer_ = TICK_INTERVAL;
log("Waiting: ...");
if(state_timer_ < 40) {
tracked_cow_ = null;
release_cow(null);
}
log("Waiting time elapsed");
return true;
}
default: {
@ -510,8 +528,9 @@ public class BlockDecorMilker extends BlockDecorDirectedHorizontal
{
@FunctionalInterface public interface TargetPositionInValidCheck { boolean test(SingleMoveGoal goal, IWorldReader world, BlockPos pos); }
@FunctionalInterface public interface StrollEvent { void apply(SingleMoveGoal goal, IWorldReader world, Vec3d pos); }
private static void log(String s) {} // println(s);
private static void log(String s) {} // println("SingleMoveGoal: "+s);
private static final HashMap<Integer, SingleMoveGoal> tracked_entities_ = new HashMap<Integer, SingleMoveGoal>();
private static final int motion_timeout = 20*20;
private boolean aborted_;
private boolean in_position_;
@ -538,14 +557,40 @@ public class BlockDecorMilker extends BlockDecorDirectedHorizontal
public static void startFor(CreatureEntity entity, BlockPos target_pos, int priority, double speed, TargetPositionInValidCheck abort_condition)
{ startFor(entity, new Vec3d(target_pos.getX(),target_pos.getY(),target_pos.getZ()), priority, speed, abort_condition, null, null); }
public static void startFor(CreatureEntity entity, Vec3d target_pos, int priority, double speed, TargetPositionInValidCheck abort_condition, @Nullable StrollEvent on_position_reached, @Nullable StrollEvent on_aborted)
{ entity.goalSelector.addGoal(priority, new SingleMoveGoal(entity, target_pos, speed, abort_condition, on_position_reached, on_aborted)); }
public static boolean startFor(CreatureEntity entity, Vec3d target_pos, int priority, double speed, TargetPositionInValidCheck abort_condition, @Nullable StrollEvent on_position_reached, @Nullable StrollEvent on_aborted)
{
synchronized(tracked_entities_) {
SingleMoveGoal goal = tracked_entities_.getOrDefault(entity.getEntityId(), null);
if(goal != null) {
if(!goal.aborted()) return false; // that is still running.
entity.goalSelector.removeGoal(goal);
}
log("::start("+entity.getEntityId()+")");
goal = new SingleMoveGoal(entity, target_pos, speed, abort_condition, on_position_reached, on_aborted);
tracked_entities_.put(entity.getEntityId(), goal);
entity.goalSelector.addGoal(priority, goal);
return true;
}
}
public static boolean isActiveFor(CreatureEntity entity)
{ return (entity != null) && (entity.goalSelector.getRunningGoals().anyMatch(g->(g.getGoal()) instanceof SingleMoveGoal)); }
{ return (entity != null) && (entity.goalSelector.getRunningGoals().anyMatch(
g->((g.getGoal()) instanceof SingleMoveGoal) && (!((SingleMoveGoal)(g.getGoal())).aborted())
)); }
public static void abortFor(CreatureEntity entity)
{ entity.goalSelector.getRunningGoals().filter(g->(g.getGoal()) instanceof SingleMoveGoal).forEach(g->((SingleMoveGoal)g.getGoal()).abort()); }
{
log("::abort("+entity.getEntityId()+")");
if(entity.isAlive()) {
entity.goalSelector.getRunningGoals().filter(g->(g.getGoal()) instanceof SingleMoveGoal).forEach(g->((SingleMoveGoal)g.getGoal()).abort());
}
final World world = entity.getEntityWorld();
if(world != null) {
// @todo: check nicer way to filter a map.
List<Integer> to_remove = tracked_entities_.keySet().stream().filter(i->(world.getEntityByID(i) == null)).collect(Collectors.toList());
for(int id:to_remove)tracked_entities_.remove(id);
}
}
public Vec3d getTargetPosition()
{ return target_pos_; }
@ -553,34 +598,48 @@ public class BlockDecorMilker extends BlockDecorDirectedHorizontal
public CreatureEntity getCreature()
{ return creature; }
public void abort()
public synchronized void abort()
{ aborted_ = true; }
public synchronized boolean aborted()
{ return aborted_; }
public synchronized void initialize(Vec3d target_pos, double speed, TargetPositionInValidCheck abort_condition, @Nullable StrollEvent on_position_reached, @Nullable StrollEvent on_aborted)
{
abort_condition_ = abort_condition;
on_target_position_reached_ = on_position_reached;
on_aborted_ = on_aborted;
destinationBlock = new BlockPos(target_pos.getX(), target_pos.getY(), target_pos.getZ());
timeoutCounter = 0;
runDelay = 0;
aborted_ = false;
was_aborted_ = false;
target_pos_ = new Vec3d(target_pos.getX(), target_pos.getY(), target_pos.getZ());
// this.movementSpeed = speed; -> that is final, need to override tick and func_whatever
}
@Override
public void resetTask()
{ runDelay = 0; timeoutCounter = 0; }
@Override
public double getTargetDistanceSq()
{ return 1.2; }
{ return 0.7; }
@Override
public boolean shouldMove()
{ return (timeoutCounter & 0x7) == 0; }
{ return (!aborted()) && (timeoutCounter & 0x7) == 0; }
@Override
public boolean shouldExecute()
{
if(!shouldMoveTo(creature.world, destinationBlock)) {
aborted_ = true;
return false;
} else if(aborted_) {
// shouldExecute is the point where in GoalSelector.tick() the goal is not in flagGoals and can be removed.
creature.goalSelector.getRunningGoals().filter(g->(g.getGoal()) instanceof SingleMoveGoal).forEach(g->creature.goalSelector.removeGoal(g));
creature.goalSelector.removeGoal(this);
if(aborted_) {
if((!was_aborted_) && (on_aborted_!=null)) on_aborted_.apply(this, creature.world, target_pos_);
was_aborted_ = true;
return false;
} else if(!shouldMoveTo(creature.world, destinationBlock)) {
synchronized(this) { aborted_ = true; }
return false;
} else if(--runDelay > 0) {
return false;
} else {
@ -592,32 +651,69 @@ public class BlockDecorMilker extends BlockDecorDirectedHorizontal
@Override
public void startExecuting()
{
log("startExecuting()");
timeoutCounter = 0;
if(!creature.getNavigator().tryMoveToXYZ(target_pos_.getX(), target_pos_.getY(), target_pos_.getZ(), this.movementSpeed)) abort();
if(!creature.getNavigator().tryMoveToXYZ(target_pos_.getX(), target_pos_.getY(), target_pos_.getZ(), this.movementSpeed)) {
abort();
log("startExecuting() -> abort, no path");
} else {
log("startExecuting() -> started");
}
}
public boolean shouldContinueExecuting()
{
if((aborted_) || (creature.getNavigator().noPath()) || (timeoutCounter > motion_timeout) || (!shouldMoveTo(creature.world, destinationBlock))) {
if(aborted()) {
log("shouldContinueExecuting() -> already aborted");
return false;
} else if(creature.getNavigator().noPath()) {
if((!creature.getNavigator().setPath(creature.getNavigator().func_225466_a(target_pos_.getX(), target_pos_.getY(), target_pos_.getZ(), 0), movementSpeed))) {
log("shouldContinueExecuting() -> abort, no path");
abort();
return false;
} else {
return true;
}
} else if(timeoutCounter > motion_timeout) {
log("shouldContinueExecuting() -> abort, timeout");
abort();
return false;
} else if(!shouldMoveTo(creature.world, destinationBlock)) {
log("shouldContinueExecuting() -> abort, !shouldMoveTo()");
abort();
return false;
} else {
log("shouldContinueExecuting() -> yes");
return true;
}
return true;
}
@Override
protected boolean shouldMoveTo(IWorldReader world, BlockPos pos)
{ if(!abort_condition_.test(this, world, pos)) { return true; } abort(); return false; }
{
if(abort_condition_.test(this, world, pos)) {
log("shouldMoveTo() -> abort_condition");
return false;
} else {
return true;
}
}
@Override
public void tick()
{
BlockPos testpos = new BlockPos(target_pos_.getX(), creature.getPositionVec().getY(), target_pos_.getZ());
if(!testpos.withinDistance(creature.getPositionVec(), getTargetDistanceSq())) {
if((++timeoutCounter > motion_timeout)) { abort(); return; }
if(shouldMove() && (!creature.getNavigator().tryMoveToXYZ(target_pos_.getX(), target_pos_.getY(), target_pos_.getZ(), movementSpeed))) abort();
if((++timeoutCounter > motion_timeout)) {
log("tick() -> abort, timeoutCounter");
abort();
return;
}
if(shouldMove() && (!creature.getNavigator().tryMoveToXYZ(target_pos_.getX(), target_pos_.getY(), target_pos_.getZ(), movementSpeed))) {
log("tick() -> abort, !tryMoveToXYZ()");
abort();
}
} else {
log("tick() -> abort, in position)");
in_position_ = true;
abort();
if(on_target_position_reached_ != null) on_target_position_reached_.apply(this, creature.world, target_pos_);

View file

@ -36,7 +36,7 @@ import java.util.Collections;
import java.util.List;
public class BlockDecorSlab extends BlockDecor implements IWaterLoggable
public class BlockDecorSlab extends BlockDecor.WaterLoggable
{
public static final IntegerProperty PARTS = IntegerProperty.create("parts", 0, 2);
public static final IntegerProperty TEXTURE_VARIANT = IntegerProperty.create("tvariant", 0, 3);
@ -92,16 +92,37 @@ public class BlockDecorSlab extends BlockDecor implements IWaterLoggable
@Override
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder)
{ super.fillStateContainer(builder); builder.add(PARTS, TEXTURE_VARIANT, WATERLOGGED); }
{ super.fillStateContainer(builder); builder.add(PARTS, TEXTURE_VARIANT); }
@Override
@Nullable
public BlockState getStateForPlacement(BlockItemUseContext context)
{
final Direction facing = context.getFace();
double y = context.getHitVec().getY();
int rnd = MathHelper.clamp((int)(MathHelper.getPositionRandom(context.getPos()) % 4), 0, 3);
return super.getStateForPlacement(context).with(PARTS, ((facing==Direction.UP) || ((facing!=Direction.DOWN) && (y < 0.6))) ? 0 : 1).with(TEXTURE_VARIANT, rnd);
BlockPos pos = context.getPos();
if(context.getWorld().getBlockState(pos).getBlock() == this) return context.getWorld().getBlockState(pos).with(PARTS, 2).with(WATERLOGGED, false);
final int rnd = MathHelper.clamp((int)(MathHelper.getPositionRandom(context.getPos()) & 0x3), 0, 3);
final Direction face = context.getFace();
final BlockState placement_state = super.getStateForPlacement(context).with(TEXTURE_VARIANT, rnd); // fluid state
if(face == Direction.UP) return placement_state.with(PARTS, 0);
if(face == Direction.DOWN) return placement_state.with(PARTS, 1);
if(!face.getAxis().isHorizontal()) return placement_state;
final boolean isupper = ((context.getHitVec().getY() - context.getPos().getY()) > 0.5);
return placement_state.with(PARTS, isupper ? 1 : 0);
}
@Override
@SuppressWarnings("deprecation")
public boolean isReplaceable(BlockState state, BlockItemUseContext context)
{
if(context.getItem().getItem() != this.asItem()) return false;
if(!context.replacingClickedOnBlock()) return true;
final Direction face = context.getFace();
final int parts = state.get(PARTS);
if((face == Direction.UP) && (parts==0)) return true;
if((face == Direction.DOWN) && (parts==1)) return true;
if(!face.getAxis().isHorizontal()) return false;
final boolean isupper = ((context.getHitVec().getY() - context.getPos().getY()) > 0.5);
return isupper ? (parts==0) : (parts==1);
}
@Override
@ -122,29 +143,6 @@ public class BlockDecorSlab extends BlockDecor implements IWaterLoggable
public List<ItemStack> dropList(BlockState state, World world, BlockPos pos, boolean explosion)
{ return new ArrayList<ItemStack>(Collections.singletonList(new ItemStack(this.asItem(), num_slabs_contained_in_parts_[state.get(PARTS) & 0x3]))); }
@Override
@SuppressWarnings("deprecation")
public boolean onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult rayTraceResult)
{
Direction face = rayTraceResult.getFace();
final ItemStack stack = player.getHeldItem(hand);
if(stack.isEmpty() || (Block.getBlockFromItem(stack.getItem()) != this)) return false;
int parts = state.get(PARTS);
if(((face == Direction.UP) && (parts == 0)) || ((face == Direction.DOWN) && (parts == 1))) {
world.setBlockState(pos, state.with(PARTS, 2), 3);
} else {
return false; // "not handled" -> let parent decide if a new slab has to be placed on top/bottom.
}
if(world.isRemote) return true;
if(!player.isCreative()) {
stack.shrink(1);
if(player.inventory != null) player.inventory.markDirty();
}
SoundType st = this.getSoundType(state, world, pos, null);
world.playSound(null, pos, st.getPlaceSound(), SoundCategory.BLOCKS, (st.getVolume()+1f)/2.5f, 0.9f*st.getPitch());
return true;
}
@Override
@SuppressWarnings("deprecation")
public void onBlockClicked(BlockState state, World world, BlockPos pos, PlayerEntity player)

View file

@ -154,6 +154,7 @@ public class ModConfig
public final ForgeConfigSpec.IntValue tree_cuttter_cutting_time_needed;
public final ForgeConfigSpec.BooleanValue tree_cuttter_requires_power;
public final ForgeConfigSpec.IntValue milking_machine_energy_consumption;
public final ForgeConfigSpec.IntValue milking_machine_milking_delay;
CommonConfig(ForgeConfigSpec.Builder builder)
{
@ -476,6 +477,10 @@ public class ModConfig
"Use zero to disable energy dependency and energy handling of the machine. " +
"The config value can be changed on-the-fly for tuning.")
.defineInRange("milking_machine_energy_consumption", BlockDecorMilker.BTileEntity.DEFAULT_ENERGY_CONSUMPTION, 0, 128);
milking_machine_milking_delay = builder
.translation(ModEngineersDecor.MODID + ".config.milking_machine_milking_delay")
.comment("Defines (for each individual cow) the minimum time between milking." )
.defineInRange("milking_machine_milking_delay", BlockDecorMilker.BTileEntity.DEFAULT_MILKING_DELAY_PER_COW, 1000, 24000);
builder.pop();
}
}
@ -609,7 +614,7 @@ public class ModConfig
BlockDecorSolarPanel.BTileEntity.on_config(COMMON.small_solar_panel_peak_production.get());
BlockDecorBreaker.BTileEntity.on_config(COMMON.block_breaker_power_consumption.get(), COMMON.block_breaker_reluctance.get(), COMMON.block_breaker_min_breaking_time.get(), COMMON.block_breaker_requires_power.get());
BlockDecorTreeCutter.BTileEntity.on_config(COMMON.tree_cuttter_energy_consumption.get(), COMMON.tree_cuttter_cutting_time_needed.get(), COMMON.tree_cuttter_requires_power.get());
BlockDecorMilker.BTileEntity.on_config(COMMON.milking_machine_energy_consumption.get());
BlockDecorMilker.BTileEntity.on_config(COMMON.milking_machine_energy_consumption.get(), COMMON.milking_machine_milking_delay.get());
without_crafting_table = isOptedOut(ModContent.TREATED_WOOD_CRAFTING_TABLE);
immersiveengineering_installed = ModAuxiliaries.isModLoaded("immersiveengineering");
with_experimental_features_ = COMMON.with_experimental.get();

View file

@ -101,8 +101,8 @@
"block.engineersdecor.clinker_brick_stairs.help": "§6По цвету выглядят немного темнее и интенсивнее, чем Кирпичный блок",
"block.engineersdecor.clinker_brick_stained_stairs": "Грязные кирпичные ступеньки",
"block.engineersdecor.clinker_brick_stained_stairs.help": "§6Выглядят немного темнее и интенсивнее, чем Кирпичный блок. Имеют более заметные следы грязи или пятен.",
"block.engineersdecor.slag_brick_stairs": "Клинкерные кирпичные ступеньки",
"block.engineersdecor.slag_brick_stairs.help": "§6По цвету выглядят немного темнее и интенсивнее, чем Кирпичный блок.",
"block.engineersdecor.slag_brick_stairs": "Кирпичные ступеньки из шлакоблока",
"block.engineersdecor.slag_brick_stairs.help": "§6Серо-коричневые кирпичные ступеньки.",
"block.engineersdecor.rebar_concrete_stairs": "Железобетонные ступеньки",
"block.engineersdecor.rebar_concrete_stairs.help": "§6Железобетонные ступеньки.§r Дорогие, но взрывоустойчивые, как обсидиан.",
"block.engineersdecor.rebar_concrete_tile_stairs": "Ступеньки из железобетонной плитки",
@ -134,7 +134,7 @@
"block.engineersdecor.treated_wood_stool": "Табурет из обработанного дерева",
"block.engineersdecor.treated_wood_stool.help": "§6Крепкий деревянный табурет.§r Для использования в помещении и на улице.",
"block.engineersdecor.treated_wood_crafting_table": "Верстак из обработанного дерева",
"block.engineersdecor.treated_wood_crafting_table.help": "§6Прочный и устойчивый к погодным условиям. Восемь слотов для хранения. Хранит инвентарь. Нажимайте кнопки со стрелками вверх/вниз для выбора из истории, выходной слот для размещения предметов, X-кнопка очистить сетку крафта и историю. Shift-клик по стеку: передача стека от игрока в хранилище при создании если сетка пуста, в противном случае перенос от игрока в сетку. Автоматически распределяет кликаемый стек.",
"block.engineersdecor.treated_wood_crafting_table.help": "§6Прочный и устойчивый к погодным условиям. Восемь слотов для хранения. Хранит инвентарь. Нажимайте кнопки со стрелками вверх/вниз для выбора из истории, выходной слот для размещения предметов, X-кнопка очистить сетку крафта и историю. Shift-клик по стеку: передача стека от игрока в хранилище при создании если сетка пуста, в противном случае перенос от игрока в сетку. Автоматически распределяет кликаемый стек. Shift-Ctrl-клик по стаку: перемещает одинаковые стаки. Колёсико мыши: добавляет/отнимает предметы в сетке.",
"block.engineersdecor.treated_wood_side_table": "Столик из обработанного дерева",
"block.engineersdecor.treated_wood_side_table.help": "§6Нужен после того, как работа выполнена.",
"block.engineersdecor.iron_inset_light": "Встраиваемый осветитель",
@ -217,4 +217,4 @@
"block.engineersdecor.halfslab_sheetmetal_aluminum.help": "§6Вертикально наращиваемая часть.§rПравый/левый щелчок со стеком частей на верхней или нижней поверхности для добавления/удаления частей.",
"block.engineersdecor.testblock": "ED тестовый блок (НЕ ИСПОЛЬЗОВАТЬ)",
"block.engineersdecor.testblock.help": "§6Несоздаваемый тестовый блок с изменениемизменяющейся экспериментальной функциональностью. НЕ ИСПОЛЬЗОВАТЬ, может даже крашнуть игру в худшем случае!!"
}
}

View file

@ -1,14 +1,15 @@
{
"homepage": "https://www.curseforge.com/minecraft/mc-mods/engineers-decor/",
"promos": {
"1.12.2-recommended": "1.0.17",
"1.12.2-latest": "1.0.18-b2",
"1.12.2-recommended": "1.0.18",
"1.12.2-latest": "1.0.18",
"1.14.4-recommended": "",
"1.14.4-latest": "1.0.18-b3",
"1.14.4-latest": "1.0.18-b4",
"1.15.1-recommended": "",
"1.15.1-latest": "1.0.18-b3"
"1.15.1-latest": "1.0.18-b4"
},
"1.12.2": {
"1.0.18": "[R] Release based on v1.0.18-b2. Release-to-release changes: * Tree cutter config fixes. * Treated Wood Crafting Table mouse tweaks. * Lang updates.\n[M] Lang update ru_ru (PR#77, thanks Smollet777).",
"1.0.18-b2": "[A] Added Treated Wood Crafting table tweaks (ctrl-shift moves all same stacks from the inventory, mouse wheel over crafting slot increases/decreases crafting grid stacks).\n[F] EN Lang file fixed (issue #76, thx Riverstar907).\n[F] Fixed Tree Cutter not respecting power-required config (thx federsavo, issue #77).",
"1.0.18-b1": "[M] Lang ru_ru updated (Smollet777).",
"1.0.17": "[R] Release based on v1.0.17-b3. Release-to-release changes: * Milking machine added. * Reverse recipes for slab slices added. * Texture and model improvements. * Lang file updates. * Minor bug fixes. * Config options added.\n[M] Updated zh_cn lang file (scikirbypoke).\n[A] Added opt-out config for the Small Tree Cutter.",
@ -83,6 +84,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."
},
"1.14.4": {
"1.0.18-b4": "[M] Lang update ru_ru (PR#77, thanks Smollet777).\n[F] Fixed Milking machine cow path issue, added milking delay cow tracking.\n[F] Slab / Slab Slice placement adapted to vanilla standard.",
"1.0.18-b3": "[A] Added Treated Wood Crafting table tweaks (ctrl-shift moves all same stacks from the inventory, mouse wheel over crafting slot increases/decreases crafting grid stacks).\n[F] EN Lang file fixed (issue #76, thx Riverstar907).\n[F] Fixed Tree Cutter not respecting power-required config (thx federsavo, issue #77).\n[F] Fixed Small Solar Panel not exposing energy capability (thx MatthiasMann, issue #78).",
"1.0.18-b2": "[F] Fixed JEI integration warning if nothing is opt'ed out (thx @SDUBZ for reporting).\n[M] Lang ru_ru updated (Smollet777).",
"1.0.18-b1": "[U] Updated to Forge 1.14.4-28.1.109/20190719-1.14.3.\n[A] Added opt-out config for the Small Tree Cutter.",
@ -124,6 +126,7 @@
"1.0.7-b3": "[A] Initial 1.14.2 port of decorative blocks."
},
"1.15.1": {
"1.0.18-b4": "[A] Ported Treated Wood Crafting Table item rendering.\n[F] Fixed Milking machine cow path issue, added milking delay cow tracking.\n[F] Slab / Slab Slice placement adapted to vanilla standard.\n[M] Lang update ru_ru (PR#77, thanks Smollet777).",
"1.0.18-b3": "[A] Added Treated Wood Crafting Table tweaks (ctrl-shift moves all same stacks from the inventory, mouse wheel over crafting slot increases/decreases crafting grid stacks).\n[F] EN Lang file fixed (issue #76, thx Riverstar907).\n[F] Fixed Tree Cutter not respecting power-required config (thx federsavo, issue #77).\n[F] Fixed Small Solar Panel not exposing energy capability (thx MatthiasMann, issue #78).",
"1.0.18-b2": "[M] Lang ru_ru updated (Smollet777).",
"1.0.18-b1": "[U] Updated to Forge 1.15.1-30.0.16/20190719-1.14.3.\n[F] Client setup Dist annotation fixed (issue #73, thx hitsu420).\n[F] Double newline escapes in lang files fixed (\"\\n\" in a tooltip).\n[M] Updated zh_cn lang file (scikirbypoke).\n[A] Added opt-out config for the Small Tree Cutter",